aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG107
-rw-r--r--Zotlabs/Lib/Activity.php20
-rw-r--r--Zotlabs/Lib/Enotify.php5
-rw-r--r--Zotlabs/Lib/JcsEddsa2022.php25
-rw-r--r--Zotlabs/Lib/JcsEddsa2022SignException.php15
-rw-r--r--Zotlabs/Lib/Keyutils.php82
-rw-r--r--Zotlabs/Lib/MessageFilter.php4
-rw-r--r--Zotlabs/Lib/ThreadItem.php66
-rw-r--r--Zotlabs/Lib/ThreadStream.php2
-rw-r--r--Zotlabs/Module/Channel.php19
-rw-r--r--Zotlabs/Module/Display.php17
-rw-r--r--Zotlabs/Module/Home.php10
-rw-r--r--Zotlabs/Module/Hq.php4
-rw-r--r--Zotlabs/Module/Id.php6
-rw-r--r--Zotlabs/Module/Item.php40
-rw-r--r--Zotlabs/Module/Like.php16
-rw-r--r--Zotlabs/Module/Login.php11
-rw-r--r--Zotlabs/Module/Network.php6
-rw-r--r--Zotlabs/Module/Pubstream.php7
-rw-r--r--Zotlabs/Module/Request.php21
-rw-r--r--Zotlabs/Module/Rpost.php2
-rw-r--r--Zotlabs/Module/Xref.php26
-rw-r--r--Zotlabs/Render/Theme.php9
-rw-r--r--Zotlabs/Web/HTTPSig.php58
-rw-r--r--Zotlabs/Web/WebServer.php2
-rw-r--r--Zotlabs/Widget/Album.php3
-rw-r--r--Zotlabs/Widget/Messages.php6
-rw-r--r--Zotlabs/Widget/Notifications.php8
-rw-r--r--Zotlabs/Widget/Pinned.php4
-rw-r--r--Zotlabs/Widget/Portfolio.php4
-rw-r--r--Zotlabs/Zot6/Zot6Handler.php2
-rw-r--r--boot.php31
-rw-r--r--composer.json5
-rw-r--r--composer.lock411
-rw-r--r--doc/de/SUMMARY.md142
-rw-r--r--doc/de/about.md88
-rw-r--r--doc/de/about/about.bb23
-rw-r--r--doc/de/about_hub.bb7
-rw-r--r--doc/de/adminmanual/CLI_tools.md103
-rw-r--r--doc/de/adminmanual/Creating-Templates.md79
-rw-r--r--doc/de/adminmanual/Installation_using_docker.md56
-rw-r--r--doc/de/adminmanual/Nomad---A-High-Level-Overview.md111
-rw-r--r--doc/de/adminmanual/Primary-Directory.md35
-rw-r--r--doc/de/adminmanual/Schema-development.md45
-rw-r--r--doc/de/adminmanual/Widgets.md99
-rw-r--r--doc/de/adminmanual/Zot---A-High-Level-Overview.md111
-rw-r--r--doc/de/adminmanual/administration.md13
-rw-r--r--doc/de/adminmanual/advanced_configurations.md326
-rw-r--r--doc/de/adminmanual/automated_installation.md26
-rw-r--r--doc/de/adminmanual/before_you_start.md17
-rw-r--r--doc/de/adminmanual/channel_directory.md15
-rw-r--r--doc/de/adminmanual/database.md74
-rw-r--r--doc/de/adminmanual/directories.md87
-rw-r--r--doc/de/adminmanual/faq_admins.md89
-rw-r--r--doc/de/adminmanual/federation_addons.md8
-rw-r--r--doc/de/adminmanual/filesync.md42
-rw-r--r--doc/de/adminmanual/further_help.md3
-rw-r--r--doc/de/adminmanual/hub_snapshot_tools.md127
-rw-r--r--doc/de/adminmanual/installation.md8
-rw-r--r--doc/de/adminmanual/installation_requirements.md14
-rw-r--r--doc/de/adminmanual/manual_for_administrators.md30
-rw-r--r--doc/de/adminmanual/manual_installation.md60
-rw-r--r--doc/de/adminmanual/pic/adm01.pngbin0 -> 371226 bytes
-rw-r--r--doc/de/adminmanual/problems-following-an-update.md31
-rw-r--r--doc/de/adminmanual/recommended_addons.md7
-rw-r--r--doc/de/adminmanual/service_classes.md57
-rw-r--r--doc/de/adminmanual/theme_management.md7
-rw-r--r--doc/de/adminmanual/troubleshooting.md51
-rw-r--r--doc/de/admins.bb10
-rw-r--r--doc/de/basicconcepts.md67
-rw-r--r--doc/de/bugs.bb34
-rw-r--r--doc/de/channels.bb26
-rw-r--r--doc/de/credits.md91
-rw-r--r--doc/de/database/db_abook.bb55
-rw-r--r--doc/de/database/db_account.bb66
-rw-r--r--doc/de/database/db_addon.bb24
-rw-r--r--doc/de/database/db_app.bb48
-rw-r--r--doc/de/database/db_attach.bb54
-rw-r--r--doc/de/database/db_auth_codes.bb19
-rw-r--r--doc/de/database/db_cache.bb15
-rw-r--r--doc/de/database/db_channel.bb104
-rw-r--r--doc/de/database/db_chat.bb16
-rw-r--r--doc/de/database/db_chatpresence.bb18
-rw-r--r--doc/de/database/db_chatroom.bb28
-rw-r--r--doc/de/database/db_clients.bb18
-rw-r--r--doc/de/database/db_config.bb14
-rw-r--r--doc/de/database/db_conv.bb25
-rw-r--r--doc/de/database/db_event.bb64
-rw-r--r--doc/de/database/db_fcontact.bb38
-rw-r--r--doc/de/database/db_ffinder.bb14
-rw-r--r--doc/de/database/db_fserver.bb14
-rw-r--r--doc/de/database/db_fsuggest.bb24
-rw-r--r--doc/de/database/db_hook.bb18
-rw-r--r--doc/de/database/db_hubloc.bb38
-rw-r--r--doc/de/database/db_issue.bb20
-rw-r--r--doc/de/database/db_item.bb151
-rw-r--r--doc/de/database/db_item_id.bb16
-rw-r--r--doc/de/database/db_likes.bb24
-rw-r--r--doc/de/database/db_mail.bb34
-rw-r--r--doc/de/database/db_menu.bb16
-rw-r--r--doc/de/database/db_menu_item.bb28
-rw-r--r--doc/de/database/db_notify.bb36
-rw-r--r--doc/de/database/db_obj.bb26
-rw-r--r--doc/de/database/db_outq.bb28
-rw-r--r--doc/de/database/db_pconfig.bb16
-rw-r--r--doc/de/database/db_pgrp.bb18
-rw-r--r--doc/de/database/db_pgrp_member.bb14
-rw-r--r--doc/de/database/db_photo.bb52
-rw-r--r--doc/de/database/db_poll.bb16
-rw-r--r--doc/de/database/db_poll_elm.bb16
-rw-r--r--doc/de/database/db_profdef.bb18
-rw-r--r--doc/de/database/db_profext.bb16
-rw-r--r--doc/de/database/db_profile.bb94
-rw-r--r--doc/de/database/db_profile_check.bb18
-rw-r--r--doc/de/database/db_register.bb18
-rw-r--r--doc/de/database/db_session.bb14
-rw-r--r--doc/de/database/db_shares.bb14
-rw-r--r--doc/de/database/db_sign.bb18
-rw-r--r--doc/de/database/db_site.bb28
-rw-r--r--doc/de/database/db_source.bb16
-rw-r--r--doc/de/database/db_spam.bb18
-rw-r--r--doc/de/database/db_sys_perms.bb16
-rw-r--r--doc/de/database/db_term.bb28
-rw-r--r--doc/de/database/db_tokens.bb18
-rw-r--r--doc/de/database/db_updates.bb20
-rw-r--r--doc/de/database/db_verify.bb18
-rw-r--r--doc/de/database/db_vote.bb16
-rw-r--r--doc/de/database/db_xchan.bb59
-rw-r--r--doc/de/database/db_xchat.bb16
-rw-r--r--doc/de/database/db_xconfig.bb16
-rw-r--r--doc/de/database/db_xign.bb13
-rw-r--r--doc/de/database/db_xlink.bb22
-rw-r--r--doc/de/database/db_xprof.bb37
-rw-r--r--doc/de/database/db_xtag.bb14
-rw-r--r--doc/de/develop.bb30
-rw-r--r--doc/de/developer/API.md787
-rw-r--r--doc/de/developer/Plugins.md251
-rw-r--r--doc/de/developer/attribution.md3
-rw-r--r--doc/de/developer/code_of_conduct.md8
-rw-r--r--doc/de/developer/coding_style.md12
-rw-r--r--doc/de/developer/dev-function-overview.md43
-rw-r--r--doc/de/developer/dev_beginner.md460
-rw-r--r--doc/de/developer/developers_guide.md27
-rw-r--r--doc/de/developer/doco.md29
-rw-r--r--doc/de/developer/enforcement.md5
-rw-r--r--doc/de/developer/federate.md56
-rw-r--r--doc/de/developer/file_system_layout.md20
-rw-r--r--doc/de/developer/git_for_non_developers.md72
-rw-r--r--doc/de/developer/git_repository.md6
-rw-r--r--doc/de/developer/hooks.md698
-rw-r--r--doc/de/developer/licensing.md3
-rw-r--r--doc/de/developer/magic_auth.md49
-rw-r--r--doc/de/developer/nomad_protocol.md47
-rw-r--r--doc/de/developer/nomad_structures.md141
-rw-r--r--doc/de/developer/our_pledge.md3
-rw-r--r--doc/de/developer/our_responsibilities.md3
-rw-r--r--doc/de/developer/our_standards.md24
-rw-r--r--doc/de/developer/scope.md3
-rw-r--r--doc/de/developer/technical_introduction.md309
-rw-r--r--doc/de/developer/testing.md131
-rw-r--r--doc/de/developer/tools_workflows.md5
-rw-r--r--doc/de/developer/translations.md57
-rw-r--r--doc/de/developer/unorganized.md56
-rw-r--r--doc/de/developer/versions.md23
-rw-r--r--doc/de/developer/who_is_a_hubzilla_developer.md14
-rw-r--r--doc/de/developer/zot_protocol.md47
-rw-r--r--doc/de/developer/zot_structures.md141
-rw-r--r--doc/de/features.bb203
-rw-r--r--doc/de/functions.md229
-rw-r--r--doc/de/general.bb17
-rw-r--r--doc/de/glossary.md31
-rw-r--r--doc/de/hook/about_hook.bb1
-rw-r--r--doc/de/hook/accept_follow.bb1
-rw-r--r--doc/de/hook/account_downgrade.bb1
-rw-r--r--doc/de/hook/account_settings.bb1
-rw-r--r--doc/de/hook/account_settings_post.bb1
-rw-r--r--doc/de/hook/activity_decode_mapper.bb1
-rw-r--r--doc/de/hook/activity_filter.bb1
-rw-r--r--doc/de/hook/activity_mapper.bb1
-rw-r--r--doc/de/hook/activity_obj_decode_mapper.bb1
-rw-r--r--doc/de/hook/activity_obj_mapper.bb1
-rw-r--r--doc/de/hook/activity_order.bb1
-rw-r--r--doc/de/hook/activity_received.bb1
-rw-r--r--doc/de/hook/addon_app_installed_filter.bb18
-rw-r--r--doc/de/hook/affinity_labels.bb1
-rw-r--r--doc/de/hook/api_perm_is_allowed.bb1
-rw-r--r--doc/de/hook/app_destroy.bb4
-rw-r--r--doc/de/hook/app_installed_filter.bb17
-rw-r--r--doc/de/hook/atom_author.bb1
-rw-r--r--doc/de/hook/atom_entry.bb1
-rw-r--r--doc/de/hook/atom_feed.bb1
-rw-r--r--doc/de/hook/atom_feed_end.bb1
-rw-r--r--doc/de/hook/attach_delete.bb11
-rw-r--r--doc/de/hook/attach_upload_file.bb1
-rw-r--r--doc/de/hook/authenticate.bb29
-rw-r--r--doc/de/hook/author_is_pmable.bb14
-rw-r--r--doc/de/hook/bb2diaspora.bb1
-rw-r--r--doc/de/hook/bbcode.bb6
-rw-r--r--doc/de/hook/bbcode_filter.bb7
-rw-r--r--doc/de/hook/build_pagehead.bb2
-rw-r--r--doc/de/hook/can_comment_on_post.bb13
-rw-r--r--doc/de/hook/change_channel.bb11
-rw-r--r--doc/de/hook/channel_links.bb12
-rw-r--r--doc/de/hook/channel_remove.bb1
-rw-r--r--doc/de/hook/chat_message.bb1
-rw-r--r--doc/de/hook/chat_post.bb1
-rw-r--r--doc/de/hook/check_account_email.bb1
-rw-r--r--doc/de/hook/check_account_invite.bb1
-rw-r--r--doc/de/hook/check_account_password.bb17
-rw-r--r--doc/de/hook/check_channelallowed.bb11
-rw-r--r--doc/de/hook/check_siteallowed.bb10
-rw-r--r--doc/de/hook/collect_public_recipients.bb42
-rw-r--r--doc/de/hook/comments_are_now_closed.bb11
-rw-r--r--doc/de/hook/connect_premium.bb1
-rw-r--r--doc/de/hook/connection_remove.bb9
-rw-r--r--doc/de/hook/connector_settings.bb1
-rw-r--r--doc/de/hook/construct_page.bb1
-rw-r--r--doc/de/hook/contact_block_end.bb1
-rw-r--r--doc/de/hook/contact_edit.bb1
-rw-r--r--doc/de/hook/contact_edit_post.bb1
-rw-r--r--doc/de/hook/contact_select_options.bb1
-rw-r--r--doc/de/hook/content_security_policy.bb39
-rw-r--r--doc/de/hook/conversation_start.bb1
-rw-r--r--doc/de/hook/create_identity.bb1
-rw-r--r--doc/de/hook/cron.bb5
-rw-r--r--doc/de/hook/cron_daily.bb3
-rw-r--r--doc/de/hook/cron_weekly.bb3
-rw-r--r--doc/de/hook/crypto_methods.bb5
-rw-r--r--doc/de/hook/daemon_addon.bb15
-rw-r--r--doc/de/hook/daemon_master_release.bb5
-rw-r--r--doc/de/hook/directory_item.bb1
-rw-r--r--doc/de/hook/discover_channel_webfinger.bb14
-rw-r--r--doc/de/hook/display_item.bb1
-rw-r--r--doc/de/hook/display_settings.bb1
-rw-r--r--doc/de/hook/display_settings_post.bb1
-rw-r--r--doc/de/hook/donate_contributors.bb1
-rw-r--r--doc/de/hook/donate_plugin.bb1
-rw-r--r--doc/de/hook/donate_sponsors.bb1
-rw-r--r--doc/de/hook/dreport_is_storable.bb1
-rw-r--r--doc/de/hook/dreport_process.bb7
-rw-r--r--doc/de/hook/drop_item.bb1
-rw-r--r--doc/de/hook/dropdown_extras.bb17
-rw-r--r--doc/de/hook/encode_object.bb1
-rw-r--r--doc/de/hook/enotify.bb1
-rw-r--r--doc/de/hook/enotify_mail.bb1
-rw-r--r--doc/de/hook/enotify_store.bb1
-rw-r--r--doc/de/hook/event_created.bb1
-rw-r--r--doc/de/hook/event_store_event.bb11
-rw-r--r--doc/de/hook/event_updated.bb1
-rw-r--r--doc/de/hook/externals_url_select.bb1
-rw-r--r--doc/de/hook/feature_enabled.bb1
-rw-r--r--doc/de/hook/feature_settings.bb1
-rw-r--r--doc/de/hook/feature_settings_post.bb1
-rw-r--r--doc/de/hook/fetch_and_store.bb1
-rw-r--r--doc/de/hook/follow.bb1
-rw-r--r--doc/de/hook/follow_allow.bb1
-rw-r--r--doc/de/hook/gender_selector.bb1
-rw-r--r--doc/de/hook/gender_selector_min.bb1
-rw-r--r--doc/de/hook/generate_map.bb1
-rw-r--r--doc/de/hook/generate_named_map.bb1
-rw-r--r--doc/de/hook/get_all_api_perms.bb1
-rw-r--r--doc/de/hook/get_all_perms.bb1
-rw-r--r--doc/de/hook/get_default_export_sections10
-rw-r--r--doc/de/hook/get_features.bb1
-rw-r--r--doc/de/hook/get_photo.bb14
-rw-r--r--doc/de/hook/get_profile_photo.bb18
-rw-r--r--doc/de/hook/get_role_perms.bb1
-rw-r--r--doc/de/hook/global_permissions.bb1
-rw-r--r--doc/de/hook/home_content.bb1
-rw-r--r--doc/de/hook/home_init.bb1
-rw-r--r--doc/de/hook/hostxrd.bb1
-rw-r--r--doc/de/hook/html2bbcode.bb1
-rw-r--r--doc/de/hook/identity_basic_export.bb10
-rw-r--r--doc/de/hook/import_author_xchan.bb1
-rw-r--r--doc/de/hook/import_channel.bb1
-rw-r--r--doc/de/hook/import_directory_profile.bb1
-rw-r--r--doc/de/hook/import_xchan.bb1
-rw-r--r--doc/de/hook/item_custom.bb24
-rw-r--r--doc/de/hook/item_photo_menu.bb1
-rw-r--r--doc/de/hook/item_store.bb1
-rw-r--r--doc/de/hook/item_store_update.bb1
-rw-r--r--doc/de/hook/item_stored.bb18
-rw-r--r--doc/de/hook/item_stored_update.bb15
-rw-r--r--doc/de/hook/item_translate.bb1
-rw-r--r--doc/de/hook/jot_header_tpl_filter.bb5
-rw-r--r--doc/de/hook/jot_networks.bb1
-rw-r--r--doc/de/hook/jot_tool.bb1
-rw-r--r--doc/de/hook/jot_tpl_filter.bb5
-rw-r--r--doc/de/hook/legal_webbie.bb10
-rw-r--r--doc/de/hook/legal_webbie_text.bb7
-rw-r--r--doc/de/hook/load_pdl.bb1
-rw-r--r--doc/de/hook/local_dir_update.bb1
-rw-r--r--doc/de/hook/logged_in.bb1
-rw-r--r--doc/de/hook/logger.bb16
-rw-r--r--doc/de/hook/logging_out.bb1
-rw-r--r--doc/de/hook/login_hook.bb1
-rw-r--r--doc/de/hook/magic_auth.bb1
-rw-r--r--doc/de/hook/magic_auth_openid_success.bb1
-rw-r--r--doc/de/hook/magic_auth_success.bb1
-rw-r--r--doc/de/hook/main_slider.bb1
-rw-r--r--doc/de/hook/marital_selector.bb1
-rw-r--r--doc/de/hook/marital_selector_min.bb1
-rw-r--r--doc/de/hook/markdown_to_bb.bb5
-rw-r--r--doc/de/hook/module_loaded.bb1
-rw-r--r--doc/de/hook/module_mod_aftercontent.bb12
-rw-r--r--doc/de/hook/module_mod_content.bb10
-rw-r--r--doc/de/hook/module_mod_init.bb1
-rw-r--r--doc/de/hook/module_mod_post.bb1
-rw-r--r--doc/de/hook/mood_verbs.bb1
-rw-r--r--doc/de/hook/nav.bb28
-rw-r--r--doc/de/hook/network_content_init.bb1
-rw-r--r--doc/de/hook/network_ping.bb1
-rw-r--r--doc/de/hook/network_to_name.bb1
-rw-r--r--doc/de/hook/notifier_end.bb1
-rw-r--r--doc/de/hook/notifier_hub.bb1
-rw-r--r--doc/de/hook/notifier_normal.bb1
-rw-r--r--doc/de/hook/obj_verbs.bb1
-rw-r--r--doc/de/hook/oembed_probe.bb1
-rw-r--r--doc/de/hook/other_encapsulate.bb7
-rw-r--r--doc/de/hook/other_unencapsulate.bb5
-rw-r--r--doc/de/hook/page_content_top.bb1
-rw-r--r--doc/de/hook/page_end.bb1
-rw-r--r--doc/de/hook/page_header.bb1
-rw-r--r--doc/de/hook/page_meta.bb13
-rw-r--r--doc/de/hook/parse_atom.bb1
-rw-r--r--doc/de/hook/parse_link.bb1
-rw-r--r--doc/de/hook/pdl_selector.bb1
-rw-r--r--doc/de/hook/perm_is_allowed.bb1
-rw-r--r--doc/de/hook/permissions_create.bb1
-rw-r--r--doc/de/hook/permissions_update.bb1
-rw-r--r--doc/de/hook/permit_hook.bb (renamed from doc/de/about/.gitkeep)0
-rw-r--r--doc/de/hook/personal_xrd.bb1
-rw-r--r--doc/de/hook/photo_post_end.bb1
-rw-r--r--doc/de/hook/photo_upload_begin.bb1
-rw-r--r--doc/de/hook/photo_upload_end.bb1
-rw-r--r--doc/de/hook/photo_upload_file.bb1
-rw-r--r--doc/de/hook/photo_upload_form.bb1
-rw-r--r--doc/de/hook/photo_view_filter.bb1
-rw-r--r--doc/de/hook/poke_verbs.bb1
-rw-r--r--doc/de/hook/post_local.bb1
-rw-r--r--doc/de/hook/post_local_end.bb1
-rw-r--r--doc/de/hook/post_local_start.bb1
-rw-r--r--doc/de/hook/post_mail.bb1
-rw-r--r--doc/de/hook/post_mail_end.bb1
-rw-r--r--doc/de/hook/post_remote.bb1
-rw-r--r--doc/de/hook/post_remote_end.bb1
-rw-r--r--doc/de/hook/post_remote_update.bb1
-rw-r--r--doc/de/hook/post_remote_update_end.bb1
-rw-r--r--doc/de/hook/prepare_body.bb1
-rw-r--r--doc/de/hook/prepare_body_final.bb1
-rw-r--r--doc/de/hook/prepare_body_init.bb1
-rw-r--r--doc/de/hook/privacygroup_extras.bb12
-rw-r--r--doc/de/hook/privacygroup_extras_drop.bb11
-rw-r--r--doc/de/hook/privacygroup_extras_post.bb11
-rw-r--r--doc/de/hook/proc_run.bb1
-rw-r--r--doc/de/hook/process_channel_sync_delivery.bb1
-rw-r--r--doc/de/hook/profile_advanced.bb1
-rw-r--r--doc/de/hook/profile_edit.bb1
-rw-r--r--doc/de/hook/profile_photo_content_end.bb1
-rw-r--r--doc/de/hook/profile_post.bb1
-rw-r--r--doc/de/hook/profile_sidebar.bb1
-rw-r--r--doc/de/hook/profile_sidebar_enter.bb1
-rw-r--r--doc/de/hook/register_account.bb1
-rw-r--r--doc/de/hook/render_location.bb1
-rw-r--r--doc/de/hook/replace_macros.bb1
-rw-r--r--doc/de/hook/reverse_magic_auth.bb1
-rw-r--r--doc/de/hook/settings_form.bb1
-rw-r--r--doc/de/hook/settings_post.bb1
-rw-r--r--doc/de/hook/sexpref_selector.bb1
-rw-r--r--doc/de/hook/sexpref_selector_min.bb1
-rw-r--r--doc/de/hook/smilie.bb19
-rw-r--r--doc/de/hook/status_editor.bb31
-rw-r--r--doc/de/hook/stream_item.bb13
-rw-r--r--doc/de/hook/system_app_installed_filter.bb18
-rw-r--r--doc/de/hook/tagged.bb16
-rw-r--r--doc/de/hook/update_unseen.bb9
-rw-r--r--doc/de/hook/validate_channelname.bb23
-rw-r--r--doc/de/hook/webfinger.bb1
-rw-r--r--doc/de/hook/well_known.bb1
-rw-r--r--doc/de/hook/wiki_preprocess.bb11
-rw-r--r--doc/de/hook/zid.bb1
-rw-r--r--doc/de/hook/zid_init.bb1
-rw-r--r--doc/de/hook/zot_best_algorithm.bb3
-rw-r--r--doc/de/hook/zot_finger.bb1
-rw-r--r--doc/de/hooks.html1
-rw-r--r--doc/de/main.bb11
-rw-r--r--doc/de/member/AdvancedSearch.md30
-rw-r--r--doc/de/member/NSFW.md21
-rw-r--r--doc/de/member/account_settings.md5
-rw-r--r--doc/de/member/accounts_profiles_channels_basics.md30
-rw-r--r--doc/de/member/additional_features.md31
-rw-r--r--doc/de/member/addressbook.md23
-rw-r--r--doc/de/member/apps.md59
-rw-r--r--doc/de/member/article.md27
-rw-r--r--doc/de/member/bbcode.md79
-rw-r--r--doc/de/member/blocking_channels.md19
-rw-r--r--doc/de/member/bookmarks.md27
-rw-r--r--doc/de/member/calendar.md27
-rw-r--r--doc/de/member/channel_locations.md7
-rw-r--r--doc/de/member/channel_roles.md41
-rw-r--r--doc/de/member/channel_settings.md15
-rw-r--r--doc/de/member/channels.md22
-rw-r--r--doc/de/member/chat_rooms.md17
-rw-r--r--doc/de/member/clone.md53
-rw-r--r--doc/de/member/cloud_storage.md10
-rw-r--r--doc/de/member/comanche.md278
-rw-r--r--doc/de/member/commenting.md9
-rw-r--r--doc/de/member/connecting_with_channels.md33
-rw-r--r--doc/de/member/connection_editor.md32
-rw-r--r--doc/de/member/connections.md24
-rw-r--r--doc/de/member/content_filters.md141
-rw-r--r--doc/de/member/conversation_features.md9
-rw-r--r--doc/de/member/create_channels.md9
-rw-r--r--doc/de/member/delete.md3
-rw-r--r--doc/de/member/delete_account.md8
-rw-r--r--doc/de/member/deleting_channel.md9
-rw-r--r--doc/de/member/diaspora_compat.md33
-rw-r--r--doc/de/member/direct_messages.md9
-rw-r--r--doc/de/member/directory.md15
-rw-r--r--doc/de/member/display_settings.md19
-rw-r--r--doc/de/member/encryption.md15
-rw-r--r--doc/de/member/files.md31
-rw-r--r--doc/de/member/follow_conversation.md3
-rw-r--r--doc/de/member/gallery.md7
-rw-r--r--doc/de/member/guest_access.md15
-rw-r--r--doc/de/member/important_apps.md17
-rw-r--r--doc/de/member/insert_images.md53
-rw-r--r--doc/de/member/interact.md25
-rw-r--r--doc/de/member/link_to_source.md3
-rw-r--r--doc/de/member/mentions.md9
-rw-r--r--doc/de/member/overview.md3
-rw-r--r--doc/de/member/permissions.md7
-rw-r--r--doc/de/member/permissions_channel_roles.md45
-rw-r--r--doc/de/member/permissions_contact_roles.md17
-rw-r--r--doc/de/member/permissions_content.md38
-rw-r--r--doc/de/member/photos.md17
-rw-r--r--doc/de/member/pic/anzeinst01.pngbin0 -> 158390 bytes
-rw-r--r--doc/de/member/pic/anzeinst02.pngbin0 -> 70507 bytes
-rw-r--r--doc/de/member/pic/anzeinst03.pngbin0 -> 87707 bytes
-rw-r--r--doc/de/member/pic/anzeinst04.pngbin0 -> 100155 bytes
-rw-r--r--doc/de/member/pic/apps01.pngbin0 -> 112759 bytes
-rw-r--r--doc/de/member/pic/apps02.pngbin0 -> 116905 bytes
-rw-r--r--doc/de/member/pic/apps03.pngbin0 -> 4966 bytes
-rw-r--r--doc/de/member/pic/apps04.pngbin0 -> 2084 bytes
-rw-r--r--doc/de/member/pic/apps05.pngbin0 -> 5269 bytes
-rw-r--r--doc/de/member/pic/apps06.pngbin0 -> 2558 bytes
-rw-r--r--doc/de/member/pic/apps07.pngbin0 -> 23964 bytes
-rw-r--r--doc/de/member/pic/apps08.pngbin0 -> 1135 bytes
-rw-r--r--doc/de/member/pic/artikel01.pngbin0 -> 26149 bytes
-rw-r--r--doc/de/member/pic/artikel02.pngbin0 -> 52995 bytes
-rw-r--r--doc/de/member/pic/artikel03.pngbin0 -> 29633 bytes
-rw-r--r--doc/de/member/pic/artikel04.pngbin0 -> 66553 bytes
-rw-r--r--doc/de/member/pic/artikel05.pngbin0 -> 7776 bytes
-rw-r--r--doc/de/member/pic/author.pngbin0 -> 5688 bytes
-rw-r--r--doc/de/member/pic/bild01.pngbin0 -> 25262 bytes
-rw-r--r--doc/de/member/pic/bild02.pngbin0 -> 24360 bytes
-rw-r--r--doc/de/member/pic/bild03.pngbin0 -> 48384 bytes
-rw-r--r--doc/de/member/pic/bild04.pngbin0 -> 41497 bytes
-rw-r--r--doc/de/member/pic/bild05.pngbin0 -> 9779 bytes
-rw-r--r--doc/de/member/pic/bild06.pngbin0 -> 42816 bytes
-rw-r--r--doc/de/member/pic/bookm01.pngbin0 -> 8889 bytes
-rw-r--r--doc/de/member/pic/bookm02.pngbin0 -> 5323 bytes
-rw-r--r--doc/de/member/pic/bookm03.pngbin0 -> 19880 bytes
-rw-r--r--doc/de/member/pic/carddav01.pngbin0 -> 54753 bytes
-rw-r--r--doc/de/member/pic/carddav02.pngbin0 -> 31096 bytes
-rw-r--r--doc/de/member/pic/carddav03.pngbin0 -> 39179 bytes
-rw-r--r--doc/de/member/pic/carddav04.pngbin0 -> 36998 bytes
-rw-r--r--doc/de/member/pic/carddav05.pngbin0 -> 75320 bytes
-rw-r--r--doc/de/member/pic/carddav06.pngbin0 -> 13325 bytes
-rw-r--r--doc/de/member/pic/carddav07.pngbin0 -> 19086 bytes
-rw-r--r--doc/de/member/pic/center.pngbin0 -> 3096 bytes
-rw-r--r--doc/de/member/pic/chat01.pngbin0 -> 26833 bytes
-rw-r--r--doc/de/member/pic/chat02.pngbin0 -> 40254 bytes
-rw-r--r--doc/de/member/pic/chat03.pngbin0 -> 38345 bytes
-rw-r--r--doc/de/member/pic/code.pngbin0 -> 9206 bytes
-rw-r--r--doc/de/member/pic/contfilter01.pngbin0 -> 39194 bytes
-rw-r--r--doc/de/member/pic/contfilter02.pngbin0 -> 48243 bytes
-rw-r--r--doc/de/member/pic/croles1.pngbin0 -> 134124 bytes
-rw-r--r--doc/de/member/pic/croles2.pngbin0 -> 50769 bytes
-rw-r--r--doc/de/member/pic/dateien01.pngbin0 -> 33138 bytes
-rw-r--r--doc/de/member/pic/dateien02.pngbin0 -> 19003 bytes
-rw-r--r--doc/de/member/pic/dateien03.pngbin0 -> 12837 bytes
-rw-r--r--doc/de/member/pic/einst01.pngbin0 -> 20963 bytes
-rw-r--r--doc/de/member/pic/einst02.pngbin0 -> 14415 bytes
-rw-r--r--doc/de/member/pic/einst03.pngbin0 -> 17916 bytes
-rw-r--r--doc/de/member/pic/feateinst01.pngbin0 -> 22554 bytes
-rw-r--r--doc/de/member/pic/feateinst02.pngbin0 -> 43748 bytes
-rw-r--r--doc/de/member/pic/feateinst03.pngbin0 -> 51816 bytes
-rw-r--r--doc/de/member/pic/feateinst04.pngbin0 -> 37917 bytes
-rw-r--r--doc/de/member/pic/feateinst05.pngbin0 -> 60531 bytes
-rw-r--r--doc/de/member/pic/feateinst06.pngbin0 -> 34318 bytes
-rw-r--r--doc/de/member/pic/feateinst07.pngbin0 -> 133220 bytes
-rw-r--r--doc/de/member/pic/feateinst08.pngbin0 -> 37036 bytes
-rw-r--r--doc/de/member/pic/feateinst09.pngbin0 -> 105349 bytes
-rw-r--r--doc/de/member/pic/feateinst10.pngbin0 -> 47171 bytes
-rw-r--r--doc/de/member/pic/feateinst11.pngbin0 -> 49836 bytes
-rw-r--r--doc/de/member/pic/font.pngbin0 -> 1700 bytes
-rw-r--r--doc/de/member/pic/fotos01.pngbin0 -> 164156 bytes
-rw-r--r--doc/de/member/pic/fotos02.pngbin0 -> 804 bytes
-rw-r--r--doc/de/member/pic/fotos03.pngbin0 -> 113084 bytes
-rw-r--r--doc/de/member/pic/fotos04.pngbin0 -> 69038 bytes
-rw-r--r--doc/de/member/pic/galerie01.pngbin0 -> 77088 bytes
-rw-r--r--doc/de/member/pic/hbar.pngbin0 -> 3998 bytes
-rw-r--r--doc/de/member/pic/highlited.pngbin0 -> 1751 bytes
-rw-r--r--doc/de/member/pic/image.pngbin0 -> 54664 bytes
-rw-r--r--doc/de/member/pic/inhber01.pngbin0 -> 1617 bytes
-rw-r--r--doc/de/member/pic/inhber02.pngbin0 -> 1392 bytes
-rw-r--r--doc/de/member/pic/inhber03.pngbin0 -> 1667 bytes
-rw-r--r--doc/de/member/pic/inhber04.pngbin0 -> 2228 bytes
-rw-r--r--doc/de/member/pic/inhber05.pngbin0 -> 21622 bytes
-rw-r--r--doc/de/member/pic/inhber06.pngbin0 -> 88677 bytes
-rw-r--r--doc/de/member/pic/interag01.pngbin0 -> 2373 bytes
-rw-r--r--doc/de/member/pic/interag02.pngbin0 -> 103021 bytes
-rw-r--r--doc/de/member/pic/interag03.pngbin0 -> 34038 bytes
-rw-r--r--doc/de/member/pic/kalender01.pngbin0 -> 44229 bytes
-rw-r--r--doc/de/member/pic/kalender02.pngbin0 -> 16968 bytes
-rw-r--r--doc/de/member/pic/kalender03.pngbin0 -> 17290 bytes
-rw-r--r--doc/de/member/pic/kalender04.pngbin0 -> 41619 bytes
-rw-r--r--doc/de/member/pic/kalender05.pngbin0 -> 16190 bytes
-rw-r--r--doc/de/member/pic/kaneinst01.pngbin0 -> 163821 bytes
-rw-r--r--doc/de/member/pic/kaneinst02.pngbin0 -> 136212 bytes
-rw-r--r--doc/de/member/pic/kanloe.pngbin0 -> 6797 bytes
-rw-r--r--doc/de/member/pic/klone01.pngbin0 -> 232388 bytes
-rw-r--r--doc/de/member/pic/klone02.pngbin0 -> 14732 bytes
-rw-r--r--doc/de/member/pic/klone03.pngbin0 -> 18088 bytes
-rw-r--r--doc/de/member/pic/klone04.pngbin0 -> 65885 bytes
-rw-r--r--doc/de/member/pic/klone05.pngbin0 -> 127551 bytes
-rw-r--r--doc/de/member/pic/klone06.pngbin0 -> 62525 bytes
-rw-r--r--doc/de/member/pic/kloneinst01.pngbin0 -> 286898 bytes
-rw-r--r--doc/de/member/pic/kmerkmale.pngbin0 -> 46119 bytes
-rw-r--r--doc/de/member/pic/kommentieren.pngbin0 -> 4656 bytes
-rw-r--r--doc/de/member/pic/ktoeinst01.pngbin0 -> 59391 bytes
-rw-r--r--doc/de/member/pic/ktoloe.pngbin0 -> 7317 bytes
-rw-r--r--doc/de/member/pic/mauth.pngbin0 -> 4785 bytes
-rw-r--r--doc/de/member/pic/nsfw01.pngbin0 -> 55823 bytes
-rw-r--r--doc/de/member/pic/nsfw02.pngbin0 -> 53680 bytes
-rw-r--r--doc/de/member/pic/nsfw03.pngbin0 -> 18230 bytes
-rw-r--r--doc/de/member/pic/nsfw04.pngbin0 -> 60267 bytes
-rw-r--r--doc/de/member/pic/ordner01.pngbin0 -> 19452 bytes
-rw-r--r--doc/de/member/pic/ordner02.pngbin0 -> 21901 bytes
-rw-r--r--doc/de/member/pic/ordner03.pngbin0 -> 9825 bytes
-rw-r--r--doc/de/member/pic/ordner04.pngbin0 -> 9629 bytes
-rw-r--r--doc/de/member/pic/pgroups01.pngbin0 -> 26547 bytes
-rw-r--r--doc/de/member/pic/pgroups02.pngbin0 -> 61552 bytes
-rw-r--r--doc/de/member/pic/pgroups03.pngbin0 -> 34222 bytes
-rw-r--r--doc/de/member/pic/priveinst01.pngbin0 -> 71156 bytes
-rw-r--r--doc/de/member/pic/qrcode.pngbin0 -> 273 bytes
-rw-r--r--doc/de/member/pic/quellcode.pngbin0 -> 53357 bytes
-rw-r--r--doc/de/member/pic/quote.pngbin0 -> 1701 bytes
-rw-r--r--doc/de/member/pic/red.pngbin0 -> 983 bytes
-rw-r--r--doc/de/member/pic/size.pngbin0 -> 7619 bytes
-rw-r--r--doc/de/member/pic/statusfilter.pngbin0 -> 14341 bytes
-rw-r--r--doc/de/member/pic/stern.pngbin0 -> 5664 bytes
-rw-r--r--doc/de/member/pic/streinst01.pngbin0 -> 153673 bytes
-rw-r--r--doc/de/member/pic/suche01.pngbin0 -> 767 bytes
-rw-r--r--doc/de/member/pic/suche02.pngbin0 -> 3815 bytes
-rw-r--r--doc/de/member/pic/suche03.pngbin0 -> 40892 bytes
-rw-r--r--doc/de/member/pic/table1.pngbin0 -> 7050 bytes
-rw-r--r--doc/de/member/pic/table2.pngbin0 -> 6532 bytes
-rw-r--r--doc/de/member/pic/table3.pngbin0 -> 3775 bytes
-rw-r--r--doc/de/member/pic/teilen.pngbin0 -> 6455 bytes
-rw-r--r--doc/de/member/pic/verbindungen01.pngbin0 -> 192790 bytes
-rw-r--r--doc/de/member/pic/verbindungen02.pngbin0 -> 36647 bytes
-rw-r--r--doc/de/member/pic/verbindungen03.pngbin0 -> 35629 bytes
-rw-r--r--doc/de/member/pic/verbindungen04.pngbin0 -> 67759 bytes
-rw-r--r--doc/de/member/pic/verbindungen05.pngbin0 -> 38471 bytes
-rw-r--r--doc/de/member/pic/verbindungen06.pngbin0 -> 33526 bytes
-rw-r--r--doc/de/member/pic/verzeichnis.pngbin0 -> 368155 bytes
-rw-r--r--doc/de/member/pic/video_poster.pngbin0 -> 17729 bytes
-rw-r--r--doc/de/member/pic/webseiten01.pngbin0 -> 23408 bytes
-rw-r--r--doc/de/member/pic/webseiten02.pngbin0 -> 35491 bytes
-rw-r--r--doc/de/member/pic/webseiten03.pngbin0 -> 28965 bytes
-rw-r--r--doc/de/member/pic/webseiten04.pngbin0 -> 34342 bytes
-rw-r--r--doc/de/member/pic/webseiten05.pngbin0 -> 52292 bytes
-rw-r--r--doc/de/member/pic/webseiten06.pngbin0 -> 23341 bytes
-rw-r--r--doc/de/member/pic/wiki01.pngbin0 -> 11513 bytes
-rw-r--r--doc/de/member/pic/wiki02.pngbin0 -> 34939 bytes
-rw-r--r--doc/de/member/pic/wiki03.pngbin0 -> 54716 bytes
-rw-r--r--doc/de/member/pic/wiki04.pngbin0 -> 171749 bytes
-rw-r--r--doc/de/member/posting.md17
-rw-r--r--doc/de/member/privacy_groups.md23
-rw-r--r--doc/de/member/privacy_settings.md6
-rw-r--r--doc/de/member/profiles.md23
-rw-r--r--doc/de/member/protection_of_privacy.md67
-rw-r--r--doc/de/member/public_stream.md12
-rw-r--r--doc/de/member/registration.md19
-rw-r--r--doc/de/member/repeat.md5
-rw-r--r--doc/de/member/save_to_folder.md25
-rw-r--r--doc/de/member/search.md15
-rw-r--r--doc/de/member/settings.md37
-rw-r--r--doc/de/member/share.md5
-rw-r--r--doc/de/member/show_source_code.md15
-rw-r--r--doc/de/member/stream_settings.md9
-rw-r--r--doc/de/member/superblock.md15
-rw-r--r--doc/de/member/tags.md5
-rw-r--r--doc/de/member/the_grid.md3
-rw-r--r--doc/de/member/the_stream.md7
-rw-r--r--doc/de/member/toggle_star_status.md9
-rw-r--r--doc/de/member/websites.md143
-rw-r--r--doc/de/member/wikis.md39
-rw-r--r--doc/de/member_faq.md17
-rw-r--r--doc/de/members.bb25
-rw-r--r--doc/de/pic/ui01.pngbin0 -> 218419 bytes
-rw-r--r--doc/de/pic/ui02.pngbin0 -> 29754 bytes
-rw-r--r--doc/de/platzhalter.md1
-rw-r--r--doc/de/profiles.bb40
-rw-r--r--doc/de/registration.bb36
-rw-r--r--doc/de/roles.bb60
-rw-r--r--doc/de/the_hubzilla_project.md116
-rw-r--r--doc/de/toc.html104
-rw-r--r--doc/de/tutorials/DerivedTheme.md96
-rw-r--r--doc/de/tutorials/customise_look.md221
-rw-r--r--doc/de/tutorials/pic/02.pngbin0 -> 69550 bytes
-rw-r--r--doc/de/tutorials/pic/03.pngbin0 -> 18731 bytes
-rw-r--r--doc/de/tutorials/pic/04.pngbin0 -> 53482 bytes
-rw-r--r--doc/de/tutorials/pic/05.pngbin0 -> 26665 bytes
-rw-r--r--doc/de/tutorials/pic/06.pngbin0 -> 39055 bytes
-rw-r--r--doc/de/tutorials/pic/07.pngbin0 -> 65530 bytes
-rw-r--r--doc/de/tutorials/pic/08.pngbin0 -> 24603 bytes
-rw-r--r--doc/de/tutorials/pic/09.pngbin0 -> 23763 bytes
-rw-r--r--doc/de/tutorials/pic/10.pngbin0 -> 67730 bytes
-rw-r--r--doc/de/tutorials/pic/11.pngbin0 -> 14390 bytes
-rw-r--r--doc/de/tutorials/pic/12.pngbin0 -> 72842 bytes
-rw-r--r--doc/de/tutorials/pic/13.pngbin0 -> 81388 bytes
-rw-r--r--doc/de/tutorials/pic/14.pngbin0 -> 60861 bytes
-rw-r--r--doc/de/tutorials/pic/15.pngbin0 -> 21657 bytes
-rw-r--r--doc/de/tutorials/pic/16.pngbin0 -> 20138 bytes
-rw-r--r--doc/de/tutorials/pic/17.pngbin0 -> 27437 bytes
-rw-r--r--doc/de/tutorials/pic/18.pngbin0 -> 65125 bytes
-rw-r--r--doc/de/tutorials/pic/19.pngbin0 -> 62228 bytes
-rw-r--r--doc/de/tutorials/pic/20.pngbin0 -> 53937 bytes
-rw-r--r--doc/de/tutorials/pic/fedidb.pngbin0 -> 244957 bytes
-rw-r--r--doc/de/tutorials/pic/fedieverse-observer.pngbin0 -> 106457 bytes
-rw-r--r--doc/de/tutorials/pic/hzreg01.pngbin0 -> 47088 bytes
-rw-r--r--doc/de/tutorials/pic/hzreg01a.pngbin0 -> 126943 bytes
-rw-r--r--doc/de/tutorials/pic/hzreg02.pngbin0 -> 80507 bytes
-rw-r--r--doc/de/tutorials/pic/hzreg03.pngbin0 -> 106952 bytes
-rw-r--r--doc/de/tutorials/pic/hzreg04.pngbin0 -> 64621 bytes
-rw-r--r--doc/de/tutorials/pic/nomadapp.pngbin0 -> 172012 bytes
-rw-r--r--doc/de/tutorials/pic/pdle01.pngbin0 -> 1339614 bytes
-rw-r--r--doc/de/tutorials/pic/pdle02.pngbin0 -> 207233 bytes
-rw-r--r--doc/de/tutorials/pic/pdle03.pngbin0 -> 428803 bytes
-rw-r--r--doc/de/tutorials/pic/pdle04.pngbin0 -> 209056 bytes
-rw-r--r--doc/de/tutorials/pic/pdle05.pngbin0 -> 199353 bytes
-rw-r--r--doc/de/tutorials/pic/pdle06.pngbin0 -> 241793 bytes
-rw-r--r--doc/de/tutorials/pic/pdle07.pngbin0 -> 216392 bytes
-rw-r--r--doc/de/tutorials/pic/pdle08.pngbin0 -> 200478 bytes
-rw-r--r--doc/de/tutorials/pic/pdle09.pngbin0 -> 79722 bytes
-rw-r--r--doc/de/tutorials/pic/pdle10.pngbin0 -> 39266 bytes
-rw-r--r--doc/de/tutorials/pic/pdle11.pngbin0 -> 28468 bytes
-rw-r--r--doc/de/tutorials/pic/pdle12.pngbin0 -> 65077 bytes
-rw-r--r--doc/de/tutorials/pic/pdle13.pngbin0 -> 236620 bytes
-rw-r--r--doc/de/tutorials/pic/pdle14.pngbin0 -> 217419 bytes
-rw-r--r--doc/de/tutorials/pic/pdle15.pngbin0 -> 235097 bytes
-rw-r--r--doc/de/tutorials/pic/pubsites.pngbin0 -> 167653 bytes
-rw-r--r--doc/de/tutorials/pic/rega.pngbin0 -> 197041 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt01.pngbin0 -> 147642 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt02.pngbin0 -> 40127 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt03.pngbin0 -> 19324 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt04.pngbin0 -> 32439 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt05.pngbin0 -> 37690 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt05a.pngbin0 -> 26775 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt06.pngbin0 -> 70132 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt07.pngbin0 -> 85328 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt08.pngbin0 -> 85325 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt09.pngbin0 -> 68681 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt10.pngbin0 -> 71936 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt11.pngbin0 -> 72832 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt12.pngbin0 -> 75061 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt13.pngbin0 -> 79633 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt14.pngbin0 -> 80973 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt15.pngbin0 -> 10289 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt16.pngbin0 -> 19490 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt17.pngbin0 -> 93005 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt18.pngbin0 -> 90178 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt19.pngbin0 -> 89177 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt20.pngbin0 -> 93194 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt21.pngbin0 -> 89314 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt22.pngbin0 -> 84386 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt23.pngbin0 -> 120335 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt24.pngbin0 -> 120201 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt25.pngbin0 -> 132346 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt26.pngbin0 -> 98324 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt27.pngbin0 -> 89246 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt28.pngbin0 -> 83517 bytes
-rw-r--r--doc/de/tutorials/pic/tut_opt29.pngbin0 -> 297471 bytes
-rw-r--r--doc/de/tutorials/step_with_hubzilla.md160
-rw-r--r--doc/de/tutorials/tutorials.md7
-rw-r--r--doc/de/ui.md5
-rw-r--r--include/account.php296
-rw-r--r--include/acl_selectors.php2
-rw-r--r--include/auth.php7
-rw-r--r--include/bbcode.php58
-rw-r--r--include/conversation.php18
-rw-r--r--include/crypto.php155
-rw-r--r--include/event.php2
-rw-r--r--include/items.php457
-rw-r--r--include/js_strings.php1
-rw-r--r--include/markdown.php12
-rw-r--r--include/network.php1
-rw-r--r--include/photo/photo_driver.php2
-rw-r--r--install/INSTALL.txt6
-rw-r--r--library/ASNValue.class.php169
-rw-r--r--library/asn1.php291
-rw-r--r--tests/unit/Lib/ActivityTest.php46
-rw-r--r--tests/unit/Lib/JcsEddsa2022Test.php8
-rw-r--r--tests/unit/Lib/KeyutilsTest.php49
-rw-r--r--tests/unit/includes/AccountTest.php21
-rw-r--r--tests/unit/includes/BBCodeTest.php24
-rw-r--r--tests/unit/includes/MarkdownTest.php4
-rw-r--r--util/hmessages.po1890
-rwxr-xr-xutil/init_sys_channel13
-rw-r--r--vendor/bakame/http-structured-fields/LICENSE21
-rw-r--r--vendor/bakame/http-structured-fields/composer.json106
-rw-r--r--vendor/bakame/http-structured-fields/src/Bytes.php83
-rw-r--r--vendor/bakame/http-structured-fields/src/DataType.php45
-rw-r--r--vendor/bakame/http-structured-fields/src/Dictionary.php788
-rw-r--r--vendor/bakame/http-structured-fields/src/DisplayString.php103
-rw-r--r--vendor/bakame/http-structured-fields/src/ForbiddenOperation.php11
-rw-r--r--vendor/bakame/http-structured-fields/src/Ietf.php73
-rw-r--r--vendor/bakame/http-structured-fields/src/InnerList.php478
-rw-r--r--vendor/bakame/http-structured-fields/src/InvalidArgument.php11
-rw-r--r--vendor/bakame/http-structured-fields/src/InvalidOffset.php38
-rw-r--r--vendor/bakame/http-structured-fields/src/Item.php502
-rw-r--r--vendor/bakame/http-structured-fields/src/Key.php53
-rw-r--r--vendor/bakame/http-structured-fields/src/Member.php116
-rw-r--r--vendor/bakame/http-structured-fields/src/MissingFeature.php13
-rw-r--r--vendor/bakame/http-structured-fields/src/OuterList.php430
-rw-r--r--vendor/bakame/http-structured-fields/src/ParameterAccess.php260
-rw-r--r--vendor/bakame/http-structured-fields/src/Parameters.php763
-rw-r--r--vendor/bakame/http-structured-fields/src/Parser.php523
-rw-r--r--vendor/bakame/http-structured-fields/src/StructuredFieldError.php11
-rw-r--r--vendor/bakame/http-structured-fields/src/StructuredFieldProvider.php28
-rw-r--r--vendor/bakame/http-structured-fields/src/SyntaxError.php11
-rw-r--r--vendor/bakame/http-structured-fields/src/Token.php52
-rw-r--r--vendor/bakame/http-structured-fields/src/Type.php88
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ErrorCode.php26
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ItemValidator.php103
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ParametersValidator.php244
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/Result.php34
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ValidatedItem.php19
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ValidatedParameters.php68
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/Violation.php12
-rw-r--r--vendor/bakame/http-structured-fields/src/Validation/ViolationList.php169
-rw-r--r--vendor/composer/autoload_classmap.php449
-rw-r--r--vendor/composer/autoload_files.php3
-rw-r--r--vendor/composer/autoload_psr4.php6
-rw-r--r--vendor/composer/autoload_static.php475
-rw-r--r--vendor/composer/installed.json429
-rw-r--r--vendor/composer/installed.php87
-rw-r--r--vendor/guzzlehttp/psr7/CHANGELOG.md475
-rw-r--r--vendor/guzzlehttp/psr7/LICENSE26
-rw-r--r--vendor/guzzlehttp/psr7/README.md887
-rw-r--r--vendor/guzzlehttp/psr7/composer.json93
-rw-r--r--vendor/guzzlehttp/psr7/src/AppendStream.php248
-rw-r--r--vendor/guzzlehttp/psr7/src/BufferStream.php147
-rw-r--r--vendor/guzzlehttp/psr7/src/CachingStream.php153
-rw-r--r--vendor/guzzlehttp/psr7/src/DroppingStream.php49
-rw-r--r--vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php14
-rw-r--r--vendor/guzzlehttp/psr7/src/FnStream.php180
-rw-r--r--vendor/guzzlehttp/psr7/src/Header.php134
-rw-r--r--vendor/guzzlehttp/psr7/src/HttpFactory.php94
-rw-r--r--vendor/guzzlehttp/psr7/src/InflateStream.php37
-rw-r--r--vendor/guzzlehttp/psr7/src/LazyOpenStream.php49
-rw-r--r--vendor/guzzlehttp/psr7/src/LimitStream.php157
-rw-r--r--vendor/guzzlehttp/psr7/src/Message.php246
-rw-r--r--vendor/guzzlehttp/psr7/src/MessageTrait.php265
-rw-r--r--vendor/guzzlehttp/psr7/src/MimeType.php1259
-rw-r--r--vendor/guzzlehttp/psr7/src/MultipartStream.php165
-rw-r--r--vendor/guzzlehttp/psr7/src/NoSeekStream.php28
-rw-r--r--vendor/guzzlehttp/psr7/src/PumpStream.php179
-rw-r--r--vendor/guzzlehttp/psr7/src/Query.php118
-rw-r--r--vendor/guzzlehttp/psr7/src/Request.php159
-rw-r--r--vendor/guzzlehttp/psr7/src/Response.php161
-rw-r--r--vendor/guzzlehttp/psr7/src/Rfc7230.php23
-rw-r--r--vendor/guzzlehttp/psr7/src/ServerRequest.php340
-rw-r--r--vendor/guzzlehttp/psr7/src/Stream.php283
-rw-r--r--vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php156
-rw-r--r--vendor/guzzlehttp/psr7/src/StreamWrapper.php207
-rw-r--r--vendor/guzzlehttp/psr7/src/UploadedFile.php211
-rw-r--r--vendor/guzzlehttp/psr7/src/Uri.php743
-rw-r--r--vendor/guzzlehttp/psr7/src/UriComparator.php52
-rw-r--r--vendor/guzzlehttp/psr7/src/UriNormalizer.php220
-rw-r--r--vendor/guzzlehttp/psr7/src/UriResolver.php211
-rw-r--r--vendor/guzzlehttp/psr7/src/Utils.php477
-rw-r--r--vendor/macgirvin/http-message-signer/LICENSE30
-rw-r--r--vendor/macgirvin/http-message-signer/README.md130
-rw-r--r--vendor/macgirvin/http-message-signer/TESTING5
-rw-r--r--vendor/macgirvin/http-message-signer/composer.json29
-rw-r--r--vendor/macgirvin/http-message-signer/phpunit.xml20
-rw-r--r--vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php876
-rw-r--r--vendor/macgirvin/http-message-signer/src/StructuredFieldTypes.php91
-rw-r--r--vendor/macgirvin/http-message-signer/src/UnProcessableSignatureException.php8
-rw-r--r--vendor/paragonie/random_compat/LICENSE22
-rwxr-xr-xvendor/paragonie/random_compat/build-phar.sh5
-rw-r--r--vendor/paragonie/random_compat/composer.json34
-rw-r--r--vendor/paragonie/random_compat/dist/random_compat.phar.pubkey5
-rw-r--r--vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc11
-rw-r--r--vendor/paragonie/random_compat/lib/random.php32
-rw-r--r--vendor/paragonie/random_compat/other/build_phar.php57
-rw-r--r--vendor/paragonie/random_compat/psalm-autoload.php9
-rw-r--r--vendor/paragonie/random_compat/psalm.xml19
-rw-r--r--vendor/phpseclib/phpseclib/README.md29
-rw-r--r--vendor/phpseclib/phpseclib/appveyor.yml27
-rw-r--r--vendor/phpseclib/phpseclib/composer.json20
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php507
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php64
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php682
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php799
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php581
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php77
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php224
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php72
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php209
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php766
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php380
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php60
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php54
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php (renamed from vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php)2256
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php57
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php46
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php535
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php405
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php77
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php132
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php75
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php49
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php337
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php118
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php143
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php146
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php112
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php85
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php132
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php62
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php74
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php154
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php87
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php480
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php218
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php373
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php335
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php279
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php785
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php215
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php81
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php92
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php333
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php273
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php47
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php58
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php58
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php58
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php58
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php18
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php46
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php78
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php49
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php52
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php46
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php549
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php189
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php101
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php71
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php209
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php194
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php237
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php138
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php486
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php116
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php62
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php68
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php94
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php283
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php173
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php2002
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php112
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php430
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php186
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php3395
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php142
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php224
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php132
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php187
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php122
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php238
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php124
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php184
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php171
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php530
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php513
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php162
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php715
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php528
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php248
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php500
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php10
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php10
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php23
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php152
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php562
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php22
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php39
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php67
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php41
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php41
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php36
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php48
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php43
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php80
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php68
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php39
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php48
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php41
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php34
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php54
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php41
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php44
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php58
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php39
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php35
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php33
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php45
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php24
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php31
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php54
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php65
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php38
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php32
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php26
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/File/X509.php3053
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php3801
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php702
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php110
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php196
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php108
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php1293
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php697
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php40
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php68
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php1360
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php143
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php89
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php25
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php296
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php42
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php500
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php126
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php76
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php59
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php371
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php372
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php203
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php516
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php22
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php44
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php118
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php442
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php349
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php2239
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php192
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php1662
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php4777
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php245
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php317
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php37
-rw-r--r--vendor/phpseclib/phpseclib/phpseclib/bootstrap.php7
-rw-r--r--vendor/phpseclib/phpseclib2_compat/.github/workflows/ci.yml61
-rw-r--r--vendor/phpseclib/phpseclib2_compat/.github/workflows/update.php12
-rw-r--r--vendor/phpseclib/phpseclib2_compat/LICENSE21
-rw-r--r--vendor/phpseclib/phpseclib2_compat/README.md92
-rw-r--r--vendor/phpseclib/phpseclib2_compat/composer.json30
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/AES.php93
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Base.php585
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php66
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/DES.php65
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Hash.php145
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/RC2.php132
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/RC4.php74
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/RSA.php948
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Random.php36
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php137
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php116
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php67
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/File/ANSI.php32
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/File/ASN1.php95
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php28
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/File/X509.php407
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Math/BigInteger.php308
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Net/SFTP.php276
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php75
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/Net/SSH2.php233
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php47
-rw-r--r--vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php34
-rw-r--r--vendor/ralouphie/getallheaders/LICENSE21
-rw-r--r--vendor/ralouphie/getallheaders/README.md27
-rw-r--r--vendor/ralouphie/getallheaders/composer.json26
-rw-r--r--vendor/ralouphie/getallheaders/src/getallheaders.php46
-rw-r--r--view/css/conversation.css96
-rw-r--r--view/js/main.js388
-rw-r--r--view/nb/hmessages.po21075
-rw-r--r--view/nb/hstrings.php5340
-rw-r--r--view/tpl/conv_frame.tpl3
-rw-r--r--view/tpl/conv_item.tpl25
-rw-r--r--view/tpl/jot-header.tpl64
-rw-r--r--view/tpl/js_strings.tpl1
-rw-r--r--view/tpl/notifications_widget.tpl1
-rw-r--r--view/tpl/pinned_item.tpl2
1184 files changed, 89434 insertions, 34342 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 951c97b6f..cb3c3b495 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,110 @@
+Hubzilla 10.4 (2025-??-??)
+ - Introduce util/init_sys_channel to create the sys channel if required
+ - Upgrade http-message-signer to version 2.2
+ - Upgrade phpseclib to version 3
+ - Minor cleanup to the account functions and added tests
+ - Do not sign (request-target) on response
+ - Start verifying incoming RFC9421 HTTP signatures
+ - Convert geo URIs into clickable links
+ - Allow geo URIs in url bbcode tags
+ - Cleanup obsolete and unused functions
+ - Remove unused Xref module
+ - Make sure we have the keys before attempting to sign with JcsEddsa2022
+ - Implement lazy loading of toplevel comments
+ - Update german help files
+ - Add App::$page_layouts attribute for comanche
+ - Refactor and fix numerous issues in guess_image_type()
+ - Add tests for Widget\Messages
+ - Refactor cache_embeds daemon to be called with uuid (instead of item id) so that it will only be processed once
+ - Add avif support for php-gd
+ - Exclude Add/Remove items from network nouveau query
+ - Always preload images and remove pre image preload setting
+ - Introduce per channel conversation mode setting
+ - Add API docs for the observer file
+ - Move observer helper functions to separate source
+ - Introduce helper functions to access the various fields of the xchan stored in App::$observer
+ - Refactor item_normal() to accept an owner uid
+ - Implement reply modal if comment replies are enabled
+ - Streamline wording conversation > comment > reply
+ - Streamline default ordering to created date
+ - Default to threaded conversation mode
+ - Implement lazy loading of reactions
+ - Do not store dismissed create activities in dreport
+ - Refactor mod item to deprecate x() and use $_POST instead of $_REQUEST superglobal
+ - Improved styling for dreport module
+ - Start deprecation of the function x()
+ - Minor cleanup and refactor for Web/Router
+ - Minor cleanup and refactor for Lib/Webserver
+ - Set App::$query_string from from server.request_uri instead of server.query_string because the latter will mostly be urldecoded by the server already
+ - Refactor language selector
+ - Extend message filter to support until=2025-04-18 20:49:00 for date/time based filtering and add tests
+ - Extend message filter to deal with && and || conditions and add tests
+ - Prevent storing files/folder with filenames exceeding their max name length
+ - Deal with attachment of type link
+ - Revert translation of network to stream
+ - Updated debian install script
+ - Add suport for strong bbcode tag
+ - Change photo.filename to type text for new installs
+ - Provide methods to get mid and uuid from activity object
+ - Minor update for boxy schema
+ - Update composer libs
+ - Reorganize emojis and allow custom site emojis
+ - Change item.obj and item.target to mediumtext for mysql new installs
+ - Move jot related functions to jot-header and some cleanup
+ - Port photo selector to vanilla javascript
+ - Enable photo selector for comments if OCAP access is enabled
+ - Add :hubzilla: emoji
+ - Add :zot: emoji
+ - Include unapproved connections in deliverable_abook_xchans()
+ - Port showHideComments() to vanilla javascript
+ - Disable browser rotating image based on EXIF metadata
+
+ Bugfixes
+ - Fix markdown issue with mentions
+ - Fix issue where item_wall was not set for article and card item types
+ - Fix first created account was not necessarily the admin account
+ - Fix notice not emited on failed login
+ - Fix intro notifications not handled via /notify/view and hence not marked seen
+ - Fix undefined static function in Zot6Handler
+ - Fix missing return in Render\Theme::current
+ - Fix announce source title (addr) not correct
+ - Fix offset calculation if element position is relative
+ - Fix autosave for comments
+ - Fix notfication issue with update activities
+ - Fix sess_data not updated to mediumtext in mysql schema file
+ - Fix title and summary converted to bbcode
+ - Fix preloading images if dom element is not yet in page
+ - Fix verb and hash for notifications
+ - Fix notification button for medium screen size (right aside collapsed)
+ - Fix new result set created for updated results (dreport)
+ - Fix regex to catch codeblocks with params like class in smilies()
+ - Fix term.imgurl not stored in item_store_update()
+ - Fix folder names are not URL escaped in Files app (issue #1903)
+ - Fix stephenhill/base58 PHP warnings
+ - Fix color bbcode markup
+ - Fix video poster display issue
+ - Fix relayed emoji reactions
+ - Fix some javascript errors on mobile devices
+ - Fix our own activities visible in unseen forum notifications
+ - Fix duplicated head_get_icon()
+
+ Addons
+ - Gallery: look for templates in theme directory first
+ - Articles: look for templates in theme directory first
+ - Wiki: look for templates in theme directory first
+ - Pubcrawl: remove unused force note setting
+ - Flashcards: major refactor and added functionality
+ - Superblock: refactor and new siteblock option for admins
+ - Cart: fix issue related to HTTP3
+ - Pubcrawl: avoid DB lookup if not valid AS request in mod followers and mod following
+ - Photocache: implement prefetch via cache_embeds daemon and minor refactor
+ - Articles: fix Add/Remove activities not dismissed in channel activities query
+ - Cards: fix Add/Remove activities not dismissed in channel activities query
+ - Diaspora: make sure item_thread_top is set for reshares (info for filters)
+ - Gallery: fix missing folder field from query
+ - Pubcrawl: update mod ap_probe to show a visual representation if applicable
+
+
Hubzilla 10.2.3 (2025-04-11)
- Fix bogus merge from 10.2.2 release
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 03b63a17b..64588f9e3 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -2277,7 +2277,7 @@ class Activity {
$s['edited'] = $s['created'];
$s['title'] = (($response_activity) ? EMPTY_STR : html2plain($content['name']));
- $s['summary'] = html2plain($content['summary']);
+ $s['summary'] = (($content['summary'] !== $content['content']) ? html2plain($content['summary']) : '');
$s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content'));
// peertube quirks
@@ -3350,10 +3350,10 @@ class Activity {
if (array_key_exists('startTime', $act) && strpos($act['startTime'], -1, 1) === 'Z') {
$adjust = true;
$event['adjust'] = 1;
- $event['dtstart'] = datetime_convert('UTC', 'UTC', $event['startTime'] . (($adjust) ? '' : 'Z'));
+ $event['dtstart'] = datetime_convert('UTC', 'UTC', $act['startTime'] . (($adjust) ? '' : 'Z'));
}
if (array_key_exists('endTime', $act)) {
- $event['dtend'] = datetime_convert('UTC', 'UTC', $event['endTime'] . (($adjust) ? '' : 'Z'));
+ $event['dtend'] = datetime_convert('UTC', 'UTC', $act['endTime'] . (($adjust) ? '' : 'Z'));
}
else {
$event['nofinish'] = true;
@@ -3552,12 +3552,22 @@ class Activity {
}
foreach ($actor['tag'] as $t) {
+ // TODO: implement FEP-fb2a at the sending side and deprecate PropertyValue
if ((isset($t['type']) && $t['type'] === 'PropertyValue') &&
(isset($t['name']) && $t['name'] === 'Protocol') &&
- (isset($t['value']) && in_array($t['value'], ['zot6', 'activitypub', 'diaspora']))
+ isset($t['value'])
) {
- $ret[] = $t['value'];
+ $ret[] = trim($t['value']);
}
+
+ // FEP-fb2a - actor metadata
+ if ((isset($t['type']) && $t['type'] === 'Note') &&
+ (isset($t['name']) && $t['name'] === 'Protocols') &&
+ isset($t['content'])
+ ) {
+ $ret = array_map('trim', explode(',', $t['content']));
+ }
+
}
return $ret;
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 12dbfee7f..6d5e249ef 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -910,14 +910,13 @@ class Enotify {
}
static public function format_notify($tt) {
-
$message = trim(strip_tags(bbcode($tt['msg'])));
if(strpos($message, $tt['xname']) === 0)
$message = substr($message, strlen($tt['xname']) + 1);
$x = [
- 'notify_link' => $tt['link'],
+ 'notify_link' => (($tt['ntype'] === NOTIFY_INTRO) ? z_root() . '/notify/view/' . $tt['id'] : $tt['link']),
'name' => $tt['xname'],
'url' => $tt['url'],
'photo' => $tt['photo'],
@@ -929,11 +928,9 @@ class Enotify {
];
return $x;
-
}
static public function format_intros($rr) {
-
return [
'notify_link' => z_root() . '/connections#' . $rr['abook_id'],
'name' => $rr['xchan_name'],
diff --git a/Zotlabs/Lib/JcsEddsa2022.php b/Zotlabs/Lib/JcsEddsa2022.php
index 14f16c94b..c56f093af 100644
--- a/Zotlabs/Lib/JcsEddsa2022.php
+++ b/Zotlabs/Lib/JcsEddsa2022.php
@@ -7,11 +7,28 @@ use StephenHill\Base58;
class JcsEddsa2022 {
- public function __construct() {
- return $this;
- }
-
+ /**
+ * Sign arbitrary data with the keys of the provided channel.
+ *
+ * @param $data The data to be signed.
+ * @param array $channel A channel as an array of key/value pairs.
+ *
+ * @return An array with the following fields:
+ * - `type`: The type of signature, always `DataIntegrityProof`.
+ * - `cryptosuite`: The cryptographic algorithm used, always `eddsa-jcs-2022`.
+ * - `created`: The UTC date and timestamp when the signature was created.
+ * - `verificationMethod`: The channel URL and the public key separated by a `#`.
+ * - `proofPurpose`: The purpose of the signature, always `assertionMethod`.
+ * - `proofValue`: The signature itself.
+ *
+ * @throws JcsEddsa2022SignatureException if the channel is missing, or
+ * don't have valid keys.
+ */
public function sign($data, $channel): array {
+ if (!is_array($channel) || !isset($channel['channel_epubkey'], $channel['channel_eprvkey'])) {
+ throw new JcsEddsa2022SignException('Invalid or missing channel provided.');
+ }
+
$base58 = new Base58();
$pubkey = (new Multibase())->publicKey($channel['channel_epubkey']);
$options = [
diff --git a/Zotlabs/Lib/JcsEddsa2022SignException.php b/Zotlabs/Lib/JcsEddsa2022SignException.php
new file mode 100644
index 000000000..81d02d631
--- /dev/null
+++ b/Zotlabs/Lib/JcsEddsa2022SignException.php
@@ -0,0 +1,15 @@
+<?php
+/*
+ * SPDX-FileCopyrightText: 2025 The Hubzilla Community
+ * SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+namespace Zotlabs\Lib;
+
+use Exception;
+
+class JcsEddsa2022SignException extends Exception
+{
+}
diff --git a/Zotlabs/Lib/Keyutils.php b/Zotlabs/Lib/Keyutils.php
index 616ecfcf6..33f910236 100644
--- a/Zotlabs/Lib/Keyutils.php
+++ b/Zotlabs/Lib/Keyutils.php
@@ -2,8 +2,8 @@
namespace Zotlabs\Lib;
-use phpseclib\Crypt\RSA;
-use phpseclib\Math\BigInteger;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Math\BigInteger;
/**
* Keyutils
@@ -16,41 +16,42 @@ class Keyutils {
* @param string $e exponent
* @return string
*/
- public static function meToPem($m, $e) {
-
- $rsa = new RSA();
- $rsa->loadKey([
+ public static function meToPem(string $m, string $e): string
+ {
+ $parsedKey = PublicKeyLoader::load([
'e' => new BigInteger($e, 256),
'n' => new BigInteger($m, 256)
]);
- return $rsa->getPublicKey();
-
+ if (method_exists($parsedKey, 'getPublicKey')) {
+ $parsedKey = $parsedKey->getPublicKey();
+ }
+ return $parsedKey->toString('PKCS8');
}
/**
* @param string key
* @return string
*/
- public static function rsaToPem($key) {
-
- $rsa = new RSA();
- $rsa->setPublicKey($key);
-
- return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
-
+ public static function rsaToPem(string $key): string
+ {
+ $parsedKey = PublicKeyLoader::load($key);
+ if (method_exists($parsedKey, 'getPublicKey')) {
+ $parsedKey = $parsedKey->getPublicKey();
+ }
+ return $parsedKey->toString('PKCS8');
}
/**
* @param string key
* @return string
*/
- public static function pemToRsa($key) {
-
- $rsa = new RSA();
- $rsa->setPublicKey($key);
-
- return $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
-
+ public static function pemToRsa(string $key): string
+ {
+ $parsedKey = PublicKeyLoader::load($key);
+ if (method_exists($parsedKey, 'getPublicKey')) {
+ $parsedKey = $parsedKey->getPublicKey();
+ }
+ return $parsedKey->toString('PKCS1');
}
/**
@@ -58,23 +59,28 @@ class Keyutils {
* @param string $m reference modulo
* @param string $e reference exponent
*/
- public static function pemToMe($key, &$m, &$e) {
-
- $rsa = new RSA();
- $rsa->loadKey($key);
- $rsa->setPublicKey();
-
- $m = $rsa->modulus->toBytes();
- $e = $rsa->exponent->toBytes();
-
+ public static function pemToMe(string $key): array
+ {
+ $parsedKey = PublicKeyLoader::load($key);
+ if (method_exists($parsedKey, 'getPublicKey')) {
+ $parsedKey = $parsedKey->getPublicKey();
+ }
+ $raw = $parsedKey->toString('Raw');
+
+ $m = $raw['n'];
+ $e = $raw['e'];
+
+ return [$m->toBytes(), $e->toBytes()];
}
/**
* @param string $pubkey
* @return string
*/
- public static function salmonKey($pubkey) {
- self::pemToMe($pubkey, $m, $e);
+ public static function salmonKey(string $pubkey): string
+ {
+ [$m, $e] = self::pemToMe($pubkey);
+ /** @noinspection PhpRedundantOptionalArgumentInspection */
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true);
}
@@ -82,11 +88,13 @@ class Keyutils {
* @param string $key
* @return string
*/
- public static function convertSalmonKey($key) {
- if (strstr($key, ','))
+ public static function convertSalmonKey(string $key): string
+ {
+ if (str_contains($key, ',')) {
$rawkey = substr($key, strpos($key, ',') + 1);
- else
+ } else {
$rawkey = substr($key, 5);
+ }
$key_info = explode('.', $rawkey);
@@ -96,4 +104,4 @@ class Keyutils {
return self::meToPem($m, $e);
}
-} \ No newline at end of file
+}
diff --git a/Zotlabs/Lib/MessageFilter.php b/Zotlabs/Lib/MessageFilter.php
index fa3d61244..3f2db88c3 100644
--- a/Zotlabs/Lib/MessageFilter.php
+++ b/Zotlabs/Lib/MessageFilter.php
@@ -8,8 +8,8 @@ class MessageFilter {
public static function evaluate($item, $incl, $excl) {
- $text = prepare_text($item['body'],((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
- $text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
+ $text = prepare_text($item['body'], ((isset($item['mimetype'])) ? $item['mimetype'] : 'text/bbcode'));
+ $text = html2plain((!empty($item['title'])) ? $item['title'] . ' ' . $text : $text);
$lang = null;
if ((strpos($incl, 'lang=') !== false) || (strpos($excl, 'lang=') !== false) || (strpos($incl, 'lang!=') !== false) || (strpos($excl, 'lang!=') !== false)) {
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 5bb658bba..46fe6d815 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -206,9 +206,9 @@ class ThreadItem {
}
if (in_array($item['obj_type'], ['Event', ACTIVITY_OBJ_EVENT])) {
- $response_verbs[] = 'attendyes';
- $response_verbs[] = 'attendno';
- $response_verbs[] = 'attendmaybe';
+ $response_verbs[] = 'accept';
+ $response_verbs[] = 'reject';
+ $response_verbs[] = 'tentativeaccept';
if($this->is_commentable() && $observer) {
$isevent = true;
$attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
@@ -222,7 +222,6 @@ class ThreadItem {
$response_verbs[] = 'comment';
$responses = get_responses($response_verbs, $item);
-
/*
* We should avoid doing this all the time, but it depends on the conversation mode
* And the conv mode may change when we change the conv, or it changes its mode
@@ -231,7 +230,13 @@ class ThreadItem {
$this->check_wall_to_wall();
+ $children = $this->get_children();
+ $children_count = count($children);
+
if($this->is_toplevel()) {
+ $conv->comments_total = $responses['comment']['count'] ?? 0;
+ $conv->comments_loaded = $children_count;
+
if((local_channel() && $conv->get_profile_owner() === local_channel()) || (local_channel() && App::$module === 'pubstream')) {
$star = [
'toggle' => t("Toggle Star Status"),
@@ -243,7 +248,6 @@ class ThreadItem {
$is_comment = true;
}
-
$verified = (intval($item['item_verified']) ? t('Message signature validated') : '');
$forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : '');
$unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : '');
@@ -324,9 +328,8 @@ class ThreadItem {
$viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode(gen_link_id($item['mid']));
$comment_count_txt = ['label' => sprintf(tt('%d comment', '%d comments', $total_children), $total_children), 'count' => $total_children];
- $list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
- $children = $this->get_children();
+ $list_unseen_txt = $unseen_comments ? ['label' => sprintf(t('%d unseen'), $unseen_comments), 'count' => $unseen_comments] : [];
$has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false);
@@ -350,6 +353,22 @@ class ThreadItem {
$contact = App::$contacts[$item['author_xchan']];
}
+ $blog_mode = $this->get_display_mode() === 'list';
+ $load_more = false;
+ $load_more_title = '';
+ $comments_total_percent = 0;
+ if (($conv->comments_total > $conv->comments_loaded) || ($blog_mode && $conv->comments_total > 3)) {
+ // provide a load more comments button
+ $load_more = true;
+ $load_more_title = sprintf(t('Load the next few of total %d comments'), $conv->comments_total);
+ $comments_total_percent = round(100 * 3 / $conv->comments_total);
+ }
+
+ $expand = '';
+ if ($this->threaded && !empty($item['comment_count'] && !$this->is_toplevel())) {
+ $expand = t('Expand Replies');
+ }
+
$tmp_item = array(
'template' => $this->get_template(),
'mode' => $mode,
@@ -424,6 +443,7 @@ class ThreadItem {
'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'],
+ 'parent_mid' => $item['parent_mid'],
'plink' => get_plink($item),
'edpost' => $edpost,
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
@@ -463,18 +483,23 @@ class ThreadItem {
'reaction_str' => [t('Add yours'), t('Remove yours')],
'is_contained' => $this->is_toplevel() && str_contains($item['tgt_type'], 'Collection'),
'observer_activity' => [
- 'like' => intval($item['observer_liked'] ?? 0),
- 'dislike' => intval($item['observer_disliked'] ?? 0),
- 'announce' => intval($item['observer_announced'] ?? 0),
- 'comment' => intval($item['observer_commented'] ?? 0),
- 'attendyes' => intval($item['observer_accepted'] ?? 0),
- 'attendno' => intval($item['observer_rejected'] ?? 0),
- 'attendmaybe' => intval($item['observer_tentativelyaccepted'] ?? 0)
+ 'like' => intval($item['observer_like_count'] ?? 0),
+ 'dislike' => intval($item['observer_dislike_count'] ?? 0),
+ 'announce' => intval($item['observer_announce_count'] ?? 0),
+ 'comment' => intval($item['observer_comment_count'] ?? 0),
+ 'accept' => intval($item['observer_accept_count'] ?? 0),
+ 'reject' => intval($item['observer_reject_count'] ?? 0),
+ 'tentativeaccept' => intval($item['observer_tentativeaccept_count'] ?? 0)
],
'threaded' => $this->threaded,
- 'blog_mode' => $this->get_display_mode() === 'list',
+ 'blog_mode' => $blog_mode,
'collapse_comments' => t('show less'),
- 'expand_comments' => $this->threaded ? t('show more') : t('show all')
+ 'expand_comments' => $this->threaded ? t('show more') : t('show all'),
+ 'load_more' => $load_more,
+ 'load_more_title' => $load_more_title,
+ 'comments_total' => $conv->comments_total,
+ 'comments_total_percent' => $comments_total_percent,
+ 'expand' => $expand
);
$arr = array('item' => $item, 'output' => $tmp_item);
@@ -483,20 +508,19 @@ class ThreadItem {
$result = $arr['output'];
$result['children'] = array();
- $nb_children = count($children);
- $visible_comments = Config::Get('system', 'expanded_comments', 3);
+ $visible_comments = 3; // Config::Get('system', 'expanded_comments', 3);
- if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
+ if(($this->get_display_mode() === 'normal') && ($children_count > 0)) {
foreach($children as $child) {
$result['children'][] = $child->get_template_data($thread_level + 1, $conv_flags);
}
// Collapse
- if($thread_level === 1 && $nb_children > $visible_comments) {
+ if($thread_level === 1 && $children_count > $visible_comments) {
$result['children'][0]['comment_firstcollapsed'] = true;
$result['children'][0]['num_comments'] = $comment_count_txt['label'];
- $result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
+ $result['children'][$children_count - ($visible_comments + 1)]['comment_lastcollapsed'] = true;
}
}
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index ee18d8de1..1d968fa1a 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -25,6 +25,8 @@ class ThreadStream {
public $reload = '';
private $cipher = 'AES-128-CCM';
public $mid_uuid_map = [];
+ public $comments_total = 0;
+ public $comments_loaded = 0;
// $prepared_item is for use by alternate conversation structures such as photos
diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php
index 9671cedf3..a72e70b79 100644
--- a/Zotlabs/Module/Channel.php
+++ b/Zotlabs/Module/Channel.php
@@ -85,7 +85,7 @@ class Channel extends Controller {
$headers = [
'Content-Type' => 'application/x-zot+json',
'Digest' => HTTPSig::generate_digest_header($data),
- '(request-target)' => strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']
+ 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T')
];
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel));
@@ -266,7 +266,7 @@ class Channel extends Controller {
'default_location' => (($is_owner) ? App::$profile['channel_location'] : ''),
'nickname' => App::$profile['channel_address'],
'lockstate' => (((strlen(App::$profile['channel_allow_cid'])) || (strlen(App::$profile['channel_allow_gid'])) || (strlen(App::$profile['channel_deny_cid'])) || (strlen(App::$profile['channel_deny_gid']))) ? 'lock' : 'unlock'),
- 'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post') : ''),
+ 'acl' => (($is_owner) ? populate_acl($channel_acl, true, PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions') : ''),
'permissions' => $channel_acl,
'showacl' => (($is_owner) ? 'yes' : ''),
'bang' => '',
@@ -337,7 +337,7 @@ class Channel extends Controller {
if (($update) && (!$load)) {
if ($mid) {
- $r = q("SELECT parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal_update
+ $r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal_update
AND item_wall = 1 $simple_update $permission_sql $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
@@ -386,7 +386,7 @@ class Channel extends Controller {
if ($noscript_content || $load) {
if ($mid) {
- $r = q("SELECT parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal
+ $r = q("SELECT *, parent AS item_id from item where $identifier = '%s' and uid = %d $item_normal
AND item_wall = 1 $permission_sql $sql_extra limit 1",
dbesc($mid),
intval(App::$profile['profile_uid'])
@@ -417,12 +417,15 @@ class Channel extends Controller {
}
}
if ($r) {
- $parents_str = ids_to_querystr($r, 'item_id');
+ $thr_parents = null;
+ if ($mid) {
+ $thr_parents = get_recursive_thr_parents($r[0]);
+ }
- $r = items_by_parent_ids($parents_str, permission_sql: $permission_sql, blog_mode: $blog_mode);
+ $items = items_by_parent_ids($r, $thr_parents, $permission_sql, $blog_mode);
- xchan_query($r);
- $items = fetch_post_tags($r, true);
+ xchan_query($items);
+ $items = fetch_post_tags($items, true);
$items = conv_sort($items, $ordering);
if ($load && $mid && (!count($items))) {
diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php
index 07973431c..15aff9a84 100644
--- a/Zotlabs/Module/Display.php
+++ b/Zotlabs/Module/Display.php
@@ -81,7 +81,7 @@ class Display extends Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
- 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
+ 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -288,15 +288,12 @@ class Display extends Controller {
}
if($r) {
- $parents_str = ids_to_querystr($r,'item_id');
- if($parents_str) {
- $thr_parents = get_recursive_thr_parents($target_item);
- $items = items_by_parent_ids($parents_str, $thr_parents, $permission_sql);
-
- xchan_query($items);
- $items = fetch_post_tags($items,true);
- $items = conv_sort($items,'created');
- }
+ $thr_parents = get_recursive_thr_parents($target_item);
+ $items = items_by_parent_ids($r, $thr_parents, $permission_sql);
+
+ xchan_query($items);
+ $items = fetch_post_tags($items,true);
+ $items = conv_sort($items,'created');
}
else {
$items = array();
diff --git a/Zotlabs/Module/Home.php b/Zotlabs/Module/Home.php
index 39a1c8ea4..0dec432d0 100644
--- a/Zotlabs/Module/Home.php
+++ b/Zotlabs/Module/Home.php
@@ -24,9 +24,13 @@ class Home extends Controller {
$key = Config::Get('system', 'prvkey');
$ret = json_encode(Libzot::site_info());
- $headers = ['Content-Type' => 'application/x-zot+json', 'Digest' => HTTPSig::generate_digest_header($ret)];
- $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
- $h = HTTPSig::create_sig($headers, $key, z_root());
+ $headers = [
+ 'Content-Type' => 'application/x-zot+json',
+ 'Digest' => HTTPSig::generate_digest_header($ret),
+ 'Date' => datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T')
+ ];
+
+ $h = HTTPSig::create_sig($headers, $key, z_root());
HTTPSig::set_headers($h);
echo $ret;
diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php
index 562278973..31faf9dfc 100644
--- a/Zotlabs/Module/Hq.php
+++ b/Zotlabs/Module/Hq.php
@@ -88,7 +88,7 @@ class Hq extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
- 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
+ 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -201,7 +201,7 @@ class Hq extends \Zotlabs\Web\Controller {
if($r) {
$thr_parents = get_recursive_thr_parents($target_item);
- $items = items_by_parent_ids($r[0]['item_id'], $thr_parents);
+ $items = items_by_parent_ids($r, $thr_parents);
xchan_query($items,true,(($sys_item) ? local_channel() : 0));
$items = fetch_post_tags($items,true);
diff --git a/Zotlabs/Module/Id.php b/Zotlabs/Module/Id.php
index e08568d00..004cad6e7 100644
--- a/Zotlabs/Module/Id.php
+++ b/Zotlabs/Module/Id.php
@@ -6,8 +6,8 @@ namespace Zotlabs\Module;
*
* Controller for responding to x-zot: protocol requests
* x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77
- *
- */
+ *
+ */
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
@@ -104,7 +104,7 @@ class Id extends Controller {
$headers['Content-Type'] = 'application/x-zot+json' ;
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
- $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
+ $headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
HTTPSig::set_headers($h);
echo $ret;
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index e164d6be3..83e8d609e 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -176,7 +176,7 @@ class Item extends Controller {
$return_path = ((!empty($_POST['return'])) ? $_POST['return'] : '');
$preview = ((!empty($_POST['preview'])) ? intval($_POST['preview']) : 0);
$categories = ((!empty($_POST['category'])) ? escape_tags($_POST['category']) : '');
- $webpage = ((!empty($_POST['webpage'])) ? intval($_POST['webpage']) : 0);
+ $item_type = ((!empty($_POST['webpage'])) ? intval($_POST['webpage']) : ITEM_TYPE_POST);
$item_obscured = ((!empty($_POST['obscured'])) ? intval($_POST['obscured']) : 0);
$item_delayed = ((!empty($_POST['delayed'])) ? intval($_POST['delayed']) : 0);
$pagetitle = ((!empty($_POST['pagetitle'])) ? escape_tags($_POST['pagetitle']) : '');
@@ -314,7 +314,7 @@ class Item extends Controller {
}
}
else {
- if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], ($webpage) ? 'write_pages' : 'post_wall')) {
+ if (!perm_is_allowed($profile_uid, $observer['xchan_hash'], (intval($item_type) === ITEM_TYPE_POST) ? 'post_wall' : 'write_pages')) {
notice(t('Permission denied.') . EOL);
if ($api_source)
return (['success' => false, 'message' => 'permission denied']);
@@ -426,16 +426,20 @@ class Item extends Controller {
$view_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'view_stream');
$comment_policy = \Zotlabs\Access\PermissionLimits::Get($channel['channel_id'], 'post_comments');
- $public_policy = ((!empty($_POST['public_policy'])) ? escape_tags($_POST['public_policy']) : map_scope($view_policy, true));
- if ($webpage)
- $public_policy = '';
- if ($public_policy)
+ $public_policy = '';
+
+ if (intval($item_type) === ITEM_TYPE_POST) {
+ $public_policy = ((!empty($_POST['public_policy'])) ? escape_tags($_POST['public_policy']) : map_scope($view_policy, true));
+ }
+
+ if ($public_policy) {
$private = 1;
+ }
if ($orig_post) {
$private = 0;
- // webpages are allowed to change ACLs after the fact. Normal conversation items aren't.
- if ($webpage) {
+ // Normal conversation items are not allowed to change ACL.
+ if (intval($item_type) !== ITEM_TYPE_POST) {
$acl->set_from_array($_POST);
}
else {
@@ -531,7 +535,7 @@ class Item extends Controller {
$private = intval($parent_item['item_private']);
$public_policy = $parent_item['public_policy'];
$owner_hash = $parent_item['owner_xchan'];
- $webpage = $parent_item['item_type'];
+ $item_type = $parent_item['item_type'];
}
@@ -592,7 +596,7 @@ class Item extends Controller {
$is_group = get_pconfig($profile_uid, 'system', 'group_actor');
- if ($is_group && $walltowall && !$walltowall_comment && !$webpage) {
+ if ($is_group && $walltowall && !$walltowall_comment && (intval($item_type) === ITEM_TYPE_POST)) {
$groupww = true;
$str_contact_allow = $owner_xchan['xchan_hash'];
$str_group_allow = '';
@@ -799,15 +803,13 @@ class Item extends Controller {
// determine if this is a wall post
+ if (in_array($item_type, [ITEM_TYPE_POST, ITEM_TYPE_CARD, ITEM_TYPE_ARTICLE])) {
+ $item_wall = 1;
+ }
+
if ($parent) {
$item_wall = $parent_item['item_wall'];
}
- else {
- if (!$webpage) {
- $item_wall = 1;
- }
- }
-
if ($moderated) {
$item_blocked = ITEM_MODERATED;
@@ -930,7 +932,7 @@ class Item extends Controller {
$datarray['item_unseen'] = intval($item_unseen);
$datarray['item_wall'] = intval($item_wall);
$datarray['item_origin'] = intval($item_origin);
- $datarray['item_type'] = $webpage;
+ $datarray['item_type'] = $item_type;
$datarray['item_private'] = intval($private);
$datarray['item_thread_top'] = intval($item_thread_top);
$datarray['item_starred'] = intval($item_starred);
@@ -1025,8 +1027,8 @@ class Item extends Controller {
if (mb_strlen($datarray['title']) > 191)
$datarray['title'] = mb_substr($datarray['title'], 0, 191);
- if ($webpage) {
- IConfig::Set($datarray, 'system', webpage_to_namespace($webpage),
+ if (intval($item_type) !== ITEM_TYPE_POST) {
+ IConfig::Set($datarray, 'system', item_type_to_namespace($item_type),
(($pagetitle) ? $pagetitle : basename($datarray['mid'])), true);
}
elseif ($namespace) {
diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php
index 218d35f1e..52c559a17 100644
--- a/Zotlabs/Module/Like.php
+++ b/Zotlabs/Module/Like.php
@@ -22,9 +22,9 @@ class Like extends Controller {
'like' => 'Like',
'dislike' => 'Dislike',
'announce' => ACTIVITY_SHARE,
- 'attendyes' => 'Accept',
- 'attendno' => 'Reject',
- 'attendmaybe' => 'TentativeAccept'
+ 'accept' => 'Accept',
+ 'reject' => 'Reject',
+ 'tentativeaccept' => 'TentativeAccept'
];
// unlike (etc.) reactions are an undo of positive reactions, rather than a negative action.
@@ -67,7 +67,7 @@ class Like extends Controller {
$items = conv_sort($items, 'commented');
}
else {
- $item = item_by_item_id($arr['item']['id']);
+ $item = item_by_item_id($arr['item']['id'], $arr['item']['parent']);
xchan_query($item, true);
$item = fetch_post_tags($item, true);
}
@@ -474,11 +474,11 @@ class Like extends Controller {
$bodyverb = t('%1$s likes %2$s\'s %3$s');
if ($verb === 'dislike')
$bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s');
- if ($verb === 'attendyes')
+ if ($verb === 'accept')
$bodyverb = t('%1$s is attending %2$s\'s %3$s');
- if ($verb === 'attendno')
+ if ($verb === 'reject')
$bodyverb = t('%1$s is not attending %2$s\'s %3$s');
- if ($verb === 'attendmaybe')
+ if ($verb === 'tentativeaccept')
$bodyverb = t('%1$s may attend %2$s\'s %3$s');
if (!isset($bodyverb))
@@ -561,7 +561,7 @@ class Like extends Controller {
call_hooks('post_local_end', $arr);
- if ($is_rsvp && in_array($verb, ['attendyes', 'attendmaybe'])) {
+ if ($is_rsvp && in_array($verb, ['accept', 'tentativeaccept'])) {
event_addtocal($item_id, local_channel());
}
diff --git a/Zotlabs/Module/Login.php b/Zotlabs/Module/Login.php
index 269990a54..f5a83a91a 100644
--- a/Zotlabs/Module/Login.php
+++ b/Zotlabs/Module/Login.php
@@ -5,10 +5,17 @@ namespace Zotlabs\Module;
class Login extends \Zotlabs\Web\Controller {
function get() {
- if(local_channel())
+ if (local_channel()) {
goaway(z_root());
- if(remote_channel() && $_SESSION['atoken'])
+ }
+
+ if (remote_channel() && $_SESSION['atoken']) {
goaway(z_root());
+ }
+
+ if (!empty($_GET['retry'])) {
+ notice( t('Login failed.') . EOL );
+ }
$o = '<div class="generic-content-wrapper">';
$o .= '<div class="section-title-wrapper">';
diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php
index fd30adccc..18f52591d 100644
--- a/Zotlabs/Module/Network.php
+++ b/Zotlabs/Module/Network.php
@@ -203,7 +203,7 @@ class Network extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($private_editing || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
- 'acl' => populate_acl((($private_editing) ? $def_acl : $channel_acl), true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
+ 'acl' => populate_acl((($private_editing) ? $def_acl : $channel_acl), true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => (($private_editing) ? $def_acl : $channel_acl),
'bang' => (($private_editing) ? $bang : ''),
'visitor' => true,
@@ -507,9 +507,7 @@ class Network extends \Zotlabs\Web\Controller {
// Then fetch all the children of the parents that are on this page
if($r) {
- $parents_str = ids_to_querystr($r, 'item_id');
-
- $items = items_by_parent_ids($parents_str, blog_mode: $blog_mode);
+ $items = items_by_parent_ids($r, blog_mode: $blog_mode);
xchan_query($items, true);
$items = fetch_post_tags($items, true);
diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php
index 79245f9c2..879d98216 100644
--- a/Zotlabs/Module/Pubstream.php
+++ b/Zotlabs/Module/Pubstream.php
@@ -84,7 +84,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
- 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
+ 'acl' => populate_acl($channel_acl,true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
@@ -251,10 +251,7 @@ class Pubstream extends \Zotlabs\Web\Controller {
$parents_str = '';
if($r) {
-
- $parents_str = ids_to_querystr($r,'item_id');
-
- $items = items_by_parent_ids($parents_str);
+ $items = items_by_parent_ids($r);
// use effective_uid param of xchan_query to help sort out comment permission
// for sys_channel owned items.
diff --git a/Zotlabs/Module/Request.php b/Zotlabs/Module/Request.php
index bfd75ad95..439f56282 100644
--- a/Zotlabs/Module/Request.php
+++ b/Zotlabs/Module/Request.php
@@ -12,9 +12,9 @@ class Request extends Controller
'like' => 'Like',
'dislike' => 'Dislike',
'announce' => 'Announce',
- 'attendyes' => 'Accept',
- 'attendno' => 'Reject',
- 'attendmaybe' => 'TentativeAccept'
+ 'accept' => 'Accept',
+ 'reject' => 'Reject',
+ 'tentativeaccept' => 'TentativeAccept'
];
if (array_key_exists($verb, $verbs)) {
@@ -29,13 +29,24 @@ class Request extends Controller
{
$mid = $_GET['mid'];
$parent = intval($_GET['parent']);
+
+ $offset = null;
+ if ($_GET['verb'] === 'load') {
+ $offset = intval($_GET['offset']);
+ }
+
$module = strip_tags($_GET['module']);
- $items = items_by_thr_parent($mid, $parent);
+ $items = items_by_thr_parent($mid, $parent, $offset);
xchan_query($items);
$items = fetch_post_tags($items,true);
+ if ($module === 'channel') {
+ $parts = explode('@', $items[0]['owner']['xchan_addr']);
+ profile_load($parts[0]);
+ }
+
$ret['html'] = conversation($items, $module, true, 'r_preview');
json_return_and_die($ret);
@@ -44,7 +55,7 @@ class Request extends Controller
public function get() : string
{
- if ($_GET['verb'] === 'comment') {
+ if (in_array($_GET['verb'], ['comment', 'load'])) {
return self::processSubthreadRequest();
}
diff --git a/Zotlabs/Module/Rpost.php b/Zotlabs/Module/Rpost.php
index 45f19d7e7..09513a44e 100644
--- a/Zotlabs/Module/Rpost.php
+++ b/Zotlabs/Module/Rpost.php
@@ -94,7 +94,7 @@ class Rpost extends \Zotlabs\Web\Controller {
'default_location' => $channel['channel_location'],
'nickname' => $channel['channel_address'],
'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'),
- 'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'),
+ 'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'member/permissions'),
'permissions' => $channel_acl,
'bang' => '',
'visitor' => true,
diff --git a/Zotlabs/Module/Xref.php b/Zotlabs/Module/Xref.php
deleted file mode 100644
index e9d494da4..000000000
--- a/Zotlabs/Module/Xref.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-namespace Zotlabs\Module;
-
-
-class Xref extends \Zotlabs\Web\Controller {
-
- function init() {
- // Sets a referral URL using an xchan directly
- // Link format: example.com/xref/[xchan]/[TargetURL]
- // Target URL is optional.
- // Cookie lasts 24 hours to survive a browser restart. Contains no personal
- // information at all - just somebody else's xchan.
- $referrer = argv(1);
- $expire=time()+60*60*2;
- $path = 'xref';
- setcookie($path, $referrer, $expire, "/");
- $url = '';
-
- if (argc() > 2)
- $url = argv(2);
-
- goaway (z_root() . '/' . $url);
-
- }
-
-}
diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php
index 9403d21a3..2faf3631a 100644
--- a/Zotlabs/Render/Theme.php
+++ b/Zotlabs/Render/Theme.php
@@ -87,8 +87,13 @@ class Theme {
// Find any theme at all and use it.
$fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php'));
- if(count($fallback))
- return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14))));
+
+ if (empty($fallback)) {
+ logger('Unable to find a theme');
+ http_status_exit(500, 'internal server error');
+ }
+
+ return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14))));
}
diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php
index 7c289ff5f..e53bcb3e9 100644
--- a/Zotlabs/Web/HTTPSig.php
+++ b/Zotlabs/Web/HTTPSig.php
@@ -2,6 +2,7 @@
namespace Zotlabs\Web;
+use App;
use DateTime;
use DateTimeZone;
use Zotlabs\Lib\Activity;
@@ -11,6 +12,9 @@ use Zotlabs\Lib\Keyutils;
use Zotlabs\Lib\Webfinger;
use Zotlabs\Lib\Zotfinger;
use Zotlabs\Lib\Libzot;
+use HttpSignature\HttpMessageSigner;
+use HttpSignature\UnProcessableSignatureException;
+
/**
* @brief Implements HTTP Signatures per draft-cavage-http-signatures-10.
@@ -88,7 +92,7 @@ class HTTPSig {
// See draft-cavage-http-signatures-10
- static function verify($data, $key = '', $keytype = '') {
+ public static function verify($data, $key = '', $keytype = '') {
$body = $data;
$headers = null;
@@ -102,11 +106,59 @@ class HTTPSig {
'content_valid' => false
];
-
$headers = self::find_headers($data, $body);
- if (!$headers)
+ if (!$headers) {
return $result;
+ }
+
+ if (App::$request && array_key_exists('signature-input', $headers) && array_key_exists('signature', $headers)) {
+ $found = preg_match('/keyid="(.*?)"/', $headers['signature-input'], $matches);
+ $keyId = ($found) ? $matches[1] : '';
+
+ if (!$keyId) {
+ return $result;
+ }
+
+ $found = preg_match('/alg="(.*?)"/', $headers['signature-input'], $matches);
+ $alg = ($found) ? $matches[1] : null;
+
+ $keyInfo = self::get_key($key, $keytype, $keyId);
+ $publicKey = $keyInfo['public_key'];
+
+ $messageSigner = new HttpMessageSigner();
+
+ $messageSigner->setPublicKey($publicKey);
+ $messageSigner->setAlgorithm($alg);
+ $messageSigner->setKeyId($keyId);
+
+ $messageSigner->setNonce(preg_match('/nonce="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : '');
+ $messageSigner->setTag(preg_match('/tag="(.*?)"/', $headers['signature-input'], $matches) ? $matches[1] : '');
+ $messageSigner->setCreated(preg_match('/created=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : '');
+ $messageSigner->setExpires(preg_match('/expires=([0-9]+)/', $headers['signature-input'], $matches) ? $matches[1] : '');
+
+ try {
+ $verified = $messageSigner->verifyRequest(App::$request);
+ if (!$verified) {
+ btlogger('RFC9421: Unable to verify request: ' . print_r($headers, true), LOGGER_DATA);
+ }
+ }
+ catch (\Exception $exception) {
+ btlogger($exception->getMessage(), LOGGER_DATA);
+ $verified = false;
+ }
+
+ logger('verified (RFC9421): ' . (($verified) ? 'true' : 'false'), LOGGER_DEBUG);
+
+ return [
+ 'signer' => $keyId,
+ 'portable_id' => $keyInfo['portable_id'] ?? '',
+ 'header_signed' => true,
+ 'header_valid' => $verified,
+ 'content_signed' => array_key_exists('content-digest', $headers),
+ 'content_valid' => $verified
+ ];
+ }
if (is_array($body)) {
btlogger('body is array:' . print_r($body, true));
diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php
index a59a509f2..89ef755d9 100644
--- a/Zotlabs/Web/WebServer.php
+++ b/Zotlabs/Web/WebServer.php
@@ -4,6 +4,7 @@ namespace Zotlabs\Web;
use App;
use Zotlabs\Lib\Text;
+use GuzzleHttp\Psr7\ServerRequest;
class WebServer {
@@ -18,6 +19,7 @@ class WebServer {
$installed = sys_boot();
+ App::$request = ServerRequest::fromGlobals();
App::$language = get_best_language();
load_translation_table(App::$language, !$installed);
diff --git a/Zotlabs/Widget/Album.php b/Zotlabs/Widget/Album.php
index f1fa69182..667952360 100644
--- a/Zotlabs/Widget/Album.php
+++ b/Zotlabs/Widget/Album.php
@@ -59,6 +59,9 @@ class Album {
//edit album name
$album_edit = null;
+ $ph = photo_factory('');
+ $phototypes = $ph->supportedTypes();
+
$photos = array();
if($r) {
$twist = 'rotright';
diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php
index 761679120..cc1811ffc 100644
--- a/Zotlabs/Widget/Messages.php
+++ b/Zotlabs/Widget/Messages.php
@@ -86,8 +86,6 @@ class Messages {
$entries = [];
$limit = 30;
$order_sql = 'i.created DESC';
- $dummy_order_sql = '';
- $filter_sql = '';
$loadtime = (($offset) ? $_SESSION['messages_loadtime'] : datetime_convert());
$vnotify = get_pconfig(local_channel(), 'system', 'vnotify', -1);
@@ -103,14 +101,18 @@ class Messages {
$vnotify_sql_i = " AND i.verb NOT IN ('Dislike', '" . dbesc(ACTIVITY_DISLIKE) . "') ";
}
+ $filter_sql = '';
if($type !== 'filed' && $author) {
$filter_sql = " AND (i.owner_xchan = '" . protect_sprintf(dbesc($author)) . "') ";
}
+ $filed_filter_sql = '';
if($type === 'filed' && $file) {
$filed_filter_sql = " AND (term.term = '" . protect_sprintf(dbesc($file)) . "') ";
}
+ $dummy_order_sql = '';
+
switch($type) {
case 'direct':
$type_sql = ' AND i.item_private = 2 AND i.item_thread_top = 1 ';
diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php
index fb256ec99..f9cee6e71 100644
--- a/Zotlabs/Widget/Notifications.php
+++ b/Zotlabs/Widget/Notifications.php
@@ -30,7 +30,7 @@ class Notifications {
'label' => t('Mark all read')
],
'filter' => [
- 'posts_label' => t('Conversation starters only'),
+ 'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
@@ -50,7 +50,7 @@ class Notifications {
'label' => t('Mark all seen')
],
'filter' => [
- 'posts_label' => t('Conversation starters only'),
+ 'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
@@ -69,7 +69,7 @@ class Notifications {
'label' => t('Mark all read')
],
'filter' => [
- 'posts_label' => t('Conversation starters only'),
+ 'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
@@ -163,7 +163,7 @@ class Notifications {
],
*/
'filter' => [
- 'posts_label' => t('Conversation starters only'),
+ 'posts_label' => t('Conversation starters'),
'name_label' => t('Filter by name or address')
]
];
diff --git a/Zotlabs/Widget/Pinned.php b/Zotlabs/Widget/Pinned.php
index 9daf396b8..7b95d3bc6 100644
--- a/Zotlabs/Widget/Pinned.php
+++ b/Zotlabs/Widget/Pinned.php
@@ -197,12 +197,12 @@ class Pinned {
return [];
- $r = q("SELECT parent FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0",
+ $r = q("SELECT parent AS item_id FROM item WHERE uuid IN ( '%s' ) AND uid = %d AND id = parent AND item_private = 0",
dbesc(implode(",", $mids_list)),
intval($this->uid)
);
- return items_by_parent_ids($r[0]['parent'], blog_mode: true);
+ return items_by_parent_ids($r, blog_mode: true);
}
diff --git a/Zotlabs/Widget/Portfolio.php b/Zotlabs/Widget/Portfolio.php
index bde1c7d6a..1c9dc162a 100644
--- a/Zotlabs/Widget/Portfolio.php
+++ b/Zotlabs/Widget/Portfolio.php
@@ -66,6 +66,10 @@ class Portfolio {
//edit album name
$album_edit = null;
+
+ $ph = photo_factory('');
+ $phototypes = $ph->supportedTypes();
+
$photos = array();
if($r) {
$twist = 'rotright';
diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php
index 2ef9e3b8d..1716ff84b 100644
--- a/Zotlabs/Zot6/Zot6Handler.php
+++ b/Zotlabs/Zot6/Zot6Handler.php
@@ -12,7 +12,7 @@ class Zot6Handler implements IHandler {
}
function Rekey($sender, $data, $hub) {
- return self::reply_rekey_request($sender, $data, $hub);
+ return self::rekey_request($sender, $data, $hub);
}
function Refresh($sender, $recipients, $hub, $force) {
diff --git a/boot.php b/boot.php
index 8fcf4d1f4..4f0a7c8ed 100644
--- a/boot.php
+++ b/boot.php
@@ -70,7 +70,7 @@ require_once('include/security.php');
define('PLATFORM_NAME', 'hubzilla');
-define('STD_VERSION', '10.3.34');
+define('STD_VERSION', '10.5');
define('ZOT_REVISION', '6.0');
define('DB_UPDATE_VERSION', 1263);
@@ -657,8 +657,10 @@ function sys_boot(): bool {
// allow somebody to set some initial settings just in case they can't
// install without special fiddling
- if (App::$install && file_exists('.htpreconfig.php'))
+ if (App::$install && file_exists('.htpreconfig.php')) {
+ // @phpstan-ignore include.fileNotFound
@include('.htpreconfig.php');
+ }
if (array_key_exists('default_timezone', get_defined_vars())) {
App::$config['system']['timezone'] = $default_timezone;
@@ -772,7 +774,7 @@ class miniApp {
*
*/
class App {
-
+ public static $request = null;
public static $install = false; // true if we are installing the software
public static $account = null; // account record of the logged-in account
public static $channel = null; // channel record of the current channel of the logged-in account
@@ -870,6 +872,9 @@ class App {
*/
public static $template_engine_instance = [];
+ /// Page layouts for comanche
+ public static array $page_layouts = [];
+
private static $ldelim = [
'internal' => '',
'smarty3' => '{{'
@@ -1797,26 +1802,6 @@ function shutdown() {
}
/**
- * @brief Returns the entity id of locally logged in account or false.
- *
- * Returns numeric account_id if authenticated or 0. It is possible to be
- * authenticated and not connected to a channel.
- *
- * @return int|bool account_id or false
- */
-function get_account_id() {
- if (isset($_SESSION['account_id'])) {
- return intval($_SESSION['account_id']);
- }
-
- if (App::$account) {
- return intval(App::$account['account_id']);
- }
-
- return false;
-}
-
-/**
* @brief Returns the entity id (channel_id) of locally logged in channel or false.
*
* Returns authenticated numeric channel_id if authenticated and connected to
diff --git a/composer.json b/composer.json
index 62793d7a5..66023d7de 100644
--- a/composer.json
+++ b/composer.json
@@ -48,7 +48,7 @@
"twbs/bootstrap": "^5.3",
"blueimp/jquery-file-upload": "^10.3",
"desandro/imagesloaded": "^4.1",
- "phpseclib/phpseclib": "^2.0.47",
+ "phpseclib/phpseclib": "^3.0.46",
"jbroadway/urlify": "^1.2",
"chillerlan/php-qrcode": "^5.0.3",
"spomky-labs/otphp": "^11.1",
@@ -56,7 +56,8 @@
"stephenhill/base58": "^1.1",
"mmccook/php-json-canonicalization-scheme": "^1.0",
"scssphp/scssphp": "^2.0.1",
- "twbs/bootstrap-icons": "^1.11"
+ "twbs/bootstrap-icons": "^1.11",
+ "macgirvin/http-message-signer": "^0.2"
},
"require-dev": {
"ext-yaml": "*",
diff --git a/composer.lock b/composer.lock
index edd07c234..d09aabf44 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,9 +4,91 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2eb30d4399172ceafecf36238f69d828",
+ "content-hash": "fc5da708517d924a34e28c262f9ef799",
"packages": [
{
+ "name": "bakame/http-structured-fields",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bakame-php/http-structured-fields.git",
+ "reference": "d0fc193e5b173a4e90f2fa589d5b97b2b653b323"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bakame-php/http-structured-fields/zipball/d0fc193e5b173a4e90f2fa589d5b97b2b653b323",
+ "reference": "d0fc193e5b173a4e90f2fa589d5b97b2b653b323",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "bakame/aide-base32": "dev-main",
+ "friendsofphp/php-cs-fixer": "^3.65.0",
+ "httpwg/structured-field-tests": "*@dev",
+ "phpbench/phpbench": "^1.3.1",
+ "phpstan/phpstan": "^2.0.3",
+ "phpstan/phpstan-deprecation-rules": "^2.0.1",
+ "phpstan/phpstan-phpunit": "^2.0.1",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^10.5.38 || ^11.5.0",
+ "symfony/var-dumper": "^6.4.15 || ^v7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Bakame\\Http\\StructuredFields\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://github.com/nyamsprod/",
+ "role": "Developer"
+ }
+ ],
+ "description": "A PHP library that parses, validates and serializes HTTP structured fields according to RFC9561 and RFC8941",
+ "keywords": [
+ "headers",
+ "http",
+ "http headers",
+ "http trailers",
+ "parser",
+ "rfc8941",
+ "rfc9651",
+ "serializer",
+ "structured fields",
+ "structured headers",
+ "structured trailers",
+ "structured values",
+ "trailers",
+ "validation"
+ ],
+ "support": {
+ "docs": "https://github.com/bakame-php/http-structured-fields",
+ "issues": "https://github.com/bakame-php/http-structured-fields/issues",
+ "source": "https://github.com/bakame-php/http-structured-fields"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-12T08:25:42+00:00"
+ },
+ {
"name": "blueimp/jquery-file-upload",
"version": "v10.32.0",
"source": {
@@ -508,6 +590,122 @@
"time": "2024-11-01T03:51:45+00:00"
},
{
+ "name": "guzzlehttp/psr7",
+ "version": "2.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+ "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-03-27T12:30:47+00:00"
+ },
+ {
"name": "jbroadway/urlify",
"version": "1.2.5-stable",
"source": {
@@ -882,6 +1080,49 @@
"time": "2016-09-22T15:10:54+00:00"
},
{
+ "name": "macgirvin/http-message-signer",
+ "version": "v0.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/macgirvin/HTTP-Message-Signer.git",
+ "reference": "47604de860b822cd202dcd8b1da910d6c84720ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/macgirvin/HTTP-Message-Signer/zipball/47604de860b822cd202dcd8b1da910d6c84720ab",
+ "reference": "47604de860b822cd202dcd8b1da910d6c84720ab",
+ "shasum": ""
+ },
+ "require": {
+ "bakame/http-structured-fields": "^2.0",
+ "ext-openssl": "*",
+ "guzzlehttp/psr7": "^2.0",
+ "php": "^8.1",
+ "phpseclib/phpseclib": "~3.0",
+ "phpseclib/phpseclib2_compat": "^1.0",
+ "psr/http-message": "^2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "HttpSignature\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "RFC 9421 HTTP Message Signer and Verifier for PSR-7 requests",
+ "support": {
+ "issues": "https://github.com/macgirvin/HTTP-Message-Signer/issues",
+ "source": "https://github.com/macgirvin/HTTP-Message-Signer/tree/v0.2.2"
+ },
+ "time": "2025-07-10T01:13:05+00:00"
+ },
+ {
"name": "michelf/php-markdown",
"version": "2.0.0",
"source": {
@@ -1063,6 +1304,56 @@
"time": "2024-05-08T12:36:18+00:00"
},
{
+ "name": "paragonie/random_compat",
+ "version": "v9.99.100",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">= 7"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*",
+ "vimeo/psalm": "^1"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "polyfill",
+ "pseudorandom",
+ "random"
+ ],
+ "support": {
+ "email": "info@paragonie.com",
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "time": "2020-10-15T08:29:30+00:00"
+ },
+ {
"name": "patrickschur/language-detection",
"version": "v5.3.1",
"source": {
@@ -1163,32 +1454,32 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "2.0.48",
+ "version": "3.0.46",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61"
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/eaa7be704b8b93a6913b69eb7f645a59d7731b61",
- "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "paragonie/constant_time_encoding": "^1|^2|^3",
+ "paragonie/random_compat": "^1.4|^2.0|^9.99.99",
+ "php": ">=5.6.1"
},
"require-dev": {
- "phing/phing": "~2.7",
- "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
- "squizlabs/php_codesniffer": "~2.0"
+ "phpunit/phpunit": "*"
},
"suggest": {
+ "ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
- "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
- "ext-xml": "Install the XML extension to load XML formatted public keys."
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"type": "library",
"autoload": {
@@ -1196,7 +1487,7 @@
"phpseclib/bootstrap.php"
],
"psr-4": {
- "phpseclib\\": "phpseclib/"
+ "phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1253,7 +1544,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/2.0.48"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
},
"funding": [
{
@@ -1269,7 +1560,55 @@
"type": "tidelift"
}
],
- "time": "2024-12-14T21:03:54+00:00"
+ "time": "2025-06-26T16:29:55+00:00"
+ },
+ {
+ "name": "phpseclib/phpseclib2_compat",
+ "version": "1.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpseclib/phpseclib2_compat.git",
+ "reference": "90976f25d6c2ff936878624b9cfaa322db11dde7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib2_compat/zipball/90976f25d6c2ff936878624b9cfaa322db11dde7",
+ "reference": "90976f25d6c2ff936878624b9cfaa322db11dde7",
+ "shasum": ""
+ },
+ "require": {
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "provide": {
+ "phpseclib/phpseclib": "2.0.47"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7|^6.0|^9.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "phpseclib\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "phpseclib 2.0 polyfill built with phpseclib 3.0",
+ "homepage": "https://github.com/phpseclib/phpseclib2_compat",
+ "support": {
+ "issues": "https://github.com/phpseclib/phpseclib2_compat/issues",
+ "source": "https://github.com/phpseclib/phpseclib2_compat"
+ },
+ "time": "2024-02-26T14:37:15+00:00"
},
{
"name": "psr/clock",
@@ -1478,6 +1817,50 @@
"time": "2024-09-11T13:17:53+00:00"
},
{
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
"name": "ramsey/collection",
"version": "2.1.1",
"source": {
diff --git a/doc/de/SUMMARY.md b/doc/de/SUMMARY.md
new file mode 100644
index 000000000..74e5a442a
--- /dev/null
+++ b/doc/de/SUMMARY.md
@@ -0,0 +1,142 @@
+# Summary
+
+- [Über](./about.md)
+ - [Grundlegende Konzepte von Hubzilla](./basicconcepts.md)
+ - [Funktionen](./functions.md)
+ - [Glossar](./glossary.md)
+ - [Oberfläche / Bezeichnungen](ui.md)
+- [Benutzerhandbuch](./usermanual/overview.md)
+ - [Anmeldung / Registrierung](./usermanual/registration.md)
+ - [Konten, Profile, Kanäle](./usermanual/accounts_profiles_channels_basics.md)
+ - [Kanäle](./usermanual/channels.md)
+ - [Kanäle erstellen](./usermanual/create_channels.md)
+ - [Kanalrollen](./usermanual/channel_roles.md)
+ - [Profile](./usermanual/profiles.md)
+ - [Einstellungen](./usermanual/settings.md)
+ - [Konto-Einstellungen](./usermanual/account_settings.md)
+ - [Kanal-Einstellungen](./usermanual/channel_settings.md)
+ - [Privacy-Einstellungen](./usermanual/privacy_settings.md)
+ - [Anzeige-Einstellungen](./usermanual/display_settings.md)
+ - [Klon-Adressen verwalten](./usermanual/channel_locations.md)
+ - [Stream-Einstellungen](./usermanual/stream_settings.md)
+ - [Zusätzliche Funktionen (verborgene Einstellungen)](./usermanual/additional_features.md)
+ - [Verbinden mit Kanälen](./usermanual/connecting_with_channels.md)
+ - [Posten](./usermanual/posting.md)
+ - [Kommentieren](./usermanual/commenting.md)
+ - [Bilder einfügen](./usermanual/insert_images.md)
+ - [bbCode](./usermanual/bbcode.md)
+ - [Das Grid](./usermanual/the_grid.md)
+ - [Der Stream](./usermanual/the_stream.md)
+ - [Mit Postings interagieren](./usermanual/interact.md)
+ - [Repeat](./usermanual/repeat.md)
+ - [Teilen](./usermanual/share.md)
+ - [Link zur Quelle](./usermanual/link_to_source.md)
+ - [In Ordner speichern](./usermanual/save_to_folder.md)
+ - [Markierungsstatus (Stern) umschalten](./usermanual/toggle_star_status.md)
+ - [Quellcode anzeigen](./usermanual/show_source_code.md)
+ - [Unterhaltung folgen / nicht mehr folgen](./usermanual/follow_conversation.md)
+ - [Löschen](./usermanual/delete.md)
+ - [Konversationsmerkmale](./usermanual/conversation_features.md)
+ - [Inhaltsfilter](./usermanual/content_filters.md)
+ - [Öffentlicher Beitrags-Stream](./usermanual/public_stream.md)
+ - [Apps](./usermanual/apps.md)
+ - [wichtige Apps](./usermanual/important_apps.md)
+ - [Verbindungen](./usermanual/connections.md)
+ - [Verbindungs-Editor](./usermanual/connection_editor.md)
+ - [Diaspora-Kompatibilität](./usermanual/diaspora_compat.md)
+ - [Verzeichnis](./usermanual/directory.md)
+ - [Erweiterte Verzeichnissuche](./usermanual/AdvancedSearch.md)
+ - [Kanäle blockieren/ignorieren/archivieren/verstecken](./usermanual/blocking_channels.md)
+ - [Superblock](./usermanual/superblock.md)
+ - [Berechtigungen](./usermanual/permissions.md)
+ - [Berechtigungen für Inhalte](./usermanual/permissions_content.md)
+ - [Berechtigungen - Benutzerdefinierte Kanalrollen](./usermanual/permissions_channel_roles.md)
+ - [Berechtigungen - Kontaktrollen](./usermanual/permissions_contact_roles.md)
+ - [Direktnachrichten](./usermanual/direct_messages.md)
+ - [Erwähnungen](./usermanual/mentions.md)
+ - [Tags](./usermanual/tags.md)
+ - [Bookmarks](./usermanual/bookmarks.md)
+ - [Suche](./usermanual/search.md)
+ - [Artikel](./usermanual/article.md)
+ - [Dateien](./usermanual/files.md)
+ - [Fotos](./usermanual/photos.md)
+ - [Cloudspeicher](./usermanual/cloud_storage.md)
+ - [Galerie](./usermanual/gallery.md)
+ - [Chaträume](./usermanual/chat_rooms.md)
+ - [Gastzugang](./usermanual/guest_access.md)
+ - [Privacy Gruppen](./usermanual/privacy_groups.md)
+ - [Kalender](./usermanual/calendar.md)
+ - [Adressbuch](./usermanual/addressbook.md)
+ - [Wikis](./usermanual/wikis.md)
+ - [Inhaltswarnung/NSFW](./usermanual/NSFW.md)
+ - [Klonen](./usermanual/clone.md)
+ - [Webseiten](./usermanual/websites.md)
+ - [Comanche Seitenbeschreibungssprache](./usermanual/comanche.md)
+ - [Verschlüsselung](./usermanual/encryption.md)
+ - [Tipps zum Schutz der Privatsphäre](./usermanual/protection_of_privacy.md)
+ - [Kanal löschen](./usermanual/deleting_channel.md)
+ - [Account löschen](./usermanual/delete_account.md)
+- [Tutorials](./tutorials/tutorials.md)
+ - [Mit Hubzilla Schritt für Schritt ins Fediverse](./tutorials/step_with_hubzilla.md)
+ - [Hubzilla optisch anpassen](./tutorials/customise_look.md)
+ - [Ein abgeleitetes Thema erstellen](./tutorials/DerivedTheme1.md)
+- [Handbuch für Administratoren](./adminmanual/manual_for_administrators.md)
+ - [Wo Sie weitere Hilfe finden](./adminmanual/further_help.md)
+ - [Bevor Sie beginnen](./adminmanual/before_you_start.md)
+ - [Installation](./adminmanual/installation.md)
+ - [Anforderungen](./adminmanual/installation_requirements.md)
+ - [Manuelle Installation](./adminmanual/manual_installation.md)
+ - [Automatisierte Installation über das Shell-Skript .homeinstall](./adminmanual/automated_installation.md)
+ - [Installation mittels Docker](./adminmanual/Installation_using_docker.md)
+ - [Empfohlene Addons](./adminmanual/recommended_addons.md)
+ - [Föderations Addons](./adminmanual/federation_addons.md)
+ - [Probleme nach einer Aktualisierung](./adminmanual/problems-following-an-update.md)
+ - [Service-Klassen](./adminmanual/service_classes.md)
+ - [Verwaltung der Themen](./adminmanual/theme_management.md)
+ - [Erstellen von Seitenvorlagen](./adminmanual/Creating-Templates.md)
+ - [Hubzilla-Entwicklung - ein Leitfaden für das Schemasystem](./adminmanual/Schema-development.md)
+ - [Grundlegende Widgets](./adminmanual/Widgets.md)
+ - [Kanal-Verzeichnis](./adminmanual/channel_directory.md)
+ - [Primäres Verzeichnis](./adminmanual/Primary-Directory.md)
+ - [Administration](./adminmanual/administration.md)
+ - [Fehlersuche](./adminmanual/troubleshooting.md)
+ - [Hub-Snapshot-Tools](./adminmanual/hub_snapshot_tools.md)
+ - [Datenbank](./adminmanual/database.md)
+ - [Erweiterte Konfigurationen für Administratoren](./adminmanual/advanced_configurations.md)
+ - [CLI-Tools (utils)](./adminmanual/CLI_tools.md)
+ - [Datei-Synchronisation und Klonen](./adminmanual/filesync.md)
+ - [Nomad - Ein Überblick auf oberer Ebene](./adminmanual/Nomad---A-High-Level-Overview.md)
+ - [Admin FAQ](./adminmanual/faq_admins.md)
+- [Entwickler](./develmanual/developers_guide.md)
+ - [Wer ist ein Hubzilla-Entwickler? Sollte ich das lesen?](./develmanual/who_is_a_hubzilla_developer.md)
+ - [Sie möchten Code beisteuern?](./develmanual/dev_beginner.md)
+ - [Versionen und Releases](./develmanual/versions.md)
+ - [Git-Repository-Zweige](./develmanual/git_repository.md)
+ - [Git für Nicht-Entwickler](./develmanual/git_for_non_developers.md)
+ - [Tools und Arbeitsabläufe für Entwickler](./develmanual/tools_workflows.md)
+ - [Dokumentation erstellen](./develmanual/doco.md)
+ - [Übersetzungen](./develmanual/translations.md)
+ - [Lizenzvergabe](./develmanual/licensing.md)
+ - [Codierungsstil](./develmanual/coding_style.md)
+ - [Dateisystem-Layout](./develmanual/file_system_layout.md)
+ - [Hubzilla-Entwicklung - einige nützliche Grundfunktionen](./develmanual/dev-function-overview.md)
+ - [Erstellen von Plugins/Addons für Hubzilla](./develmanual/Plugins.md)
+ - [Testen](./develmanual/testing.md)
+ - [Erstellen von Protokoll- Föderationsdiensten](./develmanual/federate.md)
+ - [Verhaltenskodex für Mitwirkende](./develmanual/code_of_conduct.md)
+ - [Unser Versprechen](./develmanual/our_pledge.md)
+ - [Unsere Standards](./develmanual/our_standards.md)
+ - [Unsere Verantwortlichkeiten](./develmanual/our_responsibilities.md)
+ - [Geltungsbereich](./develmanual/scope.md)
+ - [Durchsetzung](./develmanual/enforcement.md)
+ - [Namensnennung](./develmanual/attribution.md)
+ - [Das Nomad Protokoll](./develmanual/nomad_protocol.md)
+ - [Technische Einführung](./develmanual/technical_introduction.md)
+ - [Magic Auth](./develmanual/magic_auth.md)
+ - [Nomad-Strukturen](./develmanual/nomad_structures.md)
+ - [Zot API](./develmanual/API.md)
+ - [Hooks](./develmanual/hooks.md)
+ - [Noch zu organisierende Informationen](./develmanual/unorganized.md)
+- [Das Hubzilla-Projekt](./the_hubzilla_project.md)
+- [Credits](./credits.md)
+- [Über diesen Hub](./platzhalter.md)
diff --git a/doc/de/about.md b/doc/de/about.md
new file mode 100644
index 000000000..7ca94690a
--- /dev/null
+++ b/doc/de/about.md
@@ -0,0 +1,88 @@
+# Was ist Hubzilla?
+
+Hubzilla ist ein dezentralisiertes Kommunikationsnetzwerk mit dem Ziel, Kommunikationsmöglichkeiten bereitzustellen, die Zensur umgehen, die Privatsphäre respektieren und somit frei sind von den Einschränkungen, die die heutigen kommerziellen Kommunikationsgiganten uns auferlegen. Diese stellen in erster Linie Spionagenetzwerke für zahlende Kunden aller Art zur Verfügung und monopolisieren und zentralisieren das ganze Internet – was ursprünglich eben gerade nicht unter den revolutionären Zielen war, die einst zum World Wide Web führten.
+
+Hubzilla ist frei, kostenlos und Open Source. Es wurde entwickelt, um auf einem Raspberry Pi ebenso zu laufen wie auf den größten AMD- und Intel-Xeon-Multiprozessor-Servern. Es kann für die Kommunikation zwischen einigen wenigen Einzelpersonen genutzt werden oder viele tausend Leute und mehr miteinander verbinden.
+
+Ein weiteres Ziel ist es, von Können und Ressourcen unabhängig zu sein. Hubzilla ist für den einfachen Computernutzer ebenso leicht bedienbar wie für Systemadministratoren und Entwickler.
+
+Wie Du es benutzt hängt davon ab, wie Du es benutzen *willst.*
+
+Hubzilla ist in PHP geschrieben, dadurch ist es einfach, sie auf jedweder heutigen Hosting-Plattform zu installieren, inklusive Self-Hosting zu Hause, auf Shared Servern oder auf virtuellen und dedizierten Servern.
+
+Mit anderen Worten, Hubzilla kann auf jeder Plattform laufen, die einen Web-Server, eine MySQL-kompatible Datenbank und PHP mitbringt.
+
+---
+
+## Grundlegende Konzepte und Funktionen von Hubzilla
+
+
+
+### Kanäle (im Sinne von Identitäten im Gegensatz zu einer App)
+
+Als Nutzer (d. h. als Privatperson oder als Verein) können Sie eine oder mehrere Web-**Identitäten** erstellen. Die Webpräsenz einer Identität wird innerhalb von Hubzilla gebündelt: Ein Besucher der Web-Identität sieht Inhalte, die mit dieser Identität in Verbindung stehen, an einem Ort, der über das Hauptmenü *Apps* für diese Identität verfügbar ist.
+
+Intern, innerhalb der Software, wird eine Identität als „Kanal“ bezeichnet. Für einen Besucher bedeutet das Wort „Kanal“ jedoch **eine** bestimmte App, die zu dieser Identität gehört, nämlich die **Pinnwand**, auf der die (möglicherweise) föderierten Beiträge der Identität in einer zeitachsenbasierten Timeline angezeigt werden.
+
+Im Folgenden verstehen wir Kanäle als Identitäten: Als angemeldeter Benutzer können Sie zwischen Ihren Kanälen wechseln, um die Inhalte für jeden Kanal zu bearbeiten, d. h. Beiträge zu veröffentlichen oder ein oder mehrere Kanalprofile, Websites, Wiki-Seiten und mehr zu erstellen. Pro Kanal können Sie auch Dateien in einer Cloud verwalten, Fotos mit Tags versehen und benennen und die Fotos in einer Webgalerie anzeigen. Ereignisse des Kanals können in einem Kalender angezeigt werden.
+
+
+
+### Dezentrales Netzwerk: Zugriff über Grenzen hinweg (Zot/Nomad) vs. Bereitstellung über Grenzen hinweg (Zot/Nomad, ActivityPub und Diaspora)
+
+Wenn dies erlaubt ist, verbinden sich Identitäten auf Hubzilla über Server- und Verwaltungsgrenzen hinweg über das Kommunikationsprotokoll Zot/Nomad (und, sofern von Ihrem Server bereitgestellt, auch über die Protokolle ActivityPub und Diaspora).
+
+Mit Ausnahme von Beiträgen/Nachrichten bleiben alle veröffentlichten Inhalte lokal auf Ihrem Server. Sie können Inhalte zwar öffentlich veröffentlichen, aber auch lokale Inhalte nur mit bestimmten Verbindungen teilen. Letzteres ist nur mit Verbindungen über Zot/Nomad (also mit Identitäten auf Hubzilla und Streams) möglich.
+
+
+
+### Zugriffskontrolle
+
+Ihre lokalen Inhalte können von den Zot/Nomad-basierten Verbindungen „besucht“ werden, denen Sie die entsprechende Berechtigung erteilt haben. Indem Sie Ihre Verbindungen Verbindungslisten (sogenannten „Datenschutzgruppen“) zuordnen, können Sie allen Mitgliedern dieser Liste Zugriff auf bestimmte Inhalte gewähren.
+
+Sie können Beiträge/Nachrichten (auch über die Protokolle ActivityPub oder Disaspora) an eine Verbindung, an eine Datenschutzgruppe oder an die Öffentlichkeit senden, indem Sie eine Zugriffskontrollliste verwenden.
+
+Es ist möglich, eine Dauer für das Ablaufen eines Beitrags/einer Nachricht festzulegen. Auf diese Weise können Sie den Zugriff zeitlich begrenzen.
+
+
+
+### Fernauthentifizierung
+
+„Kennst du mich? – Ja, ich kenne dich, willkommen!“
+Der Zugriff auf nicht öffentliche Inhalte auf einem Remote-Server sollte nur möglich sein, wenn du dich gegenüber diesem Server authentifizieren kannst. Fediverse-Software, die nur das ActivityPub- oder das Diaspora-Protokoll verwendet, kann jedoch nur Konten vom lokalen Server authentifizieren.
+Das Zot-Protokoll hingegen verfügt über einen integrierten Mechanismus namens MagicAuth, der es einem Server ermöglicht, Identitäten, die auf einem anderen (entfernten) Server registriert sind, Zugriff auf Inhalte und Aktionen zu gewähren oder zu verweigern.
+
+
+
+### Nomadische Identität
+
+Das Zot/Nomad-Protokoll ermöglicht es, Ihre Kanäle von dem Hub zu trennen, auf dem Sie sie erstellt haben. Sie können sie auf einen anderen Hub übertragen oder klonen. In diesem Fall existieren die Identität und die Daten des Kanals gleichzeitig an mehreren Orten. Dies sorgt für Ausfallsicherheit der Kanäle, falls ein Hub abgeschaltet wird oder nicht mehr verfügbar ist.
+
+
+
+### Modulares Ökosystem durch Apps
+
+ Hubzilla stellt für jede Funktion eine separate **App** zur Verfügung, d. h. die Cloud, Fotos, Galerie, Chat, Wiki, Kalender, Kontakte oder die Verbindungs-App. Ein Serveradministrator entscheidet, welche Apps für die Benutzer dieses Servers verfügbar sein sollen. Ein Benutzer kann dann die verfügbaren Apps installieren oder deinstallieren.
+
+
+
+### Zusammenarbeit
+
+Die folgenden gemeinsamen privaten Bereiche ermöglichen die Arbeit im Team mit Hubzilla:
+- Als Benutzer erlauben Sie ausgewählten Verbindungen, Ihre Webseiten und Wiki-Seiten zu lesen und zu bearbeiten.
+- Erlauben Sie ihnen, Ihre Dateien, Ihren Kalender und Ihre Kontakte über die Weboberfläche (die Cloud-, Kalender- und Kontakt-Apps) oder WebDAV, CalDAV und CardDAV zu lesen und zu bearbeiten.
+- Verwenden Sie Konversations-Threads (Beiträge), die nur für Ihre Kollaborationsgruppe sichtbar sind, indem Sie die Zugriffskontrollliste entsprechend einstellen.
+
+
+
+### Cloud-Speicher
+
+Sie können Dokumente direkt auf Ihrer Website speichern und mit anderen teilen. Öffentlicher, privater oder eingeschränkter Zugriff. Da die Dateien auf Ihrem Domainnamen gespeichert sind, wissen die Empfänger, dass sie von Ihnen stammen.
+
+
+
+### Teilen vs. Boosten
+
+Mit Hubzilla können Sie Beiträge anderer Fediverse-Nutzer (erneut) teilen. Das (erneute) Teilen in Hubzilla ist so, als würden Sie Ihren Freunden Jennie und Omar erzählen, was Giaco gesagt hat. Und dann sagen sie: „Cool, das gefällt mir“.
+
+Hubzilla ermöglicht auch das Boosten. Etwas zu boosten ist so, als würdest du deinen Freunden Jennie und Omar erzählen, was Giaco gesagt hat, während Giaco und alle seine Freunde und seine Familie (die du nicht kennst) zuhören. Giaco und alle seine Freunde und seine Familie können jetzt mit dir sprechen.
diff --git a/doc/de/about/about.bb b/doc/de/about/about.bb
deleted file mode 100644
index 27ea4ca9a..000000000
--- a/doc/de/about/about.bb
+++ /dev/null
@@ -1,23 +0,0 @@
-[h3]Was ist $Projectname?[/h3]
-
-$Projectname ist ein dezentralisiertes Kommunikationsnetzwerk mit dem Ziel, Kommunikationsmöglichkeiten bereitzustellen, die Zensur umgehen, die Privatsphäre respektieren und somit frei sind von den Einschränkungen, die die heutigen kommerziellen Kommunikationsgiganten uns auferlegen. Diese stellen in erster Linie Spionagenetzwerke für zahlende Kunden aller Art zur Verfügung und monopolisieren und zentralisieren das ganze Internet – was ursprünglich eben gerade nicht unter den revolutionären Zielen war, die einst zum World Wide Web führten.
-
-$Projectname ist frei, kostenlos und Open Source. Sie wurde entwickelt, um auf einem Raspberry Pi für € 30,– ebenso zu laufen wie auf den größten AMD- und Intel-Xeon-Multiprozessor-Servern. Es kann für die Kommunikation zwischen einigen wenigen Einzelpersonen genutzt werden oder viele tausend Leute und mehr miteinander verbinden.
-
-Ein weiteres Ziel ist es, von Können und Ressourcen unabhängig zu sein. $Projectname ist für den einfachen Computernutzer ebenso leicht bedienbar wie für Systemadministratoren und Entwickler.
-
-Wie Du es benutzt hängt davon ab, wie Du es benutzen [i]willst.[/i]
-
-$Projectname ist in PHP geschrieben, dadurch ist es einfach, sie auf jedweder heutigen Hosting-Plattform zu installieren, inklusive Self-Hosting zu Hause, auf Shared Servern wie bei [url=https://uberspace.de/]Uberspace[/url], [url=http://mediatemple.com/]Media Temple[/url] und [url=http://www.dreamhost.com/]Dreamhost[/url], oder auf virtuellen und dedizierten Servern, wie es sie zum Beispiel bei [url=https://www.linode.com]Linode[/url], [url=http://greenqloud.com]GreenQloud[/url] oder [url=https://aws.amazon.com]Amazon AWS[/url] gibt.
-
-Mit anderen Worten, $Projectname kann auf jeder Plattform laufen, die einen Web-Server, eine MySQL-kompatible Datenbank und PHP mitbringt.
-
-Dabei bietet $Projectname einige einzigartige Leckerbissen:
-
-[b]Ein-Klick-Identifikation:[/b] Du kannst auf andere Server im $Projectname-Netzwerk zugreifen, indem Du einfach auf einen Link dorthin klickst. Die Authentifizierung wird ganz einfach automatisch hinter den Kulissen durchgeführt. Vergiss viele verschiedene Usernamen für verschiedene Seiten und die Passwörter dazu – das tut alles $Projectname für Dich.
-
-[b]Klone:[/b] Du kannst Deine Online-Identität (oder, wie wir sagen, einen Kanal) klonen. Sie ist nicht mehr länger an einen bestimmten Server, eine Domain oder eine IP-Adresse gebunden. Importiere sie einfach auf einem anderen $Projectname-Server (oder $Projectname-Hub, wie es bei uns heißt) – direkt online oder mit Hilfe eines vorher generierten Exports. Wenn Dein primärer Hub plötzlich nicht mehr online ist, kein Problem, Deine Kontakte, Posts* und Nachrichten* sind automagisch weiterhin unter Deiner geklonten Identität verfügbar und zugreifbar. [i](*: nur Posts und Nachrichten, die nach dem Moment des Klonens erstellt wurden)[/i]
-
-[b]Privatsphäre:[/b] $Projectname-Identitäten (Zot-IDs) können gelöscht, gesichert/heruntergeladen und geklont werden. Du hast volle Kontrolle über Deine Daten. Wenn Du Dich entscheidest, all Deine Daten und Deine Zot-ID zu löschen, musst Du nur auf einen Link klicken, und sie werden sofort von dem Server gelöscht. Keine Fragen, keine Umstände.
-
-#include doc/macros/main_footer.bb;
diff --git a/doc/de/about_hub.bb b/doc/de/about_hub.bb
new file mode 100644
index 000000000..0c1082f51
--- /dev/null
+++ b/doc/de/about_hub.bb
@@ -0,0 +1,7 @@
+[h3]Site Info[/h3]
+[list][*][url=[baseurl]/siteinfo]Site Info[/url]
+[*][url=[baseurl]/siteinfo/json]Site Info (JSON format)[/url][/list]
+[h3]Terms of Service[/h3]
+[list][*][url=[baseurl]/help/TermsOfService]Terms of Service for this hub[/url][/list]
+#include doc/SiteTOS.md;
+
diff --git a/doc/de/adminmanual/CLI_tools.md b/doc/de/adminmanual/CLI_tools.md
new file mode 100644
index 000000000..7bd27b5ca
--- /dev/null
+++ b/doc/de/adminmanual/CLI_tools.md
@@ -0,0 +1,103 @@
+### CLI Tools (utils)
+
+Wer als Administrator Zugang zur Shell hat, kann weitere kleine CLI Tools verwenden, welche sich im Verzeichnis "utils" finden.
+
+#### addons
+
+Mit dem Script addons können Sie sich anzeigen lassen, welche Addons installiert und welche vorhanden sind. Außerdem können Sie Addons installieren und deinstallieren, sowie alle Addons neu installieren.
+
+- `util/addons list`
+ Anzeige aller installierter Addons
+- `util/addons list all`
+ Anzeige aller Addons , die installiert sind (*) und solcher, die aufgrund von Inkompatibilität deaktiviert sind (!)
+- `util/addons install foo`
+ Installiere das Addon mit dem Namen "foo"
+- `util/addons uninstall foo`
+ Deinstalliere das Addon mit dem Namen "foo"
+- `util/addons reinstall`
+ Reinstalliere alle Addons
+
+#### admins
+
+Das Script admins ermöglicht es Ihnen, sich sämtliche Admins des Hub anzeigen zu lassen, sowie weitere Admins hinzuzufügen und bestehende Admins zu entfernen.
+
+- `util/admins`
+- `util/admins list`
+- `util/admins add <account_id>`
+- `util/admins remove <account_id>`
+
+#### config / pconfig
+
+Siehe: [Erweiterte Konfigurationen für Administratoren](/help/de/adminmanual/advanced_configurations.md)
+
+#### connect
+
+Mit connect können Sie eine Verbindung zwischen einem Kanal Ihres Hubs mit einem anderen Kanal herstellen.
+
+- `util/connect <channel_id> <channel@hub>`
+- `util/connect <channel_address> <channel@hub>`
+
+#### dcp
+
+Mit dcp können Sie Dateien in den Store-Bereich eines Kanals auf Ihrem Hub kopieren.
+
+- `util/dcp <quelle> <zielverzeichnis>` wobei Zielverzeichnis `store/$nickname/path` oder `$nickname/path` sein muss.
+
+#### dmkdir
+
+Mit dmkdir können Sie im Store-Bereich eines Kanals auf Ihrem Hub ein Unterverzeichnis anlegen.
+
+- `util/dmkdir <directory>` wobei Directory `store/$nickname/path/<directory>` oder `$nickname/path/<directory>` sein muss.
+
+#### fresh (The Freaking REd Shell)
+
+Dies funktioniert nur unter Unix/Linux. Wenn das readline-Modul installiert ist, verwenden es dieses für die Eingabe, ansonsten liest es nur von stdin und schreibt nach stdout.
+Die Befehle werden der Reihe nach abgearbeitet, bis der Befehl „exit“, „quit“ oder das Ende der Datei erreicht ist.
+
+Kommandos:
+
+- `version`
+ Zeigt die aktuelle Fresh-Version an.
+- `login <E-Mail-Adresse>`
+ Fragt nach einem Passwort und authentifiziert `<E-Mail-Adresse>` als den aktuellen
+ Benutzer.
+- `finger <Kanal-Adresse>`
+ Führt einen Lookup von `<Kanal-Adresse>` durch und meldet das Ergebnis.
+- `channel <Kanal-Nickname>`
+ Schaltet den aktuellen Kanal auf den Kanal mit dem angegebenen Spitznamen um.
+- `conn [<id1> <id2> ...]`
+- Ohne Argumente listet dies alle Verbindungen des aktuellen Kanals mit einer ID auf.
+ Wenn IDs angegeben werden, werden die Details der einzelnen Verbindungen angezeigt.
+
+#### hz
+
+Einfaches, minimalistisches Kommandozeilentool, um den Status über die API an hubzilla zu senden. Erfordert curl.
+
+`hz [<Konfigurationsdatei>]`
+
+hz benötigt eine Konfigurationsdatei. Sie können entweder eine Datei `.hubzilla` verwenden und den Parameter `<Konfigurationsdatei>` weglassen oder eine Konfigurationsdatei unter einem beliebigen Namen im Hubzilla-Verzeichnis erstellen, deren Namen Sie dann bei Aufruf von hz angeben.
+
+Format der Konfigurationsdatei:
+
+```
+USER=<Ihr Benutzername>
+PASS=<Ihr Passwort>
+HUB=<Domain Ihres Hubs>
+```
+
+Anschließend können Sie Ihr Posting eingeben und die Eingabe mit Strg-D abschließen.
+
+#### storageconf
+
+Mit storageconf können Sie den Speicherort für Thumbnails festlegen (Dateisystem oder Datenbank), sowie die aktuelle Konfiguration abfragen.
+
+- `util/storageconv stats`
+ Zeigt den aktuell eingestellten Status
+- `util/storageconv fs`
+ Verschiebt die Thumbnails von der Datenbank ins Dateisystem
+- `util/storageconv db`
+ Verschiebt die Thumbnails vom Dateisystem in die Datenbank
+
+#### thumbrepair
+
+thumbrepair erstellt die lokalen Thumbnails neu. \ No newline at end of file
diff --git a/doc/de/adminmanual/Creating-Templates.md b/doc/de/adminmanual/Creating-Templates.md
new file mode 100644
index 000000000..61b596e1f
--- /dev/null
+++ b/doc/de/adminmanual/Creating-Templates.md
@@ -0,0 +1,79 @@
+### Erstellen von Seitenvorlagen
+
+Für eine Seitenvorlage zur Verwendung mit Comanche sind zwei Dateien erforderlich - eine PHP-Vorlage und eine CSS-Datei. Die Seitenvorlagen müssen vom Systemadministrator Ihrer Website installiert werden.
+Wählen Sie zunächst einen Namen. In diesem Fall werden wir eine Vorlage erstellen und sie „demo“ nennen.
+Sie müssen die Dateien „view/php/demo.php“ und „view/css/demo.css“ erstellen, um die PHP-Vorlage bzw. das CSS zu speichern.
+Um eine bessere Vorstellung von diesem Prozess zu bekommen, schauen wir uns eine bestehende Vorlage an - die „Standard“-Vorlage. Diese wird standardmäßig in der gesamten Anwendung verwendet.
+
+#### **view/php/default.php**
+
+```
+<!DOCTYPE html >
+<html>
+<head>
+ <title><?php if(x($page,'title')) echo $page['title'] ?></title>
+ <script>var baseurl=„<?php echo z_root() ?>“;</script>
+ <?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?>
+</head>
+<body>
+ <?php if(x($page,'nav')) echo $page['nav']; ?>
+ <aside id=„region_1“><?php if(x($page,'aside')) echo $page['aside']; ?></aside>
+ <section id=„region_2“><?php if(x($page,'content')) echo $page['content']; ?>
+ <div id=„page-footer“></div>
+ <div id=„pause“></div>
+ </abschnitt>
+<aside id=„region_3“><?php if(x($page,'right_aside')) echo $page['right_aside']; ?></aside>
+<footer><?php if(x($page,'footer')) echo $page['footer']; ?></footer>
+</body>
+</html>
+```
+
+Hier ist die entsprechende CSS-Datei
+
+#### **view/php/default.css**
+
+```
+aside#region_1 {
+ Anzeige: block;
+ Breite: 210px;
+ Position: absolut;
+ oben: 65px;
+ links: 0;
+ margin-left: 10px;
+}
+
+beiseite input[type='text'] {
+ Breite: 174px;
+}
+section {
+ Position: absolut;
+ top: 65px;
+ links: 250px;
+ Anzeige: block;
+ rechts: 15px;
+ padding-bottom: 350px;
+}
+```
+
+Wenn Sie sich diese Definitionen ansehen, fallen Ihnen vielleicht einige Dinge auf:
+
+- Wir haben kein CSS für die Bereiche „nav“, „right_aside“ oder „footer“ angegeben. In dieser Vorlage werden „nav“ und „footer“ die volle Seitenbreite einnehmen, und wir lassen die Größe und Platzierung dieser Elemente durch das Thema steuern. „right_aside“ wird derzeit nicht verwendet.
+- Es gibt Elemente auf der Seite wie „page-footer“ und „pause“, für die es keinen sichtbaren Inhalt gibt. Dieser Inhalt wird von Javascript-Elementen stammen.
+- Unsere Standardvorlage verwendet eine absolute Positionierung. Modernes Webdesign verwendet häufig „float“-Div-Container, so dass bei der Anzeige auf Geräten mit kleinen Bildschirmen in der Regel keine Bildlaufleisten erforderlich sind.
+
+Um eine neue Vorlage zu entwerfen, ist es am besten, mit einer bestehenden Vorlage zu beginnen und sie nach Wunsch zu ändern. Genau das werden wir hier tun.
+Die Art und Weise, wie Comanche Inhalte innerhalb einer bestimmten Region bereitstellt, ist die Verwendung eines Region-Tags.
+
+```
+[region=aside][widget=profile][/widget][/region]
+```
+
+In diesem Beispiel wird ein „Profil“-Widget in der Region „aside“ platziert. In Wirklichkeit wird jedoch der HTML-Code für das Widget in der Code-Variablen **$page['aside']** abgelegt. Unsere Standardseitenvorlage definiert einen Bereich auf der Seite (das CSS positioniert diesen als absolute Seitenleiste) und fügt dann den Inhalt von $page['aside'] ein (falls vorhanden).
+Wenn Sie also eine Vorlage mit einem Bereich namens „foo“ erstellen wollten, würden Sie einen Platz dafür auf der Seite vorsehen, dann den Inhalt von $page['foo'] dort einfügen, wo Sie ihn verwenden wollen, und dann mit Comanche angeben
+
+```
+[region=foo][widget=profile][/widget][/region]
+```
+
+und dies würde ein Profil-Widget in der von Ihnen erstellten „foo“-Region platzieren.
+Verwenden Sie die CSS-Datei, um den Bereich an der gewünschten Stelle auf der Seite zu positionieren und optional seine Größe zu steuern. \ No newline at end of file
diff --git a/doc/de/adminmanual/Installation_using_docker.md b/doc/de/adminmanual/Installation_using_docker.md
new file mode 100644
index 000000000..0f59847a6
--- /dev/null
+++ b/doc/de/adminmanual/Installation_using_docker.md
@@ -0,0 +1,56 @@
+### Installation mittels Docker
+
+Es besteht die Möglichkeit, Hubzilla komfortabel und bequem als Docker-Container zu installieren. Dafür bietet Saiwal ([sk@hub.utsukta.org](https://hub.utsukta.org/channel/sk)) ein vorkonfigurierte Umgebung für einen Hubzilla-Container an.
+
+Hauptfeatures sind:
+
+- Einfache Bereitstellung: Verwenden Sie Docker Compose, um eine voll funktionsfähige Hubzilla-Instanz mit nur wenigen Befehlen einzurichten.
+- Benutzerdefinierte Konfiguration: Passen Sie Ihre Hubzilla-Installation ganz einfach mit Umgebungsvariablen für SMTP, Datenbank und mehr an.
+- Kontinuierliche Updates: Das Docker-Image ist so aufgebaut, dass es einfach aktualisiert werden kann, wenn neue Änderungen am Hubzilla-Kern oder seinen Abhängigkeiten vorgenommen werden.
+- SMTP-Integration: Integrierte Unterstützung für den Versand von E-Mails mit ssmtp, wodurch die Konfiguration von E-Mail-Benachrichtigungen für Ihre Hubzilla-Instanz erleichtert wird.
+
+Das Repository für den Container befindet sich hier: [**skprg/hubzilla-docker**](https://github.com/skprg/hubzilla-docker)
+
+#### Das Image von Grund auf neu erstellen
+
+- Klonen Sie das Repository:
+
+```
+git clone https://github.com/skprg/hubzilla-docker.git
+cd hubzilla-docker
+```
+
+- Konfigurieren Sie Ihre Umgebung: Aktualisieren Sie die Datei `docker-compose.yml` mit Ihren SMTP- und anderen Einstellungen.
+- Erstellen und führen Sie den Container aus:
+
+```
+docker-compose up --build -d
+```
+
+#### Vorgefertigtes Image verwenden
+
+- Ersetzen Sie die folgenden Zeilen in `docker-compose.yml`
+
+```
+build:
+ context: .
+ Dockerfile: Dockerfile
+```
+
+mit
+
+```
+image: ghcr.io/skprg/hubzilla-docker:latest
+```
+
+- Starten Sie den Container:
+
+```
+docker compose up -d
+```
+
+Greifen Sie auf Ihre Hubzilla-Instanz zu: Navigieren Sie zu http://localhost (oder der entsprechenden URL), um Ihre Hubzilla-Instanz anzuzeigen.
+
+#### Weitere Hinweise / Update / Tipps
+
+Weitere Hinweise, Upgrade-Anweisungen und Tipps finden Sie im oben verlinkten Repository. \ No newline at end of file
diff --git a/doc/de/adminmanual/Nomad---A-High-Level-Overview.md b/doc/de/adminmanual/Nomad---A-High-Level-Overview.md
new file mode 100644
index 000000000..c86febf6f
--- /dev/null
+++ b/doc/de/adminmanual/Nomad---A-High-Level-Overview.md
@@ -0,0 +1,111 @@
+### Nomad - Ein Überblick auf oberer Ebene
+
+Hier ist eine allgemeine Beschreibung der Funktionsweise von Nomad.
+
+In diesem Beispiel wird „Indigo“ eine öffentliche Nachricht von seiner Website „podunk.edu“ aus versenden. „Nickordo„ ist ein Empfänger auf einer anderen Website (“example.com").
+
+Indigo stellt seine Nachricht zuerst auf podunk.edu ein. podunk.edu schaut nach, wer die Nachricht erhalten soll und findet Nickordo. Nickordo postet normalerweise von example.com aus, also fügen wir dieses Ziel zu unserer Liste der Empfänger hinzu. Wir können auch andere Ziele für Nickordo und alle anderen, die Indigos Beiträge verfolgen, hinzufügen.
+
+In diesem Beispiel stellen wir fest, dass wir nur einen bekannten Empfänger an einem bekannten Ort haben.
+
+Wir senden ein Paket an example.com:
+
+
+
+ {
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
+ }
+
+In diesem Paket steht das Folgende:
+
+Ich bin Indigo und hier ist der Beweis. Ich poste von podunk.edu und hier ist der Beweis. Ich habe ein Paket für Sie. Die Kontrollnummer lautet „1eaa6613....“.
+
+Example.com nimmt dieses Paket an und sagt: „Moment mal - ich kenne Sie nicht. Ich möchte beweisen, wer du bist.“ Example.com stellt also eine Verbindung zu podunk.edu über eine „bekannte URL“ her, die wir für diesen Zweck verwenden, und sucht nach der oben genannten „guid“. Es sollte eine Reihe von Informationen zurückgeben, darunter auch einen öffentlichen Schlüssel. Example.com verwendet diesen Schlüssel, um die Signaturen in der Nachricht zu überprüfen und festzustellen, ob es tatsächlich eine Person namens Indigo bei podunk.edu gibt. Wir müssen dies nur einmal tun. (Beachten Sie, dass Indigo von jedem Ort aus posten kann. Wir müssen nur beweisen, dass es sich um Indigo handelt und dass Indigo beweisen kann, dass er von einem anderen Standort aus postet).
+
+Dann trennt example.com die Verbindung und weist darauf hin, dass eine Nachricht auf podunk.edu wartet. Entweder sofort oder immer dann, wenn der Drang besteht (je nachdem, wie wichtig Indigo für jemanden auf dieser Website ist), „ruft“ example.com podunk.edu an. Die Nachricht lautet in etwa so:
+
+
+
+ {
+ "type":"pickup",
+ "url":"http:\/\/example.com",
+ "callback_sig":"teE1_fLIqfyeCuZY4iS7sNU8jUlUuqYOYBiHLarkC99I9K-uSr8DAwVW8ZPZRK-uYdxRMuKFb6cumF_Gt9XjecCPBM8HkoXHOi_VselzJkxPwor4ZPtWYWWaFtRfcAm794LrWjdz62zdESTQd2JJIZWbrli1sUhK801BF3n0Ye6-X1MWhy9EUTVlNimOeRipcuD_srMhUcAXOEbLlrugZ8ovy2YBe6YOXkS8jj0RSFjsOduXAoVhQmNpcobSYsDvaQS3e3MvE6-oXE602zGQhuNLr7DIMt9PCdAeQo-ZM-DHlZGCkGk4O2oQFCXFzGPqLUMWDACGJfTfIWGoh_EJqT_SD5b_Yi_Wk9S1lj7vb-lmxe5JuIf7ezWzHoBT8vswnZxPYlidH2i9wapdzij9il_qqcCWWHIp7q_XkY_Zj52Z4r4gdmiqM-8y1c_1SDX7hrJFRwqL_PKFbEvyi5nMWTEzqp55Tay5Woiv19STK_H_8ufFfD9AOkYnk6rIOMsk9dn3a5tAFpDRyRndXkBWAXwiJjiND2zjue7BFu7Ty40THXcfYRh1a5XrAXcaGeYuagg-8J9tAufu9_LY3qGazFg8kRBVMOn4M8DRKSIhKj7z4MnbYL0s09gREojy4jqWO3VkaOjP2jUGzoPuUDLasudE1ehWFq0K_MTQNavgmp8",
+ "callback":"http:\/\/example.com\/post",
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"O7nB4_UJHBXi28Suwl9LBZF9hI_9KGVTgehnUlWF1oYMNRnBbVHB9lzUfAoalvp3STbU3xJbtD_S58tv6MfV7J5j2V_S1W5ex3dulmDGB8Pt_7Fe5mbEPmjQFcfv3Eg5dUjYIuDl0TDScfrHyImj7RZIWHbwd7wWVoMzzDa_o33klpYmKZCBvObCh55bRrlFkXZs_dRuOiPwkfX0C6_XES4OyOIYl45V30rdhmf-STrf4L9dKYy_axQ12RIwRcKychvVLwlUJn3bn9lgNXRRU_HTne-09OPcJbUOdcD3DkFoKOxMULBNKPHzsCau0ICYug7S0EP6LpCom_mW78s08LyVA1vYeFZjevBCiGecj57yIAQDYi6_rpWJfihYaWHRN0oqtScUR4Bdf0bQbEHxMs4zAtrOAxfyJCbi6U1pfnGgzXzB9ulOYGnVGNTF7Ey4K7FOZIBtk0ILY2JfvBUaVvVs8ttagOOHmhWhnbCvrnOFlkNdlce7zoJCSUJENUOCYmTRfwB_Jno5fAzRnrsYU3_Z-l1mzniU_OmUPz8mPEh7PwhkqAiVlyaM-q15gn7l2lAIDk9kp2X_iCme7v4V0ADN_DbpaI_0-6mPw5HLbKrCsA-sxlSMB4DO4lDCHYkauj0l25sbfroRWB_hax1O4Q0oWyOlVJLUqEC5nuUJCCE"
+ }
+
+Was diese Nachricht aussagt, ist: Hier ist example.com, ich habe einen Beweis, und ich bin hier, um ein Paket abzuholen. Hier ist die Sendungsverfolgungsnummer, und hier ist der Beweis, dass dies die Sendungsverfolgungsnummer ist, die Sie vermutlich an example.com gesendet haben.
+
+Gut genug. Podunk.edu überprüft die Geschichte und tatsächlich, es ist example.com, und ja, da wartet ein Paket mit dieser Kontrollnummer. Hier ist das Paket...
+
+
+
+ {
+ "success":1,
+ "pickup":{
+ "notify":{
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/z.podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
+ },
+ "message":{
+ "message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "created":"2012-11-20 04:04:16",
+ "edited":"2012-11-20 04:04:16",
+ "title":"",
+ "body":"Hi Nickordo",
+ "app":"",
+ "verb":"post",
+ "object_type":"",
+ "target_type":"",
+ "permalink":"",
+ "location":"",
+ "longlat":"",
+ "owner":{
+ "name":"Indigo",
+ "address":"indigo@podunk.edu",
+ "url":"http:\/\/podunk.edu",
+ "photo":{
+ "mimetype":"image\/jpeg",
+ "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
+ },
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ },
+ "author":{
+ "name":"Indigo",
+ "address":"indigo@podunk.edu",
+ "url":"http:\/\/podunk.edu",
+ "photo":{
+ "mimetype":"image\/jpeg",
+ "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
+ },
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ }
+ }
+ }
+ }
+
+
+
+Und das ist das Paket (die ursprüngliche Nachricht). Example.com wandelt diese in eine Form um, die von Nickordo eingesehen werden kann, und benachrichtigt Nickordo, dass es eine neue Nachricht gibt. Podunk.edu **könnte** feststellen, dass noch andere Pakete auf example.com warten. Wenn dies der Fall ist, kann es auch alle anderen wartenden Pakete zu diesem Zeitpunkt versenden. Jedes dieser Pakete ist mit der ursprünglichen Sendungsnummer versehen. \ No newline at end of file
diff --git a/doc/de/adminmanual/Primary-Directory.md b/doc/de/adminmanual/Primary-Directory.md
new file mode 100644
index 000000000..9de1d6c98
--- /dev/null
+++ b/doc/de/adminmanual/Primary-Directory.md
@@ -0,0 +1,35 @@
+### Primäres Verzeichnis
+
+Standardmäßig verwendet Hubzilla verfügbare Verzeichnisse im Internet, die Ihnen weltweit verfügbare Kanäle anzeigen.
+
+Es gibt bestimmte Szenarien, in denen Sie Ihren eigenen Verzeichnis-Server benötigen, mit dem Sie mehrere Hubs verbinden können. Dadurch werden die Kanäle, die in allen Ihren Hubs erscheinen, auf die Kanäle der Hubs beschränkt, die mit Ihrem Verzeichnis-Server verbunden sind.
+
+#### Anleitungen zum Einrichten eines Hubs als primäres Verzeichnis für eine Reihe von privaten Hubs.
+
+- Öffnen Sie auf dem Hub, der der Directory Server sein wird, die Datei .htconfig.php und setzen Sie
+ `App::$config['system']['directory_mode'] = DIRECTORY_MODE_PRIMARY;`
+
+```
+Standardmäßig sollte dies bereits **DIRECTORY_MODE_NORMAL** sein, also ändern Sie diese Zeile einfach in **DIRECTORY_MODE_PRIMARY**.
+```
+
+- Als Nächstes gehen Sie für jeden Hub (einschließlich des Verzeichnisservers) von einem Terminal aus in den Ordner, in dem er installiert ist, und führen Folgendes aus:
+
+ `util/config system directory_realm YOURREALMNAME`
+ (**YOURREALMNAME** kann jeder beliebige Name für Ihren Realm sein)
+
+ dann:
+
+ `util/config system realm_token THEPASSWORD`
+ (**THEPASSWORD** ist das Passwort, das Sie für Ihren Realm verwenden möchten)
+ **HINWEIS:** Verwenden Sie für jeden Hub denselben Realm-Namen und dasselbe Passwort.
+
+- Führen Sie schließlich für jeden „Client“-Hub (von einem Terminal aus) aus:
+
+ `util/config system directory_server https://theaddressofyourdirectoryserver.com`
+
+Wenn Sie sich nun das Verzeichnis jedes Hubs ansehen, sollten nur die Kanäle angezeigt werden, die auf den Hubs in Ihrem Realm existieren. Ich habe bisher mit zwei Hubs getestet, und es scheint gut zu funktionieren. Channels, die in jedem Hub erstellt wurden, werden im Primärverzeichnis und anschließend im Verzeichnis aller Client-Hubs angezeigt.
+
+#### Probleme
+
+Als ich den ersten Hub erstellte, lief er etwa eine Stunde lang, bevor ich ihn auf PRIMARY_MODE umstellte, und nachdem ich ihn umgestellt hatte, waren noch einige Kanäle aus der gesamten Matrix im Verzeichnis vorhanden. Ich habe sie aus der xchan-Tabelle gelöscht, und das scheint das Problem behoben zu haben. \ No newline at end of file
diff --git a/doc/de/adminmanual/Schema-development.md b/doc/de/adminmanual/Schema-development.md
new file mode 100644
index 000000000..5e2304e61
--- /dev/null
+++ b/doc/de/adminmanual/Schema-development.md
@@ -0,0 +1,45 @@
+### Hubzilla-Entwicklung - ein Leitfaden für das Schemasystem
+
+Ein Schema ist, kurz gesagt, eine Sammlung von Einstellungen für eine Reihe von Variablen, die bestimmte Elemente eines Themes definieren. Ein Schema wird so geladen, als wäre es Teil der config.php und hat Zugang zu allen Informationen. Das bedeutet, dass es identitätsbewusst ist und für einige interessante Dinge verwendet werden kann. Man könnte z. B. die Optionen nach Dienstklassen einschränken oder verschiedenen Mitgliedern unterschiedliche Optionen anbieten.
+
+Standardmäßig filtern wir nur danach, ob der Expertenmodus aktiviert ist oder nicht. Wenn der Expertenmodus aktiviert ist, werden dem Mitglied alle Optionen angezeigt. Ist dies nicht der Fall, stehen nur Schema, Hintergrundbild, Schriftart und Iconset zur Auswahl.
+
+Ein Schema wird *erst nach* den persönlichen Einstellungen des Mitglieds geladen. Um einem Mitglied die Möglichkeit zu geben, einen bestimmten Aspekt eines Schemas zu überschreiben, würden Sie daher die folgende Syntax verwenden:
+
+```
+ if (! $foo)
+ $foo = 'bar';
+```
+
+Es gibt jedoch Umstände - insbesondere bei positionalen Elementen -, unter denen es wünschenswert (oder notwendig) sein kann, die Einstellungen eines Mitglieds zu überschreiben. In diesem Fall ist die Syntax noch einfacher:
+
+```
+ $foo = 'bar';
+```
+
+Die Mitglieder werden Ihnen dies jedoch nicht danken, also verwenden Sie es nur, wenn es erforderlich ist.
+Wenn keine persönlichen Optionen eingestellt sind und kein Schema ausgewählt ist, wird zunächst versucht, ein Schema mit dem Dateinamen „default.php“ zu laden. Diese Datei sollte niemals in ein Thema eingebunden werden. Wenn dies der Fall ist, wird es zu Konflikten kommen, wenn andere ihren Code aktualisieren. Vielmehr sollte dies von den Administratoren für jede Seite einzeln festgelegt werden. default.php und default.css MÜSSEN Symlinks zu bestehenden Schemadateien sein.
+
+Ihr Schema muss - und sollte - nicht alle diese Werte enthalten. Nur die Werte, die von den Standardwerten abweichen, sollten aufgeführt werden. Dies gibt Ihnen einige sehr mächtige Optionen mit sehr wenigen Zeilen Code.
+
+Beachten Sie, dass die verfügbaren Optionen von Thema zu Thema unterschiedlich sind. Für das Redbasic-Theme sind folgende Optionen verfügbar:
+
+- nav_colour Die Farbe der Navigationsleiste. Die Optionen sind rot, schwarz und silber. Alternativ kann man $nav_bg_1, $nav_bg_2, $nav_bg_3 und $nav_bg_4 einstellen, um Farbverläufe und Hover-Effekte zu erzielen.
+- banner_colour Die Schriftfarbe des Bannerelements. Akzeptiert einen RGB- oder Hex-Wert.
+- bgcolour Legt die Hintergrundfarbe des Körpers fest. Akzeptiert einen RGB- oder Hex-Wert.
+- background_image Legt ein Hintergrundbild fest. Akzeptiert eine URL oder einen Pfad.
+- item_colour Legt die Hintergrundfarbe von Elementen fest. Akzeptiert einen RGB- oder Hex-Wert.
+- item_opacity Legt die Deckkraft der Elemente fest. Akzeptiert einen Wert von 0,01 bis 1
+- toolicon_colour Legt die Farbe der Werkzeugsymbole fest. Akzeptiert einen RGB- oder Hex-Wert.
+- toolicon_activecolour Legt die Farbe der aktiven oder mit dem Mauszeiger gehaltenen Werkzeugsymbole fest.
+- font_size Legt die Größe der Schriftarten in Artikeln und Beiträgen fest. Akzeptiert px oder em.
+- body_font_size Legt die Größe der Schriftarten auf der Body-Ebene fest. Akzeptiert px oder em.
+- font_colour Legt die Farbe der Schrift fest. Akzeptiert einen RGB- oder Hex-Wert.
+- radius Legt den Radius der Ecken fest. Akzeptiert einen Zahlenwert und ist immer in px.
+- shadow Legt die Größe der Schatten fest, die bei Inline-Bildern angezeigt werden. Akzeptiert einen numerischen Wert. Hinweis: Schatten werden nicht auf Smileys angewendet.
+- converse_width Legt die maximale Breite des Inhaltsbereichs in px fest.
+- nav_min_opacity
+- top_photo
+- antwort_foto
+
+Wenn eine Datei your_schema_name.css gefunden wird, wird der Inhalt dieser Datei an das Ende von style.css angehängt. Dies gibt dem Schema-Entwickler die Möglichkeit, jede Style-Komponente zu überschreiben. \ No newline at end of file
diff --git a/doc/de/adminmanual/Widgets.md b/doc/de/adminmanual/Widgets.md
new file mode 100644
index 000000000..a222a0533
--- /dev/null
+++ b/doc/de/adminmanual/Widgets.md
@@ -0,0 +1,99 @@
+### Grundlegende Widgets
+
+Einige/viele dieser Widgets haben Einschränkungen, die die Art der Seite, auf der sie erscheinen können, einschränken oder eine Anmeldung erfordern können
+
+- clock - zeigt die aktuelle Zeit an
+ - args: military (1 oder 0) - verwendet 24-Stunden-Zeit anstelle von AM/PM
+- profile - zeigt eine Profil-Seitenleiste auf Seiten an, die Profile laden (Seiten mit Nickname in der URL)
+- tagcloud - zeigt eine Tagcloud von Webseiten-Elementen an
+ - args: count - Anzahl der Elemente, die zurückgegeben werden sollen (Standardwert 24)
+- collections - Auswahl der Privatsphärengruppe für den aktuell eingeloggten Kanal
+ - args: mode - eine der Optionen „conversation“, „group“, „abook“ je nach Modul
+- suggestions - Freundesvorschläge für den aktuell angemeldeten Kanal
+- follow - zeigt ein Textfeld zum Folgen eines anderen Channels an
+- notes - privater Notizbereich für den aktuell eingeloggten Kanal, wenn die Funktion private_notes aktiviert ist
+- savedsearch - Netzwerk-/Matrixsuche mit Speicherung - muss eingeloggt und die savedsearch-Funktion aktiviert sein
+- filer - Auswahl abgelegter Elemente aus dem Netzwerk/Matrix-Stream - muss eingeloggt sein
+- archive - Datumsbereichswähler für Netzwerk- und Kanalseiten
+ - args: 'wall' - 1 oder 0, Begrenzung auf Wall-Posts oder Netzwerk-/Matrix-Posts (Standard)
+- fullprofile - wie das aktuelle Profil
+- categories - Kategorien-Filter (Channel-Seite)
+- tagcloud_wall - Tagcloud nur für Channelseite
+ - args: 'limit' - Anzahl der Tags, die zurückgegeben werden sollen (Standardwert 50)
+- catcloud_wall - Tagcloud für die Kategorien der Kanalseite
+ - args: 'limit' - Anzahl der zurückzugebenden Kategorien (Standardwert 50)
+- affinity - Affinitäts-Schieberegler für die Netzwerk-Seite - muss eingeloggt sein
+- settings_menu - Seitenleistenmenü für die Einstellungsseite, muss eingeloggt sein
+- mailmenu - Seitenleistenmenü für die Seite mit den privaten Nachrichten - muss eingeloggt sein
+- design_tools - Menü für Design-Tools für Seiten zur Erstellung von Webseiten, muss eingeloggt sein
+- findpeople - Werkzeuge, um andere Kanäle zu finden
+- photo_albums - Liste der Fotoalben des aktuellen Seitenbesitzers mit einem Auswahlmenü
+- vcard - Mini-Profil-Seitenleiste für die Person von Interesse (Seitenbesitzer, was auch immer)
+- dirsafemode - Werkzeug zur Verzeichnisauswahl - nur auf Verzeichnisseiten
+- dirsort - Werkzeug zur Verzeichnisauswahl - nur auf Verzeichnisseiten
+- dirtags - Verzeichnis-Werkzeug - nur auf Verzeichnisseiten
+- menu_preview - Vorschau eines Menüs - nur auf Menübearbeitungsseiten
+- chatroom_list - Liste der Chaträume für den Seitenbesitzer
+- bookmarkedchats - Liste der Chaträume mit Lesezeichen, die auf dieser Seite für den aktuellen Betrachter gesammelt wurden
+- suggestedchats - „interessante“ Chaträume für den aktuellen Betrachter ausgewählt
+- item - zeigt ein einzelnes Webpage-Element nach Mitte oder Seitentitel an
+ - args:
+ - channel_id - Kanal, dem der Inhalt gehört, Standard ist die profile_uid
+ - mid - message_id der anzuzeigenden Webseite (muss eine Webseite sein, kein Konversationselement)
+ - title - URL-Seitentitel der Webseite (muss entweder title oder mid enthalten)
+- photo - Anzeige eines einzelnen Fotos
+ - args:
+ - src - URL des Fotos, muss http oder https sein
+ - zrl - zid-authentifizierter Link verwenden
+ - style - CSS-Style-String
+- cover_photo - zeigt das Titelbild für den ausgewählten Kanal an
+ - args:
+ - channel_id - zu verwendender Kanal, Standard ist die profile_uid
+ - style - CSS-Style-String (standardmäßig wird die Größe dynamisch an die Breite des Bereichs angepasst)
+
+- photo_rand - zeigt ein zufälliges Foto aus einem Ihrer Fotoalben an. Fotoberechtigungen werden beachtet
+ - args:
+ - album - Name des Albums (sehr zu empfehlen, wenn Sie viele Fotos haben)
+ - scale - typischerweise 0 (Originalgröße), 1 (1024px), 2, (640px) oder 3 (320px)
+ - style - CSS-Style-String
+ - channel_id - wenn nicht Ihre eigene
+- random_block - zeigt ein zufälliges Blockelement aus Ihrer Sammlung von Webpage Design Tools an. Erlaubnisse werden beachtet.
+ - args:
+ - contains - gibt nur Blöcke zurück, die den String contains im Blocknamen enthalten
+ - channel_id - wenn nicht Ihre eigene
+- tasklist - liefert eine Aufgaben- oder To-Do-Liste für den aktuell eingeloggten Channel.
+ - args:
+ - all - zeigt erledigte Aufgaben an, wenn all ungleich Null ist.
+- forums - liefert eine Liste der verbundenen öffentlichen Foren mit ungesehenen Beiträgen für den aktuell eingeloggten Channel.
+- activity - liefert eine Liste der Autoren von ungelesenen Netzwerkinhalten für den aktuell eingeloggten Channel.
+- album - stellt ein Widget zur Verfügung, das ein komplettes Fotoalbum aus den Alben des Seitenbesitzers enthält; dies kann zu groß sein, um in einem Seitenleistenbereich dargestellt zu werden, und wird am besten als Widget für einen Inhaltsbereich implementiert.
+ - args:
+ - album - Name des Albums
+ - title - optionaler Titel, wenn nicht vorhanden wird der Albumname verwendet
+
+#### **Neue Widgets erstellen**
+
+#### **Klassen-Widgets**
+
+Um ein klassenbasiertes Widget namens 'slugfish' zu erstellen, erstellen Sie eine Datei mit folgendem Inhalt:
+
+```php
+<?php
+namespace Zotlabs\Widget;
+class Slugfish {
+
+ function widget($args) {
+
+ ... der Widget-Code kommt hier hin.
+ ... Die Funktion gibt einen String zurück, der den HTML-Inhalt des Widgets darstellt.
+ ... $args ist ein benanntes Array, dem alle [var]-Variablen aus dem Layout-Editor übergeben werden
+ ... Zum Beispiel füllt [widget=slugfish][var=count]3[/var][/widget] $args mit
+ ... [ 'count' => 3 ]
+ }
+
+}
+
+?>
+```
+
+Die resultierende Datei kann in Zotlabs/Widgets/Slugfish.php abgelegt werden. Sie kann auch von einem Git-Repository mit util/add_widget_repo verlinkt werden. \ No newline at end of file
diff --git a/doc/de/adminmanual/Zot---A-High-Level-Overview.md b/doc/de/adminmanual/Zot---A-High-Level-Overview.md
new file mode 100644
index 000000000..c86febf6f
--- /dev/null
+++ b/doc/de/adminmanual/Zot---A-High-Level-Overview.md
@@ -0,0 +1,111 @@
+### Nomad - Ein Überblick auf oberer Ebene
+
+Hier ist eine allgemeine Beschreibung der Funktionsweise von Nomad.
+
+In diesem Beispiel wird „Indigo“ eine öffentliche Nachricht von seiner Website „podunk.edu“ aus versenden. „Nickordo„ ist ein Empfänger auf einer anderen Website (“example.com").
+
+Indigo stellt seine Nachricht zuerst auf podunk.edu ein. podunk.edu schaut nach, wer die Nachricht erhalten soll und findet Nickordo. Nickordo postet normalerweise von example.com aus, also fügen wir dieses Ziel zu unserer Liste der Empfänger hinzu. Wir können auch andere Ziele für Nickordo und alle anderen, die Indigos Beiträge verfolgen, hinzufügen.
+
+In diesem Beispiel stellen wir fest, dass wir nur einen bekannten Empfänger an einem bekannten Ort haben.
+
+Wir senden ein Paket an example.com:
+
+
+
+ {
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
+ }
+
+In diesem Paket steht das Folgende:
+
+Ich bin Indigo und hier ist der Beweis. Ich poste von podunk.edu und hier ist der Beweis. Ich habe ein Paket für Sie. Die Kontrollnummer lautet „1eaa6613....“.
+
+Example.com nimmt dieses Paket an und sagt: „Moment mal - ich kenne Sie nicht. Ich möchte beweisen, wer du bist.“ Example.com stellt also eine Verbindung zu podunk.edu über eine „bekannte URL“ her, die wir für diesen Zweck verwenden, und sucht nach der oben genannten „guid“. Es sollte eine Reihe von Informationen zurückgeben, darunter auch einen öffentlichen Schlüssel. Example.com verwendet diesen Schlüssel, um die Signaturen in der Nachricht zu überprüfen und festzustellen, ob es tatsächlich eine Person namens Indigo bei podunk.edu gibt. Wir müssen dies nur einmal tun. (Beachten Sie, dass Indigo von jedem Ort aus posten kann. Wir müssen nur beweisen, dass es sich um Indigo handelt und dass Indigo beweisen kann, dass er von einem anderen Standort aus postet).
+
+Dann trennt example.com die Verbindung und weist darauf hin, dass eine Nachricht auf podunk.edu wartet. Entweder sofort oder immer dann, wenn der Drang besteht (je nachdem, wie wichtig Indigo für jemanden auf dieser Website ist), „ruft“ example.com podunk.edu an. Die Nachricht lautet in etwa so:
+
+
+
+ {
+ "type":"pickup",
+ "url":"http:\/\/example.com",
+ "callback_sig":"teE1_fLIqfyeCuZY4iS7sNU8jUlUuqYOYBiHLarkC99I9K-uSr8DAwVW8ZPZRK-uYdxRMuKFb6cumF_Gt9XjecCPBM8HkoXHOi_VselzJkxPwor4ZPtWYWWaFtRfcAm794LrWjdz62zdESTQd2JJIZWbrli1sUhK801BF3n0Ye6-X1MWhy9EUTVlNimOeRipcuD_srMhUcAXOEbLlrugZ8ovy2YBe6YOXkS8jj0RSFjsOduXAoVhQmNpcobSYsDvaQS3e3MvE6-oXE602zGQhuNLr7DIMt9PCdAeQo-ZM-DHlZGCkGk4O2oQFCXFzGPqLUMWDACGJfTfIWGoh_EJqT_SD5b_Yi_Wk9S1lj7vb-lmxe5JuIf7ezWzHoBT8vswnZxPYlidH2i9wapdzij9il_qqcCWWHIp7q_XkY_Zj52Z4r4gdmiqM-8y1c_1SDX7hrJFRwqL_PKFbEvyi5nMWTEzqp55Tay5Woiv19STK_H_8ufFfD9AOkYnk6rIOMsk9dn3a5tAFpDRyRndXkBWAXwiJjiND2zjue7BFu7Ty40THXcfYRh1a5XrAXcaGeYuagg-8J9tAufu9_LY3qGazFg8kRBVMOn4M8DRKSIhKj7z4MnbYL0s09gREojy4jqWO3VkaOjP2jUGzoPuUDLasudE1ehWFq0K_MTQNavgmp8",
+ "callback":"http:\/\/example.com\/post",
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"O7nB4_UJHBXi28Suwl9LBZF9hI_9KGVTgehnUlWF1oYMNRnBbVHB9lzUfAoalvp3STbU3xJbtD_S58tv6MfV7J5j2V_S1W5ex3dulmDGB8Pt_7Fe5mbEPmjQFcfv3Eg5dUjYIuDl0TDScfrHyImj7RZIWHbwd7wWVoMzzDa_o33klpYmKZCBvObCh55bRrlFkXZs_dRuOiPwkfX0C6_XES4OyOIYl45V30rdhmf-STrf4L9dKYy_axQ12RIwRcKychvVLwlUJn3bn9lgNXRRU_HTne-09OPcJbUOdcD3DkFoKOxMULBNKPHzsCau0ICYug7S0EP6LpCom_mW78s08LyVA1vYeFZjevBCiGecj57yIAQDYi6_rpWJfihYaWHRN0oqtScUR4Bdf0bQbEHxMs4zAtrOAxfyJCbi6U1pfnGgzXzB9ulOYGnVGNTF7Ey4K7FOZIBtk0ILY2JfvBUaVvVs8ttagOOHmhWhnbCvrnOFlkNdlce7zoJCSUJENUOCYmTRfwB_Jno5fAzRnrsYU3_Z-l1mzniU_OmUPz8mPEh7PwhkqAiVlyaM-q15gn7l2lAIDk9kp2X_iCme7v4V0ADN_DbpaI_0-6mPw5HLbKrCsA-sxlSMB4DO4lDCHYkauj0l25sbfroRWB_hax1O4Q0oWyOlVJLUqEC5nuUJCCE"
+ }
+
+Was diese Nachricht aussagt, ist: Hier ist example.com, ich habe einen Beweis, und ich bin hier, um ein Paket abzuholen. Hier ist die Sendungsverfolgungsnummer, und hier ist der Beweis, dass dies die Sendungsverfolgungsnummer ist, die Sie vermutlich an example.com gesendet haben.
+
+Gut genug. Podunk.edu überprüft die Geschichte und tatsächlich, es ist example.com, und ja, da wartet ein Paket mit dieser Kontrollnummer. Hier ist das Paket...
+
+
+
+ {
+ "success":1,
+ "pickup":{
+ "notify":{
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/z.podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
+ },
+ "message":{
+ "message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
+ "created":"2012-11-20 04:04:16",
+ "edited":"2012-11-20 04:04:16",
+ "title":"",
+ "body":"Hi Nickordo",
+ "app":"",
+ "verb":"post",
+ "object_type":"",
+ "target_type":"",
+ "permalink":"",
+ "location":"",
+ "longlat":"",
+ "owner":{
+ "name":"Indigo",
+ "address":"indigo@podunk.edu",
+ "url":"http:\/\/podunk.edu",
+ "photo":{
+ "mimetype":"image\/jpeg",
+ "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
+ },
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ },
+ "author":{
+ "name":"Indigo",
+ "address":"indigo@podunk.edu",
+ "url":"http:\/\/podunk.edu",
+ "photo":{
+ "mimetype":"image\/jpeg",
+ "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
+ },
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ }
+ }
+ }
+ }
+
+
+
+Und das ist das Paket (die ursprüngliche Nachricht). Example.com wandelt diese in eine Form um, die von Nickordo eingesehen werden kann, und benachrichtigt Nickordo, dass es eine neue Nachricht gibt. Podunk.edu **könnte** feststellen, dass noch andere Pakete auf example.com warten. Wenn dies der Fall ist, kann es auch alle anderen wartenden Pakete zu diesem Zeitpunkt versenden. Jedes dieser Pakete ist mit der ursprünglichen Sendungsnummer versehen. \ No newline at end of file
diff --git a/doc/de/adminmanual/administration.md b/doc/de/adminmanual/administration.md
new file mode 100644
index 000000000..ab8cd21c6
--- /dev/null
+++ b/doc/de/adminmanual/administration.md
@@ -0,0 +1,13 @@
+### Administration
+
+#### Website-Verwaltung
+
+Die Verwaltung der Website erfolgt in der Regel über die Verwaltungsseite, die sich unter /admin auf Ihrer Website befindet. Um auf diese Seite zugreifen zu können, müssen Sie über Verwaltungsrechte für den Server verfügen. Administrationsrechte werden dem ersten Konto gewährt, das sich auf Ihrer Website registriert, **vorausgesetzt,** die E-Mail-Adresse dieses Kontos stimmt genau mit der E-Mail-Adresse überein, die Sie bei der Einrichtung als E-Mail-Adresse des Administrators angegeben haben.
+
+![adm01](/help/de/pic/adm01.png)
+
+Es gibt mehrere Möglichkeiten, wie dies fehlschlagen und das System ohne ein Administratorkonto bleiben kann, z. B. wenn das erste Konto, das erstellt wurde, eine andere E-Mail-Adresse als die bei der Einrichtung angegebene Administrator-E-Mail-Adresse angegeben hat.
+
+Aus Sicherheitsgründen gibt es auf dem System keine Webseite oder Schnittstelle, die Ihnen Administratorrechte verleiht. Wenn Sie eine Situation korrigieren müssen, in der ein System kein Administratorkonto hat, **muss** dies durch Bearbeiten der Kontotabelle in der Datenbank geschehen. Es gibt keine andere Möglichkeit. Dazu müssen Sie den Eintrag in der Kontotabelle finden, der zu dem gewünschten Administrator gehört, und „account_roles“ für diesen Eintrag auf 4096 setzen. Anschließend können Sie die Administratorseite über das Profilmenü Ihres Systems oder direkt über /admin aufrufen.
+
+Ein Hub kann mehrere Administratoren haben und die Anzahl der Administratoren ist nicht begrenzt. Wiederholen Sie den obigen Vorgang für jedes Konto, das Sie mit Administrationsrechten ausstatten möchten. \ No newline at end of file
diff --git a/doc/de/adminmanual/advanced_configurations.md b/doc/de/adminmanual/advanced_configurations.md
new file mode 100644
index 000000000..fb4644384
--- /dev/null
+++ b/doc/de/adminmanual/advanced_configurations.md
@@ -0,0 +1,326 @@
+### Erweiterte Konfigurationen für Administratoren
+
+*Dieses Dokument setzt voraus, dass Sie ein Administrator sind.*
+
+Hubzilla enthält viele Konfigurationsoptionen, die im Hauptverwaltungsbereich verborgen sind. Im Allgemeinen handelt es sich dabei um Optionen, die als zu nischenartig, fortgeschritten oder verwirrend angesehen werden.
+
+Diese Einstellungen können über die Shell vom Webverzeichnis der obersten Ebene aus mit folgender Syntax geändert werden
+
+`util/config cat key value`
+für eine Website-Konfiguration, oder
+
+`util/pconfig channel_id cat key value`
+für eine Mitgliedskonfiguration.
+
+Für eine Site-Konfiguration ist eine weitere Möglichkeit, eine Zeile in .htconfig.php einzufügen, mit der Syntax:
+`App::$config['cat']['Schlüssel'] = 'Wert';`
+
+
+
+#### Mitgliederkonfiguration (pconfig)
+
+- system.always_my_theme
+
+ Verwende immer dein eigenes Theme, wenn du Kanäle im selben Hub anschaust. Dies führt zu einigen recht einfallsreichen Problemen, wenn Kanäle mit themenabhängigen Comanche angezeigt werden.
+
+- system.blocked
+
+ Ein Array von Xchans, die von diesem Channel blockiert werden. Technisch gesehen ist dies eine versteckte Konfiguration und gehört hierher, aber Addons (insbesondere Superblock) haben dies in der Benutzeroberfläche verfügbar gemacht.
+
+- system.default_cipher
+
+ Setzt den Standardchiffre, der für E2EE-Elemente verwendet wird.
+
+- system.display_friend_count
+
+ Legt die Anzahl der Verbindungen fest, die im Widget des Verbindungsprofils angezeigt werden sollen.
+
+- system.do_not_track
+
+ Wie der Browser-Header. Dies wird viele identitätsbasierte Funktionen unterbrechen. Sie sollten wirklich nur Berechtigungen setzen, die Sinn machen.
+
+- system.forcepublicuploads
+
+ Erzwingt, dass hochgeladene Fotos öffentlich sind, wenn sie als Wandelemente hochgeladen werden. Es ist viel sinnvoller, die Berechtigungen von vornherein richtig zu setzen. Tun Sie das stattdessen.
+
+- system.network_page_default
+
+ Legt die Standardparameter für die Anzeige der Netzwerkseite fest. Dies sollte den gleichen Querystring enthalten wie die manuelle Filterung.
+
+- system.paranoia
+
+ Legt die Sicherheitsstufe der IP-Überprüfung fest. Wenn sich die IP-Adresse einer angemeldeten Sitzung ändert, wird diese Stufe angewendet, um festzustellen, ob das Konto als Sicherheitsverletzung abgemeldet werden sollte. Die Optionen sind: 0 - keine IP-Überprüfung 1 - 3 Oktette prüfen 2 - 2 Oktette prüfen 3 - Prüfung auf alle Unterschiede
+
+- system.prevent_tag_hijacking
+
+ Verhindert, dass fremde Netzwerke Hashtags in Ihren Beiträgen klauen und sie auf ihre eigenen Ressourcen leiten.
+
+- system.startpage
+
+ Eine weitere dieser technisch versteckten Konfigurationen, die von Addons zur Verfügung gestellt werden. Legt die Standardseite fest, die beim Einloggen angezeigt wird. Diese wird der Benutzeroberfläche durch das startpage-Addon zur Verfügung gestellt.
+
+- system.taganyone
+
+ Erfordert, dass die gleichnamige Konfiguration aktiviert ist. Erlaubt das @mention tagging von jedem, egal ob man verbunden ist oder nicht. Dies skaliert nicht.
+
+- system.anonymous_kommentare
+
+ Standardmäßig oder wenn auf 1 gesetzt, können benutzerdefinierte Berechtigungen gesetzt werden, um anonyme (moderierte) Kommentare wie bei WordPress zu erlauben, die vom Channel-Besitzer moderiert werden. Ist der Wert auf 0 gesetzt, kann kein Mitglied Ihrer Website dies auswählen oder aktivieren.
+
+- system.user_scalable
+
+ Bestimmt, ob die App auf Touchscreens skalierbar ist. Standardmäßig auf on, zum Deaktivieren auf zero setzen - real zero, nicht nur false.
+
+#### Konfiguration der Website
+
+- randprofile.check
+
+ Bei der Anforderung eines Zufallsprofils zuerst prüfen, ob es tatsächlich existiert
+
+- randprofile.retry
+
+ Anzahl der Versuche, ein zufälliges Profil zu erhalten
+
+- system.admin_email
+
+ Gibt die E-Mail-Adresse des Administrators für diese Site an. Diese wird bei der Installation festgelegt.
+
+- system.authlog
+
+ Logdatei, die für die Protokollierung von Authentifizierungsfehlern verwendet wird. Wird verwendet, um eine Verbindung zu serverseitiger Software wie fail2ban herzustellen. Auth-Fehler werden auch in den Hauptprotokollen protokolliert.
+
+- system.auto_channel_create
+
+ Fügt die notwendigen Formularelemente hinzu, um den ersten Kanal auf der Kontoregistrierungsseite zu erstellen, und erstellt ihn (möglicherweise nach einer E-Mail-Validierung oder der Genehmigung des Administrators). Dies schließt die Möglichkeit aus, einen Channel von einer anderen Website als ersten auf dieser Website für ein neues Konto erstellten Channel zu importieren. Verwendung mit system.default_permissions_role zur Rationalisierung der Registrierung.
+
+- system.auto_follow
+
+ Der erste Channel eines Accounts folgt automatisch den hier aufgelisteten Channels - kommagetrennte Liste von Webbies (member@hub Adressen).
+
+- system.blacklisted_sites
+
+ Ein Array spezifischer Hubs, die von diesem Hub komplett blockiert werden sollen.
+
+- system.block_public_search
+
+ Ähnlich wie block_public, mit dem Unterschied, dass nur der öffentliche Zugang zu den Suchfunktionen blockiert wird. Nützlich für Seiten, die öffentlich sein wollen, aber von Suchmaschinen überrannt werden.
+
+- system.cron_hour
+
+ Geben Sie eine Stunde an, in der cron_daily ausgeführt werden soll. Standardmäßig, ohne Konfiguration, wird dies um Mitternacht UTC ausgeführt.
+
+- system.default_permissions_role
+
+ Wenn dieser Wert auf einen gültigen Namen für eine Berechtigungsrolle gesetzt ist, wird diese Rolle für den ersten Channel verwendet, der von einem neuen Konto erstellt wird, und es wird nicht nach dem „Channel-Typ“ im Formular zur Channel-Erstellung gefragt. Beispiele für gültige Namen sind: 'social', 'social_restricted', 'social_private', 'forum', 'forum_restricted' und 'forum_private'. Lesen Sie [hier](https://hub.hubzilla.hu/help/roles) mehr über Berechtigungsrollen.
+
+- system.default_profile_photo
+
+ Legt das Profilfoto fest, mit dem neue Channels beginnen. Dies sollte den Namen eines Verzeichnisses enthalten, das sich unter images/default_profile_photos/ befindet, oder es sollte nicht gesetzt werden. Wenn es nicht gesetzt ist, wird 'rainbow_man' angenommen.
+
+- system.directorytags
+
+ Legt die Anzahl der Schlüsselwort-Tags fest, die auf der Verzeichnisseite angezeigt werden. Die Voreinstellung ist 50, wenn sie nicht auf eine positive ganze Zahl gesetzt wird.
+
+- system.disable_directory_keywords
+
+ Wenn '1', werden keine Verzeichnisschlüsselwörter angezeigt. Wenn der Hub ein Verzeichnisserver ist, verhindern Sie die Rückgabe von Schlüsselwörtern an alle Verzeichnis-Clients. Bitte setzen Sie dies nicht für Verzeichnisserver im Bereich RED_GLOBAL.
+
+- system.disable_discover_tab
+
+ Hiermit können Sie die Fähigkeit, öffentliche Inhalte von externen Sites zu entdecken, vollständig deaktivieren.
+
+- system.disable_dreport
+
+ Wenn '1', werden keine Zustellungsberichte gespeichert oder verlinkt.
+
+- system.dlogfile
+
+ Logdatei, die für die Protokollierung von Entwicklungsfehlern verwendet wird. Genau dasselbe wie logger sonst. Dies ist keine Magie und erfordert Ihre eigenen Logging-Anweisungen. Entwickler-Tool.
+
+- system.email_notify_icon_url
+
+ URL des Bildes (32x32), das in E-Mail-Benachrichtigungen (HTML-Bodies) angezeigt werden soll.
+
+- system.expire_delivery_reports
+
+ Verfallsdatum in Tagen für Zustellungsberichte - Standardwert 10
+
+- system.expire_limit
+
+ Nicht mehr als diese Anzahl von Beiträgen pro Kanal pro Ablauflauf ablaufen lassen, um den Speicher nicht zu erschöpfen. Voreinstellung 5000.
+
+- system.photo_storage_type
+
+ Wenn '1', wird das Dateisystem anstelle der SQL-Datenbank zum Speichern der Thumbnails verwendet. Voreinstellung ist '0'. Eingeführt in 4.2
+
+- system.hidden_version_siteinfo
+
+ Wenn 'true', wird die Softwareversion nicht auf den Seiten von siteinfo angezeigt (system.hide_version verbirgt die Version auch auf diesen Seiten, diese Einstellung verbirgt *nur* die Version auf den Seiten von siteinfo).
+
+- system.hide_help
+
+ Link zur Hilfedokumentation nicht in der Navigationsleiste anzeigen
+
+- system.hide_in_statistiken
+
+ Weist die roten Statistikserver an, diesen Hub in Hublisten komplett auszublenden.
+
+- system.hide_version
+
+ Wenn true, wird die Softwareversion auf Webseiten und Tools nicht angezeigt. (*) Muss in .htconfig.php eingestellt werden.
+
+- system.ignore_imagick
+
+ Ignoriert imagick und verwendet GD, auch wenn imagick auf dem Server installiert ist. Verhindert einige Probleme mit PNG-Dateien in älteren Versionen von imagick.
+
+- system.max_daily_registrations
+
+ Legt die maximale Anzahl der an einem Tag erlaubten Neuanmeldungen fest. Nützlich, um eine Überzeichnung zu verhindern, wenn das Projekt in der Öffentlichkeit bekannt gemacht wird.
+
+- system.max_import_size
+
+ Falls konfiguriert, die maximale Länge einer importierten Textnachricht. Diese wird normalerweise auf 200 KByte oder mehr belassen, um private Friendica-Fotos, die eingebettet sind, unterzubringen.
+
+- system.max_tagged_forums
+
+ Spam-Schutz. Begrenzt die Anzahl der getaggten Foren, die in jedem Beitrag erkannt werden. Standard ist 2. Nur die ersten 'n' Tags werden als Foren zugestellt, die anderen verursachen keine Zustellung.
+
+- system.minimum_feedcheck_minutes
+
+ Das minimale Intervall zwischen der Abfrage von RSS-Feeds. Wenn dieses Intervall kleiner als das Cron-Intervall ist, werden die Feeds bei jedem Cronjob abgefragt. Der Standardwert ist 60, wenn er nicht festgelegt wurde. Die Site-Einstellung kann auch für jeden einzelnen Kanal durch eine Serviceklassen-Einstellung mit dem treffenden Namen „minimum_feedcheck_minutes“ außer Kraft gesetzt werden.
+
+- system.no_age_restriction
+
+ Beschränken Sie die Registrierung nicht auf Personen über 13 Jahren. In vielen Ländern ist es gesetzlich vorgeschrieben, dass das Alter angegeben werden muss und dass alle persönlichen Daten von Minderjährigen gesperrt werden müssen.
+
+- system.object_cache_days
+
+ Legt fest, wie lange zwischengespeicherte eingebettete Inhalte ohne erneutes Abrufen verwendet werden können. Die Voreinstellung ist 30 Tage.
+
+- system.openssl_conf_file
+
+ Geben Sie eine Datei an, die die OpenSSL-Konfiguration enthält. Wird in einigen Windows-Installationen benötigt, um die openssl-Konfigurationsdatei auf dem System zu finden. Lesen Sie zuerst den Code. Wenn Sie den Code nicht lesen können, spielen Sie nicht mit ihm.
+
+- system.openssl_encrypt
+
+ Verschlüsselungs-Engine von openssl verwenden, Standard ist false (verwendet mcrypt für AES-Verschlüsselung)
+
+- system.optimize_items
+
+ Führt optimise_table während einiger Aufgaben aus, um Ihre Datenbank sauber und defragmentiert zu halten. Dies geht zu Lasten der Leistung, während die Operationen laufen, sorgt aber auch dafür, dass die Dinge etwas schneller laufen, wenn sie nicht laufen. Es gibt auch CLI-Hilfsprogramme zur Durchführung dieser Operation, die Sie vielleicht bevorzugen, besonders wenn Sie eine große Site haben.
+
+- system.override_poll_lockfile
+
+ Ignoriert die Sperrdatei im Poller-Prozess, damit mehr als ein Prozess gleichzeitig laufen kann.
+
+- system.paranoia
+
+ Wie pconfig, aber auf einer site-weiten Basis. Kann durch Mitgliedereinstellungen überschrieben werden.
+
+- system.pin_types
+
+ Array der zulässigen Elementtypen für die Anheftung. Die Standardwerte hängen vom Modul ab, können aber hier geändert werden.
+
+- system.photo_cache_time
+
+ Wie lange die Fotos zwischengespeichert werden sollen, in Sekunden. Standardwert ist 86400 (1 Tag). Eine längere Zeit erhöht die Leistung, bedeutet aber auch, dass es länger dauert, bis geänderte Berechtigungen gelten.
+
+- system.plattform_name
+
+ Was als Plattformname auf Webseiten und in Statistiken angezeigt werden soll. (*) Muss in .htconfig.php eingestellt werden.
+
+- system.rating_enabled
+
+ Verteilte Reputationsberichte und Datenerfassung. Diese Funktion wird derzeit überarbeitet.
+
+- system.poke_basic
+
+ Reduziert die Anzahl der poke-Verben auf genau 1 („poke“). Deaktivieren Sie andere Verben.
+
+- system.proc_run_use_exec
+
+ Wenn 1, wird der Systemaufruf exec in proc_run verwendet, um Hintergrundaufgaben auszuführen. Standardmäßig verwenden wir proc_open und proc_close. Auf einigen (derzeit seltenen) Systemen funktioniert dies nicht gut.
+
+- system.projecthome
+
+ Zeigt die Projektseite auf Ihrer Homepage für abgemeldete Betrachter an.
+
+- system.projekthome
+
+ Legt die Projekthomepage als Startseite des Hubs fest. (Veraltet)
+
+- system.pubstream_bestellung
+
+ Legt die Pubstream-Reihenfolge fest. Mögliche Werte 'commented' (Standard), 'created' und 'edited'.
+
+- system.register_link
+
+ Pfad, auf den der „register“-Link im Anmeldeformular verweisen soll. Bei geschlossenen Sites wird dies auf 'pubsites' verweisen. Bei offenen Sites wird er normalerweise auf 'register' umgeleitet, aber Sie können dies auf eine benutzerdefinierte Site-Seite ändern, die Abonnements oder ähnliches anbietet.
+
+- system.reserved_channels
+
+ Erlaube den Mitgliedern nicht, Kanäle mit dieser durch Kommata getrennten Liste von Namen (keine Leerzeichen) zu registrieren.
+
+- system.sellpage
+
+ Eine URL, die in der Liste der öffentlichen Sites angezeigt wird, um Ihren Hub zu verkaufen - Serviceklassen anzeigen, usw.
+
+- system.startpage
+
+ Legt die Standardseite fest, die nach einer Anmeldung für alle Channels auf dieser Website angezeigt wird. Kann durch Benutzereinstellungen überschrieben werden.
+
+- system.sys_expire_days
+
+ Wie viele Tage sollen entdeckte öffentliche Inhalte von anderen Websites aufbewahrt werden?
+
+- system.taganyone
+
+ Erlaubt das @mention-Tagging von jedem, egal ob Sie verbunden sind oder nicht.
+
+- system.tempdir
+
+ Ort, an dem temporäre Dateien gespeichert werden (derzeit unbenutzt), Standard ist in der PHP-Konfiguration definiert.
+
+- system.tos_url
+
+ Setzt einen alternativen Link für den ToS-Speicherort.
+
+- system.transport_security_header
+
+ wenn ungleich Null und SSL verwendet wird, wird ein strict-transport-security-Header auf den Webseiten eingefügt
+
+- system.uploaddir
+
+ Speicherort für das Hochladen von Dateien (Standard ist system.tempdir, derzeit nur vom js_upload-Plugin verwendet)
+
+- system.workflow_channel_next
+
+ Die Seite, zu der neue Mitglieder unmittelbar nach der Erstellung eines Channels weitergeleitet werden sollen.
+
+- system.arbeitsablauf_register_next
+
+ Die Seite, auf die Mitglieder direkt nach dem Erstellen eines Kontos geleitet werden (nur wenn auto_channel_create oder UNO aktiviert ist).
+
+#### Verzeichnis-Konfiguration
+
+##### Standardwerte für die Verzeichnissuche
+
+- directory.globaldir
+
+ 0 oder 1. Standardwert 0. Wenn Sie das Verzeichnis auf einer Site besuchen, sehen Sie standardmäßig nur die Mitglieder dieser Site. Man muss einen zusätzlichen Schritt machen, um die Leute im Rest des Netzwerks zu sehen; und indem man das tut, gibt es eine klare Abgrenzung, dass diese Leute *nicht* Mitglieder dieser Site sind, sondern eines größeren Netzwerks.
+
+- directory.pubforums
+
+ 0 oder 1. Öffentliche Foren *sollten* standardmäßig 0 sein.
+
+- directory.safemode
+
+ 0 oder 1.
+
+##### Konfiguration des Verzeichnisservers
+
+- system.directory_mode
+- system.directory_primary
+- system.directory_realm
+- system.verzeichnis_server
+- system.realm_token \ No newline at end of file
diff --git a/doc/de/adminmanual/automated_installation.md b/doc/de/adminmanual/automated_installation.md
new file mode 100644
index 000000000..0cb440717
--- /dev/null
+++ b/doc/de/adminmanual/automated_installation.md
@@ -0,0 +1,26 @@
+### Automatisierte Installation über das Shell-Skript .homeinstall
+
+Es gibt ein Shell-Skript in (`.homeinstall/hubzilla-setup.sh`), das Hubzilla und seine Abhängigkeiten auf einer frischen Installation von Debian stable installiert. Es sollte auf ähnlichen Linux-Systemen funktionieren, aber Ihre Ergebnisse können variieren.
+
+#### Anforderungen
+
+Das Installationsskript wurde ursprünglich für einen kleinen Hardwareserver hinter Ihrem Heimrouter entwickelt. Es wurde jedoch auf mehreren Systemen mit Debian 9 getestet:
+
+- Home-PC (Debian-9.2-amd64) und Rapberry-Pi 3 (Rasbian = Debian 9.3)
+ - Internetanschluss und Router zu Hause
+ - Mini-PC / Raspi an den Router angeschlossen
+ - USB-Laufwerk für Backups
+ - Frische Installation von Debian auf Ihrem Mini-PC
+ - Router mit offenen Ports 80 und 443 für Ihr Debian
+
+#### Überblick über die Installationsschritte
+
+1. `apt-get install git`
+2. `mkdir -p /var/www/html`
+3. `cd /var/www/html`
+4. `git clone https://framagit.org/hubzilla/core.git .`
+5. `nano .homeeinstall/hubzilla-config.txt`
+6. `cd .homeeinstall/`
+7. `./hubzilla-setup.sh`
+8. `service apache2 neu laden`
+9. Öffnen Sie Ihre Domain mit einem Browser und gehen Sie durch die anfängliche Konfiguration von Hubzilla. \ No newline at end of file
diff --git a/doc/de/adminmanual/before_you_start.md b/doc/de/adminmanual/before_you_start.md
new file mode 100644
index 000000000..1bc425b9c
--- /dev/null
+++ b/doc/de/adminmanual/before_you_start.md
@@ -0,0 +1,17 @@
+### Bevor Sie beginnen
+
+Wählen Sie einen Domänennamen oder einen Subdomänennamen für Ihren Server.
+
+Die Software kann nur im Stammverzeichnis einer Domäne oder Subdomäne installiert werden und kann nicht über alternative TCP-Ports installiert werden. Diese Beschränkungen können in Zukunft gelockert werden, sind aber unbequem, so dass wir Ihnen weiterhin DRINGEND empfehlen, sich daran zu halten.
+
+Entscheiden Sie, ob Sie SSL verwenden wollen, und besorgen Sie sich vor der Softwareinstallation ein SSL-Zertifikat. Sie SOLLTEN SSL verwenden. Wenn Sie SSL verwenden, MÜSSEN Sie ein „browsergültiges“ Zertifikat verwenden. Sie KÖNNEN KEINE selbstsignierten Zertifikate verwenden!
+
+Bitte testen Sie Ihr Zertifikat vor der Installation. Ein Webtool zum Testen Ihres Zertifikats finden Sie unter „http://www.digicert.com/help/“. Wenn Sie Ihre Website zum ersten Mal besuchen, verwenden Sie bitte die SSL-URL („https://“), wenn SSL verfügbar ist. Dadurch werden spätere Probleme vermieden. Die Installationsroutine erlaubt es Ihnen nicht, ein nicht browsergültiges Zertifikat zu verwenden.
+
+Diese Einschränkung wird vorgenommen, weil öffentliche Beiträge von Ihnen Verweise auf Bilder in Ihrem eigenen Hub enthalten können. Andere Mitglieder, die ihren Stream auf anderen Hubs betrachten, erhalten Warnungen, wenn Ihr Zertifikat von ihrem Webbrowser nicht als vertrauenswürdig eingestuft wird. Dies wird viele Leute verwirren, da es sich um ein dezentralisiertes Netzwerk handelt, und sie werden die Warnung über Ihren Hub erhalten, während sie ihren eigenen Hub ansehen, und könnten denken, dass ihr eigener Hub ein Problem hat. Diese Warnungen sind sehr technisch und beängstigend für einige Leute, von denen viele nicht wissen, wie sie weiter vorgehen sollen, außer den Ratschlägen des Browsers zu folgen. Das ist für die Gemeinschaft störend. Abgesehen davon erkennen wir die Probleme im Zusammenhang mit der derzeitigen Zertifikatsinfrastruktur an und stimmen zu, dass es viele Probleme gibt, aber das ändert nichts an der Anforderung.
+
+Kostenlose „browsergültige“ Zertifikate gibt es von Anbietern wie ZeroSSL, LetsEncrypt und einigen anderen.
+
+Wenn Sie NICHT SSL verwenden, kann es beim ersten Installationsskript zu einer Verzögerung von bis zu einer Minute kommen - während wir den SSL-Port überprüfen, um zu sehen, ob dort etwas antwortet. Bei der Kommunikation mit neuen Sites versucht Hubzilla immer zuerst eine Verbindung über den SSL-Port herzustellen, bevor es auf eine weniger sichere Verbindung zurückgreift. Wenn Sie kein SSL verwenden, MUSS Ihr Webserver überhaupt nicht auf Port 443 lauschen.
+
+Wenn Sie LetsEncrypt für die Bereitstellung von Zertifikaten verwenden und eine Datei unter .well-known/acme-challenge erstellen, damit LetsEncrypt Ihre Domaineigentümerschaft überprüfen kann, entfernen Sie bitte das Verzeichnis .well-known oder benennen Sie es um, sobald das Zertifikat generiert wurde. Die Software stellt bei der Installation einen eigenen Handler für „.well-known“-Dienste zur Verfügung, und ein bestehendes Verzeichnis an diesem Ort kann verhindern, dass einige dieser Dienste korrekt funktionieren. Dies sollte bei Apache kein Problem sein, kann aber bei nginx oder anderen Webserver-Plattformen ein Problem darstellen. \ No newline at end of file
diff --git a/doc/de/adminmanual/channel_directory.md b/doc/de/adminmanual/channel_directory.md
new file mode 100644
index 000000000..745a20272
--- /dev/null
+++ b/doc/de/adminmanual/channel_directory.md
@@ -0,0 +1,15 @@
+### Kanal-Verzeichnis
+
+#### Schlüsselwörter
+
+Es gibt eine „Schlagwortwolke“ mit Schlüsselwörtern, die auf der Kanalverzeichnisseite erscheinen können. Wenn Sie diese Schlüsselwörter, die vom Verzeichnisserver bezogen werden, ausblenden möchten, können Sie das *Konfigurationswerkzeug* verwenden:
+
+```
+util/config system disable_directory_keywords 1
+```
+
+Wenn sich Ihr Hub im Standalone-Modus befindet, weil Sie sich nicht mit dem globalen Netz verbinden möchten, können Sie stattdessen sicherstellen, dass die Option *directory_server* system leer ist:
+
+```
+util/config system directory_server „“
+``` \ No newline at end of file
diff --git a/doc/de/adminmanual/database.md b/doc/de/adminmanual/database.md
new file mode 100644
index 000000000..d46001880
--- /dev/null
+++ b/doc/de/adminmanual/database.md
@@ -0,0 +1,74 @@
+### Datenbank
+
+#### Datenbank-Updates
+
+Auf der Seite `/admin/dbsync` kann der Administrator überprüfen, ob eine Aktualisierung fehlgeschlagen ist, und sie gegebenenfalls erneut versuchen.
+
+Wenn eine Aktualisierung fehlgeschlagen ist, aber aus irgendeinem Grund nicht als fehlgeschlagen registriert wird, kann der Administrator versuchen, die Aktualisierung erneut auszuführen. Zum Beispiel für die DB-Aktualisierung #1999, indem er die Webseite besucht:
+
+`/admin/dbsync/1999`
+
+#### Datenbank-Tabellen
+
+| Tabelle | Beschreibung |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| [abconfig](/help/de/database/db_abconfig) | beliebiger Speicherplatz für Verbindungen von lokalen Kanälen |
+| [abook](/help/de/database/db_abook) | Verbindungen der lokalen Kanäle |
+| [account](/help/de/database/db_account) | Dienstanbieterkonto |
+| [addon](/help/de/database/db_addon) | registrierte Plugins |
+| [app](/help/de/database/db_app) | persönliche App-Daten |
+| [attach](/help/de/database/db_attach) | Dateianhänge |
+| [auth_codes](/help/de/database/db_auth_codes) | OAuth Benutzung |
+| [cache](/help/de/database/db_cache) | OEmbed Cache |
+| [cal](/help/de/database/db_cal) | CalDAV-Container für Ereignisse |
+| [channel](/help/de/database/db_channel) | lokale Kanäle |
+| [chat](/help/de/database/db_chat) | Chatraum-Inhalte |
+| [chatpresence](/help/de/database/db_chatpresence) | Kanalpräsenzinformationen für den Chat |
+| [chatroom](/help/de/database/db_chatroom) | Daten für den eigentlichen Chatraum |
+| [clients](/help/de/database/db_clients) | OAuth Benutzung |
+| [config](/help/de/database/db_config) | Hauptkonfigurationsspeicher |
+| [conv](/help/de/database/db_conv) | Meta-Konversationsstruktur für private Nachrichten in Diaspora |
+| [event](/help/de/database/db_event) | Events |
+| [pgrp_member](/help/de/database/db_pgrp_member) | Datenschutzgruppen (Sammlungen), Gruppeninformationen |
+| [pgrp](/help/de/database/db_pgrp) | Datenschutzgruppen (Sammlungen), Mitgliederinformationen |
+| [hook](/help/de/database/db_hook) | Plugin-Hook-Register |
+| [hubloc](/help/de/database/db_hubloc) | xchan-Standortspeicher, verknüpft einen Hub-Standort mit einem xchan |
+| [iconfig](/help/de/database/db_iconfig) | erweiterbarer, beliebiger Speicher für Elemente |
+| [issue](/help/de/database/db_issue) | künftige Fehler-/Problemdatenbank |
+| [item](/help/de/database/db_item) | alle Beiträge und Webseiten |
+| [item_id](/help/de/database/db_item_id) | (veraltet durch iconfig) andere Identifikatoren in anderen Diensten für Beiträge |
+| [likes](/help/de/database/db_likes) | "Dinge“ mögen |
+| [mail](/help/de/database/db_mail) | private Nachrichten |
+| [menu](/help/de/database/db_menu) | Webseiten-Menü-Daten |
+| [menu_item](/help/de/database/db_menu_item) | Einträge für Menüs auf Webseiten |
+| [notify](/help/de/database/db_notify) | Benachrichtigungen |
+| [obj](/help/de/database/db_obj) | Objektdaten für Dinge (x hat y) |
+| [outq](/help/de/database/db_outq) | Ausgangswarteschlange |
+| [pconfig](/help/de/database/db_pconfig) | persönlicher (pro Kanal) Konfigurationsspeicher |
+| [photo](/help/de/database/db_photo) | Fotospeicher |
+| [poll](/help/de/database/db_poll) | Daten für Umfragen |
+| [poll_elm](/help/de/database/db_poll_elm) | Daten für Abfrageelemente |
+| [profdef](/help/de/database/db_profdef) | Definitionen für benutzerdefinierte Profilfelder |
+| [profext](/help/de/database/db_profext) | benutzerdefinierte Profilfelddaten |
+| [profile](/help/de/database/db_profile) | Kanalprofile |
+| [profile_check](/help/de/database/db_profile_check) | DFRN-Fernautorisierung, kann veraltet sein |
+| [register](/help/de/database/db_register) | Registrierungen, die eine Verwaltungsgenehmigung erfordern |
+| [session](/help/de/database/db_session) | Speicherung von Websitzungen |
+| [shares](/help/de/database/db_shares) | Informationen über gemeinsame Elemente |
+| [sign](/help/de/database/db_sign) | Diaspora-Unterschriften. Wird schrittweise abgebaut. |
+| [site](/help/de/database/db_site) | Standorttabelle zum Auffinden von Verzeichnis-Peers |
+| [source](/help/de/database/db_source) | Daten aus Kanalquellen |
+| [sys_perms](/help/de/database/db_sys_perms) | erweiterbare Berechtigungen für OAuth |
+| [term](/help/de/database/db_term) | Tabelle der Artikeltaxonomie (Kategorien, Tags usw.) |
+| [tokens](/help/de/database/db_tokens) | OAuth Benutzung |
+| [updates](/help/de/database/db_updates) | Verzeichnis-Synchronisations-Updates |
+| [verify](/help/de/database/db_verify) | allgemeine Verifikationsstruktur |
+| [vote](/help/de/database/db_vote) | Abstimmungsdaten für Umfragen |
+| [xchan](/help/de/database/db_xchan) | Liste der bekannten Kanäle im Universum |
+| [xchat](/help/de/database/db_xchat) | Chaträume mit Lesezeichen |
+| [xconfig](/help/de/database/db_xconfig) | wie pconfig, aber für Kanäle ohne lokales Konto |
+| [xign](/help/de/database/db_xign) | von Freundschaftsvorschlägen ignorierte Kanäle |
+| [xlink](/help/de/database/db_xlink) | von poco abgeleitete „Freunde von Freunden“-Verknüpfungen, auch Speicherung von Bewertungen |
+| [xperm](/help/de/database/db_xperm) | OAuth/OpenID-Connect erweiterbare Berechtigungen Berechtigungsspeicher |
+| [xprof](/help/de/database/db_xprof) | wenn dieser Knotenpunkt ein Verzeichnisserver ist, enthält er grundlegende öffentliche Profilinformationen über jeden im Netz |
+| [xtag](/help/de/database/db_xtag) | wenn dieser Hub ein Verzeichnisserver ist, enthält er Tags oder Interessen von jedem im Netzwerk |
diff --git a/doc/de/adminmanual/directories.md b/doc/de/adminmanual/directories.md
new file mode 100644
index 000000000..0fab695ae
--- /dev/null
+++ b/doc/de/adminmanual/directories.md
@@ -0,0 +1,87 @@
+### **Verzeichnis-Konfiguration**
+
+Verzeichnisse in Hubzilla dienen dem Zweck, Mitglieder überall im Netzwerk zu suchen und zu finden. Sie werden auch verwendet, um „Bewertungen“ von Mitgliedern und Sites zu speichern und abzufragen. Die Verzeichnisdienste sind verteilt und gespiegelt, so dass ein Ausfall eines Dienstes nicht das gesamte Netzwerk lahmlegt oder stört.
+
+#### **Standard-Konfiguration**
+
+Neue Sites, die als Verzeichnis-Clients arbeiten, wählen bei ihrem ersten Verzeichniszugriff automatisch aus einer fest programmierten Liste von Verzeichnisservern aus. Sie können diese Einstellung überprüfen oder außer Kraft setzen, indem Sie
+
+```
+util/config system directory_server
+```
+
+eingeben.
+
+Um einen anderen Server einzustellen, verwenden Sie diesen Befehl:
+
+```
+util/config system directory_server https://newdirectory.something
+```
+
+#### **Eigenständige Konfiguration**
+
+Einige Hubs möchten vielleicht im „Standalone“-Modus arbeiten und keine Verbindung zu externen Verzeichnisdiensten herstellen. Dies ist nützlich für isolierte Sites („off the gird“) und Test-Sites, kann aber auch für kleine Organisationen nützlich sein, die keine Verbindung mit anderen Sites im Netzwerk herstellen wollen.
+
+Um dies zu konfigurieren, suchen Sie bitte in Ihrer Datei .htconfig.php nach dem folgenden Text und stellen Sie die Konfiguration entsprechend ein.
+
+```php
+// Configure how we communicate with directory servers.
+// DIRECTORY_MODE_NORMAL = directory client, we will find a directory
+// DIRECTORY_MODE_SECONDARY = caching directory or mirror
+// DIRECTORY_MODE_PRIMARY = main directory server
+// DIRECTORY_MODE_STANDALONE = "off the grid" or private directory services
+
+App::$config['system']['directory_mode'] = DIRECTORY_MODE_STANDALONE;
+```
+
+#### **Konfiguration des Sekundärservers**
+
+Sie können Ihre Site auch als Sekundärserver konfigurieren. Dieser arbeitet als Spiegel des primären Verzeichnisses und ermöglicht die Verteilung der Last auf die verfügbaren Server. Es gibt kaum funktionale Unterschiede zwischen einem primären und einem sekundären Server, allerdings darf es nur *einen* primären Verzeichnisserver pro Realm geben (Realms werden später in diesem Dokument behandelt).
+
+Bevor Sie sich für einen Verzeichnisserver entscheiden, sollten Sie ein aktives Mitglied des Netzwerks sein und über die Ressourcen und Zeit verfügen, um diese Dienste zu verwalten. In der Regel ist keine Verwaltung erforderlich, sondern die Anforderung dient eher der Stabilität, da der Ausfall eines Verzeichnisservers zu Problemen bei den Verzeichnis-Clients führen kann, die auf ihn angewiesen sind.
+
+#### **Wechseln des Verzeichnisservers**
+
+Wenn ein Verzeichnisserver anzeigt, dass er kein Verzeichnisserver mehr ist, sollte dies von der Software erkannt werden, und die Konfiguration für diesen Server wird entfernt (geleert). Wenn er ohne Vorwarnung dauerhaft offline geht, erfahren Sie das nur, wenn die Mitglieder der Website melden, dass die Verzeichnisdienste nicht mehr verfügbar sind. Derzeit kann dies nur manuell durch den Site-Administrator repariert werden, indem er ein neues Verzeichnis auswählt und den Befehl
+
+```
+util/config system directory_server https://newdirectory.something
+```
+
+Wir hoffen, dass diese Option in Zukunft in der Administrationsoberfläche der Website aktiviert werden kann.
+
+### **Verzeichnis-Bereiche**
+
+Große Organisationen möchten vielleicht lieber Verzeichnis-„Realms“ als ein einzelnes, unabhängiges Verzeichnis verwenden. Der Standard-Realm heißt RED_GLOBAL. Durch die Erstellung eines neuen Realms hat Ihre Organisation die Möglichkeit, ihre eigene Hierarchie von primären und sekundären Servern und Clients zu erstellen.
+
+```
+util/config system directory_realm MY_REALM
+```
+
+Ihr Realm *muss* ein primäres Verzeichnis haben. Erstellen Sie dieses zuerst. Setzen Sie dann den Realm auf allen Sites innerhalb Ihres Verzeichnisbereichs (Server und Clients) auf denselben Wert.
+
+Sie können auch einen „Sub-Realm“ einrichten, der unabhängig vom RED_GLOBAL-Realm (oder jedem anderen Realm) arbeitet, aber eine übergreifende Mitgliedschaft und die Möglichkeit bietet, Mitglieder des gesamten Verzeichnisraums zu suchen. Dies wurde bisher nur leicht getestet, also seien Sie bereit, mitzuhelfen und eventuell auftretende Probleme zu beheben. Ein Sub-Realm enthält seinen übergeordneten Realm im Realm-Namen.
+
+```
+util/config system directory_realm RED_GLOBAL:MY_REALM
+```
+
+#### **Realm-Zugang**
+
+Sie möchten vielleicht, dass Ihre Verzeichnisserver und -dienste nur von Mitgliedern Ihres Realms genutzt werden. Zu diesem Zweck muss ein Token oder Passwort für den Zugriff auf die Verzeichnisdienste des Realms angegeben werden. Dieses Token wird während der Übertragung nicht verschlüsselt, reicht aber aus, um den gelegentlichen Zugriff auf Ihre Verzeichnisserver zu verhindern. Folgendes muss für alle Sites (Clients und Verzeichnisserver) innerhalb des Realms konfiguriert werden:
+
+```
+util/config system realm_token my-secret-realm-password
+```
+
+### **Verzeichnis-Spiegelung**
+
+Die Spiegelung erfolgt mit einem täglichen Transaktionsprotokoll der Aktivitäten, das von den Verzeichnisservern gemeinsam genutzt wird. Bei Verzeichnis- und Profilaktualisierungen wird die Adresse des Kanals, der die Aktualisierung durchführt, übermittelt, und die anderen Verzeichnisserver prüfen diesen Kanal an seiner Quelle auf Änderungen. Wir können und sollten den Informationen, die uns von anderen Verzeichnisservern gegeben werden, nicht vertrauen. Wir überprüfen die Informationen immer an der Quelle.
+
+Bewertungen werden etwas anders gehandhabt - ein verschlüsseltes Paket (signiert von dem Kanal, der die Bewertung erstellt hat) wird zwischen den Servern übertragen. Diese Signatur muss überprüft werden, bevor die Bewertung akzeptiert wird. Bewertungen werden immer auf dem primären Verzeichnisserver veröffentlicht und von dort aus an alle anderen Verzeichnisserver weitergegeben. Aus diesem Grund kann es nur einen primären Server in einem Bereich geben. Wenn eine falsch konfigurierte Site behauptet, ein primäres Verzeichnis zu sein, wird sie im RED_GLOBAL-Bereich ignoriert. Für andere Realms gibt es derzeit keinen solchen Schutz. Seien Sie sich dessen bewusst, wenn Sie mit alternativen Realms arbeiten.
+
+Neu erstellte Verzeichnisserver erhalten keinen „Full Dump“, sondern werden aus Leistungsgründen und um die anderen Server im Netz möglichst wenig zu stören, langsam online gestellt. Es kann bis zu einem Monat dauern, bis ein neuer sekundärer Verzeichnisserver einen vollständigen Überblick über das Netzwerk bietet. Bitte fügen Sie keine sekundären Server zur fest kodierten Liste der Fallback-Verzeichnisserver hinzu, bevor er nicht mindestens einen Monat lang als Verzeichnis in Betrieb war.
+
+Alle Channels sind so konfiguriert, dass sie ihren Verzeichnisserver einmal im Monat „anpingen“, und zwar zu einem zufälligen Zeitpunkt im Monat. Auf diese Weise kann das Verzeichnis tote Channels und Sites entdecken (sie hören auf zu pingen). In der Folge werden sie als tot oder unerreichbar markiert und mit der Zeit aus den Verzeichnisergebnissen entfernt.
+
+Kanäle können so konfiguriert werden, dass sie vor dem Verzeichnis „versteckt“ werden. Diese Kanäle sind zwar noch im Verzeichnis vorhanden, aber nicht mehr durchsuchbar, und einige „sensible“ persönliche Informationen werden nicht gespeichert. \ No newline at end of file
diff --git a/doc/de/adminmanual/faq_admins.md b/doc/de/adminmanual/faq_admins.md
new file mode 100644
index 000000000..496a49f98
--- /dev/null
+++ b/doc/de/adminmanual/faq_admins.md
@@ -0,0 +1,89 @@
+### Hubzilla FAQ
+
+#### Gibt es eine Möglichkeit, das Admin-Konto zu ändern?
+
+#### Gibt es eine Möglichkeit, mehrere Administratoren zu haben?
+
+Ja, aber im Moment ist es noch etwas umständlich, da es noch nicht in der Benutzeroberfläche sichtbar ist. Um ein Konto zu einem administrativen Konto zu machen, muss man 4096 zum account_roles-Eintrag in der Kontotabelle der Datenbank hinzufügen. Um administrative Berechtigungen zu entfernen, muss man 4096 von den Kontorollen abziehen.
+
+#### Ich kann mich einloggen, aber es gibt keine Beiträge oder Webseiten
+
+Höchstwahrscheinlich ist Ihre Artikeltabelle abgestürzt. Führen Sie den MySQL-Befehl
+
+```
+repair table item;
+```
+
+#### Das Einloggen funktioniert nicht, sofort nach dem Einloggen wird die Seite neu geladen und ich werde ausgeloggt
+
+Höchstwahrscheinlich ist Ihre Sitzungstabelle abgestürzt. Führen Sie den MySQL-Befehl
+
+```
+repair table session;
+```
+
+aus.
+
+#### Wenn ich das Thema wechsle, werden manchmal Elemente des einen Themas über das andere gelegt
+
+a) store/[data]/smarty3 ist für den Webserver nicht beschreibbar. Machen Sie es möglich.
+b) Sie verwenden Midori, oder bei bestimmten Themes Konqueror im KHTML-Modus.
+
+#### Mein Netzwerk-Tab wird nicht geladen, es scheint durch ein Foto oder Video verursacht zu werden
+
+Ihr PHP-Speicherlimit ist zu niedrig. Erhöhen Sie die Größe der memory_limit-Anweisung in Ihrer php.ini
+Entgegen der landläufigen Meinung hat die Anzahl der Nutzer eines Hubs keinen Einfluss auf das benötigte Speicherlimit, vielmehr zählt der Inhalt einer individuellen Matrix. Streams mit vielen Fotos und Videos benötigen mehr Speicher als Streams mit viel Text.
+
+#### Ich habe keine Kommunikation mit jemandem
+
+Sie lauschen auf Port 443, haben aber kein gültiges SSL-Zertifikat. Dies gilt auch, wenn Ihre Basisurl http lautet.
+Lauschen Sie nicht auf Port 443, wenn Sie ihn nicht verwenden können. Es wird dringend empfohlen, dieses Problem durch die Installation eines gültigen SSL-Zertifikats für den Browser zu lösen, anstatt Port 443 zu deaktivieren.
+
+#### Wie kann ich eine Nicht-Git-Installation aktualisieren?
+
+1. Sichern Sie .htconfig.php
+2. Sichern Sie alles in store/
+3. Sichern Sie alle benutzerdefinierten Änderungen in mod/site/ und view/site
+4. Löschen Sie Ihre bestehende Installation
+5. Laden Sie die neue Version hoch.
+6. Laden Sie die neue Version der Themes und Addons hoch.
+7. Stellen Sie alles wieder her, was Sie zuvor gesichert haben.
+
+#### Was muss ich tun, wenn ich meinen Hub auf einen anderen Server verschiebe?
+
+1. Erstellen Sie einen Git-Klon auf dem neuen Server. Wiederholen Sie den Vorgang für alle benutzerdefinierten Themes und Addons.
+2. Rsync .htconfig.php
+3. Rsync alles in store/
+4. Rsync alles in mod/site/ und view/site (diese sind nur vorhanden, wenn Sie eigene Module haben)
+5. Dump und Wiederherstellung der DB.
+
+#### Wie installiere ich einen bestehenden Hub auf demselben Server neu?
+
+```
+git reset --hard HEAD
+```
+
+setzt alle Dateien auf ihre Upstream-Standardwerte zurück. Dabei werden keine lokalen Dateien zurückgesetzt, die nicht auch im Upstream existieren. Wenn Sie z.B. lokale Änderungen an mod/channel.php vorgenommen haben, werden diese zurückgesetzt - nicht aber die Änderungen in mod/site/channel.php.
+
+1. Wenn Sie unbedingt eine Neuinstallation durchführen müssen - z.B. wenn Sie das Betriebssystem aktualisieren müssen - befolgen Sie die Schritte für den Umzug auf einen anderen Server, aber anstatt rsync zu verwenden, erstellen Sie ein Backup und stellen Sie es auf andere Weise wieder her.
+
+Installieren Sie einen Hub nicht mit einer neuen Datenbank und einer neuen .htconfig.php neu, es sei denn, dies ist der allerletzte Ausweg. Das Anlegen eines temporären Accounts und das Anfordern von Hilfe über einen Support-Kanal für nicht-triviale Neuinstallationen ist einer Neuinstallation vorzuziehen.
+
+#### Wie kann ich die Standard-Homepage für abgemeldete Besucher festlegen?
+
+Verwenden Sie das Addon custom_home, das im Hauptrepository für Addons verfügbar ist.
+
+#### Was bedeuten die verschiedenen Einstellungen für den Verzeichnismodus?
+
+```
+// Konfigurieren Sie, wie wir mit Verzeichnisservern kommunizieren.
+// DIRECTORY_MODE_NORMAL = Verzeichnis-Client, wir finden ein Verzeichnis (alle Abfragen Ihrer Mitglieder werden an andere Stellen geleitet)
+// DIRECTORY_MODE_SECONDARY = Zwischenspeicherverzeichnis oder Mirror (hält sich synchron mit dem primären Realm [fügt erhebliche Cron-Ausführungszeit hinzu])
+// DIRECTORY_MODE_PRIMARY = Hauptverzeichnisserver (dies ist nur sinnvoll, wenn Sie einen eigenen Realm betreiben. einer pro Realm)
+// DIRECTORY_MODE_STANDALONE = „Off the grid“ oder private Verzeichnisdienste (nur lokale Site-Mitglieder im Verzeichnis)
+```
+
+- Die Voreinstellung ist NORMAL. Damit werden die meisten Verzeichnisdienste auf einen anderen Server verlagert. Der verwendete Server ist der config:system/directory_server. Diese Einstellung KANN durch den Code auf einen der Projektsekundärserver aktualisiert werden, wenn der aktuelle Server nicht erreichbar ist. Sie sollten entweder die Kontrolle über diesen anderen Server haben oder ihm vertrauen, dass er diese Einstellung verwendet.
+- SEKUNDÄR. Dies ermöglicht es Ihrer lokalen Site, als Verzeichnisserver zu fungieren, ohne die Abfragen Ihrer Mitglieder einem anderen Server zu überlassen. Es erfordert zusätzliche Verarbeitungszeit während der Cron-Abfrage und wird nicht empfohlen, auf einem gemeinsam genutzten Webhost ausgeführt zu werden.
+- PRIMÄR. Dies ermöglicht es Ihnen, ein komplett separates 'Netzwerk' von Verzeichnisservern mit einem eigenen Realm zu betreiben. Standardmäßig befinden sich alle Server im Realm RED_GLOBAL, es sei denn, die Einstellung config:system/directory_realm wird überschrieben. *Verwenden Sie dies nicht, wenn Sie keinen eigenen directory_realm haben.*
+- STANDALONE. Dies ist wie primary, außer dass es sich um ein eigenständiges „Netzwerk“ handelt, das mit keinem anderen Server kommuniziert. Verwenden Sie dies, wenn Sie nur einen Server haben und von den Red#Matrix-Verzeichnislisten getrennt sein wollen. \ No newline at end of file
diff --git a/doc/de/adminmanual/federation_addons.md b/doc/de/adminmanual/federation_addons.md
new file mode 100644
index 000000000..eda62a062
--- /dev/null
+++ b/doc/de/adminmanual/federation_addons.md
@@ -0,0 +1,8 @@
+### Föderations Addons
+
+Verschiedene Web-Communities haben begonnen, sich unter Verwendung gemeinsamer Protokolle zusammenzuschließen. Die beteiligten Protokolle sind in ihren Möglichkeiten etwas eingeschränkt. Das Diaspora-Protokoll ist etwas restriktiv, was die erlaubten Kommunikationsarten angeht. Alle Kommentare müssen vom ursprünglichen Autor auf eine sehr eindeutige Weise signiert werden. Das ActivityPub-Protokoll wird ebenfalls unterstützt. Kein anderes existierendes Protokoll unterstützt nomadische Standorte (es gibt inzwischen die Unterstützung nomadischer Identitäten bei ActivityPub), wie sie in diesem Projekt verwendet werden. Dies stellt einige Herausforderungen an die Unterstützung, da einige Funktionen mit einigen Netzen funktionieren und mit anderen nicht. Nichtsdestotrotz ermöglichen die Föderationsprotokolle die Herstellung von Verbindungen zu einer viel größeren Gemeinschaft von Menschen weltweit. Sie werden als Addons bereitgestellt.
+
+- Diaspora Protocol - Das Diaspora-Protokoll wird von Diaspora und Friendica verwendet. Sie sollten zuerst 'Diaspora Statistics' (Statistiken) aktivieren, um alle verfügbaren Funktionen zu nutzen.
+- PubCrawl - Das ActivityPub-Protokoll, das von Mastodon, MIsskey, Friendica und allen Fediverse-Diensten unterstützt wird.
+
+Jedes Mitglied Ihrer Website muss selbst entscheiden, ob es diese Protokolle zulassen will oder nicht, da sie mit einigen wünschenswerten Kernfunktionen und Fähigkeiten dieser Software (wie z.B. Channel-Migration und Klonen) in Konflikt geraten können. Sie tun dies auf ihrer Seite „Einstellungen -> Feature/Addon-Einstellungen“. \ No newline at end of file
diff --git a/doc/de/adminmanual/filesync.md b/doc/de/adminmanual/filesync.md
new file mode 100644
index 000000000..97cc388c6
--- /dev/null
+++ b/doc/de/adminmanual/filesync.md
@@ -0,0 +1,42 @@
+### Datei-Synchronisation und Klonen
+
+Das Klonen von Dateien über mehrere Instanzen eines Channels hinweg ist aufgrund der Natur der PHP-Speicherzuweisung ein sehr schwieriges Problem. Dies muss völlig anders gehandhabt werden als das Klonen oder Synchronisieren anderer Informationen. (Die Verarbeitung einer großen Videodatei oder von 40-50 Fotos kann den Speicher voll auslasten). Daher können wir nicht einfach alle Daten in eine Speicherauszugsdatei auslagern und sie dann sequentiell verarbeiten. Das Laden der Speicherauszugsdatei selbst wird wahrscheinlich den Speicherplatz erschöpfen.
+
+Außerdem gibt es zwei Hauptoperationen, die wir in Betracht ziehen. Die erste ist die schwierigste - das Speichern und anschließende Importieren aller Kanalinformationen in einen neuen Kanal-Klon. Der zweite ist die Synchronisierung von Datei-Änderungen, die in zwei oder mehr „aktiven“ Klonen auftreten.
+Beim ersten Versuch mit diesem Tool werden wir uns auf den zweiten Fall konzentrieren, während wir versuchen, ein gewisses Maß an Kompatibilität mit dem ersten Fall aufrechtzuerhalten, damit wir dieselben Tools wiederverwenden können.
+
+#### Metadaten
+
+Zunächst benötigen wir die Metadaten der Datei, um ihre Struktur auf einer anderen Website genau rekonstruieren zu können. Dazu sind die folgenden Informationen erforderlich:
+die „attach“-Struktur (ohne Dateiinhalt - das ist die Standardeinstellung) für die Datei selbst **und** ihre übergeordneten Verzeichnisse, damit wir ihren genauen Platz im Dateisystem wiederherstellen können, da wir nicht wissen, ob das übergeordnete Verzeichnis schon einmal importiert worden ist oder nicht.
+Foto"-Struktur für alle Fotoelemente, die durch das Hochladen dieser Datei in das System erstellt wurden. Diese enthält in der Regel mehrere verschiedene „Skalen“ oder Miniaturbilder, von denen einige für die Verwendung als Profilfoto oder Titelbild beschnitten werden können. Wir müssen die Informationen über den Bildausschnitt aufbewahren, die nicht in den Metadaten, sondern nur in den gespeicherten Daten enthalten sind. Die eigentlichen Miniaturbilddaten können in den Metadaten enthalten sein, müssen es aber nicht. Ein großformatiges Titelbild (Maßstab #7) könnte möglicherweise zu Speicherproblemen führen. Es ist zwar nicht so schlimm wie ein 100-Meter-Video, aber wenn Sie mehrere davon haben, können sie sich summieren.
+
+Objekteinträge, die mit dieser Datei verknüpft sind. Dabei kann es sich um Dateifreigabeaktivitäten, das „übergeordnete Element“, das mit Fotos verknüpft ist, und angehängte Konversationselemente (Foto-Likes, Kommentare usw.) handeln.
+
+Für alle diese Elemente muss die URL ersetzt und das Element neu signiert werden, wenn es auf eine andere Website verschoben wird.
+
+#### Dateidaten
+
+Dann haben wir die eigentlichen Dateidaten, die wir für die Rekonstruktion der Datei benötigen. Diese müssen getrennt von den Metadaten gespeichert werden, damit der Speicher bei der Verarbeitung nicht überlastet wird. Die eigentlichen Dateidaten können zur Rekonstruktion der Anhangsstruktur und der ersten vier Fotoskalen verwendet werden. Wenn es sich um ein Foto handelt, benötigen wir Zugriff auf die Skala „#4“ (Profilfoto) und die Skala #7 (Titelbild), so wie sie ursprünglich beschnitten wurden. Alle anderen Miniaturansichten können aus diesen generiert werden.
+
+#### Dateiabgleich
+
+Wir werden diesen Vorgang zuerst betrachten, weil er wahrscheinlich am einfachsten zu implementieren ist. Wenn ein Foto zum Quellsystem hinzugefügt, entfernt oder geändert wird, senden wir ein Klon-Synchronisierungspaket an alle bekannten Klone, das die Metadaten, aber **keine Dateidaten** enthält. Wir können nur ein Sync-Paket pro zu synchronisierendem Dateivorgang senden.
+
+Die empfangende Seite erstellt alle Metadatenstrukturen, führt eine URL-Übersetzung durch und speichert sie. Dann muss es die eigentlichen Daten abrufen. Unter der Voraussetzung, dass CURL Streaming unterstützt, wird eine authentifizierte Anfrage an die ursprüngliche Website gesendet und die Originaldatei wird angefordert und direkt auf die Festplatte gestreamt (unter Umgehung aller Verarbeitungsschritte). Wenn Fotomaßstab Nr. 4 oder Maßstab Nr. 7 erforderlich ist, werden diese angefordert und in den entsprechenden Strukturen gespeichert. Wir gehen in diesem Fall davon aus, dass der große Maßstab des Titelbildes den Speicher nicht erschöpft. Wenn CURL das Streaming nicht unterstützen kann, müssen die Anforderungspakete in eine Warteschlange gestellt und an den Ursprungsort gesendet werden, um „Chunks“ der Datei zu erhalten, und wieder zusammengesetzt werden, sobald alle Chunks abgerufen wurden.
+
+Die authentifizierte Anfrage hängt vom jeweiligen Mechanismus ab. Beim CURL-Streaming muss wahrscheinlich ein signiertes Geheimnis mit einem Zeitstempel erzeugt und an die Ursprungsseite der Datei gesendet werden. Dann können die Daten mit minimaler interner Verarbeitung abgerufen und unter Verwendung von stdio-Pufferung direkt auf die Festplatte ausgegeben werden. Im Falle einer Nomad-Anforderung wird das Nomad-Anforderungspaket validiert, aber die Planung von Chunk-Batches und deren Wiederzusammensetzung könnte sich als schwierig erweisen.
+
+#### Sicherung/Wiederherstellung von Dateien
+
+Dies ist viel komplizierter, da wir keinen authentifizierten Webserver haben, von dem wir Daten anfordern können. Die Metadaten können größtenteils identisch sein, aber wir brauchen eine Form der Signalisierung, dass wir die Datei nicht über das Web abrufen werden. Dies erfordert wahrscheinlich einen clientseitigen Prozess, um jede Metadaten-Datei zu analysieren und eine Datei auf der Festplatte zu finden, mit der sie verknüpft ist. Anschließend müssten die Daten mit einem speziellen Endpunkt, der für diese Aufgabe vorgesehen ist, an den Zielserver gestreamt werden. Eine Java-Applikation könnte hier die beste Option sein, um die Plattformneutralität zu wahren.
+
+Eine andere Möglichkeit wäre die Verwendung von WebDAV für diesen Schritt. Die Metadaten würden zuerst hochgeladen, dann die Datendateien. Wenn eine Datendatei mit einer vorhandenen Metadaten-Datei übereinstimmt, würden die Metadaten verarbeitet, die Datei entsprechend gespeichert und die Metadaten-Datei anschließend entfernt. In diesem Fall müssten in den Metadaten Fotos der Skalen 4 und 7 bereitgestellt werden.
+
+Optional könnte dieser Schritt auch mit einem lokalen Dateisystem auf dem Zielserver durchgeführt werden. Dies wäre die leistungsfähigste Lösung, und eine Reihe von Shell-basierten Tools (im Falle von Linux) könnte die „Client-Seite“ der Aufgabe übernehmen.
+
+Die Komplexität dieser Aufgabe erfordert eine sorgfältige Planung, wie die Daten organisiert und gespeichert und gegebenenfalls per Fernzugriff gesichert oder von der Quellwebsite zur Sicherung übertragen werden.
+
+#### Rückwärtskompatibilität
+
+Es gibt einige offensichtliche Probleme bei der Bereitstellung von Daten für die Sicherung oder das Klonen, die auf dem System vor der Existenz von Wiederherstellungs-/Synchronisierungstools vorhanden waren. Um die Werkzeuge selbst relativ unkompliziert zu halten (soweit dies angesichts der Beschränkungen möglich ist), muss die Abwärtskompatibilität möglicherweise durch ein spezielles Plugin oder Addon hergestellt werden. \ No newline at end of file
diff --git a/doc/de/adminmanual/further_help.md b/doc/de/adminmanual/further_help.md
new file mode 100644
index 000000000..cfd465b07
--- /dev/null
+++ b/doc/de/adminmanual/further_help.md
@@ -0,0 +1,3 @@
+### Wo Sie weitere Hilfe finden
+
+Wenn Sie auf Probleme stoßen oder Fragen haben, die in dieser Dokumentation nicht behandelt werden, lassen Sie es uns bitte über den [Issue Tracker](https://framagit.org/hubzilla/core/issues) wissen. Bitte beschreiben Sie Ihre Betriebsumgebung so genau wie möglich und geben Sie so viele Details wie möglich zu den Fehlermeldungen an, die Sie sehen, damit wir diese in Zukunft vermeiden können. Aufgrund der großen Vielfalt an Betriebssystemen und PHP-Plattformen haben wir nur begrenzte Möglichkeiten, Ihre PHP-Installation zu debuggen oder fehlende Module zu beschaffen, aber wir werden unser Bestes tun, um allgemeine Code-Probleme zu lösen.
diff --git a/doc/de/adminmanual/hub_snapshot_tools.md b/doc/de/adminmanual/hub_snapshot_tools.md
new file mode 100644
index 000000000..8e56a2168
--- /dev/null
+++ b/doc/de/adminmanual/hub_snapshot_tools.md
@@ -0,0 +1,127 @@
+### Hub-Snapshot-Tools
+
+Hubzilla-Entwickler müssen häufig zwischen Zweigen wechseln, die möglicherweise inkompatible Datenbankschemata oder Inhalte haben. Die folgenden beiden Skripte erstellen und restaurieren komplette Snapshots einer Hubzilla-Instanz, einschließlich des Hub-Web-Root und des gesamten Datenbankstatus. Jedes Skript benötigt eine Konfigurationsdatei namens `hub-snapshot.conf`, die sich im selben Ordner befindet und die spezifischen Verzeichnisse und Datenbankdetails Ihres Hubs enthält.
+
+### Konfiguration
+
+Das Format der Konfigurationsdatei ist sehr streng. Es dürfen keine Leerzeichen zwischen dem Variablennamen und dem Wert stehen. Ersetzen Sie nur den Inhalt innerhalb der Anführungszeichen durch Ihre Konfiguration. Speichern Sie diese Datei als `hub-snapshot.conf` zusammen mit den Skripten.
+
+```
+# Ort des Hub-Root. Normalerweise ist dies der Speicherort des Hubzilla Repo-Klons.
+HUBROOT=„/var/www/“
+# Name der MySQL-Datenbank
+DBNAME=„hubzilla“
+# MySQL-Datenbank-Benutzer
+DBUSER=„hubzilla“
+# MySQL-Datenbank-Passwort
+DBPWD=„akeufajeuwfb“
+# Der Ziel-Snapshot-Ordner, in dem das Git-Repository initialisiert werden soll
+SNAPSHOTROOT=„/root/snapshots/hubzilla/“
+```
+
+#### Snapshot
+
+Beispiel für die Verwendung:
+
+```
+sh hub-snapshot.sh my-hub.conf „Commit-Meldung für den Snapshot“
+```
+
+**hub-snapshot.sh**:
+
+```
+#!/bin/bash
+
+if ! [ -f "$1" ]; then
+ echo "$1 is not a valid file. Aborting..."
+ exit 1
+fi
+source "$1"
+#echo "$DBNAME"
+#echo "$DBUSER"
+#echo "$DBPWD"
+#echo "$HUBROOT"
+#echo "$SNAPSHOTROOT"
+MESSAGE="snapshot: $2"
+
+if [ "$DBPWD" == "" -o "$SNAPSHOTROOT" == "" -o "$DBNAME" == "" -o "$DBUSER" == "" -o "$HUBROOT" == "" ]; then
+ echo "Required variable is not set. Aborting..."
+ exit 1
+fi
+
+if [ ! -d "$SNAPSHOTROOT"/db/ ]; then
+ mkdir -p "$SNAPSHOTROOT"/db/
+fi
+if [ ! -d "$SNAPSHOTROOT"/www/ ]; then
+ mkdir -p "$SNAPSHOTROOT"/www/
+fi
+
+if [ ! -d "$SNAPSHOTROOT"/www/ ] || [ ! -d "$SNAPSHOTROOT"/db/ ]; then
+ echo "Error creating snapshot directories. Aborting..."
+ exit 1
+fi
+
+echo "Export database..."
+mysqldump -u "$DBUSER" -p"$DBPWD" "$DBNAME" > "$SNAPSHOTROOT"/db/"$DBNAME".sql
+echo "Copy hub root files..."
+rsync -va --delete --exclude=.git* "$HUBROOT"/ "$SNAPSHOTROOT"/www/
+
+cd "$SNAPSHOTROOT"
+
+if [ ! -d ".git" ]; then
+ git init
+fi
+if [ ! -d ".git" ]; then
+ echo "Cannot initialize git repo. Aborting..."
+ exit 1
+fi
+
+git add -A
+echo "Commit hub snapshot..."
+git commit -a -m "$MESSAGE"
+
+exit 0
+```
+
+#### Restore
+
+```
+#!/bin/bash
+# Restore hub to a previous state. Input hub config and commit hash
+
+if ! [ -f "$1" ]; then
+ echo "$1 is not a valid file. Aborting..."
+ exit 1
+fi
+source "$1"
+COMMIT=$2
+
+if [ "$DBPWD" == "" -o "$SNAPSHOTROOT" == "" -o "$DBNAME" == "" -o "$DBUSER" == "" -o "$HUBROOT" == "" ]; then
+ echo "Required variable is not set. Aborting..."
+ exit 1
+fi
+RESTOREDIR="$(mktemp -d)/"
+
+if [ ! -d "$RESTOREDIR" ]; then
+ echo "Cannot create restore directory. Aborting..."
+ exit 1
+fi
+echo "Cloning the snapshot repo..."
+git clone "$SNAPSHOTROOT" "$RESTOREDIR"
+cd "$RESTOREDIR"
+echo "Checkout requested snapshot..."
+git checkout "$COMMIT"
+echo "Restore hub root files..."
+rsync -a --delete --exclude=.git* "$RESTOREDIR"/www/ "$HUBROOT"/
+echo "Restore hub database..."
+mysql -u "$DBUSER" -p"$DBPWD" "$DBNAME" < "$RESTOREDIR"/db/"$DBNAME".sql
+
+chown -R www-data:www-data "$HUBROOT"/{store,extend,addon,.htlog,.htconfig.php}
+
+echo "Restored hub to snapshot $COMMIT"
+echo "Removing temporary files..."
+
+rm -rf "$RESTOREDIR"
+
+exit 0
+``` \ No newline at end of file
diff --git a/doc/de/adminmanual/installation.md b/doc/de/adminmanual/installation.md
new file mode 100644
index 000000000..4efbf4239
--- /dev/null
+++ b/doc/de/adminmanual/installation.md
@@ -0,0 +1,8 @@
+### Installation
+
+Es gibt mehrere Möglichkeiten, einen neuen Hub zu installieren.
+
+- Manuelle Installation auf einem bestehenden Server
+- Automatisierte Installation auf einem bestehenden Server mit einem Shell-Skript
+- Automatisierte Bereitstellung über einen OpenShift Virtual Private Server (VPS)
+- Verwendung eines Docker-Containers \ No newline at end of file
diff --git a/doc/de/adminmanual/installation_requirements.md b/doc/de/adminmanual/installation_requirements.md
new file mode 100644
index 000000000..b7f772add
--- /dev/null
+++ b/doc/de/adminmanual/installation_requirements.md
@@ -0,0 +1,14 @@
+### Anforderungen
+
+- Apache mit aktiviertem mod-rewrite und „AllowOverride All“, damit Sie eine lokale .htaccess-Datei verwenden können. Einige Leute haben erfolgreich nginx und lighttpd verwendet. Beispielkonfigurations-Skripte sind für diese Plattformen in doc/install verfügbar. Apache und nginx haben die meiste Unterstützung.
+- PHP 8.1 oder höher. Beachten Sie, dass in einigen Shared-Hosting-Umgebungen die *Kommandozeilenversion* von PHP von der *Webserverversion* abweichen kann
+- *PHP-Befehlszeilenzugriff*, wenn register_argc_argv in der Datei php.ini auf true gesetzt ist und der Hosting-Provider keine Einschränkungen für die Verwendung von exec() und proc_open() hat.
+- curl, gd (mit mindestens jpeg und png Unterstützung), pdo-mysql (oder pdo-postgres), mbstring, zip und openssl Erweiterungen. Die imagick-Erweiterung ist nicht erforderlich, wird aber empfohlen.
+- Die xml-Erweiterung ist erforderlich, wenn Sie webdav verwenden möchten.
+- eine Art von E-Mail-Server oder E-Mail-Gateway, so dass PHP mail() funktioniert.
+- Ein unterstützter Datenbankserver. Die unterstützten Datenbanken sind:
+ - Mysql Version 8.0.22 oder höher
+ - MariaDB Version 10.4 oder höher
+ - PostgreSQL Version 12 oder höher
+- Fähigkeit, Aufträge mit Cron zu planen.
+- Die Installation in einer Top-Level-Domain oder Sub-Domain (ohne Verzeichnis/Pfad-Komponente in der URL) ist ERFORDERLICH. \ No newline at end of file
diff --git a/doc/de/adminmanual/manual_for_administrators.md b/doc/de/adminmanual/manual_for_administrators.md
new file mode 100644
index 000000000..9b3237620
--- /dev/null
+++ b/doc/de/adminmanual/manual_for_administrators.md
@@ -0,0 +1,30 @@
+### Handbuch für Administratoren
+
+Hubzilla ist mehr als eine einfache Webanwendung. Es handelt sich um ein komplexes Kommunikationssystem, das eher einem E-Mail-Server als einem Webserver ähnelt. Aus Gründen der Zuverlässigkeit und Leistung werden Nachrichten im Hintergrund zugestellt und für eine spätere Zustellung in eine Warteschlange gestellt, wenn Websites nicht erreichbar sind. Diese Art von Funktionalität erfordert etwas mehr vom Hostsystem als der typische Blog. Nicht jeder PHP/MySQL-Hosting-Anbieter ist in der Lage, Hubzilla zu unterstützen. Viele können es, aber bitte prüfen Sie die Anforderungen und bestätigen Sie diese mit Ihrem Hosting-Anbieter vor der Installation.
+
+Wir haben uns sehr bemüht, sicherzustellen, dass Hubzilla auf Standard-Hosting-Plattformen läuft, wie sie für Wordpress-Blogs und Drupal-Websites verwendet werden. Es läuft auf den meisten Linux-Systemen.
+
+#include doc/de/adminmanual/further_help.md;
+#include doc/de/adminmanual/before_you_start.md;
+#include doc/de/adminmanual/installation.md;
+#include doc/de/adminmanual/automated_installation.md;
+#include doc/de/adminmanual/Installation_using_docker.md;
+#include doc/de/adminmanual/recommendec_addons.md;
+#include doc/de/adminmanual/problems-following-an-update.md;
+#include doc/de/adminmanual/federation_addons.md;
+#include doc/de/adminmanual/service_classes.md;
+#include doc/de/adminmanual/theme_management.md;
+#include doc/de/adminmanual/Creating-Templates.md;
+#include doc/de/adminmanual/Schema-development.md;
+#include doc/de/adminmanual/Widgets.md;
+#include doc/de/adminmanual/channel_directory.md;
+#include doc/de/adminmanual/Primary-Directory.md;
+#include doc/de/adminmanual/administration.md;
+#include doc/de/adminmanual/troubleshooting.md;
+#include doc/de/adminmanual/hub_snapshot_tools.md;
+#include doc/de/adminmanual/database.md;
+#include doc/de/adminmanual/advanced_configurations.md;
+#include doc/de/adminmanual/CLI_tools.md;
+#include doc/de/adminmanual/filesync.md;
+#include doc/de/adminmanual/Nomad---A-High-Level-Overview.md;
+#include doc/de/adminmanual/faq_admins.md;
diff --git a/doc/de/adminmanual/manual_installation.md b/doc/de/adminmanual/manual_installation.md
new file mode 100644
index 000000000..303b5d416
--- /dev/null
+++ b/doc/de/adminmanual/manual_installation.md
@@ -0,0 +1,60 @@
+### Manuelle Installation
+
+#### Entpacken Sie die Hubzilla-Dateien in das Stammverzeichnis Ihres Webserver-Dokumentbereichs
+
+Wenn Sie den Verzeichnisbaum auf Ihren Webserver kopieren, stellen Sie sicher, dass Sie die versteckten Dateien wie .htaccess mit einbeziehen.
+
+Wenn Sie dazu in der Lage sind, empfehlen wir, das Quellcode-Repository mit git zu klonen, anstatt eine gepackte tar- oder zip-Datei zu verwenden. Dadurch lässt sich die Software viel einfacher aktualisieren. Der Linux-Befehl zum Klonen des Repositorys in ein Verzeichnis „mywebsite“ lautet wie folgt:
+
+```
+git clone https://framagit.org/hubzilla/core.git mywebsite
+```
+
+und dann können Sie die neuesten Änderungen jederzeit mit:
+
+```
+git pull
+```
+
+Stellen Sie sicher, dass die Ordner `store/[data]/smarty3` und `store` existieren und vom Webserver beschreibbar sind:
+
+```
+mkdir -p „store/[data]/smarty3“
+chmod -R 777 store
+```
+
+Diese Berechtigung (777) ist sehr gefährlich und wenn Sie über ausreichende Privilegien und Kenntnisse haben, sollten Sie diese Verzeichnisse nur für den Webserver und, falls abweichend, für den Benutzer, der den cron-Job ausführt (siehe unten). In vielen Shared-Hosting-Umgebungen kann dies schwierig sein, ohne ein Trouble-Ticket bei Ihrem Provider zu eröffnen. Die obigen Berechtigungen ermöglichen das Funktionieren der Software, sind aber nicht optimal.
+
+Die folgenden Verzeichnisse müssen ebenfalls vom Webserver beschreibbar sein, damit bestimmte webbasierte Verwaltungstools funktionieren:
+
+- `addon`
+- `extend`
+- `view/theme`
+- `widget`
+
+#### Offizielle Addons
+
+##### Installation
+
+Navigieren Sie zu Ihrer Website. Dann sollten Sie das Addon-Repository klonen (separat). Wir geben diesem Repository den Spitznamen 'hzaddons'. Du kannst andere Hubzilla-Addon-Repositories einbinden, indem du ihnen andere Nicknames gibst:
+
+```
+cd mywebsite
+util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons
+```
+
+##### Aktualisieren
+
+Um den Addon-Baum auf dem neuesten Stand zu halten, sollten Sie sich auf der obersten Ebene des Website-Verzeichnisses befinden und einen Update-Befehl für dieses Repository eingeben::
+
+```
+cd mywebsite
+util/update_addon_repo hzaddons
+```
+
+Erstellen Sie durchsuchbare Darstellungen der Online-Dokumentation. Sie können dies jedes Mal tun, wenn die Dokumentation aktualisiert wird:
+
+```
+cd mywebsite
+util/importdoc
+``` \ No newline at end of file
diff --git a/doc/de/adminmanual/pic/adm01.png b/doc/de/adminmanual/pic/adm01.png
new file mode 100644
index 000000000..5c5b1bab3
--- /dev/null
+++ b/doc/de/adminmanual/pic/adm01.png
Binary files differ
diff --git a/doc/de/adminmanual/problems-following-an-update.md b/doc/de/adminmanual/problems-following-an-update.md
new file mode 100644
index 000000000..2bfbaf83d
--- /dev/null
+++ b/doc/de/adminmanual/problems-following-an-update.md
@@ -0,0 +1,31 @@
+### Probleme nach einer Aktualisierung
+
+Gut 90 % aller Fehler, die unmittelbar nach der Aktualisierung des Codes auf die neueste Version auftreten, sind einfache Cache-Fehler der einen oder anderen Art. Wenn Sie ein Update durchführen und feststellen, dass etwas sehr Offensichtliches kaputt ist - z. B. dass Ihre Matrix-Seite nicht geladen wird, dass Benachrichtigungen fehlen oder dass Kommentarboxen fehlen - dann ist es wahrscheinlich gar kein Fehler. Das Brechen grundlegender Funktionen ist die Art von Dingen, die Entwickler eher bemerken.
+Wenn Ihnen das passiert, können Sie einige einfache Schritte unternehmen, bevor Sie sich an die Support-Foren wenden:
+
+**Browser-Cache**
+Symptome: Menüs lassen sich nicht ausklappen, die ACL-Auswahl öffnet sich nicht, die Fortschrittsanzeige wird nicht angezeigt (oder läuft in einer Endlosschleife), Matrix- und Kanalseiten werden nicht geladen.
+Erzwingen Sie das Neuladen der Seite. Gelegentlich, aber sehr, sehr selten, müssen Sie auch die Sitzungsdaten löschen, was durch einen Neustart des Browsers erreicht wird.
+
+**FastCGI**
+Symptome: Falsche Variablen. Die grundlegende Benutzeroberfläche funktioniert meistens, zeigt aber falsche Inhalte an oder es fehlen ganz Inhalte.
+Wenn Sie php-fpm verwenden, lässt sich dieses Problem in der Regel beheben mit
+
+```
+service php-fpm neu starten
+```
+
+**Smarty-Cache**
+Die Symptome:
+
+1. Weißer Bildschirm des Todes. Dies tritt vor allem auf den Einstellungs- und Verwaltungsseiten auf.
+2. Fehlende Icons, Registerkarten, Menüs oder Funktionen.
+
+Wir verwenden die Smarty3-Vorlagen-Engine, um Seiten zu generieren. Diese Vorlagen werden kompiliert, bevor sie angezeigt werden. Es kann vorkommen, dass eine neue oder geänderte Vorlage die alte kompilierte Version nicht überschreibt. Um den Smarty-Cache zu löschen, löschen Sie alle Dateien in store/[data]/smarty3/compiled **, aber nicht das Verzeichnis selbst**. Die Vorlagen werden dann bei ihrem nächsten Zugriff neu kompiliert.
+
+**Theme-Probleme**
+Es gibt viele Themes für Hubzilla. Nur Redbasic wird offiziell von den Kernentwicklern unterstützt. Dies gilt *auch dann, wenn ein Kernentwickler zufällig ein weiteres Theme unterstützt*. Das bedeutet, dass neue Funktionen garantiert nur in Redbasic funktionieren.
+
+Redbasic verwendet einige Javascript-Bibliotheken, die in anderen Themes anders oder gar nicht verwendet werden. Dies bedeutet, dass neue Funktionen möglicherweise nur in Redbasic richtig funktionieren. Bevor Sie ein Problem melden, sollten Sie daher zu Redbasic wechseln, um zu sehen, ob das Problem dort existiert. Wenn das Problem verschwindet, handelt es sich nicht um einen Fehler, sondern um ein Theme, das nicht auf dem neuesten Stand ist.
+
+Sollten Sie das Problem dann den Theme-Entwicklern melden? Nein. Theme-Entwickler verwenden ihre Themes. Die Chancen stehen gut, dass sie es wissen. Geben Sie ihnen zwei oder drei Tage Zeit, um sich auf den neuesten Stand zu bringen, und melden Sie das Problem, wenn es *dann* immer noch nicht behoben ist. Für diese Situation gibt es zwei Lösungsmöglichkeiten. Erstens können Sie vorübergehend Redbasic verwenden. Zweitens sind die meisten Themes auch Open Source - öffnen Sie einen Pull Request und machen Sie sich einen Freund. \ No newline at end of file
diff --git a/doc/de/adminmanual/recommended_addons.md b/doc/de/adminmanual/recommended_addons.md
new file mode 100644
index 000000000..d39325fed
--- /dev/null
+++ b/doc/de/adminmanual/recommended_addons.md
@@ -0,0 +1,7 @@
+### Empfohlene Addons
+
+Wir empfehlen, die folgenden Addons auf allen öffentlichen Seiten zu installieren:
+
+**nsfw** - unangemessene Beiträge/Kommentare ausblenden
+
+**superblock** - blockiert Inhalte von anstößigen Kanälen \ No newline at end of file
diff --git a/doc/de/adminmanual/service_classes.md b/doc/de/adminmanual/service_classes.md
new file mode 100644
index 000000000..ac79096d9
--- /dev/null
+++ b/doc/de/adminmanual/service_classes.md
@@ -0,0 +1,57 @@
+### Service-Klassen
+
+Mit Hilfe von Dienstklassen können Sie die Systemressourcen begrenzen, indem Sie die Möglichkeiten einzelner Konten einschränken, z. B. die Speicherung von Dateien und die Begrenzung von Posts auf der obersten Ebene. Definieren Sie benutzerdefinierte Serviceklassen entsprechend Ihren Anforderungen in der Datei `.htconfig.php`. Erstellen Sie zum Beispiel eine *Standard-* und eine *Premiumklasse* mit den folgenden Zeilen:
+
+```
+// Dienstklassen
+
+App::$config['system']['default_service_class']='standard'; // dies ist die Standard-Serviceklasse, die mit jedem neuen Konto verbunden wird
+
+// Konfiguration für die Standard-Serviceklasse
+App::$config['service_class']['standard'] =
+array('photo_upload_limit'=>2097152, // Gesamtspeichergrenze für Fotos pro Kanal (hier 2MB)
+'total_identities' =>1, // Anzahl der Kanäle, die ein Konto erstellen kann
+'total_items' =>0, // Anzahl der Beiträge der obersten Ebene, die ein Kanal erstellen kann. Gilt nur für Top-Level-Posts des Channel-Benutzers, andere Posts und Kommentare sind davon nicht betroffen
+'total_pages' =>100, // Anzahl der Seiten, die ein Channel erstellen kann
+'total_channels' =>100, // Anzahl der Channels, die der Benutzer hinzufügen kann, andere Benutzer können diesen Channel immer noch hinzufügen, auch wenn das Limit erreicht ist
+attach_upload_limit' =>2097152, // Gesamtspeicherplatz für Anhänge pro Kanal (hier 2MB)
+'chatters_inroom' =>20);
+
+// Konfiguration für Premium-Dienstklasse
+App::$config['service_class']['premium'] =
+array('photo_upload_limit'=>20000000000, // Gesamtspeichergrenze für Fotos pro Kanal (hier 20GB)
+'total_identities' =>20, // Anzahl der Kanäle, die ein Konto erstellen kann
+'total_items' =>20000, // Anzahl der Beiträge der obersten Ebene, die ein Kanal erstellen kann. Gilt nur für Beiträge der obersten Ebene des Kanalbenutzers, andere Beiträge und Kommentare sind davon nicht betroffen
+'total_pages' =>400, // Anzahl der Seiten, die ein Channel erstellen kann
+'total_channels' =>2000, // Anzahl der Channels, die der Nutzer hinzufügen kann, andere Nutzer können diesen Channel immer noch hinzufügen, auch wenn das Limit erreicht ist
+attach_upload_limit' =>20000000000, // Gesamtspeicherplatz für Anhänge pro Kanal (hier 20GB)
+'chatters_inroom' =>100);
+```
+
+Um eine Serviceklasse auf ein bestehendes Konto anzuwenden, verwenden Sie das Befehlszeilendienstprogramm aus dem Web-Root:
+
+`util/service_class` list service classes
+
+`util/config system default_service_class firstclass` setzt die Standarddienstklasse auf 'firstclass'
+
+`util/service_class firstclass` listet die Dienste auf, die Teil der Dienstklasse „firstclass“ sind
+
+`util/service_class firstclass photo_upload_limit 10000000` setzt die gesamte Fotoplattennutzung der firstclass auf 10 Millionen Bytes
+
+`util/service_class --account=5 firstclass` setzt die Konto-ID 5 auf die Serviceklasse 'firstclass' (mit Bestätigung)
+
+`util/service_class --channel=blogchan firstclass` setzt das Konto, dem der Kanal „blogchan“ gehört, auf die Serviceklasse „firstclass“ (mit Bestätigung)
+
+**Optionen zur Begrenzung der Dienstklasse**
+
+- photo_upload_limit - maximale Gesamtzahl an Bytes für Fotos
+- total_items - maximale Gesamtzahl an Beiträgen auf oberster Ebene
+- total_pages - maximale Anzahl von Seiten für Comanche
+- total_identities - maximale Anzahl von Kanälen im Besitz des Kontos
+- total_channels - maximale Anzahl von Verbindungen
+- total_feeds - Maximale Anzahl von RSS-Feed-Verbindungen
+- attach_upload_limit - maximaler Datei-Upload-Speicherplatz (Bytes)
+- minimum_feedcheck_minutes - niedrigste zulässige Einstellung für die Abfrage von RSS-Feeds
+- chatrooms - maximale Anzahl von Chaträumen
+- chatters_inroom - maximale Anzahl Chatter pro Raum
+- access_tokens - maximale Anzahl von Gastzugangstoken pro Kanal \ No newline at end of file
diff --git a/doc/de/adminmanual/theme_management.md b/doc/de/adminmanual/theme_management.md
new file mode 100644
index 000000000..bfdf4d261
--- /dev/null
+++ b/doc/de/adminmanual/theme_management.md
@@ -0,0 +1,7 @@
+### Verwaltung der Themen
+
+#### Beispiel für die Repo-Verwaltung
+
+1. Navigieren Sie zu Ihrem Hub-Web rootroot@hub`:/root# cd /var/www`
+
+2. Fügen Sie das Theme-Repo hinzu und geben Sie ihm einen nameroot@hub`:/var/www# util/add_theme_repo https://github.com/DeadSuperHero/redmatrix-themes.git DeadSuperHero` \ No newline at end of file
diff --git a/doc/de/adminmanual/troubleshooting.md b/doc/de/adminmanual/troubleshooting.md
new file mode 100644
index 000000000..384202b65
--- /dev/null
+++ b/doc/de/adminmanual/troubleshooting.md
@@ -0,0 +1,51 @@
+### Fehlersuche
+
+#### Logdateien
+
+Die Systemprotokolldatei ist eine äußerst nützliche Ressource, um Fehler aufzuspüren. Dies kann auf der Konfigurationsseite admin/log aktiviert werden. Eine Loglevel-Einstellung von `LOGGER_DEBUG` wird für stabile Produktionsstandorte bevorzugt. Die meisten Probleme bei der Kommunikation oder der Speicherung werden hier aufgelistet. Eine Einstellung von LOGGER_DATA bietet *viel* mehr Details, kann aber Ihre Festplatte füllen. In jedem Fall empfehlen wir die Verwendung von logrotate auf Ihrem Betriebssystem, um die Protokolle zu überprüfen und ältere Einträge zu löschen.
+
+Am Ende der Datei .htconfig.php befinden sich mehrere (auskommentierte) Zeilen, die die PHP-Fehlerprotokollierung aktivieren. Dies meldet Probleme mit der Codesyntax und der Ausführung des Codes und ist die erste Stelle, an der Sie nach Problemen suchen sollten, die zu einem „weißen Bildschirm“ oder einer leeren Seite führen. Dies ist in der Regel das Ergebnis von Code-/Syntaxproblemen. Datenbankfehler werden in der Systemprotokolldatei gemeldet, aber wir haben es als nützlich empfunden, in Ihrem Hauptverzeichnis eine Datei namens dbfail.out anzulegen, in der *nur* datenbankbezogene Probleme gesammelt werden. Wenn die Datei existiert und beschreibbar ist, werden Datenbankfehler sowohl in dieser Datei als auch in der Systemprotokolldatei protokolliert.
+
+Im Falle von „500“-Fehlern werden die Probleme oft in den Protokollen Ihres Webservers protokolliert, oft in /var/log/apache2/error.log oder ähnlichem. Konsultieren Sie die Dokumentation Ihres Betriebssystems.
+
+Es gibt drei verschiedene Protokollierungsmöglichkeiten.
+
+**Die erste ist das Datenbankfehlerprotokoll**. Dieses wird nur verwendet, wenn Sie eine Datei mit dem Namen `dbfail.out` im Stammverzeichnis Ihrer Website anlegen und sie für den Webserver schreibbar machen. Wenn Datenbankabfragen fehlgeschlagen sind, werden sie hier gemeldet. Sie weisen im Allgemeinen auf Tippfehler in unseren Abfragen hin, treten aber auch auf, wenn der Datenbankserver die Verbindung unterbricht oder Tabellen beschädigt werden. In seltenen Fällen werden hier Race Conditions angezeigt, wenn zwei Prozesse versuchen, einen xchan- oder Cache-Eintrag mit derselben ID zu erstellen. Alle anderen Fehler (insbesondere anhaltende Fehler) sollten untersucht werden.
+
+**Der zweite Bereich ist das PHP-Fehlerprotokoll**. Dieses wird vom Sprachprozessor erstellt und meldet nur Probleme in der Sprachumgebung. Auch hier kann es sich um Syntax- oder Programmierfehler handeln, die jedoch in der Regel fatal sind und zu einem „weißen Bildschirm des Todes“ führen, d. h. PHP wird beendet. Sie sollten sich diese Datei ansehen, wenn etwas schief läuft, das nicht zu einem weißen Bildschirm führt, aber es ist nicht ungewöhnlich, dass diese Datei tagelang leer ist.
+
+Am Ende der mitgelieferten Datei `.htconfig.php` befinden sich einige Zeilen, die, wenn sie nicht auskommentiert werden, ein PHP-Fehlerprotokoll aktivieren (*äußerst* nützlich, um die Ursache von Fehlern beim weißen Bildschirm zu finden). Dies ist standardmäßig nicht der Fall, da es Probleme mit dem Eigentum an der Logdatei und den Schreibrechten geben könnte und die Logdatei standardmäßig nicht rotiert.
+
+**Die dritte Datei ist das „Anwendungsprotokoll“.** Dieses wird von Hubzilla verwendet, um zu berichten, was im Programm vor sich geht und meldet normalerweise alle Schwierigkeiten oder unerwarteten Daten, die wir erhalten haben. Gelegentlich werden auch „Heartbeat“-Statusmeldungen ausgegeben, um anzuzeigen, dass wir einen bestimmten Punkt in einem Skript erreicht haben. **Dies** ist die wichtigste Protokolldatei für uns, da wir sie selbst nur zu dem Zweck erstellen, den Status von Hintergrundaufgaben und alles, was seltsam oder fehl am Platz erscheint, zu melden. Es muss nicht unbedingt etwas Schlimmes sein, aber vielleicht einfach nur unerwartet. Wenn Sie eine Aufgabe ausführen und ein Problem auftritt, lassen Sie uns wissen, was in dieser Datei steht, wenn das Problem auftritt. (Bitte schicken Sie mir keine 100M-Dumps, das würde mich nur verärgern). Nur ein paar relevante Zeilen, damit ich ein paar hunderttausend Codezeilen ausschließen und mich darauf konzentrieren kann, wo das Problem auftritt.
+
+Dies sind Ihre Website-Protokolle, nicht meine. Wir melden ernsthafte Probleme auf jeder Protokollebene. Für die meisten Websites empfehle ich die Log-Ebene `DEBUG` - sie liefert ein paar zusätzliche Informationen und erzeugt keine riesigen Logdateien. Wenn ein Problem auftritt, das sich allen Versuchen entzieht, es zu verfolgen, sollten Sie für einen kurzen Zeitraum die Protokollebene `DATA` verwenden, um alle Details der Strukturen zu erfassen, mit denen wir es zu diesem Zeitpunkt zu tun hatten. Diese Protokollebene verbraucht sehr viel Speicherplatz und wird daher nur für kurze Zeiträume oder für Test-Sites von Entwicklern empfohlen.
+
+Ich empfehle die Konfiguration von logrotate sowohl für das php-Protokoll als auch für das Anwendungsprotokoll. Normalerweise werfe ich alle ein bis zwei Wochen einen Blick auf dbfail.out, behebe alle gemeldeten Probleme und beginne dann mit einer neuen Datei. Das Gleiche gilt für die PHP-Protokolldatei. Ich schaue darin ab und zu nach, ob es etwas gibt, das behoben werden muss.
+
+Wenn etwas schief läuft und es sich nicht um einen schwerwiegenden Fehler handelt, schaue ich mir die Anwendungsprotokolldatei an. Oft werde ich
+
+```
+tail -f logfile.out
+```
+
+während ich einen Vorgang wiederhole, bei dem es Probleme gibt. Oft füge ich zusätzliche Logging-Anweisungen in den Code ein, wenn es keinen Hinweis darauf gibt, was schief läuft. Selbst so etwas Einfaches wie „got here“ oder der Ausdruck des Wertes einer Variable, die verdächtig sein könnte. Auch das können Sie tun - ich möchte Sie sogar dazu ermutigen, dies zu tun. Sobald Sie gefunden haben, was Sie suchen müssen, können Sie
+
+```
+git checkout file.php
+```
+
+um sofort alle zusätzlichen Protokollierungsdaten, die Sie hinzugefügt haben, zu löschen. Verwenden Sie die Informationen aus diesem Protokoll und alle Details, die Sie aus Ihrer Untersuchung des Problems liefern können, um Ihren Fehlerbericht einzureichen - es sei denn, Ihre Analyse weist auf die Quelle des Problems hin. In diesem Fall beheben Sie es einfach.
+
+##### Rotierende Protokolldateien
+
+1. Aktiviere das **logrot** Addon im offiziellen [hubzilla-addons](https://framagit.org/hubzilla/addons) repo
+2. Legen Sie in Ihrem Web-Root ein Verzeichnis namens `log` mit Schreibrechten für den Webserver an
+3. Gehen Sie zu den **logrot-Administrationseinstellungen** und geben Sie diesen Ordnernamen sowie die maximale Größe und Anzahl der aufbewahrten Protokolldateien ein.
+
+#### Probleme melden
+
+Wenn Sie Probleme melden, versuchen Sie bitte, so viele Details wie nötig anzugeben, damit die Entwickler das Problem reproduzieren können, und geben Sie den vollständigen Text aller Fehlermeldungen an.
+
+Wir ermutigen Sie, diese Protokolle zusammen mit dem Quellcode, den Sie besitzen, so gut wie möglich zu nutzen, um Probleme zu beheben und ihre Ursache zu finden. Die Community kann oft helfen, aber nur Sie haben Zugang zu den Logdateien Ihrer Website, und es wird als Sicherheitsrisiko angesehen, sie weiterzugeben.
+
+Wenn ein Code-Problem aufgedeckt wurde, melden Sie es bitte im Bugtracker des Projekts (https://framagit.org/hubzilla/core/issues). Geben Sie auch hier so viele Details wie möglich an, damit wir nicht immer wieder Fragen zu Ihrer Konfiguration stellen oder das Problem duplizieren müssen, damit wir das Problem direkt angehen und herausfinden können, was zu tun ist. Sie können auch gerne Ihre eigenen Lösungen anbieten und Patches einreichen. Wir ermutigen Sie sogar dazu, da wir alle Freiwillige sind und nur wenig Zeit zur Verfügung haben. Je mehr Leute mithelfen, desto leichter wird die Arbeit für alle. Es ist in Ordnung, wenn Ihre Lösung nicht perfekt ist. Jedes bisschen hilft, und vielleicht können wir es ja noch verbessern. \ No newline at end of file
diff --git a/doc/de/admins.bb b/doc/de/admins.bb
deleted file mode 100644
index d278c04ac..000000000
--- a/doc/de/admins.bb
+++ /dev/null
@@ -1,10 +0,0 @@
-[h2]Dokumentation für Hub-Administratoren[/h2]
-
-[zrl=[baseurl]/help/install]Installation[/zrl]
-[zrl=[baseurl]/help/red2pi]$Projectname auf einem Raspberry Pi installieren[/zrl]
-[zrl=[baseurl]/help/troubleshooting]Troubleshooting-Tipps[/zrl]
-[zrl=[baseurl]/help/hidden_configs]Versteckte Konfigurations-Optionen[/zrl]
-[zrl=[baseurl]/help/faq_admins]FAQ für Admins[/zrl]
-[zrl=[baseurl]/help/service_classes]Serviceklassen[/zrl]
-[zrl=[baseurl]/help/directories]Arbeit mit Verzeichnissen und ihre Konfiguration[/zrl]
-[zrl=[baseurl]/help/theme_management]Theme-Management[/zrl]
diff --git a/doc/de/basicconcepts.md b/doc/de/basicconcepts.md
new file mode 100644
index 000000000..65a845f73
--- /dev/null
+++ b/doc/de/basicconcepts.md
@@ -0,0 +1,67 @@
+## Grundlegende Konzepte von Hubzilla
+
+
+
+### Kanäle (im Sinne von Identitäten im Gegensatz zu einer App)
+
+Als Nutzer (d. h. als Privatperson oder als Verein) können Sie eine oder mehrere Web-**Identitäten** erstellen. Die Webpräsenz einer Identität wird innerhalb von Hubzilla gebündelt: Ein Besucher der Web-Identität sieht Inhalte, die mit dieser Identität in Verbindung stehen, an einem Ort, der über das Hauptmenü *Apps* für diese Identität verfügbar ist.
+
+Intern, innerhalb der Software, wird eine Identität als „Kanal“ bezeichnet. Für einen Besucher bedeutet das Wort „Kanal“ jedoch **eine** bestimmte App, die zu dieser Identität gehört, nämlich die **Pinnwand**, auf der die (möglicherweise) föderierten Beiträge der Identität in einer zeitachsenbasierten Timeline angezeigt werden.
+
+Im Folgenden verstehen wir Kanäle als Identitäten: Als angemeldeter Benutzer können Sie zwischen Ihren Kanälen wechseln, um die Inhalte für jeden Kanal zu bearbeiten, d. h. Beiträge zu veröffentlichen oder ein oder mehrere Kanalprofile, Websites, Wiki-Seiten und mehr zu erstellen. Pro Kanal können Sie auch Dateien in einer Cloud verwalten, Fotos mit Tags versehen und benennen und die Fotos in einer Webgalerie anzeigen. Ereignisse des Kanals können in einem Kalender angezeigt werden.
+
+
+
+### Dezentrales Netzwerk: Zugriff über Grenzen hinweg (Zot/Nomad) vs. Bereitstellung über Grenzen hinweg (Zot/Nomad, ActivityPub und Diaspora)
+
+Wenn dies erlaubt ist, verbinden sich Identitäten auf Hubzilla über Server- und Verwaltungsgrenzen hinweg über das Kommunikationsprotokoll Zot/Nomad (und, sofern von Ihrem Server bereitgestellt, auch über die Protokolle ActivityPub und Diaspora).
+
+Mit Ausnahme von Beiträgen/Nachrichten bleiben alle veröffentlichten Inhalte lokal auf Ihrem Server. Sie können Inhalte zwar öffentlich veröffentlichen, aber auch lokale Inhalte nur mit bestimmten Verbindungen teilen. Letzteres ist nur mit Verbindungen über Zot/Nomad (also mit Identitäten auf Hubzilla und Streams) möglich.
+
+
+
+### Zugriffskontrolle
+
+Ihre lokalen Inhalte können von den Zot/Nomad-basierten Verbindungen „besucht“ werden, denen Sie die entsprechende Berechtigung erteilt haben. Indem Sie Ihre Verbindungen Verbindungslisten (sogenannten „Datenschutzgruppen“) zuordnen, können Sie allen Mitgliedern dieser Liste Zugriff auf bestimmte Inhalte gewähren.
+
+Sie können Beiträge/Nachrichten (auch über die Protokolle ActivityPub oder Disaspora) an eine Verbindung, an eine Datenschutzgruppe oder an die Öffentlichkeit senden, indem Sie eine Zugriffskontrollliste verwenden.
+
+Es ist möglich, eine Dauer für das Ablaufen eines Beitrags/einer Nachricht festzulegen. Auf diese Weise können Sie den Zugriff zeitlich begrenzen.
+
+
+
+### Fernauthentifizierung
+
+„Kennst du mich? – Ja, ich kenne dich, willkommen!“
+Der Zugriff auf nicht öffentliche Inhalte auf einem Remote-Server sollte nur möglich sein, wenn du dich gegenüber diesem Server authentifizieren kannst. Fediverse-Software, die nur das ActivityPub- oder das Diaspora-Protokoll verwendet, kann jedoch nur Konten vom lokalen Server authentifizieren.
+Das Zot-Protokoll hingegen verfügt über einen integrierten Mechanismus namens MagicAuth, der es einem Server ermöglicht, Identitäten, die auf einem anderen (entfernten) Server registriert sind, Zugriff auf Inhalte und Aktionen zu gewähren oder zu verweigern.
+
+
+
+### Nomadische Identität
+
+Das Zot/Nomad-Protokoll ermöglicht es, Ihre Kanäle von dem Hub zu trennen, auf dem Sie sie erstellt haben. Sie können sie auf einen anderen Hub übertragen oder klonen. In diesem Fall existieren die Identität und die Daten des Kanals gleichzeitig an mehreren Orten. Dies sorgt für Ausfallsicherheit der Kanäle, falls ein Hub abgeschaltet wird oder nicht mehr verfügbar ist.
+
+
+
+### Modulares Ökosystem durch Apps
+
+ Hubzilla stellt für jede Funktion eine separate **App** zur Verfügung, d. h. die Cloud, Fotos, Galerie, Chat, Wiki, Kalender, Kontakte oder die Verbindungs-App. Ein Serveradministrator entscheidet, welche Apps für die Benutzer dieses Servers verfügbar sein sollen. Ein Benutzer kann dann die verfügbaren Apps installieren oder deinstallieren.
+
+
+
+### Zusammenarbeit
+
+Die folgenden gemeinsamen privaten Bereiche ermöglichen die Arbeit im Team mit Hubzilla:
+
+- Als Benutzer erlauben Sie ausgewählten Verbindungen, Ihre Webseiten und Wiki-Seiten zu lesen und zu bearbeiten.
+- Erlauben Sie ihnen, Ihre Dateien, Ihren Kalender und Ihre Kontakte über die Weboberfläche (die Cloud-, Kalender- und Kontakt-Apps) oder WebDAV, CalDAV und CardDAV zu lesen und zu bearbeiten.
+- Verwenden Sie Konversations-Threads (Beiträge), die nur für Ihre Kollaborationsgruppe sichtbar sind, indem Sie die Zugriffskontrollliste entsprechend einstellen.
+
+
+
+### Teilen vs. Boosten
+
+Mit Hubzilla können Sie Beiträge anderer Fediverse-Nutzer (erneut) teilen. Das (erneute) Teilen in Hubzilla ist so, als würden Sie Ihren Freunden Jennie und Omar erzählen, was Giaco gesagt hat. Und dann sagen sie: „Cool, das gefällt mir“.
+
+Hubzilla ermöglicht auch das Boosten. Etwas zu boosten ist so, als würdest du deinen Freunden Jennie und Omar erzählen, was Giaco gesagt hat, während Giaco und alle seine Freunde und seine Familie (die du nicht kennst) zuhören. Giaco und alle seine Freunde und seine Familie können jetzt mit dir sprechen. \ No newline at end of file
diff --git a/doc/de/bugs.bb b/doc/de/bugs.bb
new file mode 100644
index 000000000..57efd10a6
--- /dev/null
+++ b/doc/de/bugs.bb
@@ -0,0 +1,34 @@
+[h2]Fehler, Probleme und Dinge, die nachts für Unruhe sorgen...[/h2]
+[h3]Etwas ist schiefgelaufen! Wer ist für die Behebung zuständig?[/h3]
+
+[b]Hubzilla Community Server[/b]
+
+Hubzilla Community Server ist eine Open-Source-Software, die von „der Community“ – im Wesentlichen unbezahlten Freiwilligen – gepflegt wird. Niemand ist für die Behebung von Fehlern zuständig. Wir arbeiten gemeinsam daran, die Software und das Netzwerk reibungslos und fehlerfrei zu betreiben. Sie sind Mitglied dieser Community, daher benötigen wir auch Ihre Hilfe, um qualitativ hochwertige Software anbieten zu können. Es gibt keine mythischen „Entwickler“, die auf magische Weise alles reparieren. Es liegt an uns allen, mit anzupacken und zu helfen.
+
+Als Erstes sollten Sie sich an Ihren Hub-Administrator wenden – die Person, die Ihre Website betreibt und verwaltet. Er hat als Einziger Zugriff auf die interne Software, die Datenbank und die [b]Logdateien[/b] und muss daher in die Behebung Ihres Problems einbezogen werden. Andere Personen „im Netz“ können Ihnen dabei nicht wirklich helfen. Der Hub-Administrator muss als Erstes die Logdateien überprüfen und/oder versuchen, das Problem zu reproduzieren. Seien Sie daher so hilfsbereit und höflich wie möglich, um ihm bei der Suche nach dem Problem zu helfen.
+
+
+Um Ihren Hub-Administrator zu finden (falls Sie ihn nicht kennen), schauen Sie bitte auf [url=[baseurl]/siteinfo]dieser Seite[/url]. Wenn er auf dieser Seite keine Kontaktinformationen oder ein „Impressum“ angegeben hat, finden Sie diese unter [url=[baseurl]/siteinfo.json]dieser Zusammenfassung der Website-Informationen[/url] unter der Überschrift „admin:“.
+
+Es wird dringend empfohlen, dass Fehlerberichte von Hub-Administratoren eingereicht werden, damit diese die relevanten Logdateien und Datenbankinformationen zum Problem hinzufügen und Workarounds und Folgetests durchführen können. Ohne diese Zusammenarbeit ist es möglicherweise nicht möglich, das Problem zu beheben.
+
+[h3]Ich bin Hub-Administrator; was soll ich tun?[/h3]
+
+Die Softwareanweisungen, die diesen Webdienst bereitstellen, sind Open Source und können von Ihnen eingesehen werden. Wir empfehlen allen, diese zu lesen, um sich über die Funktionsweise zu informieren und sich davon zu überzeugen, dass wir Ihre persönlichen Daten nicht missbräuchlich oder fahrlässig verwenden. Wenn eine Fehlermeldung gemeldet wurde, kann man häufig in den Quelldateien nach dieser Fehlermeldung suchen und herausfinden, wodurch sie ausgelöst wurde. Mit diesen Informationen und den Logdateien der Website lässt sich möglicherweise die Abfolge der Ereignisse ermitteln, die zu dem Fehler geführt haben. Möglicherweise sind auch andere Websites beteiligt, und das Problem liegt gar nicht auf Ihrer Website, sondern an anderer Stelle im Netzwerk. Versuchen Sie, die am Problem beteiligten Kommunikationsendpunkte (Hubs oder Websites) zu ermitteln, und wenden Sie sich an den Administrator dieser Website(s). Geben Sie bitte den Zeitpunkt an, zu dem der Fehler aufgetreten ist, damit er in den Protokollen gefunden werden kann. Arbeiten Sie mit den anderen Administratoren zusammen, um die Ursache des Problems zu finden. Logdateien sind Ihr Freund. Wenn in der Software etwas Unerwartetes passiert, wird dies fast immer protokolliert.
+
+[h3]Der weiße Bildschirm des Todes[/h3]
+
+Wenn Sie bei einer Aktion einen leeren weißen Bildschirm erhalten, handelt es sich fast immer um einen Code- oder Syntaxfehler. In der Datei .htconfig.php der Website finden Sie Anweisungen, mit denen der Website-Administrator die Syntaxprotokollierung aktivieren kann. Wir empfehlen allen Websites, diese Funktion zu verwenden. Wiederholen Sie mit aktivierter Syntaxprotokollierung die Sequenz, die zu dem Fehler geführt hat, und die fehlerhafte Codezeile sollte protokolliert werden. Hoffentlich können Sie das Problem mit diesen Informationen beheben. Wenn ja, reichen Sie die Korrektur bitte „upstream“ ein, damit wir sie mit den anderen Projektmitgliedern und anderen Communities teilen können. Dies ist ein wesentlicher Vorteil der Verwendung von Open-Source-Software – wir teilen miteinander und alle profitieren davon.
+
+[h3]Ich bin ratlos. Ich kann nicht herausfinden, was falsch ist.[/h3]
+
+An dieser Stelle könnte es sich lohnen, das Problem in einem der Online-Foren zu diskutieren. Es gibt möglicherweise mehrere davon, und einige sind vielleicht besser für Ihre Sprache geeignet. Derzeit ist der Kanal „Hubzilla Support Forum“ (adminsforum@hubzilla.org) das empfohlene Forum für die Diskussion von Fehlern.
+
+Wenn Community-Mitglieder mit einer Ausbildung/Erfahrung im Bereich Softwareentwicklung Ihnen nicht sofort helfen können, haben Sie bitte Verständnis dafür, dass sie ehrenamtlich tätig sind und möglicherweise viele andere Aufgaben und Verpflichtungen haben. An diesem Punkt müssen Sie einen Fehlerbericht erstellen. Dazu benötigen Sie ein Konto auf framagit.org. Registrieren Sie sich also und besuchen Sie dann https://framagit.org/hubzilla/core/issues . Erstellen Sie hier ein Ticket und geben Sie alle Informationen an, die Sie online angegeben haben. Lassen Sie nichts aus.
+
+Dann warten Sie. Wenn es sich um ein wichtiges Problem handelt, wird es möglicherweise schnell behoben. Aber niemand ist für die Behebung von Fehlern zuständig. Wenn das Problem weiterhin besteht, nehmen Sie sich bitte etwas mehr Zeit, um es zu untersuchen. Fragen Sie nach allem, was Sie im Zusammenhang mit dem Verhalten nicht verstehen. Sie werden mehr über die Funktionsweise der Software erfahren und möglicherweise herausfinden, warum sie derzeit nicht funktioniert. Letztendlich wird jemand aus der Community das Problem beheben, und Sie sind ein Mitglied dieser Community. So funktioniert der Open-Source-Prozess.
+
+
+
+Andere Personen, die an der Behebung des Problems arbeiten, benötigen möglicherweise weitere Informationen. Bereiten Sie sich daher gut vor und dokumentieren Sie, was passiert ist und was Sie bereits versucht haben. Sagen Sie nicht einfach: „Ich habe xyz gemacht und es hat nicht funktioniert.“ Das sagt uns nichts. Teilen Sie uns genau mit, welche Schritte Sie unternommen haben, welches Ergebnis Sie erwartet haben und was genau passiert ist. Welche Seite/URL haben Sie aufgerufen oder welches Formular haben Sie ausgefüllt? Wenn Fehlermeldungen angezeigt wurden, sagen Sie nicht „Es wurde eine Fehlermeldung angezeigt“. Teilen Sie uns den genauen Wortlaut der Meldung mit. Teilen Sie uns außerdem mit, welchen Hub Sie verwenden, welche Softwareversion Sie ausführen und alle weiteren Details, die für die Konfiguration Ihrer Website relevant sein könnten. Wir verstehen, dass Sie möglicherweise einige Informationen und Ihre Verbindungen geheim halten möchten. Wenn Sie jedoch nicht bereit sind, die Informationen weiterzugeben, die andere Personen benötigen, um das Problem zu reproduzieren/beheben, kann es möglicherweise nicht behoben werden.
+
diff --git a/doc/de/channels.bb b/doc/de/channels.bb
deleted file mode 100644
index 0030208c2..000000000
--- a/doc/de/channels.bb
+++ /dev/null
@@ -1,26 +0,0 @@
-[size=large][b]Kanäle[/b][/size]
-
-Kanäle sind Sammlungen von Inhalten, die an einem Ort gespeichert werden. Ein Kanal kann alles mögliche repräsentieren. Zum Beispiel Dich, eine Website, ein Forum oder ein Fotoalbum. Normalerweise wird Dein erster Kanal Dich selbst repräsentieren.
-
-Die wichtigsten Funktionen für einen Kanal, der einen selbst repräsentiert, sind:
-
-[ul][*]Sichere und private, spamfreie Kommunikation
-[*]Identifikation und automatisches Einloggen im gesamten $Projectname-Netzwerk
-[*]Datenschutzeinstellungen und Zugriffsberechtigungen, die im gesamten Netzwerk gültig sind
-[*]Verzeichnisdienste (ähnlich einem Telefonbuch)[/ul]
-
-Kurz gesagt, ein Kanal der Dich repräsentiert ist sozusagen „Ich im Internet“.
-
-Du musst Deinen ersten Kanal erstellen, während Du Dich anmeldest. Du kannst auch weitere Kanäle erstellen und zwischen ihnen wechseln, indem Du auf „Kanal-Manager“ im Menü unter Deinem Profilbild klickst.
-
-Du wirst nach einem Kanalnamen und einem kurzen Spitznamen gefragt. Für einen Kanal, der Dich repräsentiert, ist es eine gute Idee, hier Deinen Realnamen anzugeben, damit Deine Freunde Dich finden und sich mit Dir verbinden können. Der Spitzname wird genutzt, um Deinen „Webbie“ zu erstellen. Das ist so etwas wie ein Username und sieht aus wie eine E-Mail-Adresse, zum Beispiel spitzname@hubzilla-hub.de. Überlege ein bisschen, was Du als Spitzname nutzen willst. Stell Dir vor, Du wirst nach Deinem Webbie gefragt und musst Deinem Bekannten dann buchstabieren, dass Dein Webbie „llamas.sind-cool_274@example.com“ ist. „llamassindcool@example.com“ wäre da viel einfacher gewesen.
-
-Nachdem Du Deinen Kanal erstellt hast, wirst Du zu den Einstellungen weitergeleitet. Hier kannst Du Deinen Kanal einrichten und die Standard-Berechtigungen setzen.
-
-Nachdem Du auch das getan hast, kannst Du Deinen Kanal verwenden. Unter der Addresse https://example.com/channel/spitzname [observer=1]( [observer.url] )[/observer] findest Du Deinen Kanal. Hier werden Deine letzten Aktivitäten gezeigt, die neuesten oben. Wenn Du etwas in die Textbox schreibst, in der „Teilen“ steht, wird der neue Eintrag ganz oben in Deinem Kanal auftauchen. Du findest hier auch Links zu den anderen Kommunikationsbereichen Deines Kanals. Der „Über“-Reiter enthält Dein Profil, der „Fotos“-Reiter Deine Fotoalben, und der Kalender enthält Termine und Veranstaltungen, die Du und Deine Kontakte geteilt haben.
-
-Die „Grid“-Seite enthält alle neuen Beiträge aus dem gesamten $Projectname-Netzwerk, wieder die neuesten oben. Was genau zu sehen ist ist abhängig von den Zugriffsrechten. Falls die Zugriffsrechte Deines Kanals so eingestellt sind, dass jeder Beiträge in Deinen Stream stellen kann, wirst du auch Beiträge von Dir völlig unbekannten Personen hier sehen. Am anderen Ende der Skala kannst Du die Berechtigungen aber auch so einstellen, dass du nur die Beiträge deiner Freunde oder gar nur Deine eigenen siehst.
-
-Wie zu Anfang erwähnt sind viele Arten von Kanälen möglich, diese unterscheiden sich hauptsächlich durch die Berechtigungen. Das Anlegen dieser Kanäle unterscheidet sich dagegen nicht. Beispiel: Um einen Kanal zum Austausch von Dokumenten zu erstellen, wirst du vermutlich die Berechtigung „Kann in meinen öffentlichen Dateiordner schreiben“ freizügiger einstellen. Für weitere Informationen sieh bitte in der Hilfe unter Zugriffsrechte nach.
-
-#include doc/macros/main_footer.bb;
diff --git a/doc/de/credits.md b/doc/de/credits.md
new file mode 100644
index 000000000..3386d2529
--- /dev/null
+++ b/doc/de/credits.md
@@ -0,0 +1,91 @@
+## Credits
+
+Vielen Dank an alle, die im Laufe der Jahre zu diesem Projekt und seinen Vorgängern beigetragen haben.
+Es ist möglich, dass wir Ihren Namen nicht erwähnt haben, aber das ist nicht beabsichtigt. Wir danken auch der Gemeinschaft und
+ihren Mitgliedern, die uns wertvolle Beiträge geliefert haben und ohne die diese ganze Arbeit sinnlos wäre.
+
+Es lohnt sich auch, die Beiträge und Problemlösungen zu würdigen, die sich aus
+Diskussionen zwischen Mitgliedern und Entwicklern anderer, in gewisser Weise verwandter und konkurrierender Projekte;
+auch wenn wir gelegentlich Meinungsverschiedenheiten hatten.
+
+- Mike Macgirvin
+- Mario Vavti
+- Scott M. Stolz
+- Chris Burger
+- Emanuel Han
+- Fabio Comuni
+- Simon L'nu
+- marijus
+- Tobias Diekershoff
+- fabrixxm
+- tommy tomson
+- Simon
+- zottel
+- Christian Vogeley
+- jeroenpraat
+- Michael Vogel
+- erik
+- Zach Prezkuta
+- Paolo T
+- Michael Meer
+- Michael
+- Abinoam P. Marques Jr
+- Tobias Hößl
+- Alexander Kampmann
+- Olaf Conradi
+- Paolo Tacconi
+- tobiasd
+- Devlon Duthie
+- Zvi ben Yaakov (a.k.a rdc)
+- Alexandre Hannud Abdo
+- Olivier Migeot
+- Chris Case
+- Klaus Weidenbach
+- Michael Johnston
+- olivierm
+- Vasudev Kamath
+- pixelroot
+- Max Weller
+- duthied
+- Martin Schmitt
+- Sebastian Egbers
+- Erkan Yilmaz
+- sasiflo
+- Stefan Parviainen
+- Haakon Meland Eriksen
+- Oliver Hartmann (23n)
+- Erik Lundin
+- habeascodice
+- sirius
+- Charles
+- Tony Baldwin
+- Hauke Zuehl
+- Keith Fernie
+- Anne Walk
+- toclimb
+- Daniel Frank
+- Matthew Exon
+- Michal Supler
+- Tobias Luther
+- U-SOUND\mike
+- mrjive
+- nostupidzone
+- tonnerkiller
+- Antoine G
+- Christian Drechsler
+- Ludovic Grossard
+- RedmatrixCanada
+- Stanislav Lechev [0xAF]
+- aweiher
+- bufalo1973
+- dsp1986
+- felixgilles
+- ike
+- maase2
+- mycocham
+- ndurchx
+- pafcu
+- Simó Albert i Beltran
+- Manuel Reva
+- Manuel Jiménez Friaza
+- Gustav Wall aka "neue medienordnung plus" \ No newline at end of file
diff --git a/doc/de/database/db_abook.bb b/doc/de/database/db_abook.bb
new file mode 100644
index 000000000..a346480d7
--- /dev/null
+++ b/doc/de/database/db_abook.bb
@@ -0,0 +1,55 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]abook_id[/td][td]Sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]abook_account[/td][td]account.account_id of the channel which owns this record[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]abook_channel[/td][td]channel.channel_id of the channel which owns this record[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]abook_xchan[/td][td]xchan.xchan_hash of the target identity (this channel's connection)[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]abook_my_perms[/td][td]bitfield of all specific permissions granted this connection[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_their_perms[/td][td]bitfield of all permissions granted to you by this connection[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_closeness[/td][td]"closeness" value for optional affinity tool, 0-99[/td][td]tinyint(3) unsigned[/td][td]NO[/td][td]MUL[/td][td]99[/td][td]
+[/td][/tr]
+[tr][td]abook_created[/td][td]Datetime this record was created[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]abook_updated[/td][td]Datetime this record was modified[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]abook_connected[/td][td]datetime of last successful "poll" for this connection[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]abook_dob[/td][td]Datetime of connection's birthday converted from *their* timezone to UTC[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]abook_flags[/td][td]No longer used[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_profile[/td][td]profile.guid of profile to display to this connection if authenticated[/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]abook_blocked[/td][td]Bi-directional communications with this channel are blocked, regardless of other permissions. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_ignored[/td][td]Incoming communications from this channel are blocked, regardless of other permissions. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_hidden[/td][td]This connection will not be shown as a connection to anybody but the channel owner[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_archived[/td][td]This connection is likely non-functioning and the entry and conversations are preserved, but further polled communications will not be attempted. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_pending[/td][td]A connection request was received from this channel but has not been approved by the channel owner, public communications may still be visible but no additional permissions have been granted. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_unconnected[/td][td]currently unused. Projected usage is to indicate "one-way" connections which were insitgated on this end but are still pending on the remote end. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_self[/td][td]is a special case where the owner is the target. Every channel has one abook entry with abook_self and with a target abook_xchan set to channel.channel_hash . When this flag is present, abook_my_perms is the default permissions granted to all new connections and several other fields are unused.[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_feed[/td][td]indicates this connection is an RSS/Atom feed and may trigger special handling.[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_incl[/td][td]connection filter allow rules separated by LF[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_excl[/td][td]connection filter deny rules separated by LF[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]abook_instance[/td][td]comma separated list of site urls of all channel clones that this connection is connected with (used only for singleton networks which don't support cloning)[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_account.bb b/doc/de/database/db_account.bb
new file mode 100644
index 000000000..35d7a9eb3
--- /dev/null
+++ b/doc/de/database/db_account.bb
@@ -0,0 +1,66 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]account_id[/td][td]table index[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]account_parent[/td][td]for hierarchical accounts, the account_id of the parent to this one, if account_parent = account_id, this is the top level account[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]account_default_channel[/td][td]channel_id of channel to connect on login[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]account_salt[/td][td]complexity token for account_password[/td][td]char(32)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_password[/td][td]hashed password for this account[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_email[/td][td]essentially the login ID, although it is usually possible to login with a channel address[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_external[/td][td]Currently unused[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_language[/td][td]default language (closest available browser-accept language when account was created)[/td][td]char(16)[/td][td]NO[/td][td][/td][td]en[/td][td]
+[/td][/tr]
+[tr][td]account_created[/td][td]timestamp of account creation[/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]account_lastlog[/td][td]timestamp of last login (or daily update if "remember me" is in effect)[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]account_flags[/td][td]see notes[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]account_roles[/td][td]see notes[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]account_reset[/td][td]verification token for password reset[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_expires[/td][td]timestamp when account expires and will be deleted[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]account_expire_notified[/td][td]timestamp of last warning of account expiration[/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]account_service_class[/td][td]service class for this account, determines what if any limits/restrictions are in place[/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_level[/td][td]future use[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]account_password_changed[/td][td]timestamp of last password change - to limit account deletion for 48 hours to prevent malicious activity[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Notes:
+
+
+
+/**
+ * Account Flags
+ */
+
+define ( 'ACCOUNT_OK', 0x0000 );
+define ( 'ACCOUNT_UNVERIFIED', 0x0001 );
+define ( 'ACCOUNT_BLOCKED', 0x0002 );
+define ( 'ACCOUNT_EXPIRED', 0x0004 );
+define ( 'ACCOUNT_REMOVED', 0x0008 );
+define ( 'ACCOUNT_PENDING', 0x0010 );
+
+/**
+ * Account roles
+ */
+
+define ( 'ACCOUNT_ROLE_SYSTEM', 0x0002 ); // 2 - this is the special system account
+define ( 'ACCOUNT_ROLE_DEVELOPER', 0x0004 );
+define ( 'ACCOUNT_ROLE_ADMIN', 0x1000 ); // 4096 - this account is an administrator
+
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_addon.bb b/doc/de/database/db_addon.bb
new file mode 100644
index 000000000..bccd295f5
--- /dev/null
+++ b/doc/de/database/db_addon.bb
@@ -0,0 +1,24 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td]generated index[td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]aname[/td][td]plugin base (file)name[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]version[/td][td]currently unused[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]installed[/td][td]currently always 1[/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hidden[/td][td]currently unused[/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]tstamp[/td][td]file timestamp to check for reloads[/td][td]bigint(20)[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]plugin_admin[/td][td]1 = has admin config, 0 = has no admin config[/td][td]tinyint(1)[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Notes:
+
+These are addons which have been enabled by the site administrator on the admin/plugin page
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_app.bb b/doc/de/database/db_app.bb
new file mode 100644
index 000000000..09df473ee
--- /dev/null
+++ b/doc/de/database/db_app.bb
@@ -0,0 +1,48 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]generated index[/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]app_id[/td][td]hash identifying this app[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_sig[/td][td]currently unused[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_author[/td][td]xchan_hash of app creator[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_name[/td][td]name of app[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_desc[/td][td]optional description of app[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]app_url[/td][td]target_url[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_photo[/td][td]app icon[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_version[/td][td]version of app[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_channel[/td][td]channel_id owning this instance of the app[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]app_addr[/td][td]reddress/webbie of app creator[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_price[/td][td]free-form price field[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_page[/td][td]currently unused[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_requires[/td][td]access rules[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+
+[tr][td]app_created[/td][td]datetime of app creation[/td][td]datetime[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_edited[/td][td]datetime of last app edit[/td][td]datetime[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+
+[tr][td]app_deleted[/td][td]1 = deleted, 0 = normal[/td][td]int(11)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]app_system[/td][td]1 = imported system app, 0 = member created app[/td][td]int(11)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+
+
+[/table]
+
+Storage for personal apps
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_attach.bb b/doc/de/database/db_attach.bb
new file mode 100644
index 000000000..5098401de
--- /dev/null
+++ b/doc/de/database/db_attach.bb
@@ -0,0 +1,54 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]generated index[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]aid[/td][td]account_id of owner[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel_id of owner[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hash[/td][td]hash for cross-site identification[/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]creator[/td][td]xchan_hash of author/creator[/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]filename[/td][td]filename of original[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]filetype[/td][td]mimetype[/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]filesize[/td][td]size in bytes[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]revision[/td][td]for version control (partially implemented)[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]folder[/td][td]attach.hash of parent folder[/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]flags[/td][td]no longer used[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]is_dir[/td][td]0 (file) or 1 to indicate a directory[/td][td]tinyint[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]is_photo[/td][td]if 1, a photo is linked to this resource[/td][td]tinyint[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]os_storage[/td][td]if 0, data contains content; if 1 data contains path to content (always 1 in hubzilla)[/td][td]tinyint[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]os_path[/td][td]under construction, store the system path[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]display_path[/td][td]under construction, store the human readable path[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]content[/td][td]file data or pathname to stored data if ATTACH_FLAG_OS[/td][td]longblob[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td]creation time[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]edited[/td][td]last edit time[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td]permissions[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td]permissions[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td]permissions[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td]permissions[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+permissions are xchan_hash or group_hash surrounded by angle chars. e.g. '<abc123><xyz789>'
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_auth_codes.bb b/doc/de/database/db_auth_codes.bb
new file mode 100644
index 000000000..c60f064a4
--- /dev/null
+++ b/doc/de/database/db_auth_codes.bb
@@ -0,0 +1,19 @@
+
+OAuth2 authorisation register - currently implemented but unused
+
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]varchar(40)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]client_id[/td][td][/td][td]varchar(20)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]redirect_uri[/td][td][/td][td]varchar(200)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]expires[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]scope[/td][td][/td][td]varchar(250)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_cache.bb b/doc/de/database/db_cache.bb
new file mode 100644
index 000000000..02c292f20
--- /dev/null
+++ b/doc/de/database/db_cache.bb
@@ -0,0 +1,15 @@
+
+OEmbed information cache
+
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]k[/td][td]horizontal width + url or resource[/td][td]char(255)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]v[/td][td]OEmbed response from site[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]updated[/td][td]datetime of cache insertion[/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_channel.bb b/doc/de/database/db_channel.bb
new file mode 100644
index 000000000..518ff0978
--- /dev/null
+++ b/doc/de/database/db_channel.bb
@@ -0,0 +1,104 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]channel_id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]channel_account_id[/td][td]account.id of the account owning this channel[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_primary[/td][td]1 = this is the primary instance of this channel[/td][td]tinyint(1) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_name[/td][td]Name that this channel is known by[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_address[/td][td]"username" or URL-and-email safe nickname[/td][td]char(255)[/td][td]NO[/td][td]UNI[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_guid[/td][td]Long hash representing a psuedo-unique ID, does not have ot be globally unique[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_guid_sig[/td][td]channel.gui signed with channel.prvkey and base64url_encoded[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_hash[/td][td]base64url_encode of a 64-char whirlpool hash of channel.guid and channel_guid_sig concatenated, synonymous with xchan_hash.[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_timezone[/td][td]PHP-legal timezone[/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td]UTC[/td][td]
+[/td][/tr]
+[tr][td]channel_location[/td][td]Default for item.location[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_theme[/td][td]channel theme preference[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_startpage[/td]relative site URL to visit after logging in[td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_pubkey[/td][td]RSA public key 4096 bit[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_prvkey[/td][td]RSA private key 4096 bit[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_notifyflags[/td][td]bifield representing what notification types are active[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]65535[/td][td]
+[/td][/tr]
+[tr][td]channel_pageflags[/td][td]bitfield of special channel uses[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_dirdate[/td][td]time when directory was last pinged. Must do this once a month[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]channel_lastpost[/td][td]date of last post for this channel. May not be fully implemented[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]channel_deleted[/td][td]time when channel was deleted[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]channel_max_anon_mail[/td][td]unused[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]10[/td][td]
+[/td][/tr]
+[tr][td]channel_max_friend_req[/td][td]unused[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]10[/td][td]
+[/td][/tr]
+[tr][td]channel_expire_days[/td][td]expire imported content that hasn't been otherwise protected after this many days, 0 is no expiration[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_passwd_reset[/td][td]password reset token[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_default_group[/td][td]put all new connections into the group with this name[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]channel_allow_cid[/td][td]Default permissions for this channel[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_allow_gid[/td][td]Default permissions for this channel[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_deny_cid[/td][td]Default permissions for this channel[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_deny_gid[/td][td]Default permissions for this channel[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channel_r_stream[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_r_profile[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_r_photos[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_r_abook[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_stream[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_wall[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_tagwall[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_comment[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_mail[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_photos[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_chat[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_a_delegate[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_r_storage[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_storage[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_r_pages[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_pages[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_a_republish[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_w_like[/td][td]specific permission bitfield[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_removed[/td][td]if 1, this channel has been deleted[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_system[/td][td]if 1, this is the special system channel on this site[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]128[/td][td]
+[/td][/tr]
+[tr][td]channel_moved[/td][td]URL of relocated channel, making this instance abandoned if set[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl]
diff --git a/doc/de/database/db_chat.bb b/doc/de/database/db_chat.bb
new file mode 100644
index 000000000..1aac2bd15
--- /dev/null
+++ b/doc/de/database/db_chat.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]chat_id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]chat_room[/td][td]chatroom.cr_id for this chat[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]chat_xchan[/td][td]author xchan_hash[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]chat_text[/td][td]the text of the chat message[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td]timestamp of this message[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_chatpresence.bb b/doc/de/database/db_chatpresence.bb
new file mode 100644
index 000000000..0a7f666c9
--- /dev/null
+++ b/doc/de/database/db_chatpresence.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]cp_id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]cp_room[/td][td]chatroom.cr_id of the chatroom[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]cp_xchan[/td][td]xchan_hash of the chatroom participant[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]cp_last[/td][td]datetime last ping[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]cp_status[/td][td]text status description e.g. "online"[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cp_client[/td][td]IP address of this client[/td][td]char(128)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_chatroom.bb b/doc/de/database/db_chatroom.bb
new file mode 100644
index 000000000..1d316288d
--- /dev/null
+++ b/doc/de/database/db_chatroom.bb
@@ -0,0 +1,28 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]cr_id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]cr_aid[/td][td]account.id of chatroom owner[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]cr_uid[/td][td]channel.channel_id of chatroom owner[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]cr_name[/td][td]visible name of chatroom[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]cr_created[/td][td]creation timestampe[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]cr_edited[/td][td]edited timestamp[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]cr_expire[/td][td]expiration period for chats in this chatroom in minutes, 0 is no expiration[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td]permissions for this room[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td]permissions for this room[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td]permissions for this room[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td]permissions for this room[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_clients.bb b/doc/de/database/db_clients.bb
new file mode 100644
index 000000000..0c66a4fc2
--- /dev/null
+++ b/doc/de/database/db_clients.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]client_id[/td][td][/td][td]varchar(20)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]pw[/td][td][/td][td]varchar(20)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]redirect_uri[/td][td][/td][td]varchar(200)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]clname[/td][td][/td][td]text[/td][td]YES[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]icon[/td][td][/td][td]text[/td][td]YES[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_config.bb b/doc/de/database/db_config.bb
new file mode 100644
index 000000000..f32d3c259
--- /dev/null
+++ b/doc/de/database/db_config.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]cat[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]k[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]v[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_conv.bb b/doc/de/database/db_conv.bb
new file mode 100644
index 000000000..5adfa8c80
--- /dev/null
+++ b/doc/de/database/db_conv.bb
@@ -0,0 +1,25 @@
+
+Used in Diaspora private mails
+
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]guid[/td][td]A unique identifier for this conversation[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]recips[/td][td]sender_handle;recipient_handle[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel.channel_id of the owner of this data[/td][td]int(11)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]creator[/td][td]handle of creator[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td]creation timestamp[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]updated[/td][td]edited timestamp[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]subject[/td][td]subject of initial message (obscured for privacy)[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_event.bb b/doc/de/database/db_event.bb
new file mode 100644
index 000000000..ad3c15789
--- /dev/null
+++ b/doc/de/database/db_event.bb
@@ -0,0 +1,64 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]aid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]event_hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]edited[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]dtstart[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]dtend[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]summary[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]description[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]location[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]etype[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]nofinish[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]adjust[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]1[/td][td]
+[/td][/tr]
+[tr][td]dismissed[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+
+[tr][td]event_status[/td][td][/td][td]charr(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_status_date[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_percent[/td][td][/td][td]smallint(6)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_repeat[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_sequence[/td][td][/td][td]smallint[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_priority[/td][td][/td][td]smallint[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]event_vdata[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cal_id[/td][td][/td][td]int(10)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_fcontact.bb b/doc/de/database/db_fcontact.bb
new file mode 100644
index 000000000..9bd8c20fe
--- /dev/null
+++ b/doc/de/database/db_fcontact.bb
@@ -0,0 +1,38 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]url[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]name[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]photo[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]request[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]nick[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]addr[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]batch[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]notify[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]poll[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]confirm[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]priority[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]network[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]alias[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]pubkey[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]updated[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_ffinder.bb b/doc/de/database/db_ffinder.bb
new file mode 100644
index 000000000..c20158d56
--- /dev/null
+++ b/doc/de/database/db_ffinder.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]fid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_fserver.bb b/doc/de/database/db_fserver.bb
new file mode 100644
index 000000000..4c4b0b530
--- /dev/null
+++ b/doc/de/database/db_fserver.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]server[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]posturl[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]key[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_fsuggest.bb b/doc/de/database/db_fsuggest.bb
new file mode 100644
index 000000000..9da1f2f6d
--- /dev/null
+++ b/doc/de/database/db_fsuggest.bb
@@ -0,0 +1,24 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cid[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]name[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]url[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]request[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]photo[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]note[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_hook.bb b/doc/de/database/db_hook.bb
new file mode 100644
index 000000000..233062f98
--- /dev/null
+++ b/doc/de/database/db_hook.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]hook[/td][td]name of hook[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]file[/td][td]relative filename of hook handler[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]fn[/td][td]function name of hook handler[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]priority[/td][td]can be used to sort conflicts in hook handling by calling handlers in priority order[/td][td]int(11) unsigned[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hook_version[/td][td]version 0 hooks must have two arguments, the App and the hook data. version 1 hooks have 1 argument - the hook data[/td][td]int(11) unsigned[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_hubloc.bb b/doc/de/database/db_hubloc.bb
new file mode 100644
index 000000000..e4ab7159d
--- /dev/null
+++ b/doc/de/database/db_hubloc.bb
@@ -0,0 +1,38 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]hubloc_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]hubloc_guid[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_guid_sig[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]hubloc_hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]hubloc_addr[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_network[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_flags[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hubloc_status[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hubloc_url[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_url_sig[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]hubloc_host[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_callback[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_connect[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]hubloc_sitekey[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]hubloc_updated[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]hubloc_connected[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_issue.bb b/doc/de/database/db_issue.bb
new file mode 100644
index 000000000..0a6f2912b
--- /dev/null
+++ b/doc/de/database/db_issue.bb
@@ -0,0 +1,20 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]issue_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]issue_created[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]issue_updated[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]issue_assigned[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]issue_priority[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]issue_status[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]issue_component[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_item.bb b/doc/de/database/db_item.bb
new file mode 100644
index 000000000..6383e13f8
--- /dev/null
+++ b/doc/de/database/db_item.bb
@@ -0,0 +1,151 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]Sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]mid[/td][td]Message-id - globally unique, there can be several items with the same message-ID in the table as they may have different uid owners[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]aid[/td][td]channel_account_id of the channel_id (uid) which owns this copy of the item[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel_id (uid) which owns this copy of the item[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]parent[/td][td]item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]parent_mid[/td][td]Globally unique message-id of the parent to this item[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]thr_parent[/td][td]If the parent of this item is not the top-level item in the conversation, the message-id of the immediate parent; otherwise set to parent_mid[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]created[/td][td]Creation timestamp. If creation is more than ten minutes into the future, set item_delayed to 1; it will automatically be delivered by the poller once the created time has passed[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]edited[/td][td]Date of last edit (default is created)[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]expires[/td][td]Date this item expires and will be removed[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]commented[/td][td]Date of last comment/reply to this item[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]received[/td][Date the item was received at this sitetd][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]changed[/td][td]Date that something in the conversation changed, indicating clients should fetch the conversation again[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]comments_closed[/td][td]Date after which no more comments will be accepted[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]owner_xchan[/td][td]xchan_hash of the owner of this conversation (this is who replies are sent to)[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]author_xchan[/td][td]xchan_hash of the author of this item[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]source_xchan[/td][td]xchan_hash of the external source of this item belongs to multiple delivery chains and comments need to be uplinked[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]mimetype[/td][td]mime type of the content body[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]title[/td][td]item title[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]body[/td][td]item body content[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]app[/td][td]application which generated this item[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]lang[/td][td]auto-detected language[/td][td]char(64)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]revision[/td][td]future use, version control[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]verb[/td][td]ActivityStreams verb (old style URI)[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]obj_type[/td][td]ActivityStreams object type (old style URI)[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]obj[/td][td]JSON encoded object structure unless it is an implied object (normal post)[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]tgt_type[/td][td]ActivityStreams target type if applicable (URI)[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]target[/td][td]JSON encoded target structure if used[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]layout_mid[/td][td]For webpages, which layout (mid or message_id) to use when displaying this page[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]postopts[/td][td]External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]route[/td][td]comma separated xchan list of xchans where this message was routed on its way to this destination, used for route loop discovery and rejection of comments which arrived by alternate routes and may have different permissions[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]llink[/td][td]URL of a displayable copy of this post/conversation on this site[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]plink[/td][td]permalink or URL toa displayable copy of the message at its source[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]resource_id[/td][td]Used to link other tables to items, it identifies the linked resource and if set must also set resource_type[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]resource_type[/td][td]default none, if a linked resource this should be the name of the resource type such as "photo" or "event"[/td][td]char(16)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]attach[/td][td]JSON structure representing attachments to this item[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]sig[/td][td]RSA signature of the item body by the original author if the private key is available[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]diaspora_meta[/td][td]Used to store Diaspora comment signatures with their weird requirements[/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]location[/td][td]text location where this item originated[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]coord[/td][td] longitude/latitude pair representing location where this item originated[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]public_policy[/td][td]If the author has specified restrictions (this network, this site) etc. for distribution, the corresponding policy text is present here and item_private = 1[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]comment_policy[/td][td]If the author has specified comment restrictions (thei network, this site, etc.) the corresponding policy text is present here[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td]Access Control - list of allowed xchans '<xchan1><xchan2>...'[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td]Access Control - list of allowed group hashes, see allow_cid[td][/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td]Access Control - list of denied xchans[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td]Access Control - list of denied groups[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]item_restrict[/td]no longer used[td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_flags[/td][td]no longer used[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_private[/td][td]distribution is restricted[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_origin[/td][td]item originated at this site[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_unseen[/td][td]item has not been seen[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_starred[/td][td]item has been favourited[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_uplink[/td][td]This item is part of a multiple delivery chain and must be uplinked to the original sender (source_xchan)[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_consensus[/td][td]This item allows voting tools[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_wall[/td][td]This item was posted to the wall of uid[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_thread_top[/td][td]parent = id, this is the top post in a conversation [/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_notshown[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_nsfw[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_relay[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_mentionsme[/td][td]The owner of this item was mentioned in it[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_nocomment[/td][td]if 1, no comments are allowed[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_obscured[/td][td]no longer used[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_verified[/td][td]the signature has been verified on this site[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_retained[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_rss[/td][td]item originated in a feed[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_deleted[/td][td]item has been deleted[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_type[/td][td]used to identify webpage and design element types, 0 is a normal conversation item[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_hidden[/td][td]0 or 1 if item is not to be displayed[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_unpublished[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_delayed[/td][td]item is posted in the future[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_pending_remove[/td][td]item is in the process of being removed[/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]item_blocked[/td][td][/td][td]tinyint(4)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_item_id.bb b/doc/de/database/db_item_id.bb
new file mode 100644
index 000000000..ba4cca247
--- /dev/null
+++ b/doc/de/database/db_item_id.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]iid[/td][td]item.id of the referenced item[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel.channel_id of the owner of this data[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]sid[/td][td]an additional identifier to attach or link to the referenced item (often used to store a message_id from another system in order to suppress duplicates)[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]service[/td][td]the name or description of the service which generated this identifier[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_likes.bb b/doc/de/database/db_likes.bb
new file mode 100644
index 000000000..118c9a87e
--- /dev/null
+++ b/doc/de/database/db_likes.bb
@@ -0,0 +1,24 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]channel_id[/td][td][/td][td]int(11) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]liker[/td][td][/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]likee[/td][td][/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]iid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]verb[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]target_type[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]target_id[/td][td][/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]target[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_mail.bb b/doc/de/database/db_mail.bb
new file mode 100644
index 000000000..0628584ae
--- /dev/null
+++ b/doc/de/database/db_mail.bb
@@ -0,0 +1,34 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]convid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]mail_flags[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]from_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]to_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]account_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]channel_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]title[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]body[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]attach[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]mid[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]parent_mid[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]expires[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_menu.bb b/doc/de/database/db_menu.bb
new file mode 100644
index 000000000..5b478115d
--- /dev/null
+++ b/doc/de/database/db_menu.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]menu_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]menu_channel_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]menu_name[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]menu_desc[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]menu_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_menu_item.bb b/doc/de/database/db_menu_item.bb
new file mode 100644
index 000000000..b14aac5e4
--- /dev/null
+++ b/doc/de/database/db_menu_item.bb
@@ -0,0 +1,28 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]mitem_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]mitem_link[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]mitem_desc[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]mitem_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]mitem_channel_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]mitem_menu_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]mitem_order[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_notify.bb b/doc/de/database/db_notify.bb
new file mode 100644
index 000000000..4787266cd
--- /dev/null
+++ b/doc/de/database/db_notify.bb
@@ -0,0 +1,36 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]hash[/td][td][/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xname[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]url[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]photo[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]date[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]msg[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]aid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]link[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]parent[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]seen[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]ntype[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]verb[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]otype[/td][td][/td][td]char(16)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_obj.bb b/doc/de/database/db_obj.bb
new file mode 100644
index 000000000..cc5e75598
--- /dev/null
+++ b/doc/de/database/db_obj.bb
@@ -0,0 +1,26 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]obj_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]obj_page[/td][td][/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]obj_verb[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]obj_type[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]obj_obj[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]obj_channel[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_outq.bb b/doc/de/database/db_outq.bb
new file mode 100644
index 000000000..970f99de5
--- /dev/null
+++ b/doc/de/database/db_outq.bb
@@ -0,0 +1,28 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]outq_hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]outq_account[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]outq_channel[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]outq_driver[/td][td][/td][td]char(32)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]outq_posturl[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]outq_async[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]outq_delivered[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]outq_created[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]outq_updated[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]outq_notify[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]outq_msg[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_pconfig.bb b/doc/de/database/db_pconfig.bb
new file mode 100644
index 000000000..2ac36e61a
--- /dev/null
+++ b/doc/de/database/db_pconfig.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]cat[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]k[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]v[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_pgrp.bb b/doc/de/database/db_pgrp.bb
new file mode 100644
index 000000000..73265b90e
--- /dev/null
+++ b/doc/de/database/db_pgrp.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]hash[/td][td]unique hash representing this group with the group name appended[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel.channel_id owning this data[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]visible[/td][td]1 indicates the member list is not private[/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]deleted[/td][td]1 indicates the group has been deleted[/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]gname[/td][td]human readable name of group[/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_pgrp_member.bb b/doc/de/database/db_pgrp_member.bb
new file mode 100644
index 000000000..b9ab8171d
--- /dev/null
+++ b/doc/de/database/db_pgrp_member.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td]channel.channel_id of the owner of this data[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]gid[/td][td]groups.id of the associated group[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xchan[/td][td]xchan.xchan_hash of the member assigned to the associated group[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_photo.bb b/doc/de/database/db_photo.bb
new file mode 100644
index 000000000..91840ec1e
--- /dev/null
+++ b/doc/de/database/db_photo.bb
@@ -0,0 +1,52 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]aid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]resource_id[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]edited[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]title[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]description[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]album[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]filename[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]mimetype[/td][td][/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td]image/jpeg[/td][td]
+[/td][/tr]
+[tr][td]height[/td][td][/td][td]smallint(6)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]width[/td][td][/td][td]smallint(6)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]filesize[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]content[/td][td][/td][td]mediumblob[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]scale[/td][td][/td][td]tinyint(3)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]profile[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]photo_flags[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]allow_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]allow_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_cid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]deny_gid[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_poll.bb b/doc/de/database/db_poll.bb
new file mode 100644
index 000000000..57d808b71
--- /dev/null
+++ b/doc/de/database/db_poll.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]poll_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]poll_channel[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]poll_desc[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]poll_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]poll_votes[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_poll_elm.bb b/doc/de/database/db_poll_elm.bb
new file mode 100644
index 000000000..fd649d5a6
--- /dev/null
+++ b/doc/de/database/db_poll_elm.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]pelm_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]pelm_poll[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]pelm_desc[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]pelm_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td][/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]pelm_result[/td][td][/td][td]float[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_profdef.bb b/doc/de/database/db_profdef.bb
new file mode 100644
index 000000000..a0904fd79
--- /dev/null
+++ b/doc/de/database/db_profdef.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]field_name[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]field_type[/td][td][/td][td]char(16)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]field_desc[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]field_help[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]field_inputs[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_profext.bb b/doc/de/database/db_profext.bb
new file mode 100644
index 000000000..ada9dce2a
--- /dev/null
+++ b/doc/de/database/db_profext.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]channel_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]k[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]v[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_profile.bb b/doc/de/database/db_profile.bb
new file mode 100644
index 000000000..717fae585
--- /dev/null
+++ b/doc/de/database/db_profile.bb
@@ -0,0 +1,94 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]profile_guid[/td][td][/td][td]char(64)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]aid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]profile_name[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]is_default[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]hide_friends[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]fullname[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]pdesc[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]chandesc[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]dob[/td][td][/td][td]char(32)[/td][td]NO[/td][td][/td][td]0000-00-00[/td][td]
+[/td][/tr]
+[tr][td]dob_tz[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]UTC[/td][td]
+[/td][/tr]
+[tr][td]address[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]locality[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]region[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]postal_code[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]country_name[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]hometown[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]gender[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]marital[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]partner[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]howlong[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]sexual[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]politic[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]religion[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]keywords[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]likes[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]dislikes[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]about[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]summary[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]music[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]book[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]tv[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]film[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]interest[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]romance[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]employment[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]education[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]contact[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]channels[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]homepage[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]photo[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]thumb[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]publish[/td][td][/td][td]tinyint(1)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_profile_check.bb b/doc/de/database/db_profile_check.bb
new file mode 100644
index 000000000..3be64c5da
--- /dev/null
+++ b/doc/de/database/db_profile_check.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]dfrn_id[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]sec[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]expire[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_register.bb b/doc/de/database/db_register.bb
new file mode 100644
index 000000000..50672b5e1
--- /dev/null
+++ b/doc/de/database/db_register.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]password[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]language[/td][td][/td][td]char(16)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_session.bb b/doc/de/database/db_session.bb
new file mode 100644
index 000000000..d7ff0482d
--- /dev/null
+++ b/doc/de/database/db_session.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]bigint(20) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]sid[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]data[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]expire[/td][td][/td][td]bigint(20) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_shares.bb b/doc/de/database/db_shares.bb
new file mode 100644
index 000000000..be5255c03
--- /dev/null
+++ b/doc/de/database/db_shares.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]share_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]share_type[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]share_target[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]share_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_sign.bb b/doc/de/database/db_sign.bb
new file mode 100644
index 000000000..e80ea7ef3
--- /dev/null
+++ b/doc/de/database/db_sign.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]iid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]retract_iid[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]signed_text[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]signature[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]signer[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_site.bb b/doc/de/database/db_site.bb
new file mode 100644
index 000000000..8dea4dae6
--- /dev/null
+++ b/doc/de/database/db_site.bb
@@ -0,0 +1,28 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]site_url[/td][td][/td][td]char(255)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]site_access[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]site_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]site_update[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]site_pull[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]site_sync[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]site_directory[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]site_register[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]site_sellpage[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]site_location[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]site_realm[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_source.bb b/doc/de/database/db_source.bb
new file mode 100644
index 000000000..92850a82e
--- /dev/null
+++ b/doc/de/database/db_source.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]src_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]src_channel_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]src_channel_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]src_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]src_patt[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_spam.bb b/doc/de/database/db_spam.bb
new file mode 100644
index 000000000..b75e1edd3
--- /dev/null
+++ b/doc/de/database/db_spam.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(11)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]spam[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]ham[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]term[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]date[/td][td][/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_sys_perms.bb b/doc/de/database/db_sys_perms.bb
new file mode 100644
index 000000000..04416a26b
--- /dev/null
+++ b/doc/de/database/db_sys_perms.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]cat[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]k[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]v[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]public_perm[/td][td][/td][td]tinyint(1) unsigned[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_term.bb b/doc/de/database/db_term.bb
new file mode 100644
index 000000000..bd155fe21
--- /dev/null
+++ b/doc/de/database/db_term.bb
@@ -0,0 +1,28 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]tid[/td][td]sequential index[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]aid[/td][td]channel_account_id of the controlling channel[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td]channel_id of the controlling channel[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]oid[/td][td]DB index of linked thing[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]otype[/td][td]type of linked thing[/td][td]tinyint(3) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]ttype[/td][td]taxonomy type (See Tag/term types in boot.php)[/td][td]tinyint(3) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]term[/td][td]the actual taxonomy term[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]url[/td]relevant link (for tags and mentions, a link to the associated resource)[td][/td][td]char(255)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]imgurl[/td][td]rarely used - an image associated with this taxonomy term[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]term_hash[/td][td]unique hash for this entry[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]parent_hash[/td][td]for hierarchical taxonomies, the hash of the relevant parent[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_tokens.bb b/doc/de/database/db_tokens.bb
new file mode 100644
index 000000000..35da2458c
--- /dev/null
+++ b/doc/de/database/db_tokens.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]varchar(40)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]secret[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]client_id[/td][td][/td][td]varchar(20)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]expires[/td][td][/td][td]bigint(20) unsigned[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]scope[/td][td][/td][td]varchar(200)[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]uid[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_updates.bb b/doc/de/database/db_updates.bb
new file mode 100644
index 000000000..f2e25d84c
--- /dev/null
+++ b/doc/de/database/db_updates.bb
@@ -0,0 +1,20 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]ud_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]ud_hash[/td][td][/td][td]char(128)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]ud_guid[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]ud_date[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]ud_last[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]ud_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]ud_addr[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_verify.bb b/doc/de/database/db_verify.bb
new file mode 100644
index 000000000..9d01181c5
--- /dev/null
+++ b/doc/de/database/db_verify.bb
@@ -0,0 +1,18 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]channel[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]type[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]token[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]meta[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]created[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_vote.bb b/doc/de/database/db_vote.bb
new file mode 100644
index 000000000..0b9a423eb
--- /dev/null
+++ b/doc/de/database/db_vote.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]vote_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]vote_poll[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]vote_element[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]vote_result[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]vote_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xchan.bb b/doc/de/database/db_xchan.bb
new file mode 100644
index 000000000..8932969c5
--- /dev/null
+++ b/doc/de/database/db_xchan.bb
@@ -0,0 +1,59 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]xchan_hash[/td][td]calculated hash of this extended channel[/td][td]char(255)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xchan_guid[/td][td]channel_guid of this extended channel[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_guid_sig[/td][td]base64url encoded signature of the guid[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xchan_pubkey[/td][td]public key for verifying signed data and assertions[/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xchan_photo_mimetype[/td][td]mimetype of the profile photo[/td][td]char(32)[/td][td]NO[/td][td][/td][td]image/jpeg[/td][td]
+[/td][/tr]
+[tr][td]xchan_photo_l[/td][td]photo url 300px[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_photo_m[/td][td]photo url 80 px[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_photo_s[/td][td]photo url 48 px[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_addr[/td][td]user@host[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_url[/td][td]url of channel page on primary hub location[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_connurl[/td]poco url[td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_follow[/td]url template for following %s[td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_connpage[/td][td]for premium channels url of channel to display when connecting[/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_name[/td][td]human readabl name of channel[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_network[/td][td]network of channel for instance 'zot', 'diaspora', 'unknown'[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_instance_url[/td][td]no longer used[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchan_flags[/td][td]no longer used[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_photo_date[/td][td]timestamp of last photo change in GMT[/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]xchan_name_date[/td][td]timestamp of last name change in GMT[/td][td]datetime[/td][td]NO[/td][td][/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[tr][td]xchan_hidden[/td][td]flag - channel is hidden[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_orphan[/td][td]flag - channel has no known hubloc locations[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_censored[/td][td]flag - channel has been censored[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_selfcensored[/td][td]flag - channel is self censored (adult or nsfw)[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_system[/td][td]flag - this represents a system channel[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_pubforum[/td][td]flag - channel is a public forum[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan_deleted[/td][td]flag - channel was deleted[/td][td]int(10) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xchat.bb b/doc/de/database/db_xchat.bb
new file mode 100644
index 000000000..0897408d1
--- /dev/null
+++ b/doc/de/database/db_xchat.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]xchat_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]xchat_url[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchat_desc[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchat_xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xchat_edited[/td][td][/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xconfig.bb b/doc/de/database/db_xconfig.bb
new file mode 100644
index 000000000..111d1ce3a
--- /dev/null
+++ b/doc/de/database/db_xconfig.bb
@@ -0,0 +1,16 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]xchan[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]cat[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]k[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]v[/td][td][/td][td]mediumtext[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xign.bb b/doc/de/database/db_xign.bb
new file mode 100644
index 000000000..63c6569de
--- /dev/null
+++ b/doc/de/database/db_xign.bb
@@ -0,0 +1,13 @@
+xign - holds xchan information for channels that have been ignored in 'friend suggestions'
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]uid[/td][td]local channel.channel_id[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xchan[/td][td]xchan.xchan_hash of ignored channel[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xlink.bb b/doc/de/database/db_xlink.bb
new file mode 100644
index 000000000..528f8da19
--- /dev/null
+++ b/doc/de/database/db_xlink.bb
@@ -0,0 +1,22 @@
+xlink - used to store social graph and channel ratings
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]xlink_id[/td][td]sequential ID[/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]xlink_xchan[/td][td]xchan.xchan_hash of controlling channel[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xlink_link[/td][td]xchan.xchan_hash of link target (connection or rating)[/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xlink_rating[/td][td]int rating[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xlink_rating_txt[/td][td]rating text[/td][td]mediumtext[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xlink_updated[/td][td]timestamp of update in GMT[/td][td]datetime[/td][td]NO[/td][td]MUL[/td][td]0000-00-00 00:00:00[/td][td]
+[tr][td]xlink_static[/td][td]0 for social graph, 1 for ratings[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xlink_sig[/td][td]base64url encoded signature of rating information[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xprof.bb b/doc/de/database/db_xprof.bb
new file mode 100644
index 000000000..bed79e9ca
--- /dev/null
+++ b/doc/de/database/db_xprof.bb
@@ -0,0 +1,37 @@
+xprof - stores searchable public profile information on directory servers
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]xprof_hash[/td][td]xchan.xchan_hash of this channel[/td][td]char(255)[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xprof_age[/td][td]current age (updated monthly)[/td][td]tinyint(3) unsigned[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[tr][td]xprof_desc[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_dob[/td][td][/td][td]char(12)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_gender[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_marital[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_sexual[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_locale[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_region[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_postcode[/td][td][/td][td]char(32)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_country[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_keywords[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xprof_about[/td][td][/td][td]text[/td][td]NO[/td][td][/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xprof_homepage[/td][td][/td][td]char(255)[/td][td]NO[/td][td][/td][td][/td][td]
+[/td][/tr]
+[tr][td]xprof_hometown[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/database/db_xtag.bb b/doc/de/database/db_xtag.bb
new file mode 100644
index 000000000..1e6fb9961
--- /dev/null
+++ b/doc/de/database/db_xtag.bb
@@ -0,0 +1,14 @@
+[table]
+[tr][th]Field[/th][th]Description[/th][th]Type[/th][th]Null[/th][th]Key[/th][th]Default[/th][th]Extra
+[/th][/tr]
+[tr][td]xtag_id[/td][td][/td][td]int(10) unsigned[/td][td]NO[/td][td]PRI[/td][td]NULL[/td][td]auto_increment
+[/td][/tr]
+[tr][td]xtag_hash[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td]NULL[/td][td]
+[/td][/tr]
+[tr][td]xtag_term[/td][td][/td][td]char(255)[/td][td]NO[/td][td]MUL[/td][td][/td][td]
+[/td][/tr]
+[tr][td]xtag_flags[/td][td][/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td]
+[/td][/tr]
+[/table]
+
+Return to [zrl=[baseurl]/help/database]database documentation[/zrl] \ No newline at end of file
diff --git a/doc/de/develop.bb b/doc/de/develop.bb
deleted file mode 100644
index 30e2954c6..000000000
--- a/doc/de/develop.bb
+++ /dev/null
@@ -1,30 +0,0 @@
-[h2]Dokumentation für Entwickler[/h2]
-
-[h3]Technische Dokumentation[/h3]
-[zrl=[baseurl]/help/Zot---A-High-Level-Overview]Zot – ein grober Überblick[/zrl]
-[zrl=[baseurl]/help/zot]Eine Einführung ins Zot-Protokoll[/zrl]
-[zrl=[baseurl]/help/zot_structures]Zot-Strukturen[/zrl]
-[zrl=[baseurl]/help/comanche]Seitenbeschreibung in Comanche[/zrl]
-[zrl=[baseurl]/help/Creating-Templates]Vorlagen erstellen mit Comanche[/zrl]
-[zrl=[baseurl]/help/Widgets]Widgets[/zrl]
-[zrl=[baseurl]/help/plugins]Plugins[/zrl]
-[zrl=[baseurl]/help/doco]Selbst Dokumentation beisteuern[/zrl]
-[zrl=[baseurl]/help/DerivedTheme1]Einen Theme basierend auf einem anderen erstellen[/zrl]
-[zrl=[baseurl]/help/schema_development]Schemata[/zrl]
-[zrl=[baseurl]/help/Translations]Übersetzungen[/zrl]
-[zrl=[baseurl]/help/developers]Entwickler[/zrl]
-[zrl=[baseurl]/help/intro_for_developers]Einführung für Entwickler[/zrl]
-[zrl=[baseurl]/help/database]Datenbank-Schema[/zrl]
-[zrl=[baseurl]/help/api_functions]API-Funktionen[/zrl]
-[zrl=[baseurl]/help/api_posting]Mit der API einen Beitrag erstellen[/zrl]
-[zrl=[baseurl]/help/developer_function_primer]Übersicht der wichtigsten $Projectname-Funktionen[/zrl]
-[zrl=[baseurl]/doc/html/]Code-Referenz (mit doxygen generiert - setzt Cookies)[/zrl]
-[zrl=[baseurl]/help/to_do_doco]To-Do-Liste für das Projekt $Projectname-Dokumentation[/zrl]
-[zrl=[baseurl]/help/to_do_code]To-Do-Liste für Entwickler[/zrl]
-[zrl=[baseurl]/help/roadmap]Roadmap[/zrl]
-[zrl=[baseurl]/help/git_for_non_developers]Git für Nicht-Entwickler[/zrl]
-[zrl=[baseurl]/help/dev_beginner]Schritt-für-Schritt-Einführung für neue Entwickler[/zrl]
-
-[h3]Externe Ressourcen[/h3]
-[url=https://zothub.com/channel/one]Entwickler-Kanal[/url]
-[url=https://federated.social/channel/postgres]Postgres-spezifischer Admin-Support-Kanal[/url]
diff --git a/doc/de/developer/API.md b/doc/de/developer/API.md
new file mode 100644
index 000000000..6966024df
--- /dev/null
+++ b/doc/de/developer/API.md
@@ -0,0 +1,787 @@
+### Zot API
+
+Many existing social applications and tools can interface directly using the Twitter/StatusNet API, which is available using the 'twitter_api' addon.
+
+This document describes the native API; which allows direct programmatic access to several internal data structures and libraries extending beyond the basic social interface.
+
+The API endpoints detailed below are relative to `api/z/1.0`, meaning that if an API is listed as `channel/stream` the full API URL is `https://hub.hubzilla.hu/api/z/1.0/channel/stream`.
+
+
+
+### channel/export/basic
+
+Export basic channel data
+
+Options:
+ \- sections
+ comma-separated list of data types to export
+
+ \- posts
+ if true, return default sections plus 3 months of posts
+
+ If no sections are requested, the following sections are returned:
+ channel, connections, config, apps, chatrooms, events, webpages, mail, wikis
+
+ Files and large collections of posts may run into memory limits; these must generally be
+ requested separately.
+
+
+
+### channel/stream
+
+Fetch channel conversation items
+
+
+
+### network/stream
+
+Fetch network conversation items
+
+
+
+
+
+### files
+
+List file storage (attach DB)
+
+GET /api/z/1.0/files
+
+
+Options:
+
+ \- filehash
+ return only entries matching hash (exactly)
+
+ \- filename
+ return only entries matching filename (substring)
+
+ \- filetype
+ return only entries matching filetype/mimetype (substring)
+
+ \- start
+ start at record (default 0)
+
+ \- records
+ number of records to return or 0 for unlimited
+
+
+
+Example:
+
+```
+curl -u mychannel:mypassword https://xyz.macgirvin.com/api/z/1.0/files -d filetype=multipart/mixed
+```
+
+
+Returns:
+
+```
+ {
+
+ "success": true,
+ "results": [
+ {
+ "id": "1",
+ "aid": "1",
+ "uid": "2",
+ "hash": "44ee8b2a1a7f36dea07b93b7747a2383a1bc0fdd08339e8928bfcbe45f65d939",
+ "filename": "Profile Photos",
+ "filetype": "multipart/mixed",
+ "filesize": "0",
+ "revision": "0",
+ "folder": "",
+ "os_storage": "1",
+ "is_dir": "1",
+ "is_photo": "0",
+ "flags": "0",
+ "created": "2016-01-02 21:51:17",
+ "edited": "2016-01-02 21:51:17",
+ "allow_cid": "",
+ "allow_gid": "",
+ "deny_cid": "",
+ "deny_gid": ""
+ },
+ {
+ "id": "12",
+ "aid": "1",
+ "uid": "2",
+ "hash": "71883f1fc64af33889229cbc79c5a056deeec5fc277d765f182f19073e1b2998",
+ "filename": "Cover Photos",
+ "filetype": "multipart/mixed",
+ "filesize": "0",
+ "revision": "0",
+ "folder": "",
+ "os_storage": "1",
+ "is_dir": "1",
+ "is_photo": "0",
+ "flags": "0",
+ "created": "2016-01-15 00:24:33",
+ "edited": "2016-01-15 00:24:33",
+ "allow_cid": "",
+ "allow_gid": "",
+ "deny_cid": "",
+ "deny_gid": ""
+ },
+ {
+ "id": "16",
+ "aid": "1",
+ "uid": "2",
+ "hash": "f48f7ec3278499d1dd86b72c3207beaaf4717b07df5cc9b373f14d7aad2e1bcd",
+ "filename": "2016-01",
+ "filetype": "multipart/mixed",
+ "filesize": "0",
+ "revision": "0",
+ "folder": "",
+ "os_storage": "1",
+ "is_dir": "1",
+ "is_photo": "0",
+ "flags": "0",
+ "created": "2016-01-22 03:24:55",
+ "edited": "2016-01-22 03:26:57",
+ "allow_cid": "",
+ "allow_gid": "",
+ "deny_cid": "",
+ "deny_gid": ""
+ }
+ ]
+ }
+```
+
+### filemeta
+
+Export file metadata for any uploaded file
+
+
+
+### filedata
+
+Provides the ability to download a file from cloud storage in chunks
+
+GET /api/z/1.0/filedata
+
+
+Required:
+
+ \- file_id
+ attach.hash of desired file ('begins with' match)
+
+
+Optional:
+
+ \- start
+ starting byte of returned data in file (counting from 0)
+
+ \- length
+ length (prior to base64 encoding) of chunk to download
+
+
+Returns:
+
+ attach (DB) structure with base64 encoded 'content' comprised of the desired chunk
+
+
+
+Example:
+
+ `https://xyz.macgirvin.com/api/z/1.0/filedata?f=&file_id=9f5217770fd&start=0&length=48`
+
+Returns:
+
+```
+ {
+
+ "attach": {
+ "id": "107",
+ "aid": "1",
+ "uid": "2",
+ "hash": "9f5217770fd55d563bd77f84d534d8e119a187514bbd391714626cd9c0e60207",
+ "creator": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "filename": "pcxtopbm.c",
+ "filetype": "application/octet-stream",
+ "filesize": "3934",
+ "revision": "0",
+ "folder": "",
+ "flags": "0",
+ "is_dir": "0",
+ "is_photo": "0",
+ "os_storage": "1",
+ "os_path": "",
+ "display_path": "",
+ "content": "LyogcGN4dG9wYm0uYyAtIGNvbnZlcnQgUEMgcGFpbnRicnVzaCAoLnBjeCkgZmls",
+ "created": "2016-07-24 23:13:01",
+ "edited": "2016-07-24 23:13:01",
+ "allow_cid": "",
+ "allow_gid": "",
+ "deny_cid": "",
+ "deny_gid": "",
+ "start": 0,
+ "length": 48
+ }
+
+ }
+```
+
+### file/export
+
+### file
+
+### albums
+
+Description: list photo albums
+
+GET /api/z/1.0/albums
+
+
+Output:
+
+ text - textual name
+
+ total - number of photos in this album
+
+ url - web URL
+
+ urlencode - textual name, urlencoded
+
+ bin2hex - textual name using bin2hex (which is used in the web URL link)
+
+
+Example:
+
+
+
+```
+ {
+
+ "success": true,
+ "albums": [
+ {
+ "text": "/",
+ "total": "2",
+ "url": "https://xyz.macgirvin.com/photos/hubzilla/album/",
+ "urlencode": "",
+ "bin2hex": ""
+ },
+ {
+ "text": "2016-01",
+ "total": "6",
+ "url": "https://xyz.macgirvin.com/photos/hubzilla/album/323031362d3031",
+ "urlencode": "2016-01",
+ "bin2hex": "323031362d3031"
+ },
+ {
+ "text": "2016-02",
+ "total": "7",
+ "url": "https://xyz.macgirvin.com/photos/hubzilla/album/323031362d3032",
+ "urlencode": "2016-02",
+ "bin2hex": "323031362d3032"
+ },
+ {
+ "text": "Cover Photos",
+ "total": "5",
+ "url": "https://xyz.macgirvin.com/photos/hubzilla/album/436f7665722050686f746f73",
+ "urlencode": "Cover+Photos",
+ "bin2hex": "436f7665722050686f746f73"
+ },
+ {
+ "text": "Profile Photos",
+ "total": "26",
+ "url": "https://xyz.macgirvin.com/photos/hubzilla/album/50726f66696c652050686f746f73",
+ "urlencode": "Profile+Photos",
+ "bin2hex": "50726f66696c652050686f746f73"
+ }
+ ]
+
+ }
+```
+
+### photos
+
+list photo metadata
+
+
+
+### photo
+
+### group
+
+```
+GET /api/z/1.0/group
+```
+
+Description: list privacy groups
+
+Returns: DB tables of all privacy groups.
+
+To use with API group_members, provide either 'group_id' from the id element returned in this call, or 'group_name' from the gname returned in this call.
+
+
+
+```
+ [
+
+ {
+ "id": "1",
+ "hash": "966c946394f3e2627bbb8a55026b5725e582407098415c02f85232de3f3fde76Friends",
+ "uid": "2",
+ "visible": "0",
+ "deleted": "0",
+ "gname": "Friends"
+ },
+ {
+ "id": "2",
+ "hash": "852ebc17f8c3ed4866f2162e384ded0f9b9d1048f93822c0c84196745f6eec66Family",
+ "uid": "2",
+ "visible": "1",
+ "deleted": "0",
+ "gname": "Family"
+ },
+ {
+ "id": "3",
+ "hash": "cc3cb5a7f9818effd7c7c80a58b09a189b62efa698a74319117babe33ee30ab9Co-workers",
+ "uid": "2",
+ "visible": "0",
+ "deleted": "0",
+ "gname": "Co-workers"
+ }
+ ]
+```
+
+### group_members
+
+```
+GET /api/z/1.0/group_members
+```
+
+Required:
+
+group_id or group_name
+
+
+Returns:
+
+group_member+abook+xchan (DB join) for each member of the privacy group
+
+
+
+```
+ [
+
+ {
+ "id": "1",
+ "uid": "2",
+ "gid": "1",
+ "xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "abook_id": "2",
+ "abook_account": "1",
+ "abook_channel": "2",
+ "abook_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "abook_my_perms": "218555",
+ "abook_their_perms": "0",
+ "abook_closeness": "0",
+ "abook_created": "2016-01-02 21:16:26",
+ "abook_updated": "2016-01-02 21:16:26",
+ "abook_connected": "0000-00-00 00:00:00",
+ "abook_dob": "0000-00-00 00:00:00",
+ "abook_flags": "0",
+ "abook_blocked": "0",
+ "abook_ignored": "0",
+ "abook_hidden": "0",
+ "abook_archived": "0",
+ "abook_pending": "0",
+ "abook_unconnected": "0",
+ "abook_self": "1",
+ "abook_feed": "0",
+ "abook_profile": "",
+ "abook_incl": "",
+ "abook_excl": "",
+ "abook_instance": "",
+ "xchan_hash": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "xchan_guid": "lql-1VnxtiO4-WF0h72wLX1Fu8szzHDOXgQaTbELwXW77k8AKFfh-hYr70vqMrc3SSvWN-Flrc5HFhRTWB7ICw",
+ "xchan_guid_sig": "PafvEL0VpKfxATxlCqDjfOeSIMdmpr3iU7X-Sysa1h5LzDpjSXsjO37tYZL-accb1M5itLlfnW5epkTa5I4flsW21zSY1A2jCuBQUTLLGV7rNyyBy7lgqJUFvAMRx0TfXzP9lcaPqlM9T1tA6jfWOsOmkdzwofGeXBnsjGfjsO2xdGYe6vwjOU0DSavukvzDMnOayB9DekpvDnaNBTxeGLM45Skzr7ZEMcNF7TeXMbnvpfLaALYEKeQs9bGH-UgAG8fBWgzVAzeBfx_XSR1rdixjyiZGP0kq0h35SlmMPcEjliodOBFwMXqpXFB7Ibp4F6o6te2p2ErViJccQVG8VNKB6SbKNXY6bhP5zVcVsJ-vR-p4xXoYJJvzTN7yTDsGAXHOLF4ZrXbo5yi5gFAlIrTLAF2EdWQwxSGyLRWKxG8PrDkzEzX6cJJ0VRcLh5z6OI5QqQNdeghPZbshMFMJSc_ApCPi9_hI4ZfctCIOi3T6bdgTNKryLm5fhy_eqjwLAZTGP-aUBgLZpb1mf2UojBn6Ey9cCyq-0T2RWyk-FcIcbV4qJ-p_8oODqw13Qs5FYkjLr1bGBq82SuolkYrXEwQClxnrfKa4KYc2_eHAXPL01iS9zVnI1ySOCNJshB97Odpooc4wk7Nb2Fo-Q6THU9zuu0uK_-JbK7IIl6go2qA",
+ "xchan_pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA18JB76lyP4zzL/y7BCej\neJnfZIWZNtM3MZvI1zEVMWmmwOS+u/yH8oPwyaDk4Y/tnj8GzMPj1lCGVRcd8EJa\nNrCMd50HODA5EsJtxpsOzRcILYjOcTtIAG1K4LtKqELi9ICAaFp0fNfa+Jf0eCek\nvPusx2/ORhy+o23hFoSMhL86o2gmaiRnmnA3Vz4ZMG92ieJEDMXt9IA1EkIqS4y5\nBPZfVPLD1pv8iivj+dtN1XjwplgjUbtxmU0/Ej808nHppscRIqx/XJ0XZU90oNGw\n/wYoK2EzJlPbRsAkwNqoFrAYlr5HPpn4BJ2ebFYQgWBUraD7HwS5atsQEaxGfO21\nlUP0+lDg9t3CXvudDj0UG1jiEKbVIGA+4aG0GN2DSC5AyRq/GRxqyay5W2vQbAZH\nyvxPGrZFO24I65g3pjhpjEsLqZ4ilTLQoLMs0drCIcRm5RxMUo4s/LMg16lT4cEk\n1qRtk2X0Sb1AMQQ2uRXiVtWz77QHMONEYkf6OW4SHbwcv5umvlv69NYEGfCcbgq0\nAV7U4/BWztUz/SWj4r194CG43I9I8dmaEx9CFA/XMePIAXQUuABfe1QMOR6IxLpq\nTHG1peZgHQKeGz4aSGrhQkZNNoOVNaZoIfcvopxcHDTZLigseEIaPPha4WFYoKPi\nUPbZ5o8gTLc750uzrnb2jwcCAwEAAQ==\n-----END PUBLIC KEY-----\n",
+ "xchan_photo_mimetype": "image/png",
+ "xchan_photo_l": "https://xyz.macgirvin.com/photo/profile/l/2",
+ "xchan_photo_m": "https://xyz.macgirvin.com/photo/profile/m/2",
+ "xchan_photo_s": "https://xyz.macgirvin.com/photo/profile/s/2",
+ "xchan_addr": "teller@xyz.macgirvin.com",
+ "xchan_url": "https://xyz.macgirvin.com/channel/teller",
+ "xchan_connurl": "https://xyz.macgirvin.com/poco/teller",
+ "xchan_follow": "https://xyz.macgirvin.com/follow?f=&url=%s",
+ "xchan_connpage": "",
+ "xchan_name": "Teller",
+ "xchan_network": "zot",
+ "xchan_instance_url": "",
+ "xchan_flags": "0",
+ "xchan_photo_date": "2016-10-19 01:26:50",
+ "xchan_name_date": "2016-01-02 21:16:26",
+ "xchan_hidden": "0",
+ "xchan_orphan": "0",
+ "xchan_censored": "0",
+ "xchan_selfcensored": "0",
+ "xchan_system": "0",
+ "xchan_pubforum": "0",
+ "xchan_deleted": "0"
+ },
+ {
+ "id": "12",
+ "uid": "2",
+ "gid": "1",
+ "xchan": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
+ "abook_id": "24",
+ "abook_account": "1",
+ "abook_channel": "2",
+ "abook_xchan": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
+ "abook_my_perms": "218555",
+ "abook_their_perms": "218555",
+ "abook_closeness": "80",
+ "abook_created": "2016-01-27 00:48:43",
+ "abook_updated": "2016-12-04 17:16:58",
+ "abook_connected": "2016-12-04 17:16:58",
+ "abook_dob": "0001-01-01 00:00:00",
+ "abook_flags": "0",
+ "abook_blocked": "0",
+ "abook_ignored": "0",
+ "abook_hidden": "0",
+ "abook_archived": "0",
+ "abook_pending": "0",
+ "abook_unconnected": "0",
+ "abook_self": "0",
+ "abook_feed": "0",
+ "abook_profile": "debb5236efb1626cfbad33ccb49892801e5f844aa04bf81f580cfa7d13204819",
+ "abook_incl": "",
+ "abook_excl": "",
+ "abook_instance": "",
+ "xchan_hash": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
+ "xchan_guid": "d5EMLlt1tHHZ0dANoA7B5Wq9UgXoWcFS9-gXOkL_AAejcPApoQRyxfHTuu8DoTbUaO-bYmX5HPuWuK9PHyqNmA",
+ "xchan_guid_sig": "CVWEMRPtzI1YcHfnnWHTuv3H964OAmSElgUfxMoX6RdQdxNpqb_POirpVuyP8s3W17mVCfO5V9IAjkg5iKcqCk6YcvOD_egmMy-AnM9TC1kKndQHw55CunD82Q8K_xBNSXkSROizcNkKh9DVLjJPFjW1AqtI4njkZ3EMgrWqnbFRM1qPToUoCY9zM3tEMHoAD9YX1zP90wl40LzfN-dtcNWpSBbiz9owou62uzLbN7mrCwKOMlXLjwwGswRnxIsEnb3O-FXOs8hs0mArKe9snq1-BKeD16LyzxgwlpVLElzIJZGEZGtMdIJgeRzKuBvPjsOIpQ1yAkuOpFJ3nGCM-IPOIIjAmyVl5zD3xPVcxxpZlJRn5fG1Y-gnqTgsrEQCA7M6XPWQdrdHU4akZfyUyFJDhv3uM-jon9VzrYTBw68R0WA-1Z8WafEHA4qh5OWAj85lUarwhr7iTiEckH51ypPCPs6VbT6Pw7yMaxfjFOcipashQagx0tfOlDhE5dQANOXKASFtH1J9-CZY2MQdLPQ6u54d5whuHKMGaJ0V68pnmZ2rOn7g344Ah2WCJrm17jj60QsRMorqRFj7GMdPIA1XB8Wrk88MuYOe3Dhyuu6ZWKI7YTWJS690ZVkKUqAiNHqj0W86DtaiPUc_mmGR0fHl4Gksnko3WmCFv9q2X2E",
+ "xchan_pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoj2xCJktBA8Ww7Hp+ZNL\nrNuQpo8UB/bfvRkIy+yua3xpF1TuXcnAH61kyRz8vXgOu/l2CyxQbIoaGslCV5Sy\n8JKeNXe+IilUdSSEjMIwCPfSPsYnMHsSnHWmPmclvJwEtQUKOZmW5mMuVBvXy7D2\njomFwc69AYphdyys6eQ7Dcn6+FRBiQbyMprZ5lxyVW+O4DuXVNa3ej2ebx0gCJZ4\ntTIlBoKwEey91dY+FyKVFjdwfNczpmL7LgmZXqcVx+MG3mYgibwdVMiXVj5X06cs\nV9hJ5Xi+Aklsv/UWJtjw9FVt7y9TLptnhh4Ra6T/MDmnBBIAkOR7P/X8cRv078MT\nl0IMsP0RJcDEtTLtwHFVtDs6p52KDFqclKWbqmxmxqV3OTPVYtArRGIzgnJi/5ur\nHRr5G6Cif7QY3UowsIOf78Qvy28LwSbdymgBAWwPPKIviXWxGO+9kMWdmPSUQrWy\nK0+7YA9P9fBUFfn9Hc+p8SJQmQ6OAqLwrDGiPSOlGaNrbEqwqLGgIpXwK+lEFcFJ\n3SPOjJRWdR2whlMxvpwX+39+H7dWN3vSa3Al4/Sq7qW8yW2rYwf+eGyp4Z0lRR+8\nJxFMCwZkSw5g14YdlikAPojv5V1c6KuA5ieg8G1hwyONV7A4JHPyEdPt0W0TZi6C\nCOVkPaC3xGrguETZpJfVpwUCAwEAAQ==\n-----END PUBLIC KEY-----\n",
+ "xchan_photo_mimetype": "image/png",
+ "xchan_photo_l": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-4",
+ "xchan_photo_m": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-5",
+ "xchan_photo_s": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-6",
+ "xchan_addr": "cloner@xyz.macgirvin.com",
+ "xchan_url": "http://abc.macgirvin.com/channel/cloner",
+ "xchan_connurl": "http://abc.macgirvin.com/poco/cloner",
+ "xchan_follow": "https://xyz.macgirvin.com/follow?f=&url=%s",
+ "xchan_connpage": "",
+ "xchan_name": "Karen",
+ "xchan_network": "zot",
+ "xchan_instance_url": "",
+ "xchan_flags": "0",
+ "xchan_photo_date": "2016-03-31 19:59:20",
+ "xchan_name_date": "2016-01-26 23:23:42",
+ "xchan_hidden": "0",
+ "xchan_orphan": "0",
+ "xchan_censored": "0",
+ "xchan_selfcensored": "0",
+ "xchan_system": "0",
+ "xchan_pubforum": "0",
+ "xchan_deleted": "0"
+ }
+
+ ]
+```
+
+### xchan
+
+An xchan is a global location independent channel and is the primary record for a network
+identity. It may refer to channels on other websites, networks, or services.
+
+```
+GET /api/z/1.0/xchan
+```
+
+Required: one of [ address, hash, guid ] as GET parameters
+
+Returns a portable xchan structure
+
+Example: `https://xyz.macgirvin.com/api/z/1.0/xchan?f=&address=mike@macgirvin.com`
+
+Returns:
+
+```
+ {
+ "hash": "jr54M_y2l5NgHX5wBvP0KqWcAHuW23p1ld-6Vn63_pGTZklrI36LF8vUHMSKJMD8xzzkz7s2xxCx4-BOLNPaVA",
+ "guid": "sebQ-IC4rmFn9d9iu17m4BXO-kHuNutWo2ySjeV2SIW1LzksUkss12xVo3m3fykYxN5HMcc7gUZVYv26asx-Pg",
+ "guid_sig": "Llenlbl4zHo6-g4sa63MlQmTP5dRCrsPmXHHFmoCHG63BLq5CUZJRLS1vRrrr_MNxr7zob_Ykt_m5xPKe5H0_i4pDj-UdP8dPZqH2fqhhx00kuYL4YUMJ8gRr5eO17vsZQ3XxTcyKewtgeW0j7ytwMp6-hFVUx_Cq08MrXas429ZrjzaEwgTfxGnbgeQYQ0R5EXpHpEmoERnZx77VaEahftmdjAUx9R4YKAp13pGYadJOX5xnLfqofHQD8DyRHWeMJ4G1OfWPSOlXfRayrV_jhnFlZjMU7vOdQwHoCMoR5TFsRsHuzd-qepbvo3pzvQZRWnTNu6oPucgbf94p13QbalYRpBXKOxdTXJrGdESNhGvhtaZnpT9c1QVqC46jdfP0LOX2xrVdbvvG2JMWFv7XJUVjLSk_yjzY6or2VD4V6ztYcjpCi9d_WoNHruoxro_br1YO3KatySxJs-LQ7SOkQI60FpysfbphNyvYMkotwUFI59G08IGKTMu3-GPnV1wp7NOQD1yzJbGGEGSEEysmEP0SO9vnN45kp3MiqbffBGc1r4_YM4e7DPmqOGM94qksOcLOJk1HNESw2dQYWxWQTBXPfOJT6jW9_crGLMEOsZ3Jcss0XS9KzBUA2p_9osvvhUKuKXbNztqH0oZIWlg37FEVsDs_hUwUJpv2Ar09k4",
+ "pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7QCwvuEIwCHjhjbpz3Oc\ntyei/Pz9nDksNbsc44Cm8jxYGMXsTPFXDZYCcCB5rcAhPPdZSlzaPkv4vPVcMIrw\n5cdX0tvbwa3rNTng6uFE7qkt15D3YCTkwF0Y9FVZiZ2Ko+G23QeBt9wqb9dlDN1d\nuPmu9BLYXIT/JXoBwf0vjIPFM9WBi5W/EHGaiuqw7lt0qI7zDGw77yO5yehKE4cu\n7dt3SakrXphL70LGiZh2XGoLg9Gmpz98t+gvPAUEotAJxIUqnoiTA8jlxoiQjeRK\nHlJkwMOGmRNPS33awPos0kcSxAywuBbh2X3aSqUMjcbE4cGJ++/13zoa6RUZRObC\nZnaLYJxqYBh13/N8SfH7d005hecDxWnoYXeYuuMeT3a2hV0J84ztkJX5OoxIwk7S\nWmvBq4+m66usn6LNL+p5IAcs93KbvOxxrjtQrzohBXc6+elfLVSQ1Rr9g5xbgpub\npSc+hvzbB6p0tleDRzwAy9X16NI4DYiTj4nkmVjigNo9v2VPnAle5zSam86eiYLO\nt2u9YRqysMLPKevNdj3CIvst+BaGGQONlQalRdIcq8Lin+BhuX+1TBgqyav4XD9K\nd+JHMb1aBk/rFLI9/f2S3BJ1XqpbjXz7AbYlaCwKiJ836+HS8PmLKxwVOnpLMbfH\nPYM8k83Lip4bEKIyAuf02qkCAwEAAQ==\n-----END PUBLIC KEY-----\n",
+ "photo_mimetype": "image/jpeg",
+ "photo_l": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-4",
+ "photo_m": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-5",
+ "photo_s": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-6",
+ "address": "mike@macgirvin.com",
+ "url": "https://macgirvin.com/channel/mike",
+ "connurl": "https://macgirvin.com/poco/mike",
+ "follow": "https://macgirvin.com/follow?f=&url=%s",
+ "connpage": "https://macgirvin.com/connect/mike",
+ "name": "Mike Macgirvin",
+ "network": "zot",
+ "instance_url": "",
+ "flags": "0",
+ "photo_date": "2012-12-06 05:06:11",
+ "name_date": "2012-12-06 04:59:13",
+ "hidden": "1",
+ "orphan": "0",
+ "censored": "0",
+ "selfcensored": "0",
+ "system": "0",
+ "pubforum": "0",
+ "deleted": "0"
+ }
+```
+
+### item/update
+
+Create or update an item (post, activity, webpage, etc.)
+
+Usage: `POST /api/z/1.0/item/update`
+
+Description: item/update posts an item (typically a conversation item or post, but can be any item) using form input.
+
+
+Required:
+
+\- body
+
+ text/bbcode contents by default.
+
+
+Optional:
+
+\- $_FILES['media']
+
+ uploaded media file to include with post
+
+\- title
+
+ title of post/item
+
+\- contact_allow
+
+ array of xchan.xchan_hash allowed to view this item
+
+\- group_allow
+
+ array of group.hash allowed to view this item
+
+\- contact_deny
+
+ array of xchan.xchan_hash not allowed to view this item
+
+\- group_deny
+
+ array of group.hash not allowed to view this item
+
+\- coord
+
+ geographic coordinates
+
+\- location
+
+ freefrom location
+
+\- expire
+
+ datetime this post will expire or be removed
+
+\- mimetype
+
+ mimetype if not text/bbcode
+
+\- parent
+
+ item.id of parent to this post (makes it a comment)
+
+\- parent_mid
+
+ alternate form of parent using message_id
+
+\- remote_xchan
+
+ xchan.xchan_hash of this message author if not the channel owner
+
+\- consensus
+
+ boolean set to true if this is a consensus or voting item (default false)
+
+\- nocomment
+
+ boolean set to true if comments are to be disabled (default false)
+
+\- origin
+
+ do not use this without reading the code
+
+\- namespace
+
+ persistent identity for a remote network or service
+
+\- remote_id
+
+ message_id of this resource on a remote network or service
+
+\- message_id
+
+ message_id of this item (leave unset to generate one)
+
+\- created
+
+ datetime of message creation
+
+\- post_id
+
+ existing item.id if this is an edit operation
+
+\- app
+
+ application or network name to display with item
+
+\- category
+
+ comma separated categories for this item
+
+\- webpage
+
+ item.page_type if not 0
+
+\- pagetitle
+
+ for webpage and design elements, the 'page name'
+
+\- layout_mid
+
+ item.mid of layout for this design element
+
+\- plink
+
+ permalink for this item if different than the default
+
+\- verb
+
+ activitystream verb for this item/activity
+
+\- obj_type
+
+ activitystream object type for this item/activity
+
+
+
+Example:
+
+
+
+```
+curl -u mychannel:mypassword https://xyz.macgirvin.com/api/z/1.0/item/update -d body="hello world"
+```
+
+
+Returns:
+
+
+
+```
+ {
+
+ "success": true,
+ "item_id": "2245",
+ "item": {
+ "id": "2245",
+ "mid": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
+ "aid": "1",
+ "uid": "2",
+ "parent": "2245",
+ "parent_mid": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
+ "thr_parent": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
+ "created": "2016-12-03 20:00:12",
+ "edited": "2016-12-03 20:00:12",
+ "expires": "0001-01-01 00:00:00",
+ "commented": "2016-12-03 20:00:12",
+ "received": "2016-12-03 20:00:12",
+ "changed": "2016-12-03 20:00:12",
+ "comments_closed": "0001-01-01 00:00:00",
+ "owner_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "author_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
+ "source_xchan": "",
+ "mimetype": "text/bbcode",
+ "title": "",
+ "body": "hello world",
+ "html": "",
+ "app": "",
+ "lang": "",
+ "revision": "0",
+ "verb": "http://activitystrea.ms/schema/1.0/post",
+ "obj_type": "http://activitystrea.ms/schema/1.0/note",
+ "obj": "",
+ "tgt_type": "",
+ "target": "",
+ "layout_mid": "",
+ "postopts": "",
+ "route": "",
+ "llink": "https://xyz.macgirvin.com/display/14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
+ "plink": "https://xyz.macgirvin.com/channel/mychannel/?f=&mid=14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
+ "resource_id": "",
+ "resource_type": "",
+ "attach": "",
+ "sig": "sa4TOQNfHtV13HDZ1tuQGWNBpZp-nWhT2GMrZEmelXxa_IvEepD2SEsCTWOBqM8OKPJLfNy8_i-ORXjrOIIgAa_aT8cw5vka7Q0C8L9eEb_LegwQ_BtH0CXO5uT30e_8uowkwzh6kmlVg1ntD8QqrGgD5jTET_fMQOIw4gQUBh40GDG9RB4QnPp_MKsgemGrADnRk2vHO7-bR32yQ0JI-8G-eyeqGaaJmIwkHoi0vXsfjZtU7ijSLuKEBWboNjKEDU89-vQ1c5Kh1r0pmjiDk-a5JzZTYShpuhVA-vQgEcADA7wkf4lJZCYNwu3FRwHTvhSMdF0nmyv3aPFglQDky38-SAXZyQSvd7qlABHGCVVDmYrYaiq7Dh4rRENbAUf-UJFHPCVB7NRg34R8HIqmOKq1Su99bIWaoI2zuAQEVma9wLqMoFsluFhxX58KeVtlCZlro7tZ6z619-dthS_fwt0cL_2dZ3QwjG1P36Q4Y4KrCTpntn9ot5osh-HjVQ01h1I9yNCj6XPgYJ8Im3KT_G4hmMDFM7H9RUrYLl2o9XYyiS2nRrf4aJHa0UweBlAY4zcQG34bw2AMGCY53mwsSArf4Hs3rKu5GrGphuwYX0lHa7XEKMglwBWPWHI49q7-oNWr7aWwn1FnfaMfl4cQppCMtKESMNRKm_nb9Dsh5e0",
+ "diaspora_meta": "",
+ "location": "",
+ "coord": "",
+ "public_policy": "",
+ "comment_policy": "contacts",
+ "allow_cid": "",
+ "allow_gid": "",
+ "deny_cid": "",
+ "deny_gid": "",
+ "item_restrict": "0",
+ "item_flags": "0",
+ "item_private": "0",
+ "item_origin": "1",
+ "item_unseen": "0",
+ "item_starred": "0",
+ "item_uplink": "0",
+ "item_consensus": "0",
+ "item_wall": "1",
+ "item_thread_top": "1",
+ "item_notshown": "0",
+ "item_nsfw": "0",
+ "item_relay": "0",
+ "item_mentionsme": "0",
+ "item_nocomment": "0",
+ "item_obscured": "0",
+ "item_verified": "1",
+ "item_retained": "0",
+ "item_rss": "0",
+ "item_deleted": "0",
+ "item_type": "0",
+ "item_hidden": "0",
+ "item_unpublished": "0",
+ "item_delayed": "0",
+ "item_pending_remove": "0",
+ "item_blocked": "0"
+ }
+
+ }
+```
+
+### item/full
+
+Get all data associated with an item
+
+
+
+### abook
+
+Connections
+
+
+
+### abconfig
+
+Connection metadata (such as permissions)
+
+
+
+### perm_allowed
+
+Check a permission for a given xchan \ No newline at end of file
diff --git a/doc/de/developer/Plugins.md b/doc/de/developer/Plugins.md
new file mode 100644
index 000000000..e6d76b2d5
--- /dev/null
+++ b/doc/de/developer/Plugins.md
@@ -0,0 +1,251 @@
+### Erstellen von Plugins/Addons für Hubzilla
+
+Sie möchten also, dass Hubzilla etwas tut, was es noch nicht tut. Da gibt es viele Möglichkeiten. Aber lass uns lernen, wie man ein Plugin oder Addon schreibt.
+In deinem Hubzilla-Ordner/Verzeichnis wirst du wahrscheinlich ein Unterverzeichnis namens 'addon' finden. Wenn Sie noch keins haben, erstellen Sie es einfach.
+
+```
+mkdir addon
+```
+
+Überlegen Sie sich dann einen Namen für Ihr Addon. Wahrscheinlich haben Sie zumindest eine vage Vorstellung davon, was es tun soll. Für unser Beispiel werde ich ein Plugin mit dem Namen „randplace“ erstellen, das einen zufälligen Speicherort für jeden Ihrer Beiträge angibt. Der Name Ihres Plugins wird verwendet, um die Funktionen zu finden, auf die wir zugreifen müssen, und ist Teil des Funktionsnamens, also verwenden Sie zur Sicherheit nur einfache Textzeichen.
+Sobald Sie einen Namen gewählt haben, erstellen Sie ein Verzeichnis unterhalb von 'addon', um Ihre Arbeitsdatei(en) zu speichern.
+
+```
+mkdir addon/randplace
+```
+
+Erstellen Sie nun Ihre Plugin-Datei. Sie muss den gleichen Namen haben und ist ein PHP-Skript. Erstellen Sie also mit Ihrem Lieblingseditor die Datei
+
+```
+addon/randplace/randplace.php
+```
+
+Die allererste Zeile dieser Datei muss lauten
+
+```
+<?php
+```
+
+Dann werden wir einen Kommentarblock erstellen, der das Plugin beschreibt. Hierfür gibt es ein spezielles Format. Wir verwenden /* ... */ Kommentar-Stil und einige markierte Zeilen, die aus
+
+
+
+ /**
+ *
+ * Name: Random Place (here you can use better descriptions than you could in the filename)
+ * Description: Sample Hubzilla plugin, Sets a random place when posting.
+ * Version: 1.0
+ * Author: Mike Macgirvin <mike@zothub.com>
+ *
+ */
+
+Diese Tags sind für den Website-Administrator sichtbar, wenn er Plugins über die Verwaltungskonsole installiert oder verwaltet. Es kann mehr als einen Autor geben. Fügen Sie einfach eine weitere Zeile hinzu, die mit „Autor:“ beginnt.
+Ein typisches Plugin hat mindestens die folgenden Funktionen:
+
+- pluginname_load()
+- pluginname_unload()
+
+In unserem Fall werden wir sie randplace_load() und randplace_unload() nennen, da dies der Name unseres Plugins ist. Diese Funktionen werden immer dann aufgerufen, wenn wir das Plugin entweder initialisieren oder von der aktuellen Webseite entfernen wollen. Auch wenn Ihr Plugin Dinge wie die Änderung des Datenbankschemas erfordert, bevor es zum ersten Mal ausgeführt werden kann, würden Sie diese Anweisungen wahrscheinlich in den Funktionen namens
+
+- pluginname_install()
+- pluginname_uninstall()
+
+Als nächstes werden wir über **Hooks** sprechen. Hooks sind Stellen im Hubzilla-Code, an denen wir Plugins erlauben, etwas zu tun. Es gibt eine [Menge davon](help/hooks), und jedes hat einen Namen. Normalerweise verwenden wir die Funktion pluginname_load(), um eine „Handler-Funktion“ für alle Hooks zu registrieren, an denen Sie interessiert sind. Wenn dann einer dieser Hooks ausgelöst wird, wird Ihr Code aufgerufen.
+Wir registrieren Hook-Handler mit der Funktion 'register_hook()'. Sie benötigt 3 Argumente. Das erste ist der Hook, den wir abfangen wollen, das zweite ist der Dateiname der Datei, in der sich unsere Handler-Funktion befindet (relativ zur Basis Ihrer Hubzilla-Installation), und das dritte ist der Funktionsname Ihrer Handler-Funktion. Lassen Sie uns also jetzt unsere randplace_load()-Funktion erstellen.
+
+
+ function randplace_load() {
+ register_hook('post_local', 'addon/randplace/randplace.php', 'randplace_post_hook');
+
+ register_hook('feature_settings', 'addon/randplace/randplace.php', 'randplace_settings');
+ register_hook('feature_settings_post', 'addon/randplace/randplace.php', 'randplace_settings_post');
+
+ }
+
+Wir werden also drei Ereignisse abfangen: „post_local“, das ausgelöst wird, wenn ein Beitrag im lokalen System erstellt wird, „feature_settings“, um einige Einstellungen für unser Plugin festzulegen, und „feature_settings_post“, um diese Einstellungen zu speichern.
+
+Als nächstes erstellen wir eine unload-Funktion. Das ist ganz einfach, denn sie hebt nur die Registrierung unserer Hooks auf. Sie nimmt genau die gleichen Argumente entgegen.
+
+ function randplace_unload() {
+ unregister_hook('post_local', 'addon/randplace/randplace.php', 'randplace_post_hook');
+
+ unregister_hook('feature_settings', 'addon/randplace/randplace.php', 'randplace_settings');
+ unregister_hook('feature_settings_post', 'addon/randplace/randplace.php', 'randplace_settings_post');
+
+ }
+
+Hooks werden mit zwei Argumenten aufgerufen. Das erste ist immer $a, das unsere globale App-Struktur ist und eine riesige Menge an Informationen über den Zustand der Web-Anfrage enthält, die wir gerade verarbeiten, sowie darüber, wer der Betrachter ist, und was unser Login-Status ist, und der aktuelle Inhalt der Webseite, die wir wahrscheinlich aufbauen.
+
+Das zweite Argument ist spezifisch für den Hook, den Sie aufrufen. Es enthält Informationen, die für diese bestimmte Stelle im Programm relevant sind, und ermöglicht es Ihnen oft, sie anzusehen und sogar zu ändern. Um sie zu ändern, müssen Sie dem Variablennamen ein '&' hinzufügen, damit sie als Referenz an Ihre Funktion übergeben wird. Andernfalls wird eine Kopie erstellt, und alle Änderungen, die Sie vornehmen, gehen verloren, wenn der Hook-Prozess zurückkehrt. Normalerweise (aber nicht immer) ist das zweite Argument ein benanntes Array von Datenstrukturen. Bitte lesen Sie die „Hook-Referenz“ (zu diesem Zeitpunkt noch nicht geschrieben) für Details zu jedem spezifischen Hook. Gelegentlich müssen Sie sich den Programmcode ansehen, um genau zu sehen, wie ein bestimmter Hook aufgerufen wird und wie die Ergebnisse verarbeitet werden.
+
+Fügen wir nun etwas Code hinzu, um unseren post_local-Hook-Handler zu implementieren.
+
+ function randplace_post_hook($a, &$item) {
+
+ /**
+ *
+ * An item was posted on the local system.
+ * We are going to look for specific items:
+ * - A status post by a profile owner
+ * - The profile owner must have allowed our plugin
+ *
+ */
+
+ logger('randplace invoked');
+
+ if(! local_channel()) /* non-zero if this is a logged in user of this system */
+ return;
+
+ if(local_channel() != $item['uid']) /* Does this person own the post? */
+ return;
+
+ if(($item['parent']) || (! is_item_normal($item))) {
+ /* If the item has a parent, or isn't "normal", this is a comment or something else, not a status post. */
+ return;
+ }
+
+ /* Retrieve our personal config setting */
+
+ $active = get_pconfig(local_channel(), 'randplace', 'enable');
+
+ if(! $active)
+ return;
+ /**
+ *
+ * OK, we're allowed to do our stuff.
+ * Here's what we are going to do:
+ * load the list of timezone names, and use that to generate a list of world cities.
+ * Then we'll pick one of those at random and put it in the "location" field for the post.
+ *
+ */
+
+ $cities = array();
+ $zones = timezone_identifiers_list();
+ foreach($zones as $zone) {
+ if((strpos($zone,'/')) && (! stristr($zone,'US/')) && (! stristr($zone,'Etc/')))
+ $cities[] = str_replace('_', ' ',substr($zone,strpos($zone,'/') + 1));
+ }
+
+ if(! count($cities))
+ return;
+ $city = array_rand($cities,1);
+ $item['location'] = $cities[$city];
+
+ return;
+ }
+
+
+Fügen wir nun unsere Funktionen zum Erstellen und Speichern von Einstellungswerten hinzu.
+
+ /**
+ *
+ * Callback from the settings post function.
+ * $post contains the global $_POST array.
+ * We will make sure we've got a valid user account
+ * and that only our own submit button was clicked
+ * and if so set our configuration setting for this person.
+ *
+ */
+
+ function randplace_settings_post($a,$post) {
+ if(! local_channel())
+ return;
+ if($_POST['randplace-submit'])
+ set_pconfig(local_channel(),'randplace','enable',intval($_POST['randplace']));
+ }
+
+ /**
+ *
+ * Called from the Feature Setting form.
+ * The second argument is a string in this case, the HTML content region of the page.
+ * Add our own settings info to the string.
+ *
+ * For uniformity of settings pages, we use the following convention
+ * <div class="settings-block">
+ * <h3>title</h3>
+ * .... settings html - many elements will be floated...
+ * <div class="clear"></div> <!-- generic class which clears all floats -->
+ * <input type="submit" name="pluginnname-submit" class="settings-submit" ..... />
+ * </div>
+ */
+
+ function randplace_settings(&$a,&$s) {
+
+ if(! local_channel())
+ return;
+
+ /* Add our stylesheet to the page so we can make our settings look nice */
+
+ head_add_css('/addon/randplace/randplace.css');
+
+ /* Get the current state of our config variable */
+
+ $enabled = get_pconfig(local_channel(),'randplace','enable');
+
+ $checked = (($enabled) ? ' checked="checked" ' : '');
+
+ /* Add some HTML to the existing form */
+
+ $s .= '<div class="settings-block">';
+ $s .= '<h3>' . t('Randplace Settings') . '</h3>';
+ $s .= '<div id="randplace-enable-wrapper">';
+ $s .= '<label id="randplace-enable-label" for="randplace-checkbox">' . t('Enable Randplace Plugin') . '</label>';
+ $s .= '<input id="randplace-checkbox" type="checkbox" name="randplace" value="1" ' . $checked . '/>';
+ $s .= '</div><div class="clear"></div>';
+
+ /* provide a submit button */
+
+ $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="randplace-submit" class="settings-submit" value="' . t('Submit') . '" /></div></div>';
+
+ }
+
+#### *Erweiterte Plugins*
+
+Manchmal möchten Ihre Plugins eine Reihe neuer Funktionen bereitstellen, die gar nicht oder nur umständlich über Hooks bereitgestellt werden können. In diesem Fall kann Ihr Plugin auch als „Modul“ fungieren. Ein Modul ist in unserem Fall ein strukturierter Webpage-Handler, der auf eine bestimmte URL reagiert. Dann wird alles, was auf diese URL zugreift, vollständig von Ihrem Plugin behandelt.
+Der Schlüssel dazu ist die Erstellung einer einfachen Funktion namens pluginname_module(), die nichts tut.
+
+```
+function randplace_module() { return; }
+```
+
+Sobald diese Funktion existiert, wird die URL `https://yoursite/randplace` auf Ihr Plugin als Modul zugreifen. Dann können Sie Funktionen definieren, die an verschiedenen Stellen aufgerufen werden, um eine Webseite aufzubauen, genau wie die Module im mod/-Verzeichnis. Die typischen Funktionen und die Reihenfolge, in der sie aufgerufen werden, sind
+
+ modulename_init($a) // (e.g. randplace_init($a);) called first - if you wish to emit json or xml,
+ // you should do it here, followed by killme() which will avoid the default action of building a webpage
+ modulename_aside($a) // Often used to create sidebar content
+ modulename_post($a) // Called whenever the page is accessed via the "post" method
+ modulename_content($a) // called to generate the central page content. This function should return a string
+ // consisting of the central page content.
+
+Ihre Modulfunktionen haben Zugriff auf den URL-Pfad, als ob sie eigenständige Programme im Unix-Betriebssystem wären. Wenn Sie zum Beispiel die Seite
+
+```
+https://yoursite/randplace/something/somewhere/whatever
+```
+
+besuchen, erstellen wir eine argc/argv-Liste zur Verwendung durch Ihre Modulfunktionen
+
+ $x = argc(); $x will be 4, the number of path arguments after the sitename
+
+ for($x = 0; $x < argc(); $x ++)
+ echo $x . ' ' . argv($x);
+
+
+ 0 randplace
+ 1 something
+ 2 somewhere
+ 3 whatever
+
+#### *Portierung von Friendica-Plugins*
+
+Hubzilla verwendet eine ähnliche Plugin-Architektur wie das Friendica-Projekt. Die Authentifizierungs-, Identitäts- und Rechtesysteme sind jedoch völlig unterschiedlich. Viele Friendica-Plugins können relativ einfach portiert werden, indem man ein paar Funktionen umbenennt - und dann sicherstellt, dass das Berechtigungsmodell befolgt wird. Die Funktionen, die umbenannt werden müssen, sind:
+
+- Friendica's pluginname_install() wird zu pluginname_load()
+- Friendicas pluginname_uninstall() wird zu pluginname_unload()
+
+Hubzilla hat _install und _uninstall Funktionen, aber diese werden unterschiedlich benutzt.
+
+- Friendicas „plugin_settings“-Haken wird „feature_settings“ genannt
+- Friendicas „plugin_settings_post“-Haken wird „feature_settings_post“ genannt
+
+Wenn Sie diese ändern, wird Ihr Plugin oft funktionieren, aber bitte überprüfen Sie Ihren gesamten Berechtigungs- und Identitätscode, da die Konzepte dahinter in Hubzilla völlig anders sind. Viele strukturierte Datennamen (insbesondere DB-Schema-Spalten) sind auch ganz anders. \ No newline at end of file
diff --git a/doc/de/developer/attribution.md b/doc/de/developer/attribution.md
new file mode 100644
index 000000000..555abfa92
--- /dev/null
+++ b/doc/de/developer/attribution.md
@@ -0,0 +1,3 @@
+#### Namensnennung
+
+Dieser Verhaltenskodex ist eine Anpassung des [Contributor Covenant](http://contributor-covenant.org/), Version 1.4, verfügbar unter [http://contributor-covenant.org/version/1/4.](http://contributor-covenant.org/version/1/4/) \ No newline at end of file
diff --git a/doc/de/developer/code_of_conduct.md b/doc/de/developer/code_of_conduct.md
new file mode 100644
index 000000000..c197f0965
--- /dev/null
+++ b/doc/de/developer/code_of_conduct.md
@@ -0,0 +1,8 @@
+### Verhaltenskodex für Mitwirkende
+
+#include doc/de/developer/our_pledge.md;
+#include doc/de/developer/our_standards.md;
+#include doc/de/developer/our_responsibilities.md;
+#include doc/de/developer/scope.md;
+#include doc/de/developer/enforcement.md;
+#include doc/de/developer/attribution.md;
diff --git a/doc/de/developer/coding_style.md b/doc/de/developer/coding_style.md
new file mode 100644
index 000000000..fab3d7157
--- /dev/null
+++ b/doc/de/developer/coding_style.md
@@ -0,0 +1,12 @@
+### Codierungsstil
+
+Im Interesse der Konsistenz verwenden wir den folgenden Code-Stil. Wir akzeptieren auch Patches, die andere Stile verwenden, aber bitte versuchen Sie, wenn möglich, einen einheitlichen Code-Stil zu verwenden. Wir werden nicht über die Vorzüge dieses Stils streiten oder diskutieren, und es ist irrelevant, was Projekt „xyz“ verwendet. Dies ist nicht Projekt „xyz“. Dies ist eine Grundlinie, um zu versuchen, den Code jetzt und in Zukunft lesbar zu halten.
+
+- Alle Kommentare sollten in Englisch sein.
+- Wir verwenden Doxygen, um Dokumentation zu erstellen. Dies wurde nicht konsequent angewandt, aber es wird dringend empfohlen, es zu lernen und zu benutzen.
+- Die Einrückung erfolgt hauptsächlich durch Tabulatoren mit einer Tabulatorbreite von 4.
+- String-Verkettungen und Operatoren sollten durch Leerzeichen getrennt werden. z.B. `$foo = $bar . 'abc';` anstelle von `$foo=$bar.‘abc';`
+- Im Allgemeinen verwenden wir einfache Anführungszeichen für String-Variablen und doppelte Anführungszeichen für SQL-Anweisungen. „Hier Dokumente“ sollten vermieden werden. Manchmal ist die Verwendung von Strings in doppelten Anführungszeichen mit Variablenersetzung das effizienteste Mittel zur Erstellung des Strings. In den meisten Fällen sollten Sie einfache Anführungszeichen verwenden.
+- Verwenden Sie Leerzeichen großzügig, um die Lesbarkeit zu verbessern. Bei der Erstellung von Arrays mit vielen Elementen wird oft ein Schlüssel/Wert-Paar pro Zeile gesetzt, das entsprechend von der übergeordneten Zeile eingerückt wird. Das Aneinanderreihen der Zuweisungsoperatoren erfordert etwas mehr Arbeit, erhöht aber auch die Lesbarkeit.
+- Im Allgemeinen werden öffnende geschweifte Klammern in dieselbe Zeile gesetzt wie das, was die Klammer öffnet. Sie sind das letzte Zeichen in der Zeile. Schließende Klammern stehen in einer eigenen Zeile.
+- Einige Funktionen nehmen Argumente im argc/argv-Stil entgegen, wie main() in C oder Funktions-Args in Bash oder Perl. Urls werden innerhalb eines Moduls aufgeteilt. z.B. bei `http://example.com/module/arg1/arg2` ist $this->argc 3 (integer) und $this->argv enthält: [0] => 'module', [1] => 'arg1', [2] => 'arg2'. Es wird immer nur ein Argument angegeben. Wenn eine nackte Domain-URL angegeben wird, wird $this->argv[0] auf „home“ gesetzt. \ No newline at end of file
diff --git a/doc/de/developer/dev-function-overview.md b/doc/de/developer/dev-function-overview.md
new file mode 100644
index 000000000..36b27b449
--- /dev/null
+++ b/doc/de/developer/dev-function-overview.md
@@ -0,0 +1,43 @@
+### Hubzilla-Entwicklung - einige nützliche Grundfunktionen
+
+- `get_account_id()`
+
+Gibt die numerische account_id zurück, wenn authentifiziert oder 0. Es ist möglich, authentifiziert und nicht mit einem Channel verbunden zu sein.
+
+- `local_channel()`
+
+Gibt die numerische channel_id zurück, wenn sie authentifiziert und mit einem Channel verbunden ist, oder 0. Wird im Code manchmal als $uid bezeichnet.
+
+- `remote_channel()`
+
+Gibt einen authentifizierten String-Hash des Red Global Identifier zurück, wenn er über Remote-Auth authentifiziert wurde, oder einen leeren String.
+
+- `App::get_observer()`
+
+gibt eine xchan-Struktur zurück, die den aktuellen Betrachter repräsentiert, wenn er authentifiziert ist (lokal oder remote).
+
+- `get_config($family,$key), get_pconfig($uid,$family,$key)`
+
+Gibt die Konfigurationseinstellung für $family und $key zurück oder false, wenn sie nicht gesetzt ist.
+Veraltet: Verwenden Sie stattdessen `Zotlabs\Lib\Config::Get`.
+
+- `set_config($family,$key,$value), set_pconfig($uid,$family,$key,$value)`
+
+Setzt den Wert der Konfigurationseinstellung für $family und $key auf $value. Gibt $value zurück. Die config-Versionen arbeiten mit systemweiten Einstellungen. Die pconfig-Versionen erhalten/setzen die Werte für eine bestimmte Integer uid (channel_id).
+Veraltet: Verwenden Sie stattdessen `Zotlabs\Lib\Config::Set`.
+
+- `dbesc()`
+
+Escape-Zeichenfolgen, die in DB-Abfragen verwendet werden. Diese Funktion gibt die escapte Zeichenkette zurück. Ganzzahlige DB-Parameter sollten alle mit intval() in Ganzzahlen umgewandelt werden
+
+- `q($sql,$var1...)`
+
+Führt eine DB-Abfrage mit der SQL-Anweisung $sql durch. Die printf-artigen Argumente %s und %d werden durch variable Argumente ersetzt, die jeweils entsprechend dbesc() oder intval() sein sollten. SELECT-Abfragen geben ein Array von Ergebnissen oder false zurück, wenn ein SQL- oder DB-Fehler vorliegt. Andere Abfragen geben true zurück, wenn der Befehl erfolgreich war, oder false, wenn er nicht erfolgreich war.
+
+- `t($Zeichenfolge)`
+
+Gibt die übersetzte Variante von $string für die aktuelle Sprache zurück oder $string (Standardsprache 'en'), wenn die Sprache nicht bekannt ist oder eine übersetzte Version des Strings nicht existiert.
+
+- `x($var), $x($array,$key)`
+
+Kurzer Test, um zu sehen, ob die Variable $var gesetzt und nicht leer ist. Die Tests variieren je nach Typ. Gibt false zurück, wenn $var oder $key nicht gesetzt ist. Wenn die Variable gesetzt ist, wird 1 zurückgegeben, wenn sie einen Wert ungleich Null hat, andernfalls wird 0 zurückgegeben. -- z.B. x('') oder x(0) gibt 0 zurück; \ No newline at end of file
diff --git a/doc/de/developer/dev_beginner.md b/doc/de/developer/dev_beginner.md
new file mode 100644
index 000000000..91f19a109
--- /dev/null
+++ b/doc/de/developer/dev_beginner.md
@@ -0,0 +1,460 @@
+### Sie möchten Code beisteuern?
+
+**...und wissen nicht, wie Sie anfangen sollen...**
+
+- Hubzilla debuggen (php auf dem Webserver),
+- Code für das Projekt beisteuern,
+- optional - das alles von einer virtuellen Maschine aus zu tun
+
+Diese Anleitung wurde für Debian (Wheezy) als virtuelle Maschine auf Lubuntu (Ubuntu 14.0) als Host getestet.
+
+#### Installation einer virtuellen Maschine (KVM)
+
+[Hier](https://wiki.debian.org/KVM) die Installationsanleitung für Linux Debian. Die Zusammenfassung:
+
+1. KVM installieren
+
+```
+# apt-get install qemu-kvm libvirt-bin
+```
+
+1. füge dich der Gruppe libvirt hinzu
+
+```
+# adduser <deinBenutzer> libvirt
+```
+
+1. Installieren Sie die Benutzeroberfläche zur Verwaltung der virtuellen Maschinen
+
+```
+# apt-get install virt-manager
+```
+
+1. Laden Sie ein Betriebssystem herunter, das in der virtuellen Maschine laufen soll ([mini.iso](http://ftp.nl.debian.org/debian/dists/wheezy/main/installer-amd64/current/images/netboot/mini.iso))
+2. Starten Sie den Virt-Manager
+
+- Erstellen Sie eine neue virtuelle Maschine (klicken Sie auf das Symbol)
+- Wählen Sie Ihr Iso-Image (soeben heruntergeladen) als Installationsquelle
+- optional: konfigurieren Sie die neue vm: ram, cpu's,...
+- Virtuelle Maschine starten > Ergebnis: Linux Debian startet in einem neuen Fenster.
+
+1. (optional) Vermeiden Sie Netzwerkfehler nach dem Neustart des Host-Betriebssystems
+
+```
+# virsh net-start default
+# virsh net-autostart standard
+```
+
+#### Apache-Webserver installieren
+
+Öffnen Sie ein Terminal und machen Sie sich zum root
+
+```
+su -l
+```
+
+Erstellen Sie die Standardgruppe für den Apache-Webserver
+
+```
+groupadd www-data
+```
+
+könnte bereits existieren
+
+```
+usermod -a -G www-data www-data
+```
+
+Prüfen Sie, ob das System wirklich auf dem neuesten Stand ist
+
+```
+apt-get update
+apt-get upgrade
+```
+
+Optionaler Neustart der Dienste nach der Installation
+
+```
+reboot
+```
+
+Wenn Sie neu gestartet haben, machen Sie sich zum root
+
+```
+su -l
+```
+
+Installieren Sie Apache:
+
+```
+apt-get install apache2 apache2-doc apache2-utils
+```
+
+Öffnen Sie den Webbrowser auf dem PC und überprüfen Sie [localhost](localhost) Sollte Ihnen eine Seite wie „It works“ anzeigen
+(Quelle http://www.manfred-steger.de/tuts/20-der-eigene-webserver-mit-dem-raspberry-pi#)
+
+#### PHP, MySQL und phpMyAdmin installieren
+
+##### PHP, MySQL
+
+```
+su -l
+apt-get install libapache2-mod-php8.2 php8.2 php-pear php8.2-xcache php8.2-curl php8.2-mcrypt php8.2-xdebug
+apt-get install php8.2-mysql
+apt-get install mysql-server mysql-client
+```
+
+das mysql-Passwort eingeben und notieren
+Optional, da es bereits während der phpmyadmin-Einrichtung aktiviert wurde
+
+```
+phpenmod mcrypt
+```
+
+##### phpMyAdmin
+
+phpMyAdmin installieren
+
+```
+apt-get install phpmyadmin
+```
+
+Konfigurieren von phpmyadmin
+
+- Wählen Sie apache2 aus (Tipp: benutzen Sie die Tabulator-Taste zum Auswählen)
+- Datenbank für phpmyadmin mit dbconfig-common? konfigurieren: Wählen Sie Ja
+
+(Quelle [http://www.manfred-steger.de/tuts/20-der-eigene-webserver-mit-dem-raspberry-pi#](http://www.manfred-steger.de/tuts/20-der-eigene-webserver-mit-dem-raspberry-pi#))
+
+##### Aktivieren Sie Rewrite
+
+Bei der Standardinstallation von Apache2 ist mod_rewrite bereits installiert. Um zu prüfen, ob dies der Fall ist, überprüfen Sie die Existenz von `/etc/apache2/mods-available/rewrite.load`
+
+```
+nano /etc/apache2/mods-available/rewrite.load
+```
+
+(Sie sollten den Inhalt finden: `LoadModule rewrite_module` `/usr/lib/apache2/modules/mod_rewrite.so`) Um mod_rewrite zu aktivieren und zu laden, führen Sie die restlichen Schritte aus. Erstellen Sie einen symbolischen Link in `/etc/apache2/mods-enabled`
+
+```
+cd /var/www
+a2enmod rewrite
+```
+
+Öffnen Sie dann die folgende Datei und ersetzen Sie jedes Vorkommen von „`AllowOverride None`“ durch „`AllowOverride all`“.
+
+```
+nano /etc/apache2/apache2.conf
+```
+
+oder
+
+```
+gedit /etc/apache2/sites-enabled/000-default
+```
+
+Starten Sie schließlich Apache2 neu.
+
+```
+service apache2 restart
+```
+
+##### Testen Sie die Installation
+
+```
+cd /var/www
+```
+
+Erstellen Sie eine php-Datei, um die php-Installation zu testen
+
+```
+nano phpinfo.php
+```
+
+In die Datei einfügen:
+
+```
+<?php
+ phpinfo();
+?>
+```
+
+(STRG+0, ENTER, STRG+X speichern)
+Webbrowser auf dem PC öffnen und `http://localhost/phpinfo.php` ausprobieren (Seite zeigt Infos zu php)
+phpMyAdmin mit der MySQL-Datenbank verbinden
+
+```
+nano /etc/apache2/apache2.conf
+```
+
+- CTRL+V... bis zum Ende der Datei
+- Am Ende der Datei einfügen: (CTRL+0, ENTER, CTRL+X speichern)
+
+```
+Einfügen von /etc/phpmyadmin/apache.conf
+```
+
+Apache neu starten
+
+```
+/etc/init.d/apache2 neu starten
+apt-get update
+apt-get upgrade
+reboot
+```
+
+phpMyAdmin
+Webbrowser auf dem PC öffnen und `http://localhost/phpmyadmin` ausprobieren
+(Quelle [http://www.manfred-steger.de/tuts/20-der-eigene-webserver-mit-dem-raspberry-pi#](http://www.manfred-steger.de/tuts/20-der-eigene-webserver-mit-dem-raspberry-pi#))
+
+##### Erstelle eine leere Datenbank... die später von Hubzilla verwendet wird
+
+öffne den Webbrowser auf dem PC und probiere `http://localhost/phpmyadmin`
+Erstelle eine leere Datenbank, zum Beispiel mit dem Namen „red“. Erstelle einen Datenbankbenutzer, z.B. „red“. Erteile dem Benutzer „red“ alle Rechte für die Datenbank „red“.
+Notieren Sie sich die Zugangsdaten (Hostname, Benutzername, Passwort, Datenbankname).
+
+##### Forken Sie das Projekt auf github
+
+Bitte folgen Sie den Anweisungen in der offiziellen [Dokumentation](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) von git. Es ist eine gute Idee, das gesamte Handbuch zu lesen! Git unterscheidet sich in vielerlei Hinsicht von anderen Versionskontrollsystemen.
+Jetzt sollten Sie
+
+- ein Konto bei github.com erstellen
+- fork `https://framagit.org/hubzilla/core`
+- fork `https://framagit.org/hubzilla/addons`
+
+Wenn Sie GIT nicht von der Kommandozeile aus benutzen wollen - es gibt ein nützliches Eclipse-Plugin namens „Eclipse Mylyn to GitHub connector“.
+
+#### Installieren Sie Hubzilla und seine Addons
+
+##### Git auf Ihrem Computer / vm
+
+Sie sollten ein Konto auf GitHub erstellt und die Projekte geforkt haben, bevor Sie fortfahren.
+Löschen Sie das Verzeichnis www
+
+```
+rm -R www/
+```
+
+Installieren Sie git (und optional git-gui, eine Client-Gui)
+
+```
+apt-get install git git-gui
+```
+
+##### Hubzilla und Addons herunterladen
+
+Laden Sie das Hauptprojekt hubzilla und hubzilla-addons herunter
+
+```
+git clone https://github.com/yourname/hubzilla www
+cd www/
+git clone https://github.com/yourname/hubzilla-addons addon
+```
+
+Erstelle diesen zusätzlichen Ordner
+
+```
+mkdir -p „store/[data]/smarty3“
+```
+
+Erstellen Sie .htconfig.php und machen Sie sie für den Webserver schreibbar
+
+```
+touch .htconfig.php
+chmod ou+w .htconfig.php
+```
+
+Machen Sie den Benutzer www-data (Webserver) zum Eigentümer aller Projektdateien
+
+```
+cd ..
+chown -R www-data:www-data www/
+```
+
+Fügen Sie sich selbst („surfer“ in diesem Beispiel) zur Gruppe www-data hinzu. Warum das? Wenn Sie später Dateien in eclipse oder einem anderen Editor bearbeiten wollen.
+Dann machen Sie alle Dateien für die Gruppe www-data, in der Sie jetzt Mitglied sind, schreibbar.
+
+```
+cd www/
+usermod -G www-data surfer
+chmod -R g+w www/
+```
+
+Starten Sie den Computer (oder vm) neu
+Wenn Sie immer noch nicht in der Lage sind, die Projektdateien zu ändern, können Sie die Mitglieder der Gruppe www-data überprüfen mit
+
+```
+cat /etc/group
+```
+
+##### Melden Sie sich als Admin an
+
+Öffnen Sie `http://localhost` und starten Sie die Matrix
+Bevor Sie einen ersten Benutzer registrieren, schalten Sie die Registrierungsmails aus. Öffnen Sie `/var/www/.htconfig.php` und stellen Sie sicher, dass in dieser Zeile „0“ gesetzt ist
+
+```
+App::$config['system']['verify_email'] = 0;
+```
+
+Sie sollten in der Lage sein, die Datei als „Sie selbst“ zu ändern (anstelle von root oder www-data).
+
+##### Cron und der Poller
+
+Wichtig! Lassen Sie den Poller laufen, um die jüngsten „öffentlichen“ Beiträge Ihrer Freunde zu erfassen. Richten Sie einen Cron-Job oder eine geplante Aufgabe ein, um den Poller alle 5-10 Minuten laufen zu lassen, um die jüngsten „öffentlichen“ Beiträge Ihrer Freunde zu erfassen
+
+```
+crontab -e
+```
+
+Fügen Sie hinzu.
+
+```
+*/10 * * * * cd /var/www/; /usr/bin/php include/poller.php
+```
+
+Wenn du den Pfad zu PHP nicht kennst, gib ein
+
+```
+which php
+```
+
+#### Debuggen Sie den Server über eclipse
+
+##### Überprüfen Sie die Konfiguration von xdebug
+
+Sie sollten xdebug bereits in den vorhergehenden Schritten installiert haben
+
+```
+apt-get install php-xdebug
+```
+
+Xdebug konfigurieren
+Öffnen Sie Ihr Terminal und geben Sie als root (su -l)
+
+```
+gedit /etc/php/mods-available/xdebug.ini
+```
+
+Wenn die Datei leer ist, versuchen Sie es an dieser Stelle
+
+```
+gedit /etc/php/conf.d/xdebug.ini
+```
+
+Dieser Befehl sollte den Texteditor gedit mit der Xdebug-Konfigurationsdatei öffnen Am Ende des Dateiinhalts fügen Sie den folgenden Text ein
+
+```
+xdebug.remote_enable=on
+xdebug.remote_handler=dbgp
+xdebug.remote_host=localhost
+xdebug.remote_port=9000
+```
+
+Speichern Sie die Änderungen und schließen Sie den Editor. Geben Sie in Ihr Terminal ein, um den Webserver neu zu starten.
+
+```
+service apache2 neustart
+```
+
+##### Eclipse installieren und Debugging starten
+
+Installieren Sie Eclipse. Starten Sie Eclipse mit dem Standard-Arbeitsbereich (oder wie Sie möchten)
+Installieren Sie das PHP-Plugin Menü > Hilfe > Neue Software installieren... Installieren Sie „PHP-Entwicklungswerkzeuge ...“
+Optional - Installieren Sie das GitHub Connector Plugin Menü > Hilfe > Neue Software installieren... Installieren Sie „Eclipse Mylyn zu GitHub Connector“.
+Konfigurieren Sie das PHP-Plugin Menü > Fenster > Präferenzen...
+
+> Allgemein > Webbrowser > Ändern Sie auf „Externen Webbrowser verwenden“ PHP > Debug > Debug-Einstellungen > PHP Debugger > Ändern Sie auf „XDebug“.
+
+Erstellen Sie ein neues PHP-Projekt Menü > Datei > Neues Projekt > Wählen Sie PHP > „PHP-Projekt“.
+
+> Wählen Sie „Projekt an bestehendem Ort erstellen“ und „/var/www“.
+
+Debugging starten Öffnen Sie index.php und „Debuggen als...“. Wählen Sie als Start-URL: „`http://localhost/`“
+Erwartet:
+
+- Der Webbrowser startet
+- Der Debugger wird bei der ersten php-Zeile anhalten
+
+#### Stellen Sie Ihre Änderungen über github zur Verfügung
+
+##### Vorbereitungen
+
+Es gibt eine entsprechende Seite in dieser Dokumentation: [zrl=[baseurl]/help/git_for_non_developers]Git for Non-Developers[/zrl]. Wie bereits erwähnt, ist es empfehlenswert, die offizielle Dokumentation [GitHub-Contributing-to-a-Project](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) von Git zu lesen.
+Eclipse hat ein nützliches Plugin für GIT: „Eclipse Mylyn to GitHub connector“.
+Stellen Sie sicher, dass Sie Ihre Daten eingestellt haben
+
+```
+git config --global user.name „Ihr Name“
+git config --global user.email „your@mail.com“
+```
+
+##### Dein erster Beitrag
+
+Erstelle einen beschreibenden Themenzweig
+
+```
+git checkout -b dev_beginning
+```
+
+Stellen Sie sicher, dass Ihr lokales Repository mit dem Hauptprojekt auf dem neuesten Stand ist. Fügen Sie das ursprüngliche Repository als Remote-Repository mit dem Namen „upstream“ hinzu, falls noch nicht geschehen
+
+```
+git remote add upstream https://framagit.org/hubzilla/core/
+```
+
+Holen Sie sich die neueste Arbeit von dieser Remote
+
+```
+git fetch upstream
+git merge upstream/master
+```
+
+Tipp: Sie können die Zweige auflisten
+
+```
+git branch -v
+```
+
+Nehmen Sie Ihre Änderungen vor. In diesem Beispiel ist es eine neue Doc-Datei.
+Prüfen Sie Ihre Änderungen
+
+```
+git status
+```
+
+Fügen Sie die neue Datei hinzu (stage)
+
+```
+git add doc/dev_beginner.bb
+```
+
+Übertragen Sie die Änderungen in Ihren lokalen Zweig. Dadurch wird ein Editor geöffnet, um eine Nachricht zu übermitteln.
+
+```
+git commit -a
+```
+
+Pushen Sie online zurück in den gleichen Themenzweig
+
+```
+git push
+```
+
+Nun können Sie zu Ihrem (Online-)Konto bei github gehen und den Pull Request erstellen.
+
+##### Folgende Beiträge
+
+Für den Fall, dass die Hauptentwickler wollen, dass du etwas änderst. Holen Sie sich die neueste Arbeit aus dem entfernten Upstream/Master, um sicher zu sein, dass Sie die neuesten Änderungen haben.
+
+```
+git fetch upstream
+git merge upstream/master
+```
+
+Nehmen Sie Ihre Änderungen vor, testen Sie sie, committen Sie (in das lokale Repository), pushen Sie (in das Online-Repository)
+
+```
+git status
+git commit -a -m „Änderung des Zweigs hinzugefügt“
+git push
+``` \ No newline at end of file
diff --git a/doc/de/developer/developers_guide.md b/doc/de/developer/developers_guide.md
new file mode 100644
index 000000000..898cb17c7
--- /dev/null
+++ b/doc/de/developer/developers_guide.md
@@ -0,0 +1,27 @@
+## Entwicklerhandbuch
+
+Informationen für Hubzilla-Entwickler
+
+#include doc/de/developer/who_is_a_hubzilla_developer.md;
+#include doc/de/developer/dev_beginner.md;
+#include doc/de/developer/versions.md;
+#include doc/de/developer/git_repository.md;
+#include doc/de/developer/git_for_non_developers.md;
+#include doc/de/developer/tools_workflows.md;
+#include doc/de/developer/doco.md;
+#include doc/de/developer/translations.md;
+#include doc/de/developer/licensing.md;
+#include doc/de/developer/coding_style.md;
+#include doc/de/developer/file_system_layour.md;
+#include doc/de/developer/dev-functions-overview.md;
+#include doc/de/developer/Plugins.md;
+#include doc/de/developer/testing.md;
+#include doc/de/developer/federate.md;
+#include doc/de/developer/code_of_conduct.md;
+#include doc/de/developer/nomad_protocol.md;
+#include doc/de/developer/technical_introductions.md;
+#include doc/de/developer/magic_auth.md;
+#include doc/de/developer/nomad_structures.md;
+#include doc/de/developer/API.md;
+#include doc/de/developer/hooks.md;
+#include doc/de/developer/unorganized.md;
diff --git a/doc/de/developer/doco.md b/doc/de/developer/doco.md
new file mode 100644
index 000000000..c62db2703
--- /dev/null
+++ b/doc/de/developer/doco.md
@@ -0,0 +1,29 @@
+### Dokumentation erstellen
+
+Um eine Dokumentation zu erstellen, müssen Sie nur ein paar Wörter in eine geschickte Reihenfolge bringen und ihre Existenz einem Entwickler bekannt geben. Sie können dies buchstäblich überall tun, solange ein Entwickler es sehen kann. Sobald Sie es bekannt gemacht haben, wird es jemand für Sie einchecken. Sie sollten versuchen, proprietäre Formate oder Orte, die eine Authentifizierung mit anderen Methoden als Nomad erfordern, zu vermeiden, um einem Entwickler den Zugang zu erleichtern, aber selbst das ist keine strikte Voraussetzung.
+Wenn Sie direkt beitragen wollen, ist das auch in Ordnung. Um direkt beizutragen, sollte die Dokumentation in einem der folgenden Formate vorliegen:
+
+- Markdown
+- BBCode
+- HTML
+- Einfacher Text
+- Andere Formate sind ebenfalls erlaubt, aber die Unterstützung für das Format muss zuerst zu mod/help.php hinzugefügt werden.
+
+Wenn Sie eine reine Textdatei bearbeiten, halten Sie bitte die Spaltenbreite auf 80. Dies liegt daran, dass reiner Text in Fällen verwendet wird, in denen wir keine funktionierende Installation haben - zum Beispiel die Installationsdokumentation - und es sollte einfach sein, diese mit einem CLI-Texteditor zu lesen.
+Der Vorteil von Markdown ist, dass es für Menschen lesbar ist.
+
+Der Vorteil von BBCode ist, dass es identitätsbewusst ist.
+
+Wenn Sie also BBCode verwenden, versuchen Sie, das Beste daraus zu machen:
+
+- Verwenden Sie gegebenenfalls ZRL-Links, um sicherzustellen, dass ein Link zu einer anderen Website die Authentifizierung beibehält und die identitätsbasierte Dokumentation funktioniert
+- Verwenden Sie baseurl- oder observer.baseurl-Tags, wo es angebracht ist, anstelle von example.com für authentifizierte Betrachter.
+- Unterstützen Sie nicht-authentifizierte Benutzer mit observer=0-Tags. Aufgrund historischer Versäumnisse tun wir dies derzeit nicht. Dies muss überall hinzugefügt werden.
+
+**Übersetzungen**
+Um die Dokumentation zu übersetzen oder in anderen Sprachen als Englisch bereitzustellen:
+
+- Erstellen Sie ein Verzeichnis in doc/ mit Ihrem zweistelligen Ländercode, falls es noch nicht existiert (z.B. doc/de/ für Deutsch oder doc/fr/ für Französisch)
+- Erstellen Sie ein Dokument mit demselben Dateinamen wie die englische Version, aber mit dem Inhalt in Ihrer eigenen Sprache. So können wir auf die englische Version zurückgreifen, wenn die Übersetzung für eine bestimmte Seite nicht vorhanden ist.
+
+Um eine Dokumentation zu erstellen, für die es keine gleichwertige Datei auf Englisch gibt, können Sie eine neue Datei mit einem Namen Ihrer Wahl erstellen - allerdings müssen Sie auch eine lokalisierte Version der Indexseite (main.bb auf Englisch) bereitstellen, damit sie über das Menü zugänglich ist. \ No newline at end of file
diff --git a/doc/de/developer/enforcement.md b/doc/de/developer/enforcement.md
new file mode 100644
index 000000000..0878fed3f
--- /dev/null
+++ b/doc/de/developer/enforcement.md
@@ -0,0 +1,5 @@
+#### Durchsetzung
+
+Fälle von beleidigendem, belästigendem oder anderweitig inakzeptablem Verhalten können beim Projektteam unter project@hubzilla.org gemeldet werden. Alle Beschwerden werden geprüft und untersucht und führen zu einer Reaktion, die als notwendig und den Umständen angemessen erachtet wird. Das Projektteam ist zur Vertraulichkeit gegenüber der Person, die einen Vorfall meldet, verpflichtet. Weitere Einzelheiten zu spezifischen Durchsetzungsrichtlinien können separat veröffentlicht werden.
+
+Projektbetreuer, die den Verhaltenskodex nicht in gutem Glauben befolgen oder durchsetzen, können mit vorübergehenden oder dauerhaften Konsequenzen rechnen, die von anderen Mitgliedern der Projektleitung festgelegt werden. \ No newline at end of file
diff --git a/doc/de/developer/federate.md b/doc/de/developer/federate.md
new file mode 100644
index 000000000..6b07dca15
--- /dev/null
+++ b/doc/de/developer/federate.md
@@ -0,0 +1,56 @@
+### Erstellen von Protokoll- Föderationsdiensten
+
+Es gibt drei Hauptkomponenten beim Schreiben von Föderations-Plugins. Diese sind:
+
+1. Kanalerkennung und Verbindungsaufbau
+2. Senden von Beiträgen/Nachrichten
+3. Empfangen von Beiträgen/Nachrichten
+
+Darüber hinaus müssen Verbundtreiber Folgendes bewältigen
+
+4. Unterschiede in den Datenschutzrichtlinien (und Inhaltsformaten)
+
+#### Herstellen von Verbindungen
+
+Die Kernanwendung bietet eine Kanalerkennung in der folgenden Reihenfolge:
+
+1. Nomad-Kanal-Erkennung
+2. Webfinger (channel@hub) Lookup 2.1 RFC7033 Webfinger 2.2 XRD basierter Webfinger (alter Stil)
+3. URL-Ermittlung (derzeit nur zur Ermittlung von RSS-Feeds verwendet)
+4. Wenn alle diese Methoden fehlschlagen, ist das Netzwerk „unbekannt“ und wir sind nicht in der Lage, mit dem Kanal zu kommunizieren oder eine Verbindung zu ihm herzustellen. Ein „xchan“-Datensatz *kann* dennoch erstellt werden, **wenn** genügend Informationen bekannt sind, um einen eindeutigen Kanal zu identifizieren.
+
+Jeder der Lookup-Dienste kann an ein Plugin gebunden und erweitert werden. Wenn ein Kanal entdeckt wird, erstellen wir einen „xchan“-Datensatz, der eine plattformneutrale Darstellung der Kanalidentität ist. Wenn wir Informationen an den Kanal senden müssen, ist in der Regel auch ein „hubloc“-Datensatz (Standort des Hubs) erforderlich. Ein 'follow'-Plugin-Hook wird bereitgestellt, um Webfinger und diese Erkennungssequenz vollständig zu umgehen.
+
+Der letzte Schritt, um dies alles zusammenzufügen, ist die Erstellung eines „abook“-Datensatzes, der einen xchan in einer Beziehung zu einem lokalen Kanal mit einem bestimmten Satz von Berechtigungen anlegt.
+Bei Netzwerken, die keine nomadische Identität unterstützen, muss Ihr Plugin auch „abook_instance“ setzen, eine durch Kommata getrennte Liste von lokalen URLs, mit denen der entfernte Kanal verbunden ist. Wenn Ihr Mitglied zum Beispiel mit meinem Channel-Klon unter [https://example.com](https://example.com/) verbunden war, würde die abook_instance für diese Verbindung['https://example.com](https://example.com/)' lauten. Wenn Sie sich auch mit meinem Klon unter [https://abc.example.com](https://abc.example.com/) verbunden haben, würde die Zeichenfolge in[„https://example.com,https://abc.example.com](https://example.com,https//abc.example.com)“ geändert werden. Dies ermöglicht es den lokalen Kanälen zu unterscheiden, mit welcher Instanz ein bestimmter entfernter Kanal verbunden ist und vermeidet Zustellungsfehler an diese Kanäle von anderen Kloninstanzen.
+
+Ein Föderations-Plugin für ein Webfinger-basiertes System muss nur die Webfinger- oder XRD-Ergebnisse untersuchen und einen Protokoll-Stack identifizieren, der für die Verbindung oder Kommunikation verwendet werden kann. Dann erstellt es einen xchan-Datensatz mit dem angegebenen 'xchan_network'-Typ und einen hubloc-Datensatz mit demselben 'hubloc_network' mit den angegebenen Informationen. Derzeit muss das Plugin den gesamten Datensatz erstellen, aber in Zukunft wird dies erweitert, so dass das Plugin nur einen Netzwerknamen identifizieren muss und der Datensatz mit allen anderen bekannten Werten gefüllt wird.
+
+Ein xchan-Datensatz ist immer erforderlich, um eine Verbindung herzustellen. Um eine Verbindung herzustellen, erstellen Sie einen abook-Eintrag mit den gewünschten Berechtigungen.
+Zusätzliche Informationen, die Ihr Plugin für die Kommunikation benötigt, können in der xconfig-Tabelle und/oder abconfig-Tabelle gespeichert werden, wenn es in den xchan- oder hubloc-Tabellen keine passende Tabellenspalte gibt.
+
+Wenn eine Verbindung hergestellt wird, rufen wir normalerweise den Notifier (include/notifier.php) auf, um eine Nachricht an den entfernten Kanal zu senden. Dies ist an den Hook 'permissions_create' gebunden. Ihr Plugin muss dies verarbeiten, um eine „Follow“- oder „Make Friends“-Nachricht an das andere Netzwerk zu senden.
+
+Anmerkungen: Die erste Stufe des Nomad lookup wird durch einen webfinger lookup ersetzt. Diese Arbeit ist in Arbeit. Ursprünglich war ein separater Lookup erforderlich, da Webfinger keine Nicht-SSL-Verbindungen zulässt. Um diese Einschränkung zu umgehen, werden wir Nomad-Lookups, die nicht auf SSL basieren (in der Regel Test- und Entwicklungsseiten), über den „alten“ XRD-basierten Webfinger anbieten.
+
+Die Kernanwendung wird versuchen, xchan-Einträge für Projekte zu erstellen, die als Mitglieder des „offenen Webs“ identifiziert wurden; derzeit Hubzilla, Friendica, Diaspora, GNU-Social und Pump.io. Dies geschieht, damit Kommentare zwischen den Projektseiten weitergegeben werden können und das Netzwerk korrekt identifiziert werden kann. Ein Föderations-Plugin ist erforderlich, um eine vollständige Verbindung mit anderen Netzwerken herzustellen, aber Kommentare können auch an Seiten weitergeleitet werden, auf denen ein solches Plugin nicht installiert ist, damit es keine unerklärlichen Löcher in den Unterhaltungen gibt.
+
+Die Kernanwendung muss auch die Möglichkeit bieten, Diaspora-Kommentare zu signieren, da diese ein spezielles Signierformat erfordern und vom Verfasser des Kommentars signiert werden müssen, unabhängig davon, ob dieser Kanal mit Diaspora föderiert ist. Der Eigentümer der Konversation kann mit Diaspora föderieren, so dass die Kommentare signiert werden müssen. Dies ist bedauerlich, aber notwendig.
+
+#### Versenden von Nachrichten
+
+Jedes Mal, wenn eine Nachricht gesendet wird (mit der einzigen Ausnahme der Verzeichniskommunikation), rufen wir den Notifier (include/notifier.php) auf und übergeben ihm die Art der Nachricht und im Allgemeinen einen Identifikator, um die Informationen, die gesendet werden, aus der Datenbank abzurufen (Artikel oder Konversationsdinge, private E-Mails, Aktualisierungen von Berechtigungen usw.).
+Der Notifier verfügt über mehrere Hooks, die von Plugins auf unterschiedliche Weise verwendet werden können, je nachdem, wie ihre Zustellschleife funktioniert. Für verschiedene Nachrichtentypen und komplexe Zustellungssituationen müssen Sie möglicherweise mehrere Hooks verwenden. Der „permissions_create“-Hook wurde bereits im ersten Abschnitt erwähnt. Es gibt auch eine „permissions_update“-Nachricht, wenn sich die Berechtigungen geändert haben und das andere Ende der Verbindung darüber informiert werden muss. Nur wenige Dienste werden dies bereitstellen oder handhaben (da ihre Berechtigungen statisch sind), aber es wird z. B. auch verwendet, um Nachrichten zur Aktualisierung von Profilen und Profilfotos zu senden, und Sie möchten dies vielleicht handhaben.
+Der nächste Plugin-Hook ist 'notifier_process'. Ihm wird ein Array übergeben, das den kompletten Status des Notifizierers enthält und einmal pro Notifiziereraufruf aufgerufen wird. Er enthält die vollständige Liste der Empfänger (mit jeweils gesetztem xchan_network).
+
+Es gibt auch 'notifier_hub', dem wie 'notifier_process' der komplette Status des Antragstellers übergeben wird, aber der Unterschied besteht darin, dass er für jede einzelne Hub- oder eindeutige URL-Zustellung aufgerufen wird und auf den Typ hubloc_network abgestimmt werden kann. Die Hub-Zustellung ist wesentlich effizienter als die Empfängerzustellung, eignet sich aber möglicherweise nicht für alle Protokollstacks.
+
+Ihr Plugin muss den Nachrichtenstatus und die Empfänger verstehen und das gesendete Element in das gewünschte Format übersetzen. Sie müssen auch den Datenschutz prüfen und die Kommunikation an alle außer den vorgesehenen Empfängern blockieren - *wenn* es sich um eine private Kommunikation handelt. Das Plugin wird an dieser Stelle die Nachricht oft in die Warteschlange stellen und die Warteschlangen-ID an den Melder zurückgeben.
+
+Es gibt bereits Warteschlangen-Handler für einfache gepostete Daten. Wenn Sie einen Warteschlangeneintrag vom Typ 'post' erstellen, öffnen wir eine HTTP-POST-Anfrage und senden die bereitgestellten Daten und bestätigen den Erfolg oder Misserfolg. Sie können andere Kommunikationsformen erstellen, indem Sie einen anderen outq_driver-Typ angeben und die Verarbeitung von Warteschlangenanfragen selbst übernehmen. Wenn Sie verschiedene Fehlerzustände oder etwas anderes als Erfolg/Misserfolg bestätigen möchten, ist eine Zustellungsmeldung verfügbar. Für erweiterte Zustellungsberichte ist ebenfalls ein eigener Warteschlangentyp erforderlich. Der Basistyp „post“ behandelt nur Erfolg (erfolgreiche Kommunikation mit der Gegenstelle) und Misserfolg.
+
+
+
+#### Empfang von Nachrichten
+
+Für den Empfang von Nachrichten aus dem entfernten Netz ist wahrscheinlich ein „Empfangs“-Endpunkt oder -Modul erforderlich, der bzw. das für Ihr Netzwerk-Kommunikationsprotokoll vorgesehen ist. Dabei handelt es sich um eine URL-Route, die Ihr Plugin möglicherweise mit dem Hook 'module_loaded' registrieren muss. Ihr Modul übernimmt dann die Verantwortung für den Import aller Daten, die an diesem Endpunkt ankommen, und die Übersetzung in das für dieses Projekt erforderliche Format sowie die Speicherung der resultierenden Daten. Die von uns verwendete Grundstruktur ist ein erweiterbares Activitystream-Element, jedoch mit leicht abweichenden Feldnamen und mehreren optionalen Feldern. Sie kann leicht auf einen Activitystream abgebildet werden. Zusätzliche Daten können in der Tabelle „iconfig“ gespeichert werden. item_store() und item_store_update() werden im Allgemeinen verwendet, um die Daten zu speichern und entsprechende Benachrichtigungen zu senden. Ähnliche Möglichkeiten gibt es für private Post und Profilinformationen. \ No newline at end of file
diff --git a/doc/de/developer/file_system_layout.md b/doc/de/developer/file_system_layout.md
new file mode 100644
index 000000000..ae62791ee
--- /dev/null
+++ b/doc/de/developer/file_system_layout.md
@@ -0,0 +1,20 @@
+### Dateisystem-Layout
+
+| Directory | Description |
+| ------------------------- | ------------------------------------------------------------ |
+| addon | optionale Addons/Plugins |
+| boot.php | Jeder Prozess verwendet dies, um die Anwendungsstruktur zu booten |
+| doc | Hilfedateien |
+| images | erforderliche Bilder |
+| include | Das „Modell“ in MVC - (Back-End-Funktionen), enthält auch PHP „Executables“ für die Hintergrundverarbeitung |
+| index.php | Der Front-End-Controller für den Webzugang |
+| install | Installations- und Upgrade-Dateien und DB-Schema |
+| library | Module von Drittanbietern (müssen lizenzkompatibel sein) |
+| mod | Steuerungsmodule basierend auf URL-Pfadnamen (z.B. http://sitename/foo lädt mod/foo.php) |
+| mod/site/ | Site-spezifische Mod-Overrides, die von Git ausgeschlossen sind |
+| util | Übersetzungstools, Hauptdatenbank für englische Zeichenketten und andere verschiedene Dienstprogramme |
+| version.inc | enthält die aktuelle Version (die automatisch über cron für das Haupt-Repository aktualisiert und über git verteilt wird) |
+| view | Themen- und Sprachdateien |
+| view/(css,js,img,php,tpl) | Standard-Theme-Dateien |
+| view/(en,it,es ...) | Sprachstrings und Ressourcen |
+| view/theme/ | Einzelne benannte Themen, die (css,js,img,php,tpl) Overrides enthalten | \ No newline at end of file
diff --git a/doc/de/developer/git_for_non_developers.md b/doc/de/developer/git_for_non_developers.md
new file mode 100644
index 000000000..3f053b891
--- /dev/null
+++ b/doc/de/developer/git_for_non_developers.md
@@ -0,0 +1,72 @@
+### Git für Nicht-Entwickler
+
+Sie kümmern sich um eine Übersetzung oder tragen zu einem Thema bei, und jedes Mal, wenn Sie eine Pull-Anfrage stellen, müssen Sie mit einem der Entwickler sprechen, bevor Ihre Änderungen eingefügt werden können?
+Wahrscheinlich haben Sie noch keine Kurzanleitung gefunden, die erklärt, wie Sie die Dinge auf Ihrer Seite synchronisieren können. Es ist wirklich sehr einfach.
+Nachdem Sie einen Fork des Repo erstellt haben (klicken Sie einfach auf „fork“ bei github), müssen Sie Ihre eigene Kopie klonen.
+Für das Beispiel nehmen wir an, dass du an einem Thema namens redexample arbeitest (das nicht existiert).
+
+```
+git clone https://github.com/username/red.git
+```
+
+Wechseln Sie anschließend in das Verzeichnis und fügen Sie einen Upstream hinzu.
+
+```
+cd rot
+git remote add upstream https://framagit.org/hubzilla/core/
+```
+
+Von nun an können Sie Änderungen im Upstream mit dem Befehl
+
+```
+git fetch upstream
+```
+
+Bevor Ihre Änderungen automatisch zusammengeführt werden können, müssen Sie oft Änderungen im Upstream zusammenführen.
+
+```
+git merge upstream/master
+```
+
+Sie sollten Upstream-Änderungen immer zusammenführen, bevor Sie sie veröffentlichen, und *Sie müssen* Upstream-Änderungen mit allen Pull-Requests zusammenführen, damit sie automatisch zusammengeführt werden können.
+In 99% der Fälle wird das alles gut gehen. Das einzige Mal, dass es nicht klappt, ist, wenn jemand anderes die gleichen Dateien wie Sie bearbeitet hat - und oft nur, wenn sie die gleichen Zeilen der gleichen Dateien bearbeitet haben. In diesem Fall wäre es ein guter Zeitpunkt, um Hilfe zu bitten, bis Sie den Umgang mit Ihren eigenen Konflikten beim Zusammenführen in den Griff bekommen haben.
+Dann müssen Sie nur noch Ihre Änderungen hinzufügen
+
+```
+git add view/theme/redexample/
+```
+
+Dadurch werden alle Dateien in view/theme/redexample und alle Unterverzeichnisse hinzugefügt. Wenn Ihre speziellen Dateien über den gesamten Code verteilt sind, sollten Sie eine nach der anderen hinzufügen. Versuchen Sie nicht, git add -a auszuführen, da dies alles hinzufügt, einschließlich temporärer Dateien (wir fangen diese meist, aber nicht immer, mit .gitignore ab) und lokaler Änderungen, die Sie haben, aber nicht übertragen wollten.
+Wenn Sie alle Dateien, die Sie geändert haben, hinzugefügt haben, müssen Sie sie übertragen.
+
+```
+git commit
+```
+
+Daraufhin öffnet sich ein Editor, in dem Sie die vorgenommenen Änderungen beschreiben können. Speichern Sie diese Datei und beenden Sie den Editor.
+Zum Schluss pushen Sie die Änderungen auf Ihr eigenes Git
+
+```
+git push
+```
+
+Und das war's, Ihr Projektarchiv ist auf dem neuesten Stand!
+Alles, was Sie jetzt noch tun müssen, ist die Pull-Anfrage zu erstellen. Es gibt zwei Möglichkeiten, dies zu tun.
+Der einfache Weg, wenn Sie Github benutzen, ist, einfach auf den grünen Button oben in Ihrer eigenen Kopie des Repositorys zu klicken, eine Beschreibung der Änderungen einzugeben und auf „create pull request“ zu klicken. Das Haupt-Repository, Themes und Addons haben alle ihren Hauptzweig bei Github, so dass diese Methode meistens verwendet werden kann.
+Die meisten Leute können hier aufhören.
+Einige Projekte in der erweiterten RedMatrix-Ökosphäre haben keine Github-Präsenz, um für diese eine Pull-Anfrage zu stellen, ist es etwas anders - Sie müssen Ihre Pull-Anfrage manuell erstellen. Glücklicherweise ist das nicht viel schwieriger.
+
+```
+git request-pull -p <start> <url>
+```
+
+Start ist der Name eines Commits, mit dem begonnen werden soll. Dieser muss stromaufwärts existieren. Normalerweise wollen Sie nur master.
+URL ist die URL *Ihres* Repos.
+Man kann auch `<end>` angeben. Die Voreinstellung ist HEAD.
+Beispiel:
+
+```
+git request-pull master https://example.com/project
+```
+
+Und senden Sie die Ausgabe einfach an den Projektbetreuer.
diff --git a/doc/de/developer/git_repository.md b/doc/de/developer/git_repository.md
new file mode 100644
index 000000000..653e2f401
--- /dev/null
+++ b/doc/de/developer/git_repository.md
@@ -0,0 +1,6 @@
+### Git-Repository-Zweige
+
+Es gibt zwei offizielle Zweige des Hubzilla-Git-Projektarchivs.
+
+- Die stabile Version wird im **Master-Zweig** gepflegt. Die letzte Übertragung in diesem Zweig gilt als geeignet für Produktions-Hubs.
+- Die experimentelle Entwicklung findet im **dev-Zweig** statt, der in den **master-Zweig** überführt wird, sobald er als getestet und stabil genug erachtet wird. \ No newline at end of file
diff --git a/doc/de/developer/hooks.md b/doc/de/developer/hooks.md
new file mode 100644
index 000000000..16b8672cf
--- /dev/null
+++ b/doc/de/developer/hooks.md
@@ -0,0 +1,698 @@
+### Hooks
+
+Hooks ermöglichen es Plugins/Addons, sich an vielen Stellen in den Code „einzuhaken“ und das Verhalten zu ändern oder anderweitig unabhängige Aktionen durchzuführen, wenn eine Aktivität stattfindet oder auf bestimmte Datenstrukturen zugegriffen wird. Es gibt viele Hooks, die es Ihnen ermöglichen, sich an fast jeder Stelle in die Software einzuklinken und etwas anderes zu tun als das, was standardmäßig vorgesehen ist. Diesen Hooks werden zwei Variablen übergeben. Die erste ist die App-Struktur, die Details über den gesamten Zustand der Seitenanforderung enthält, während wir die resultierende Seite aufbauen. Die zweite ist eindeutig für den spezifischen Hook, der aufgerufen wird, und liefert spezifische Details darüber, was in der Software zum Zeitpunkt des Aufrufs des Hooks passiert.
+
+[Erstellter Index aller Hooks und der Dateien, die sie aufrufen](/help/de/hooks)
+
+[module_mod_aftercontent](/help/de/hook/module_mod_aftercontent)
+Allgemeiner Hook für jedes Modul, ausgeführt nach mod_content(). Ersetzen Sie „module“ durch den Namen des Moduls, z. B. „photos_mod_aftercontent“.
+
+[module_mod_content](/help/de/hook/module_mod_content)
+Allgemeiner Hook für ein beliebiges Modul, wird vor mod_content() ausgeführt. Ersetzen Sie 'module' durch den Modulnamen, z. B. 'photos_mod_content'.
+
+[module_mod_init](/help/de/hook/module_mod_init)
+Allgemeiner Hook für ein beliebiges Modul, wird vor mod_init() ausgeführt. Ersetzen Sie 'module' durch den Modulnamen, z. B. 'photos_mod_init'.
+
+[module_mod_post](/help/de/hook/module_mod_post)
+Allgemeiner Hook für ein beliebiges Modul, der vor mod_post() ausgeführt wird. Ersetzen Sie 'module' durch den Namen des Moduls, z. B. 'photos_mod_post'.
+
+[about_hook](/help/de/hook/about_hook)
+Aufgerufen von der Seite siteinfo
+
+[accept_follow](/help/de/hook/accept_follow)
+Wird aufgerufen, wenn eine Verbindung akzeptiert wird (Freundschaftsanfrage)
+
+[account_downgrade](/help/de/hook/account_downgrade)
+Wird aufgerufen, wenn ein Konto abgelaufen ist, was auf eine mögliche Herabstufung auf die Serviceklasse „basic“ hinweist
+
+[Konto_Einstellungen](/help/de/hook/account_settings)
+Wird bei der Erstellung des Formulars für die Kontoeinstellungen aufgerufen
+
+[account_settings_post](/help/de/hook/account_settings_post)
+Wird bei der Buchung aus dem Kontoeinstellungsformular aufgerufen
+
+[tätigkeit_filter](/help/de/hook/activity_filter)
+Wird bei der Erstellung der Liste der Filter für die Netzwerkseite aufgerufen
+
+[activity_mapper](/help/de/hook/activity_filter)
+Wird bei der Bestimmung der Vorgangsart für die Übertragung aufgerufen.
+
+[activity_decode_mapper](/help/de/hook/activity_filter)
+Wird aufgerufen, wenn die Vorgangsart für die Übertragung bestimmt wird.
+
+[activity_obj_mapper](/help/de/hook/activity_filter)
+Wird aufgerufen, wenn der Objekttyp für die Übertragung bestimmt wird.
+
+[activity_obj_decode_mapper](/help/de/hook/activity_filter)
+Wird bei der Bestimmung des Objekttyps für die Übertragung aufgerufen.
+
+[activity_order](/help/de/hook/activity_order)
+Wird bei der Generierung der Liste der Bestelloptionen für die Netzseite aufgerufen
+
+[addon_app_installed_filter](/help/de/hook/addon_app_installed_filter)
+Wird aufgerufen, wenn festgestellt wird, ob eine addon_app installiert ist
+
+[activity_received](/help/de/hook/activity_received)
+Wird aufgerufen, wenn eine Aktivität (Beitrag, Kommentar, Like, etc.) von einer Nomad-Quelle empfangen wurde
+
+[admin_aside](/help/de/hook/admin_aside)
+Wird aufgerufen, wenn das Seitenleisten-Widget der Verwaltungsseite erzeugt wird
+
+[affinity_labels](/help/de/hook/affinity_labels)
+Wird verwendet, um alternative Beschriftungen für den Affinitätsslider zu generieren.
+
+[api_perm_is_allowed](/help/de/hook/api_perm_is_allowed)
+Wird aufgerufen, wenn perm_is_allowed() von einem API-Aufruf ausgeführt wird.
+
+[app_destroy](/help/de/hook/app_destroy)
+Wird aufgerufen, wenn eine App gelöscht wird.
+
+[app_installed_filter](/help/de/hook/app_installed_filter)
+Wird aufgerufen, wenn festgestellt wird, ob eine App installiert ist
+
+[app_menu](/help/de/hook/app_menu)
+Wird bei der Erstellung des app_menu Dropdowns aufgerufen (kann veraltet sein)
+
+[attach_delete](/help/de/hook/attach_delete)
+Wird aufgerufen, wenn Anhänge aus der Tabelle attach gelöscht werden
+
+[atom_author](/help/de/hook/atom_author)
+Wird aufgerufen, wenn ein Autor- oder Eigentümer-Element für einen Atom ActivityStream-Feed erzeugt wird
+
+[atom_entry](/help/de/hook/atom_entry)
+Wird bei der Erzeugung jedes Eintrags eines Atom ActivityStreams Feeds aufgerufen
+
+[atom_feed](/help/de/hook/atom_feed)
+Wird bei der Generierung eines Atom ActivityStreams Feeds aufgerufen
+
+[atom_feed_end](/help/de/hook/atom_feed_end)
+Wird aufgerufen, wenn die Erzeugung eines Atom ActivityStreams Feeds abgeschlossen ist
+
+[attach_upload_file](/help/de/hook/attach_upload_file)
+Wird beim Hochladen einer Datei aufgerufen
+
+[authentifizieren](/help/de/hook/authenticate)
+Kann alternative Authentifizierungsmechanismen bereitstellen
+
+[author_is_pmable](/help/de/hook/author_is_pmable)
+Wird aus dem Aktionsmenü des Threads aufgerufen, um festzustellen, ob wir dem Verfasser des Beitrags eine private E-Mail schicken können
+
+[bb2diaspora](/help/de/hook/bb2diaspora)
+Wird bei der Umwandlung von bbcode in Markdown aufgerufen
+
+[bbcode](/help/de/hook/bbcode)
+Wird am Ende der Konvertierung von bbcode in HTML aufgerufen
+
+[bbcode_filter](/help/de/hook/bbcode_filter)
+Wird zu Beginn der Umwandlung von bbcode in HTML aufgerufen
+
+[bb_translate_video](/help/de/hook/bb_translate_video)
+Wird aufgerufen, wenn eingebettete Dienste aus bbcode-Videoelementen extrahiert werden (wird selten verwendet)
+
+[build_pagehead](/help/de/hook/build_pagehead)
+Wird bei der Erstellung des HTML-Seitenkopfes aufgerufen
+
+[can_comment_on_post](/help/de/hook/can_comment_on_post)
+Wird aufgerufen, wenn entschieden wird, ob ein Kommentarfeld für einen Beitrag angezeigt werden soll oder nicht
+
+[change_channel](/help/de/hook/change_channel)
+Wird aufgerufen, wenn man sich bei einem Channel anmeldet (entweder während des Logins oder danach über den Channelmanager)
+
+[channel_remove](/help/de/hook/channel_remove)
+Wird aufgerufen, wenn ein Channel entfernt wird
+
+[channel_links](/help/de/hook/channel_links)
+Wird bei der Generierung des Link: HTTP-Header für einen Kanal
+
+[channel_settings](/help/de/hook/channel_settings)
+Wird aufgerufen, wenn die Seite mit den Channel-Einstellungen angezeigt wird
+
+[chat_message](/help/de/hook/chat_message)
+Wird aufgerufen, um eine Chat-Nachricht zu erstellen.
+
+[chat_post](/help/de/hook/chat_post)
+Wird aufgerufen, wenn eine Chat-Nachricht gepostet wurde.
+
+[check_account_email](/help/de/hook/check_account_email)
+Überprüft die bei einer Kontoregistrierung angegebene E-Mail
+
+[check_account_invite](/help/de/hook/check_account_invite)
+Validierung eines Einladungscodes bei der Verwendung von Website-Einladungen
+
+[check_account_password](/help/de/hook/check_account_password)
+Dient der Kontrolle von Kontopasswörtern (Mindestlänge, Einbeziehung von Zeichensätzen usw.)
+
+[check_channelallowed](/help/de/hook/check_channelallowed)
+Wird verwendet, um die Sperrlisten für schwarze und weiße Kanäle außer Kraft zu setzen oder zu umgehen.
+
+[check_siteallowed](/help/de/hook/check_siteallowed)
+Wird verwendet, um die schwarzen/weißen Sperrlisten für Websites außer Kraft zu setzen oder zu umgehen.
+
+[collect_public_recipients](/help/de/hook/collect_public_recipients)
+Wird verwendet, um eine Liste von Empfängern zu erstellen, an die eine öffentliche Nachricht gesendet werden soll.
+
+[comment_buttons](/help/de/hook/comment_buttons)
+Wird aufgerufen, wenn die Bearbeitungsschaltflächen für Kommentare angezeigt werden.
+
+[comments_are_now_closed](/help/de/hook/comments_are_now_closed)
+Wird aufgerufen, wenn entschieden wird, ob ein Kommentarfeld für einen Beitrag angezeigt werden soll oder nicht
+
+[connect_premium](/help/de/hook/connect_premium)
+Wird aufgerufen, wenn eine Verbindung zu einem Premium-Kanal hergestellt wird
+
+[connection_remove](/help/de/hook/connection_remove)
+Wird aufgerufen, wenn eine Verbindung gelöscht/entfernt wird
+
+[connector_settings](/help/de/hook/connector_settings)
+Wird aufgerufen, wenn die Seite mit den Features/Addon-Einstellungen aufgerufen wird
+
+[construct_page](/help/de/hook/construct_page)
+Allgemeiner Hook zur Bereitstellung von Inhalten für bestimmte Seitenbereiche. Wird aufgerufen, wenn die Comanche-Seite erstellt wird.
+
+[kontakt_block_ende](/help/de/hook/contact_block_end)
+Wird bei der Erstellung des „Connections“-Widgets in der Seitenleiste aufgerufen
+
+[kontakt_edit](/help/de/hook/contact_edit)
+Wird bei der Bearbeitung einer Verbindung über connedit aufgerufen
+
+[kontakt_edit_post](/help/de/hook/contact_edit_post)
+Wird aufgerufen, wenn ein Beitrag an connedit gesendet wird
+
+[Kontakt_Auswahl_Optionen](/help/de/hook/contact_select_options)
+Veraltet/unbenutzt
+
+[content_security_policy](/help/de/hook/content_security_policy)
+Wird vor der Ausgabe des Content-Security-Policy-Headers aufgerufen
+
+[conversation_start](/help/de/hook/conversation_start)
+Wird zu Beginn des Renderns einer Konversation (Nachricht oder Nachrichtensammlung oder Stream) aufgerufen
+
+[cover_photo_content_end](/help/de/hook/cover_photo_content_end)
+Wird aufgerufen, nachdem ein Titelbild hochgeladen wurde
+
+[create_identity](/help/de/hook/create_identity)
+Wird bei der Erstellung eines Channels aufgerufen
+
+[cron](/help/de/hook/cron)
+Wird aufgerufen, wenn eine geplante Aufgabe (Poller) ausgeführt wird
+
+[cron_daily](/help/de/hook/cron_daily)
+Wird aufgerufen, wenn täglich geplante Aufgaben ausgeführt werden
+
+[cron_weekly](/help/de/hook/cron_weekly)
+Wird aufgerufen, wenn wöchentlich geplante Aufgaben ausgeführt werden
+
+[crypto_methods](/help/de/hook/crypto_methods)
+Wird aufgerufen, wenn eine Liste von Kryptoalgorithmen in der lokal bevorzugten Reihenfolge erstellt wird
+
+[daemon_addon](/help/de/hook/daemon_addon)
+Wird aufgerufen, wenn der erweiterbare Hintergrund-Daemon aufgerufen wird
+
+[daemon_master_release](/help/de/hook/daemon_master_release)
+Wird zu Beginn der Verarbeitung von \Zotlabs\Daemon\Master::Release() aufgerufen
+
+[directory_item](/help/de/hook/directory_item)
+Wird beim Erzeugen einer Verzeichnisliste für die Anzeige aufgerufen
+
+[discover_channel_webfinger](/help/de/hook/discover_channel_webfinger)
+Wird aufgerufen, wenn ein Webfinger-Lookup durchgeführt wird
+
+[display_item](/help/de/hook/display_item)
+Wird für jedes Element aufgerufen, das in einem Gesprächsfaden angezeigt wird
+
+[display_settings](/help/de/hook/display_settings)
+Wird vom Einstellungsmodul aufgerufen, wenn der Abschnitt 'Anzeigeeinstellungen' angezeigt wird
+
+[display_settings_post](/help/de/hook/display_settings_post)
+Wird aufgerufen, wenn ein Beitrag aus dem Formular „Einstellungen anzeigen“ des Einstellungsmoduls angezeigt wird
+
+[donate_contributors](/help/de/hook/donate_contributors)
+Wird vom 'donate'-Addon aufgerufen, wenn eine Liste von Spendenempfängern erstellt wird
+
+[donate_plugin](/help/de/hook/donate_plugin)
+wird vom 'donate'-Addon aufgerufen
+
+[donate_sponsoren](/help/de/hook/donate_sponsors)
+aufgerufen durch das 'donate'-Addon
+
+[dreport_ist_storable](/help/de/hook/dreport_is_storable)
+wird vor dem Speichern eines Dreport-Datensatzes aufgerufen, um festzustellen, ob er gespeichert werden soll
+
+[dreport_process](/help/de/hook/dreport_process)
+wird für jeden gültigen Lieferbericht aufgerufen
+
+[dropdown_extras](/help/de/hook/dropdown_extras)
+Hinzufügen zusätzlicher Elemente zum Dropdown-Menü, wenn Element/Threads angezeigt werden.
+
+[drop_item](/help/de/hook/drop_item)
+wird aufgerufen, wenn ein 'item' entfernt wird
+
+[encode_object](/help/de/hook/encode_object)
+wird aufgerufen, wenn ein Objekt für die Übertragung kodiert wird.
+
+[enotify](/help/de/hook/enotify)
+wird vor jeder Benachrichtigung aufgerufen
+
+[enotify_mail](/help/de/hook/enotify_mail)
+wird aufgerufen, wenn eine Benachrichtigungs-E-Mail gesendet wird
+
+[enotify_store](/help/de/hook/enotify_store)
+wird beim Speichern eines Benachrichtigungsdatensatzes aufgerufen
+
+[enotify_store_end](/help/de/hook/enotify_store_end)
+wird aufgerufen, nachdem ein Benachrichtigungsdatensatz gespeichert wurde
+
+[event_created](/help/de/hook/event_created)
+wird aufgerufen, wenn ein Ereignisdatensatz erstellt wird
+
+[event_store_event](/help/de/hook/event_store_event)
+wird aufgerufen, wenn ein Ereignisdatensatz erstellt oder aktualisiert wird
+
+[event_updated](/help/de/hook/event_updated)
+wird aufgerufen, wenn ein Ereignisdatensatz geändert wird
+
+[externals_url_select](/help/de/hook/externals_url_select)
+wird aufgerufen, wenn eine Liste mit zufälligen Websites erstellt wird, von denen öffentliche Beiträge abgerufen werden sollen
+
+[feature_enabled](/help/de/hook/feature_enabled)
+wird aufgerufen, wenn 'feature_enabled()' verwendet wird
+
+[merkmal_settings](/help/de/hook/feature_settings)
+wird von der Einstellungsseite aufgerufen, wenn man 'addon/feature settings' besucht
+
+[feature_settings_post](/help/de/hook/feature_settings_post)
+wird von der Einstellungsseite aufgerufen, wenn von 'addon/feature settings' aus gepostet wird
+
+[fetch_and_store](/help/de/hook/fetch_and_store)
+wird aufgerufen, um das Filtern von 'entschlüsselten' Elementen vor der Speicherung zu ermöglichen.
+
+[file_thumbnail](/help/de/hook/file_thumbnail)
+wird aufgerufen, wenn Miniaturbilder für die Wolkenseite im Modus „Kacheln anzeigen“ erzeugt werden
+
+[folgen](/help/de/hook/follow)
+wird aufgerufen, wenn eine Follow-Operation stattfindet
+
+[follow_from_feed](/help/de/hook/follow_from_feed)
+wird aufgerufen, wenn eine Follow-Operation in einem RSS-Feed stattfindet
+
+[follow_allow](/help/de/hook/follow_allow)
+wird aufgerufen, bevor die Ergebnisse einer Follow-Operation gespeichert werden
+
+[gender_selector](/help/de/hook/gender_selector)
+wird bei der Erstellung der Dropdown-Liste „Geschlecht“ aufgerufen (erweitertes Profil)
+
+[gender_selector_min](/help/de/hook/gender_selector_min)
+wird bei der Erstellung der Dropdown-Liste „Geschlecht“ aufgerufen (normales Profil)
+
+[generate_map](/help/de/hook/generate_map)
+wird aufgerufen, um den HTML-Code für die Anzeige eines Orts auf der Karte nach Koordinaten zu erzeugen
+
+[generate_named_map](/help/de/hook/generate_named_map)
+wird aufgerufen, um die HTML-Datei für die Anzeige eines Kartenorts anhand eines Textes zu erzeugen
+
+[get_all_api_perms](/help/de/hook/get_all_api_perms)
+Wird aufgerufen, wenn die Berechtigungen für API-Verwendungen abgerufen werden
+
+[get_all_perms](/help/de/hook/get_all_perms)
+wird aufgerufen, wenn get_all_perms() verwendet wird
+
+[get_best_language](/help/de/hook/get_best_language)
+wird aufgerufen, wenn die bevorzugte Sprache für die Seite ausgewählt wird
+
+[get_default_export_sections](/help/de/hook/get_default_export_sections)
+Wird aufgerufen, um die Standardliste der zu exportierenden Funktionsdatengruppen in identity_basic_export() zu erhalten
+
+[get_features](/help/de/hook/get_features)
+Wird aufgerufen, wenn get_features() aufgerufen wird
+
+[get_photo](/help/de/hook/get_photo)
+Wird aufgerufen, wenn Fotoinhalte (außer Profilfotos) in mod_photo abgerufen werden
+
+[get_profile_photo](/help/de/hook/get_profile_photo)
+Wird aufgerufen, wenn der Inhalt des lokalen Profilfotos in mod_photo abgerufen wird
+
+[get_role_perms](/help/de/hook/get_role_perms)
+Wird aufgerufen, wenn get_role_perms() aufgerufen wird, um Berechtigungen für benannte Berechtigungsrollen zu erhalten
+
+[global_permissions](/help/de/hook/global_permissions)
+Wird aufgerufen, wenn die globale Berechtigungsliste erstellt wird
+
+[home_content](/help/de/hook/home_content)
+Wird von mod_home aufgerufen, um den Inhalt der Home-Seite zu ersetzen
+
+[home_init](/help/de/hook/home_init)
+Wird von der Funktion home_init() der Homepage aufgerufen
+
+[hostxrd](/help/de/hook/hostxrd)
+Wird bei der Erzeugung von .well-known/hosts-meta für „old webfinger“ aufgerufen (wird vom Diaspora-Protokoll verwendet)
+
+[html2bb_video](/help/de/hook/html2bb_video)
+Wird aufgerufen, wenn die html2bbcode-Übersetzung verwendet wird, um eingebettete Medien zu behandeln
+
+[html2bbcode](/help/de/hook/html2bbcode)
+Wird bei der Verwendung der html2bbcode-Übersetzung aufgerufen
+
+[identität_basic_export](/help/de/hook/identity_basic_export)
+Wird aufgerufen, wenn die Basisinformationen eines Channels zur Sicherung oder Übertragung exportiert werden.
+
+[import_autor_xchan](/help/de/hook/import_author_xchan)
+Wird aufgerufen, wenn ein Autor eines Beitrags mit xchan_hash gesucht wird, um sicherzustellen, dass er einen xchan-Eintrag auf unserer Website hat
+
+[import_channel](/help/de/hook/import_channel)
+Wird aufgerufen, wenn ein Kanal aus einer Datei oder einer API-Quelle importiert wird
+
+[import_directory_profile](/help/de/hook/import_directory_profile)
+Wird aufgerufen, wenn die Lieferung einer Profilstruktur aus einer externen Quelle verarbeitet wird (normalerweise für die Speicherung in Verzeichnissen)
+
+[import_xchan](/help/de/hook/import_xchan)
+Wird bei der Verarbeitung des Ergebnisses von zot_finger() aufgerufen, um das Ergebnis zu speichern
+
+[item_photo_menu](/help/de/hook/item_photo_menu)
+Wird aufgerufen, wenn die Liste der Aktionen erzeugt wird, die mit einem angezeigten Konversationselement verbunden sind
+
+[item_store](/help/de/hook/item_store)
+Wird aufgerufen, wenn item_store() einen Datensatz vom Typ item speichert
+
+[item_stored](/help/de/hook/item_stored)
+Wird aufgerufen, nachdem item_store() einen Datensatz des Typs item in der Datenbank gespeichert hat.
+
+[item_custom](/help/de/hook/item_custom)
+Wird aufgerufen, bevor item_store() einen Datensatz des Typs item speichert (damit Addons ITEM_TYPE_CUSTOM-Elemente verarbeiten können).
+
+[item_store_update](/help/de/hook/item_store_update)
+Wird aufgerufen, wenn item_store_update() aufgerufen wird, um einen gespeicherten Eintrag zu aktualisieren.
+
+[item_stored_update](/help/de/hook/item_stored_update)
+Wird aufgerufen, nachdem item_store_update() ein gespeichertes Element aktualisiert hat.
+
+[item_translate](/help/de/hook/item_translate)
+Wird von item_store und item_store_update aufgerufen, nachdem die Sprache des Beitrags automatisch erkannt wurde.
+
+[jot_networks](/help/de/hook/jot_networks)
+Wird aufgerufen, um die Liste der zusätzlichen Post-Plugins zu generieren, die aus dem ACL-Formular aktiviert werden sollen
+
+[jot_tool](/help/de/hook/jot_tool)
+Veraltet und möglicherweise überflüssig. Ermöglicht das Hinzufügen von Aktionsschaltflächen zum Beitragseditor.
+
+[jot_tpl_filter](/help/de/hook/jot_tpl_filter)
+Wird aufgerufen, um Vorlagenvariablen vor der Ersetzung in jot.tpl zu filtern.
+
+[jot_header_tpl_filter](/help/de/hook/jot_header_tpl_filter)
+Wird aufgerufen, um Vorlagenvariablen vor der Ersetzung in jot_header.tpl zu filtern.
+
+[legal_webbie](/help/de/hook/legal_webbie)
+Wird aufgerufen, um eine Kanaladresse zu validieren
+
+[legal_webbie_text](/help/de/hook/legal_webbie_text)
+Bietet eine Erklärung der Text-/Zeichenbeschränkungen für legal_webbie()
+
+[load_pdl](/help/de/hook/load_pdl)
+Wird aufgerufen, wenn wir eine PDL-Datei oder eine Beschreibung laden
+
+[local_dir_update](/help/de/hook/local_dir_update)
+Wird aufgerufen, wenn eine Verzeichnisaktualisierung von einem Channel auf dem Verzeichnisserver verarbeitet wird
+
+[location_move](/help/de/hook/location_move)
+Wird aufgerufen, wenn einem UNO-Channel ein neuer Standort mitgeteilt wurde (was auf eine Verschiebung und nicht auf einen Klon hinweist)
+
+[protokolliert](/help/de/hook/logged_in)
+Wird aufgerufen, wenn die Authentifizierung auf irgendeine Weise erfolgreich war
+
+[Logger](/help/de/hook/logger)
+Wird aufgerufen, wenn ein Eintrag in die Logdatei der Anwendung gemacht wird
+
+[logging_out](/help/de/hook/logging_out)
+Wird bei der Abmeldung aufgerufen
+
+[login_hook](/help/de/hook/login_hook)
+Wird bei der Generierung des Anmeldeformulars aufgerufen
+
+[magic_auth](/help/de/hook/magic_auth)
+Wird bei der Verarbeitung einer magic-auth-Sequenz aufgerufen
+
+[markdown_to_bb](/help/de/hook/markdown_to_bb)
+Wird bei der Verarbeitung der Markdown-Konvertierung aufgerufen
+
+[match_webfinger_location](/help/de/hook/match_webfinger_location)
+Wird bei der Verarbeitung von Webfinger-Anfragen aufgerufen
+
+[magic_auth_openid_success](/help/de/hook/magic_auth_openid_success)
+Wird aufgerufen, wenn ein magic-auth aufgrund von openid-Anmeldedaten erfolgreich war
+
+[magic_auth_success](/help/de/hook/magic_auth_success)
+Wird aufgerufen, wenn ein magic-auth erfolgreich war
+
+[main_slider](/help/de/hook/main_slider)
+Wird bei der Generierung des Affinitätswerkzeugs aufgerufen
+
+[marital_selector](/help/de/hook/marital_selector)
+Wird aufgerufen, wenn die Auswahlliste für das Dropdown-Menü des Profils „Familienstand“ erstellt wird (erweitertes Profil)
+
+[marital_selector_min](/help/de/hook/marital_selector_min)
+Wird bei der Erstellung der Auswahlliste für das Dropdown-Profil „Familienstand“ aufgerufen (normales Profil)
+
+[module_loaded](/help/de/hook/module_loaded)
+Wird aufgerufen, wenn ein Modul erfolgreich für eine URL-Anfrage auf dem Server lokalisiert wurde.
+
+[mood_verbs](/help/de/hook/mood_verbs)
+Wird bei der Erstellung der Liste der Stimmungen aufgerufen
+
+[nav](/help/de/hook/nav)
+Wird bei der Erstellung der Navigationsleiste aufgerufen
+
+[network_content_init](/help/de/hook/network_content_init)
+Wird beim Laden des Inhalts für die Netzwerkseite aufgerufen
+
+[netzwerk_ping](/help/de/hook/network_ping)
+Wird bei einer Ping-Anfrage aufgerufen
+
+[netzwerk_zu_name](/help/de/hook/network_to_name)
+Veraltet
+
+[notifier_end](/help/de/hook/notifier_end)
+Wird aufgerufen, wenn eine Zustellschleife abgeschlossen ist
+
+[notifier_hub](/help/de/hook/notifier_hub)
+Wird aufgerufen, wenn ein Hub zugestellt wurde
+
+[notifier_normal](/help/de/hook/notifier_normal)
+Wird aufgerufen, wenn der Notifizierer für eine 'normale' Zustellung aufgerufen wird
+
+[notifier_process](/help/de/hook/notifier_process)
+Wird aufgerufen, wenn der Notifizierende eine Nachricht/Ereignis verarbeitet
+
+[obj_verbs](/help/de/hook/obj_verbs)
+Wird bei der Erstellung der Liste der für das Profil „Dinge“ verfügbaren Verben aufgerufen.
+
+[oembed_action](/help/de/hook/oembed_action)
+Wird aufgerufen, wenn entschieden wird, ob eine Oembed-Url gefiltert, blockiert oder genehmigt werden soll
+
+[oembed_probe](/help/de/hook/oembed_probe)
+Wird aufgerufen, wenn eine Suche nach Oembed-Inhalten durchgeführt wird.
+
+[other_encapsulate](/help/de/hook/other_encapsulate)
+Wird aufgerufen, wenn Inhalte verschlüsselt werden, für die der Algorithmus unbekannt ist (siehe auch crypto_methods)
+
+[other_unencapsulate](/help/de/hook/other_unencapsulate)
+Wird aufgerufen, wenn Inhalte entschlüsselt werden, deren Algorithmus unbekannt ist (siehe auch crypto_methods)
+
+[page_content_top](/help/de/hook/page_content_top)
+Wird aufgerufen, wenn wir eine Webseite generieren (vor dem Aufruf der Modul-Content-Funktion)
+
+[page_end](/help/de/hook/page_end)
+Wird aufgerufen, nachdem wir den Seiteninhalt generiert haben
+
+[page_header](/help/de/hook/page_header)
+Wird bei der Generierung der Navigationsleiste aufgerufen
+
+[page_meta](/help/de/hook/page_header)
+Wird bei der Generierung der Metadaten im Seitenkopf aufgerufen.
+
+[parse_atom](/help/de/hook/parse_atom)
+Wird aufgerufen, wenn ein Atom/RSS-Feed-Element geparst wird.
+
+[parse_link](/help/de/hook/parse_link)
+Wird aufgerufen, wenn eine URL abgefragt wird, um daraus einen Beitrag zu generieren
+
+[pdl_selector](/help/de/hook/pdl_selector)
+Wird bei der Erstellung einer Layoutauswahl in einem Formular aufgerufen
+
+[perm_is_allowed](/help/de/hook/perm_is_allowed)
+Wird während perm_is_allowed() aufgerufen, um festzustellen, ob eine Berechtigung für diesen Kanal und Beobachter erlaubt ist
+
+[permissions_create](/help/de/hook/permissions_create)
+Wird aufgerufen, wenn ein Bucheintrag (Verbindung) erstellt wird
+
+[permissions_update](/help/de/hook/permissions_update)
+Wird aufgerufen, wenn eine Berechtigungsaktualisierung übertragen wird
+
+[permit_hook](/help/de/hook/permit_hook)
+Wird aufgerufen, bevor ein registrierter Hook tatsächlich ausgeführt wird, um festzustellen, ob er erlaubt oder blockiert werden soll
+
+[personal_xrd](/help/de/hook/personal_xrd)
+Wird bei der Generierung der persönlichen XRD für „old webfinger“ (Diaspora) aufgerufen
+
+[photo_post_end](/help/de/hook/photo_post_end)
+Wird nach dem Hochladen eines Fotos aufgerufen
+
+[photo_upload_begin](/help/de/hook/photo_upload_begin)
+Wird aufgerufen, wenn versucht wird, ein Foto hochzuladen
+
+[photo_upload_end](/help/de/hook/photo_upload_end)
+Wird aufgerufen, wenn ein Foto-Upload verarbeitet wurde
+
+[photo_upload_file](/help/de/hook/photo_upload_file)
+Wird aufgerufen, um alternative Dateinamen für einen Upload zu generieren
+
+[photo_upload_form](/help/de/hook/photo_upload_form)
+Wird aufgerufen, wenn ein Foto-Upload-Formular generiert wird
+
+[photo_view_filter](/help/de/hook/photo_view_filter)
+Wird aufgerufen, bevor die Daten an die photo_view-Vorlage übergeben werden
+
+[poke_verbs](/help/de/hook/poke_verbs)
+Wird bei der Erstellung der Liste der Aktionen für das Modul „poke“ aufgerufen
+
+[post_local](/help/de/hook/post_local)
+Wird aufgerufen, wenn ein Artikel auf diesem Rechner über mod/item.php eingestellt wurde (auch über API)
+
+[post_local_end](/help/de/hook/post_local_end)
+Wird aufgerufen, wenn ein lokaler Postvorgang abgeschlossen ist
+
+[post_local_start](/help/de/hook/post_local_start)
+Wird aufgerufen, wenn ein lokaler Postvorgang beginnt
+
+[post_mail](/help/de/hook/post_mail)
+Wird aufgerufen, wenn eine Mail-Nachricht verfasst wurde
+
+[post_mail_end](/help/de/hook/post_mail_end)
+Wird aufgerufen, wenn eine Mail-Nachricht zugestellt wurde
+
+[post_remote](/help/de/hook/post_remote)
+Wird aufgerufen, wenn eine Aktivität von einem anderen Standort eintrifft
+
+[post_remote_end](/help/de/hook/post_remote_end)
+Wird nach der Verarbeitung einer Remote-Post aufgerufen
+
+[post_remote_update](/help/de/hook/post_remote_update)
+Wird bei der Verarbeitung eines entfernten Beitrags aufgerufen, der eine Bearbeitung oder Aktualisierung beinhaltet
+
+[post_remote_update_end](/help/de/hook/post_remote_update_end)
+Wird nach der Verarbeitung eines entfernten Beitrags aufgerufen, der eine Bearbeitung oder Aktualisierung beinhaltete
+
+[prepare_body](/help/de/hook/prepare_body)
+Wird aufgerufen, wenn der HTML-Code für ein angezeigtes Konversationsobjekt generiert wird.
+
+[prepare_body_final](/help/de/hook/prepare_body_final)
+Wird nach der Generierung des HTML für ein angezeigtes Konversationselement aufgerufen
+
+[prepare_body_init](/help/de/hook/prepare_body_init)
+Wird vor der Generierung des HTML für ein angezeigtes Konversationselement aufgerufen
+
+[privacygroup_extras](/help/de/hook/privacygroup_extras)
+Wird vor der Generierung des HTML für die Bearbeitungsoptionen der Privacy Group aufgerufen
+
+[privacygroup_extras_delete](/help/de/hook/privacygroup_extras_delete)
+Wird aufgerufen, nachdem die Privatsphärengruppe gelöscht wurde.
+
+[privacygroup_extras_post](/help/de/hook/privacygroup_extras_post)
+Wird aufgerufen, wenn das Formular zur Bearbeitung der Privatsphärengruppe abgeschickt wird.
+
+[proc_run](/help/de/hook/proc_run)
+Wird beim Aufruf von PHP-Unterprozessen aufgerufen
+
+[process_channel_sync_delivery](/help/de/hook/process_channel_sync_delivery)
+Wird aufgerufen, wenn ein 'Sync-Paket' mit Struktur- und Tabellenaktualisierungen von einem Channel-Clone angenommen wird.
+
+[profile_advanced](/help/de/hook/profile_advanced)
+Wird bei der Generierung einer erweiterten Profilseite aufgerufen
+
+[profil_edit](/help/de/hook/profile_edit)
+Wird bei der Bearbeitung eines Profils aufgerufen
+
+[profil_foto_inhalt_ende](/help/de/hook/profile_photo_content_end)
+Wird bei der Änderung eines Profilfotos aufgerufen
+
+[profil_post](/help/de/hook/profile_post)
+Wird beim Posten eines bearbeiteten Profils aufgerufen
+
+[profile_sidebar](/help/de/hook/profile_sidebar)
+Wird aufgerufen, wenn die „Kanal-Seitenleiste“ oder das Miniprofil erstellt wird
+
+[profile_sidebar_enter](/help/de/hook/profile_sidebar_enter)
+Wird vor der Erstellung der 'Channel Sidebar' oder des Miniprofils aufgerufen
+
+[queue_deliver](/help/de/hook/queue_deliver)
+Wird aufgerufen, wenn eine Nachricht in der Warteschlange zugestellt wird
+
+[register_account](/help/de/hook/register_account)
+Wird aufgerufen, wenn ein Konto erstellt worden ist
+
+[render_location](/help/de/hook/render_location)
+Wird aufgerufen, um eine ineraktive Inline-Map zu erzeugen
+
+[replace_macros](/help/de/hook/replace_macros)
+Wird vor dem Aufrufen des Vorlagenprozessors aufgerufen
+
+[reverse_magic_auth](/help/de/hook/reverse_magic_auth)
+Wird vor dem Aufruf von reverse magic auth aufgerufen, um Sie auf Ihre eigene Website zu schicken, damit Sie sich auf dieser Website authentifizieren können
+
+[Einstellungen_Konto](/help/de/hook/settings_account)
+Wird bei der Erstellung des Formulars für die Kontoeinstellungen aufgerufen
+
+[einstellungen_form](/help/de/hook/settings_form)
+Wird bei der Erstellung des Formulars für die Channel-Einstellungen aufgerufen
+
+[einstellungen_post](/help/de/hook/settings_post)
+Wird bei der Buchung aus dem Formular für die Kanaleinstellungen aufgerufen
+
+[sexpref_selector](/help/de/hook/sexpref_selector)
+Wird aufgerufen, wenn ein Dropdown-Menü für sexuelle Präferenzen erstellt wird (erweitertes Profil)
+
+[sexpref_selector_min](/help/de/hook/sexpref_selector_min)
+Wird aufgerufen, wenn eine Auswahlliste der sexuellen Präferenzen erstellt wird (normales Profil)
+
+[smilie](/help/de/hook/smilie)
+Wird beim Übersetzen von Emoticons aufgerufen
+
+[status_editor](/help/de/hook/status_editor)
+Wird bei der Erstellung des status_editor aufgerufen.
+
+[stream_item](/help/de/hook/stream_item)
+Wird für jedes Element aufgerufen, das über conversation() zur Anzeige gerendert wird
+
+[system_app_installed_filter](/help/de/hook/system_app_installed_filter)
+Wird aufgerufen, wenn festgestellt wird, ob eine System-App installiert ist.
+
+[tagged](/help/de/hook/tagged)
+Wird aufgerufen, wenn eine Lieferung verarbeitet wird, die dazu führt, dass Sie getaggt werden
+
+[thumbnail](/help/de/hook/thumbnail)
+Wird beim Erzeugen von Miniaturbildern für die Kachelansicht des Cloud-Speichers aufgerufen
+
+[update_unseen](/help/de/hook/update_unseen)
+Wird vor dem automatischen Markieren von im Browser geladenen Sendungen, die gesehen wurden, aufgerufen
+
+[validate_channelname](/help/de/hook/validate_channelname)
+Wird verwendet, um die von einem Kanal verwendeten Namen zu validieren
+
+[webfinger](/help/de/hook/webfinger)
+Wird beim Besuch des Dienstes webfinger (RFC7033) aufgerufen
+
+[well_known](/help/de/hook/well_known)
+Wird beim Zugriff auf die speziellen '.well-known'-Site-Adressen aufgerufen
+
+[wiki_preprocess](/help/de/hook/wiki_preprocess)
+Wird aufgerufen, bevor Markdown-/Bbcode-Prozessoren für Wikiseiten ausgeführt werden
+
+[zot_best_algorithm](/help/de/hook/zot_best_algorithm)
+Wird bei der Aushandlung von Verschlüsselungsalgorithmen mit entfernten Sites aufgerufen
+
+[zid](/help/de/hook/zid)
+Wird aufgerufen, wenn die zid des Beobachters zu einer URL hinzugefügt wird
+
+[zid_init](/help/de/hook/zid_init)
+Wird bei der Authentifizierung eines Besuchers aufgerufen, der zid verwendet hat
+
+[zot_finger](/help/de/hook/zot_finger)
+Wird aufgerufen, wenn ein Nomad-Infopaket angefordert wurde (dies ist unser Webfinger-Erkennungsmechanismus)
diff --git a/doc/de/developer/licensing.md b/doc/de/developer/licensing.md
new file mode 100644
index 000000000..b7ff16748
--- /dev/null
+++ b/doc/de/developer/licensing.md
@@ -0,0 +1,3 @@
+### Lizenzvergabe
+
+Der gesamte zum Projekt beigetragene Code unterliegt der MIT-Lizenz, sofern nicht anders angegeben. Wir akzeptieren Code von Drittanbietern, der unter MIT, BSD und LGPL fällt, aber Copyleft-Lizenzen (GPL und AGPL) sind nur in Addons erlaubt. Es muss möglich sein, den GPL-(Copyleft-)Code vollständig aus dem Hauptprojekt zu entfernen, ohne etwas zu zerstören. \ No newline at end of file
diff --git a/doc/de/developer/magic_auth.md b/doc/de/developer/magic_auth.md
new file mode 100644
index 000000000..0625fb983
--- /dev/null
+++ b/doc/de/developer/magic_auth.md
@@ -0,0 +1,49 @@
+### Magic Auth
+
+Der so genannte „magic auth“ erfolgt durch einen speziellen Austausch. Auf dem entfernten Computer wird eine Umleitung zum Nomad-Endpunkt mit speziellen GET-Parametern vorgenommen.
+
+Endpunkt: [https://example.com/post/name](https://example.com/post/name?f=&zid=pepecyb@hub.hubzilla.hu)
+
+wobei 'name' die linke Seite des Kanal-Webbies ist, zum Beispiel 'mike', wenn der Webbie 'mike@zothub.com' ist.
+
+Zusätzlich werden vier Parameter übergeben:
+
+- auth => die Webbie der Person, die den Zugang beantragt
+- dest => die gewünschte Ziel-URL (urlencodiert)
+- sec => eine zufällige Zeichenkette, die auch lokal gespeichert wird, um sie während der Verifizierungsphase zu verwenden.
+- version => die Zot-Revision
+
+Wenn dieses Paket empfangen wird, wird eine Nomad-Nachricht an die auth-Identität gesendet:
+
+```
+ {
+ "type":"auth_check",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ },
+ "recipients":{
+ {
+ "guid":"ZHSqb3yGar3TYV_o9S-JkD-6o_V4DhUcxtv0VeyX8Kj_ENHPI_M3SyAUucU835-mIayGMmTpqJz3ujPkcavVhA",
+ "guid_sig":"JsAAXigNghTkkbq8beGMJjj9LBKZn28hZ-pHSsoQuwYWvBJ2lSnfc4r9l--WgO6sucH-SR6RiBo78eWn1cZrh_cRMu3x3LLx4y-tjixg-oOOgtZakkBg4vmOhkKPkci0mFtzvUrpY4YHySqsWTuPwRx_vOlIYIGEY5bRXpnkNCoC8J4EJnRucDrgSARJvA8QQeQQL0H4mWEdGL7wlsZp_2VTC6nEMQ48Piu6Czu5ThvLggGPDbr7PEMUD2cZ0jE4SeaC040REYASq8IdXIEDMm6btSlGPuskNh3cD0AGzH2dMciFtWSjmMVuxBU59U1I6gHwcxYEV6BubWt_jQSfmA3EBiPhKLyu02cBMMiOvYIdJ3xmpGoMY1Cn__vhHnx_vEofFOIErb6nRzbD-pY49C28AOdBA5ffzLW3ss99d0A-6GxZmjsyYhgJu4tFUAa7JUl84tMbq28Tib0HW6qYo6QWw8K1HffxcTpHtwSL5Ifx0PAoGMJsGDZDD1y_r9a4vH5pjqmGrjL3RXJJUy-m4eLV5r7xMWXsxjqu3D8r04_dcw4hwwexpMT1Nwf8CTB0TKb8ElgeOpDFjYVgrqMYWP0XdhatcFtAJI7gsS-JtzsIwON9Kij66-VAkqy_z1IXI0ziyqV1yapSVYoUV1vMScRZ_nMqwiB5rEDx-XLfzko"
+ }
+ }
+ "callback":"\/post",
+ "version":1,
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"eKV968b1sDkOVdSMi0tLRtOhQw4otA8yFKaVg6cA4I46_zlAQGbFptS-ODiZlSAqR7RiiZQv4E2uXCKar52dHo0vvNKbpOp_ezWYcwKRu1shvAlYytsflH5acnDWL-FKOOgz5zqLLZ6cKXFMoR1VJGG_Od-DKjSwajyV9uVzTry58Hz_w0W2pjxwQ-Xv11rab5R2O4kKSW77YzPG2R5E6Q7HN38FrLtyWD_ai3K9wlsFOpwdYC064dk66X7BZtcIbKtM6zKwMywcfJzvS5_0U5yc5GGbIY_lY6SViSfx9shOKyxkEKHfS29Ynk9ATYGnwO-jnlMqkJC7t149H-sI9hYWMkLuCzaeLP56k2B2TmtnYvE_vHNQjzVhTwuHCIRVr-p6nplQn_P3SkOpYqPi3k_tnnOLa2d3Wtga8ClEY90oLWFJC3j2UkBf_VEOBNcg-t5XO3T-j9O4Sbk96k1Qoalc-QlznbGx4bOVsGkRBBMiH4YUqiiWB_OkFHtdqv7dqGeC-u-B4u9IxzYst46vvmyA3O-Q4APSZ1RY8ITUH0jLTbh6EAV7Oki8pIbOg0t56p-8RlanOZqmFvR-grVSc7Ak1ZcD8NACmvidUpa1B7WEvRcOeffx9lype0bt5XenDnMyx6szevwxZIiM8qGM2lsSk4fu8HI9cW0mLywzZT0"
+ }
+```
+
+auth_check-Nachrichten MÜSSEN verschlüsselt werden. Diese Nachricht wird an die Herkunftsseite gesendet, die prüft, ob das „secret“ mit dem „sec“ übereinstimmt, das sie ursprünglich übermittelt hat. Sie prüft auch secret_sig, das mit dem privaten Schlüssel des Zielkanals signiert und mit base64url kodiert ist. Wenn alles in Ordnung ist, wird ein json-Paket zurückgegeben:
+
+```
+ {
+ "success":1,
+ "confirm":"q0Ysovd1uQRsur2xG9Tg6bC23ynzw0191SkVd7CJcYoaePy6e_v0vnmPg2xBUtIaHpx_aSuhgAkd3aVjPeaVBmts6aakT6a_yAEy7l2rBydntu2tvrHhoVqRNOmw0Q1tI6hwobk1BgK9Pm0lwOeAo8Q98BqIJxf47yO9pATa0wktOg6a7LMogC2zkkhwOV5oEqjJfeHeo27TiHr1e2WaphfCusjmk27V_FAYTzw05HvW4SPCx55EeeTJYIwDfQwjLfP4aKV-I8HQCINt-2yxJvzH7Izy9AW-7rYU0Il_gW5hrhIS5MTM12GBXLVs2Ij1CCLXIs4cO0x6e8KEIKwIjf7iAu60JPmnb_fx4QgBlF2HLw9vXMwZokor8yktESoGl1nvf5VV5GHWSIKAur3KPS2Tb0ekNh-tIk9u-xob4d9eIf6tge_d3aq1LcAtrDBDLk8AD0bho5zrVuTmZ9k-lBVPr_DRHSV_dlpu088j3ThaBsuV1olHK3vLFRhYCDIO0CqqK5IuhqtRNnRaqhlNN6fQUHpXk2SwHiJ2W36RCYMTnno6ezFk_tN-RA2ly-FomNZoC5FPA9gFwoJR7ZmVFDmUeK3bW-zYTA5vu15lpBPnt7Up_5rZKkr0WQVbhWJmylqOuwuNWbn3SrMQ8rYFZ23Tv300cOfKVgRBaePWQb4"
+ }
+```
+
+"confirm“ ist in diesem Fall die base64url-kodierte RSA-Signatur der Verkettung von ‚secret‘ mit dem base64url-kodierten Whirlpool-Hash der Quellguid und guid_sig; signiert mit dem privaten Schlüssel des Quellkanals. Dies verhindert, dass ein Manin-the-Middle ein betrügerisches Erfolgspaket einfügt. Nach Empfang und erfolgreicher Überprüfung dieses Pakets wird die Zielseite zur ursprünglichen Ziel-URL umgeleitet und zeigt eine erfolgreiche Fernanmeldung an. \ No newline at end of file
diff --git a/doc/de/developer/nomad_protocol.md b/doc/de/developer/nomad_protocol.md
new file mode 100644
index 000000000..67252eddf
--- /dev/null
+++ b/doc/de/developer/nomad_protocol.md
@@ -0,0 +1,47 @@
+### Das Nomad Protokoll
+
+#### Was ist Nomad?
+
+Nomad ist das revolutionäre Protokoll, das Hubzilla antreibt und die **Kommunikation**, das **Identitätsmanagement** und die **Zugangskontrolle** in einem vollständig **dezentralisierten** Netzwerk unabhängiger Websites, oft als „das Grid“ bezeichnet, ermöglicht. Die daraus resultierende Plattform ist ein robustes System, das den Datenschutz und die Sicherheit unterstützt und gleichzeitig die Art von umfangreichen Webdiensten ermöglicht, die normalerweise nur in zentralisierten, proprietären Lösungen zu finden sind.
+
+Betrachten Sie dieses typische Szenario:
+
+Jaquelina möchte Fotos von ihrem Blog unter **jaquelina.org** mit Roberto teilen, aber mit niemandem sonst. Roberto unterhält seinen eigenen Familien-Hub unter **roberto.net** auf einem völlig unabhängigen Server. Nomad erlaubt Jaquelina, ihre Fotos mit einer *Zugriffskontrollliste (ACL)* zu veröffentlichen, die nur Roberto einschließt. Das bedeutet, dass Roberto die Fotos zwar sehen kann, wenn er ihren Blog besucht, sein Bruder Marco jedoch nicht, und auch kein anderes Familienmitglied, das ein Konto auf **roberto.net** hat.
+
+Der Clou an diesem Szenario ist die Tatsache, dass Roberto sich nie auf Jaquelinas Website angemeldet hat. Stattdessen musste er sich nur einmal mit seinem Passwort auf seiner *eigenen* Website **roberto.net** anmelden. Wenn Roberto **jaquelina.org** besucht, wird er von ihrem Hub nahtlos authentifiziert, indem sein Server im Hintergrund abgefragt wird.
+
+Es ist nicht ungewöhnlich, dass Server technische Probleme haben oder aus verschiedenen Gründen unzugänglich werden. Nomad bietet Robustheit für Robertos Online-Aktivitäten, indem es ihm erlaubt, *Klone* seiner Online-Identität oder seines *Kanals* auf mehreren unabhängigen Hubs zu haben. Stellen Sie sich vor, dass Robertos Server aus irgendeinem Grund zusammenbricht und er sich dort nicht mehr einloggen kann. Er meldet sich einfach bei einem seiner Klone auf **gadfly.com** an, einer Website, die von seinem Freund Peter betrieben wird. Sobald er sich bei **gadfly.com** authentifiziert hat, kann Roberto Jaquelinas Blog wie zuvor ansehen, ohne dass Jaquelina zusätzlichen Zugang gewähren muss!
+
+#### Kommunikation
+
+Kommunikation und soziale Netzwerke sind ein wesentlicher Bestandteil des Grids. Jeder Kanal (und jeder Dienst, der von diesem Kanal bereitgestellt wird) kann die funktionsreiche soziale Kommunikation auf globaler Ebene in vollem Umfang nutzen. Diese Kommunikation kann öffentlich oder privat sein - und private Kommunikation umfasst nicht nur einen vollständig verschlüsselten Transport, sondern auch eine verschlüsselte Speicherung zum Schutz vor versehentlichem Schnüffeln und Offenlegung durch unseriöse Systemadministratoren und Internetdienstanbieter.
+
+Nomad unterstützt eine breite Palette von Hintergrunddiensten im Grid, von Freundschaftsvorschlägen bis hin zu Verzeichnisdiensten. Neue Inhalte und Datenaktualisierungen werden im Hintergrund zwischen den Hubs im gesamten Grid gemäß den Zugriffskontrolllisten und den von den *Sender- und* Empfängerkanälen festgelegten Berechtigungen weitergegeben. Die Daten werden auch zwischen einer beliebigen Anzahl von Kanalklonen synchronisiert, so dass Hub-Mitglieder auch dann auf Daten zugreifen und nahtlos weiter zusammenarbeiten können, wenn ihr primärer Hub nicht erreichbar oder offline ist.
+
+#### Identität
+
+Die Identitätsschicht von Nomad ist einzigartig. Sie bietet ein **unsichtbares Single Sign-On** für alle Standorte im Grid.
+
+Sie bietet auch eine **nomadische Identität**, so dass Ihre Kommunikation mit Freunden, Familie oder anderen Personen, mit denen Sie kommunizieren, nicht durch den Verlust Ihres primären Kommunikationsknotens beeinträchtigt wird - weder vorübergehend noch dauerhaft.
+
+Die wichtigen Teile Ihrer Identität und Ihrer Beziehungen können auf einem USB-Stick oder Ihrem Laptop gesichert werden und jederzeit an einem beliebigen Knoten im Netz erscheinen - mit all Ihren Freunden und Vorlieben.
+
+Entscheidend ist, dass diese nomadischen Instanzen synchron gehalten werden, so dass jede Instanz übernehmen kann, wenn eine andere gefährdet oder beschädigt ist. Dies schützt Sie nicht nur vor größeren Systemausfällen, sondern auch vor vorübergehender Überlastung der Website und staatlicher Manipulation oder Zensur.
+
+Wir sind der Meinung, dass die nomadische Identität, die einmalige Anmeldung und die Dezentralisierung von Hubzilla ein hohes Maß an **Widerstandsfähigkeit** und **Beständigkeit** in die Internetkommunikation einbringen, die angesichts der weltweiten Tendenzen zur Zentralisierung von Unternehmen sowie der massenhaften und wahllosen staatlichen Überwachung und Zensur dringend benötigt werden.
+
+Beim Durchsuchen des Netzes, beim Anzeigen von Kanälen und deren einzigartigen Inhalten, werden Sie nahtlos authentifiziert, sogar über völlig unterschiedliche Server-Hubs hinweg. Sie müssen keine Passwörter eingeben. Sie müssen nichts eintippen. Sie werden auf jeder neuen Seite, die Sie besuchen, einfach mit Ihrem Namen begrüßt.
+
+Wie funktioniert das bei Nomad? Wir nennen es **„magic-auth“**, weil Hubzilla die Details der Komplexität von Single-Sign-On-Logins und nomadischen Identitäten vor dem Surfen im Netz verbirgt. Dies ist eines der Designziele von Hubzilla: die Privatsphäre und die Freiheit im Internet zu erhöhen und gleichzeitig die Komplexität und die Langeweile zu reduzieren, die durch die Notwendigkeit entstehen, für jeden Besuch im Internet neue Passwörter und Anmeldenamen einzugeben. Sie melden sich nur einmal auf Ihrem Home-Hub (oder einem von Ihnen gewählten nomadischen Backup-Hub) an. Dadurch können Sie auf alle authentifizierten Dienste zugreifen, die überall im Netz angeboten werden - wie Einkaufen, Blogs, Foren und Zugang zu privaten Informationen. Ihr Passwort wird nicht auf tausend verschiedenen Websites gespeichert, sondern auf Servern, die Sie kontrollieren oder denen Sie vertrauen.
+
+Sie können nicht zum Schweigen gebracht werden. Sie können nicht aus dem Netz entfernt werden, es sei denn, Sie selbst entscheiden sich dafür, es zu verlassen.
+
+#### Zugangskontrolle
+
+Die Identitätsschicht von Nomad ermöglicht es Ihnen, fein abgestufte Berechtigungen für jeden Inhalt zu vergeben, den Sie veröffentlichen möchten - und diese Berechtigungen erstrecken sich über das gesamte Grid. Das ist so, als hätte man eine riesige Website, die aus einer Armee kleiner individueller Websites besteht - und bei der jeder Kanal im Grid seine Datenschutz- und Freigabepräferenzen für alle von ihm erstellten Webressourcen vollständig kontrollieren kann.
+
+Derzeit unterstützt Hubzilla die Zugriffskontrolle für viele Datentypen, darunter Diskussionsbeiträge und -kommentare, Fotoalben, Veranstaltungen, Cloud-Dateien, Webseiten, Wikis und mehr. Jedes Objekt und die Art und Weise, wie und mit wem es geteilt wird, unterliegt vollständig Ihrer Kontrolle.
+
+Diese Art der Kontrolle ist bei großen Unternehmensanbietern trivial, da sie die Benutzerdatenbank besitzen. Im Grid brauchen Sie keine riesige Benutzerdatenbank auf Ihrem Rechner - denn das Grid **ist** Ihre Benutzerdatenbank. Sie verfügt über eine im Grunde unendliche Kapazität (begrenzt durch die Gesamtzahl der Hubs, die im Internet online sind) und ist auf Hunderte, möglicherweise sogar Millionen von Computern verteilt.
+
+Der Zugang kann für jede Ressource, jeden Kanal oder jede Gruppe von Kanälen gewährt oder verweigert werden - überall im Grid. Andere können auf Ihre Inhalte zugreifen, wenn Sie es ihnen erlauben, und sie müssen nicht einmal ein Konto in Ihrem Hub haben. \ No newline at end of file
diff --git a/doc/de/developer/nomad_structures.md b/doc/de/developer/nomad_structures.md
new file mode 100644
index 000000000..c3f77c8b7
--- /dev/null
+++ b/doc/de/developer/nomad_structures.md
@@ -0,0 +1,141 @@
+### Nomad-Strukturen
+
+#### Nomad-Signaturen
+
+Alle signierten Daten in Nomad werden durch eine RSA-Signaturoperation mit dem privaten Schlüssel des Initiators erzeugt. Das binäre Ergebnis wird dann für den Transport mit base64url kodiert.
+
+#### Nomad-Verschlüsselung
+
+Die Verschlüsselung erfolgt derzeit mit AES256CTR. Weitere Algorithmen KÖNNEN unterstützt werden. Ein 32-Oktett-Schlüssel und ein 16-Oktett-Initialisierungsvektor werden nach dem Zufallsprinzip erzeugt. Die gewünschten Daten werden dann mit diesen generierten Zeichenketten verschlüsselt und das Ergebnis base64url-kodiert. Dann wird ein Array erstellt:
+
+- data
+
+ Die base64url-kodierten verschlüsselten Daten
+
+- alg
+
+ Der gewählte Algorithmus, in diesem Fall die Zeichenkette „aes256ctr“.
+
+- key
+
+ Der zufällig erzeugte Schlüssel, RSA-verschlüsselt mit dem öffentlichen Schlüssel des Empfängers, und das Ergebnis base64url-kodiert
+
+- iv
+
+ Der zufällig erzeugte Initialisierungsvektor, RSA-verschlüsselt mit dem öffentlichen Schlüssel des Empfängers, und das Ergebnis base64url-kodiert
+
+#### Grundlegendes Nomad-Paket
+
+Wird verwendet, um einen Dialog mit einer anderen Nomad-Site einzuleiten. Dieses Paket KANN verschlüsselt sein. Das Vorhandensein eines Array-Elements 'iv' zeigt an, dass eine Verschlüsselung vorgenommen wurde. Beim Senden eines 'auth_check'-Pakets MUSS dieses Paket verschlüsselt werden, wobei der öffentliche Schlüssel der Zielsite (der Site-Schlüssel, im Gegensatz zu einem Absenderschlüssel) verwendet wird.
+
+```
+ {
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ "sitekey":"-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxTeIwXZWrw/S+Ju6gewh
+LgkKnNNe2uCUqCqMZoYgJar3T5sHCDhvXc4dDCbDkxVIaA/+V1mURtBV60a3IGjn
+OAO0W0XGGLe2ED7G5o9U8T9mVGq8Mauv0v1oQ5wIR1gEAhBavkQ2OUGuF/YKn2nj
+HlKsv9HzUAHpcDMUe3Uklc2RhQbMcnJxEgkyjCkDyrTtCZzISkTAocHvpCG1KSog
+njUZdiz9UWxvM4rCFkCJvQU4RwRZJb7GA9ul+9JrF7NvUQTx8csRP2weBk1E9yyj
+wbe187E0eVj9RXX2Mx3mYhgrTdodxLOVMSXZLg1/SMpeFFl7QBhuM0SiOPg8a7Et
+e2iNA/RD4WiUFqCDfafasRa1TOozOm7LA+08lkAh5PeQPJsJbrX0wVVft++Y+5/z
+BvcUOP73vcbz7j5hJ7HLsbQtye/UUCfODBFybuDqRM84Aet8rjZohX7vukXdMD4I
+2HuB7pjR4uIfyYr0J63ANkvbsn8LR+RnirmHrK5H/OgxxjXcfYbGEQgFxvxhF6lA
+FpMu6Do4dx3CIb6pRmZ8bjSImXexJ0BSo9n3gtrz0XYLecsYFlQ9+QQjm83qxyLb
+M23in0xqMVsyQvzjNkpImrO/QdbEFRIIMee83IHq+adbyjQR49Z2hNEIZhkLPc3U
+2cJJ2HkzkOoF2K37qwIzk68CAwEAAQ==
+-----END PUBLIC KEY-----
+"
+ },
+ "recipients":{
+ {
+ "guid":"lql-1VnxtiO4-WF0h72wLX1Fu8szzHDOXgQaTbELwXW77k8AKFfh-hYr70vqMrc3SSvWN-Flrc5HFhRTWB7ICw",
+ "guid_sig":"PafvEL0VpKfxATxlCqDjfOeSIMdmpr3iU7X-Sysa1h5LzDpjSXsjO37tYZL-accb1M5itLlfnW5epkTa5I4flsW21zSY1A2jCuBQUTLLGV7rNyyBy7lgqJUFvAMRx0TfXzP9lcaPqlM9T1tA6jfWOsOmkdzwofGeXBnsjGfjsO2xdGYe6vwjOU0DSavukvzDMnOayB9DekpvDnaNBTxeGLM45Skzr7ZEMcNF7TeXMbnvpfLaALYEKeQs9bGH-UgAG8fBWgzVAzeBfx_XSR1rdixjyiZGP0kq0h35SlmMPcEjliodOBFwMXqpXFB7Ibp4F6o6te2p2ErViJccQVG8VNKB6SbKNXY6bhP5zVcVsJ-vR-p4xXoYJJvzTN7yTDsGAXHOLF4ZrXbo5yi5gFAlIrTLAF2EdWQwxSGyLRWKxG8PrDkzEzX6cJJ0VRcLh5z6OI5QqQNdeghPZbshMFMJSc_ApCPi9_hI4ZfctCIOi3T6bdgTNKryLm5fhy_eqjwLAZTGP-aUBgLZpb1mf2UojBn6Ey9cCyq-0T2RWyk-FcIcbV4qJ-p_8oODqw13Qs5FYkjLr1bGBq82SuolkYrXEwQClxnrfKa4KYc2_eHAXPL01iS9zVnI1ySOCNJshB97Odpooc4wk7Nb2Fo-Q6THU9zuu0uK_-JbK7IIl6go2qA"
+ },
+ },
+ "callback":"\/post",
+ "version":"1.2",
+ "encryption":{
+ "aes256ctr"
+ },
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ }
+```
+
+type
+
+Der Nachrichtentyp: **notify, purge, refresh, force_refresh, auth_check, ping** oder **pickup**. Der Inhalt der Pakete variiert je nach Nachrichtentyp. Hier wird das **notify-Paket** beschrieben.
+
+
+
+callback
+
+Eine Zeichenkette, die an die URL angehängt wird und den Nomad-Kommunikationsendpunkt auf diesem System identifiziert. In der Regel ist dies die Zeichenfolge „/post“.
+
+
+
+version
+
+Die Kennung des Nomad-Protokolls, damit künftige Protokollrevisionen nebeneinander bestehen können.
+
+
+
+encryption
+
+Array der unterstützten Verschlüsselungsalgorithmen, geordnet nach abnehmender Präferenz. Wenn keine kompatiblen Verschlüsselungsmethoden angegeben sind, MÜSSEN Anwendungen „aes256cbc“ verwenden.
+
+
+
+secret
+
+Eine 64-stellige Zeichenkette, die von der sendenden Seite zufällig generiert wird.
+
+
+
+secret_sig
+
+Die RSA-Signatur des Geheimnisses, signiert mit dem privaten Schlüssel des Absenders.
+
+
+
+sender
+
+Ein Array aus vier Komponenten, die eine tragbare Identität liefern. Wir können die angegebene URL kontaktieren und ein Nomad-Infopaket herunterladen, um den öffentlichen Schlüssel des Absenders zu erhalten, und diesen zur Überprüfung der Absender-Guid und der Signaturen der Entsende-URL verwenden.
+
+- guid
+
+ In der Regel eine 64 Zeichen lange base64url-kodierte Zeichenfolge. Sie wird erzeugt, wenn eine Identität erstellt wird, und es wird versucht, dass sie eindeutig ist; dies ist jedoch nicht erforderlich.
+
+- guid_sig
+
+ Die RSA-Signatur der guid, signiert mit dem privaten Schlüssel des Absenders und base64url-kodiert.
+
+- url
+
+ Die Basis-URL des Ortes, von dem dieser Beitrag stammt.
+
+- url_sig
+
+ Die RSA-Signatur der url, signiert mit dem privaten Schlüssel des Absenders und base64url kodiert.
+
+- sitekey
+
+ Der öffentliche Schlüssel der in der Url angegebenen Website
+
+recipients
+
+Wird nur für private Nachrichten verwendet. Ein Array von Umschlag-Empfängern. Jeder Empfänger wird durch ein Array aus guid und guid_sig dargestellt. Wenn Empfänger angegeben sind, wird das gesamte Paket auch mit einem ausgehandelten kryptografischen Algorithmus oder 'aes256cbc' gekapselt, wenn keiner ausgehandelt werden konnte.
+
+- guid
+
+ Die guid eines privaten Empfängers.
+
+- guid_sig
+
+ Die RSA-Signatur der guid, signiert mit dem privaten Schlüssel des Empfängers und base64url-kodiert \ No newline at end of file
diff --git a/doc/de/developer/our_pledge.md b/doc/de/developer/our_pledge.md
new file mode 100644
index 000000000..0a8c17349
--- /dev/null
+++ b/doc/de/developer/our_pledge.md
@@ -0,0 +1,3 @@
+#### Unser Versprechen
+
+Im Interesse der Förderung eines offenen und einladenden Umfelds verpflichten wir uns als Mitwirkende und Betreuer, die Teilnahme an unserem Projekt und unserer Gemeinschaft zu einer belästigungsfreien Erfahrung für jeden zu machen, unabhängig von Alter, Körpergröße, Behinderung, ethnischer Zugehörigkeit, Geschlechtsidentität und -ausdruck, Erfahrungsniveau, Nationalität, persönlichem Aussehen, Ethnie, Religion oder sexueller Identität und Orientierung. \ No newline at end of file
diff --git a/doc/de/developer/our_responsibilities.md b/doc/de/developer/our_responsibilities.md
new file mode 100644
index 000000000..32acdbf07
--- /dev/null
+++ b/doc/de/developer/our_responsibilities.md
@@ -0,0 +1,3 @@
+#### Unsere Verantwortlichkeiten
+
+Die Projektbetreuer sind dafür verantwortlich, die Standards für akzeptables Verhalten zu klären, und es wird von ihnen erwartet, dass sie angemessene und faire Korrekturmaßnahmen ergreifen, wenn ein inakzeptables Verhalten festgestellt wird. Die Projektbetreuer haben das Recht und die Verantwortung, Kommentare, Commits, Code, Wiki-Edits, Issues und andere Beiträge, die nicht mit diesem Verhaltenskodex übereinstimmen, zu entfernen, zu bearbeiten oder abzulehnen, oder Beiträge, die sie als unangemessen, bedrohlich, beleidigend oder schädlich erachten, vorübergehend oder dauerhaft zu verbieten. \ No newline at end of file
diff --git a/doc/de/developer/our_standards.md b/doc/de/developer/our_standards.md
new file mode 100644
index 000000000..f745d29d4
--- /dev/null
+++ b/doc/de/developer/our_standards.md
@@ -0,0 +1,24 @@
+#### Unsere Standards
+
+Beispiele für Verhaltensweisen, die zur Schaffung eines positiven Umfelds beitragen
+umfassen:
+
+- Eine einladende und integrative Sprache verwenden
+
+- Respekt vor unterschiedlichen Standpunkten und Erfahrungen
+
+- Konstruktive Kritik anständig annehmen
+
+- Sich auf das konzentrieren, was für die Gemeinschaft am besten ist
+
+- Einfühlungsvermögen gegenüber anderen Mitgliedern der Gemeinschaft zeigen
+
+
+
+Beispiele für inakzeptables Verhalten von Teilnehmern sind:
+
+- Die Verwendung sexualisierter Sprache oder Bilder und unerwünschte sexuelle Aufmerksamkeit oder Annäherungsversuche
+- Trolling, beleidigende/abwertende Kommentare und persönliche oder politische Angriffe
+- Öffentliche oder private Belästigung
+- Veröffentlichung privater Informationen anderer, wie z. B. einer physischen oder elektronischen Adresse, ohne ausdrückliche Erlaubnis
+- sonstiges Verhalten, das in einem beruflichen Umfeld als unangemessen angesehen werden könnte \ No newline at end of file
diff --git a/doc/de/developer/scope.md b/doc/de/developer/scope.md
new file mode 100644
index 000000000..03c71d1dd
--- /dev/null
+++ b/doc/de/developer/scope.md
@@ -0,0 +1,3 @@
+#### Geltungsbereich
+
+Dieser Verhaltenskodex gilt sowohl innerhalb der Projektbereiche als auch im öffentlichen Umfeld, wenn eine Person das Projekt oder seine Gemeinschaft vertritt. Beispiele für das Repräsentieren eines Projekts oder einer Gemeinschaft sind die Verwendung einer offiziellen Projekt-E-Mail-Adresse, das Posten über ein offizielles Social-Media-Konto oder das Handeln als ernannter Vertreter bei einer Online- oder Offline-Veranstaltung. Die Repräsentation eines Projekts kann von den Projektbetreuern weiter definiert und präzisiert werden. \ No newline at end of file
diff --git a/doc/de/developer/technical_introduction.md b/doc/de/developer/technical_introduction.md
new file mode 100644
index 000000000..320544ce0
--- /dev/null
+++ b/doc/de/developer/technical_introduction.md
@@ -0,0 +1,309 @@
+### Technische Einführung
+
+Nomad ist ein JSON-basiertes Web-Framework zur Implementierung sicherer dezentraler Kommunikation und Dienste. Um diese Funktionalität bereitzustellen, erstellt Nomad eine dezentralisierte, weltweit eindeutige Kennung für jeden Knotenpunkt im Netz. Dieser globale Identifikator ist nicht untrennbar mit dem DNS verbunden, wodurch die erforderliche Mobilität gewährleistet wird. Viele der bestehenden dezentralen Kommunikationsrahmen bieten zwar den Kommunikationsaspekt, aber keine Fernzugriffskontrolle und Authentifizierung. Außerdem basieren die meisten dieser Systeme auf dem „Webfinger“, der die Identität immer noch an Domänennamen bindet und keine nomadische Identität unterstützen kann.
+
+Die Hauptprobleme, die Nomad angeht, sind
+
+- vollständig dezentralisierte Kommunikation
+- Unabhängigkeit von DNS-basierter Identität
+- Knotenmobilität
+- nahtlose Fernauthentifizierung
+- hohe Leistung
+
+Wir werden uns auf DNS-basierte user@host-Adressen als „benutzerfreundlichen“ Mechanismus stützen, um anderen mitzuteilen, wo man sich befindet, nämlich auf einem benannten Host mit einem bestimmten Benutzernamen, und die Kommunikation mit DNS-Entitäten wird über TCP und das Web abgewickelt.
+
+Das zugrundeliegende Protokoll bietet jedoch eine Abstraktionsschicht darüber, so dass ein Kommunikationsknoten (z. B. „Identität“) zu einem anderen DNS-Standort wechseln kann und sich (nach bestem Wissen und Gewissen) von Standortwechseln erholt und/oder bereits bestehende Kommunikationsbeziehungen aufrechterhält. Ein Nebeneffekt dieser Anforderung ist die Möglichkeit, von alternativen/mehreren DNS-Standorten und Dienstanbietern aus zu kommunizieren und dennoch eine einzige Online-Identität beizubehalten.
+
+Wir nennen dieses Overlay-Netz das „Grid“. Die an dieses Netz angeschlossenen Server werden als „Hubs“ bezeichnet und können eine beliebige Anzahl von individuellen Identitäten unterstützen.
+
+Eine Identität muss nicht unbedingt mit einer Person übereinstimmen. Sie ist lediglich etwas, das die Fähigkeit erfordert, innerhalb des Netzes zu kommunizieren.
+
+Die Fähigkeit zur Wiederherstellung wird durch Kommunikation mit dem ursprünglichen Standort erreicht, wenn eine neue oder Ersatzidentität erstellt wird, oder als Rückgriff auf eine gespeicherte Datei, die die Identität und ihre Kontakte beschreibt, für den Fall, dass der alte Standort nicht mehr antwortet.
+
+Zumindest kurzfristig hat die Mobilität bestehender Inhalte nicht die höchste Priorität. Dies kann zu einem späteren Zeitpunkt geschehen oder auch nicht. Die wichtigsten Dinge, die wir behalten wollen, sind Ihre Identität und Ihre Freunde.
+
+Adressen, die von mehreren Personen gemeinsam genutzt werden, sind user@host und beschreiben die **aktuellen** lokalen Zugangsdaten für eine bestimmte Identität. Es handelt sich um DNS-basierte Adressen, die als Seed verwendet werden, um eine bestimmte Identität innerhalb des Netzes zu lokalisieren. Die Maschinenkommunikation bindet diese Adresse an eine weltweit eindeutige ID. Eine einzelne, weltweit eindeutige ID kann an eine beliebige Anzahl von DNS-Standorten angehängt oder gebunden werden. Sobald eine Identität einem DNS-Standort zugeordnet oder an diesen gebunden ist, besteht die Kommunikation nur noch aus der Kenntnis der weltweit eindeutigen Adresse und des derzeit verwendeten DNS (url) (um die aktuelle Kommunikation zurückzurufen und zu überprüfen/abzuschließen). Diese Konzepte werden in Zukunft noch genauer spezifiziert.
+
+Damit eine Identität über Standorte hinweg bestehen bleibt, muss man in der Lage sein, Folgendes bereitzustellen oder wiederherzustellen
+
+
+
+- die weltweit eindeutige ID für diese Identität
+- den dieser Identität zugewiesenen privaten Schlüssel
+- (falls der ursprüngliche Server nicht mehr existiert) ein Adressbuch mit Kontakten für diese Identität.
+
+Diese Informationen können vom ursprünglichen Server über eine API exportiert und/oder auf eine Festplatte oder einen USB-Stick heruntergeladen werden.
+
+Wir können auch versuchen, die Identität mit noch weniger Informationen wiederherzustellen, aber dies ist anfällig für Account-Hijacking und erfordert, dass Ihre Kontakte die Änderung bestätigen.
+
+Um eine hochleistungsfähige Kommunikation zu ermöglichen, ist das Datenübertragungsformat für alle Aspekte von Nomad JSON. XML-Kommunikation erfordert viel zu viel Overhead.
+
+Die bidirektionale Verschlüsselung basiert auf RSA-Schlüsseln mit 4096 Bit, die im DER/ASN.1-Format unter Verwendung der PKCS#8-Kodierungsvariante ausgedrückt werden, mit AES-Verschlüsselung von Elementen mit variabler Länge oder großen Elementen. Die genauen Verschlüsselungsalgorithmen sind zwischen den Standorten verhandelbar.
+
+Einige Aspekte der bekannten „Föderationsprotokolle“ (Webfinger, Salmon, Activitystreams, Portablecontacts usw.) können in Nomad verwendet werden, aber wir sind nicht an sie gebunden und werden auch nicht an sie gebunden sein. Das Hubzilla-Projekt versucht einige ziemlich neuartige Entwicklungen im Bereich der dezentralen Kommunikation, und wenn es notwendig sein sollte, von solchen „Standardprotokollen“ abzuweichen, werden wir dies ohne Fragen oder Zögern tun.
+
+Um eine weltweit eindeutige ID zu erstellen, werden wir sie auf einem Whirlpool-Hash der Identitäts-URL des Ursprungsknotens und einer Pseudo-Zufallszahl basieren, was uns eine 256-Bit-ID mit einer extrem geringen Kollisionswahrscheinlichkeit liefern sollte (256 Bits entsprechen ungefähr 115 Quattuorviginitillionen oder 1,16 X 10^77 eindeutigen Zahlen). Diese wird in der Kommunikation als base64url-kodierte Zeichenfolge dargestellt. Wir werden uns jedoch nicht auf Wahrscheinlichkeiten verlassen, und die ID muss auch mit einem öffentlichen Schlüssel verbunden sein, wobei die Kryptographie mit öffentlichem Schlüssel verwendet wird, um eine Identitätsgarantie zu bieten, die nicht kopiert oder irgendwie im Whirlpool-Hash-Raum kollidiert wurde.
+
+Durch die Verwendung der DNS als Grundlage für die ID wird ein weltweit eindeutiger Seed bereitgestellt, was nicht der Fall wäre, wenn die ID vollständig auf einer Pseudo-Zufallszahlengenerierung basieren würde.
+
+Wir bezeichnen die kodierte, weltweit eindeutige uid-Zeichenkette als zot_uid
+
+Da es mehr als einen DNS-Standort geben kann, der mit einer bestimmten zot_uid-Identität verknüpft/verbunden ist, sollten die Zustellungsprozesse an alle diese Standorte liefern, da wir nicht mit Sicherheit wissen, auf welche Hub-Instanz zu einem bestimmten Zeitpunkt zugegriffen werden kann. Wir werden jedoch einen DNS-Standort als „primär“ bezeichnen, der der bevorzugte Standort für die Anzeige von Webprofilinformationen sein wird.
+
+Wir müssen auch die Möglichkeit bieten, den primären Standort auf einen neuen Standort zu ändern. Ein Nachschlagen von Informationen über den aktuellen primären Standort kann einen „Weiterleitungszeiger“ liefern, der uns anweist, unsere Einträge zu aktualisieren und alles an den neuen Standort zu verschieben. Es besteht auch die Möglichkeit, dass der Hauptstandort nicht mehr existiert und nicht mehr antwortet. In diesem Fall kann ein Ort, der bereits dieser zot_uid zugeordnet ist, die Kontrolle übernehmen und sich selbst als primär deklarieren. In beiden Fällen wird die primäre Bezeichnung automatisch bestätigt und verschoben. Es kann auch ein Antrag von einem primären Standort gestellt werden, der die Entfernung eines anderen Standorts verlangt.
+
+Um eine zot_uid einem zweiten (oder tertiären) Standort zuzuordnen, ist ein sicherer Austausch erforderlich, bei dem überprüft wird, ob der neue Standort im Besitz des privaten Schlüssels für diese zot_uid ist. Die Sicherheit des privaten Schlüssels ist daher von grundlegender Bedeutung, um ein Hijacking von Identitäten zu verhindern.
+
+Die Kommunikation erfordert dann
+
+- zot_uid (Zeichenfolge)
+- uid_sig
+- callback (aktueller Standort Nomad-Endpunkt url)
+- callback_sig
+- spec (int)
+
+wird mit jeder Mitteilung übergeben. Bei spec handelt es sich um eine Revisionsnummer der geltenden Nomad-Spezifikation, damit die Kommunikation mit Hubs, die ältere und möglicherweise inkompatible Protokollspezifikationen verwenden, aufrechterhalten werden kann. Die Kommunikation wird mit Hilfe eines gespeicherten öffentlichen Schlüssels verifiziert, der beim ersten Aufbau der Verbindung zu dieser zot_uid kopiert wurde.
+
+Der Widerruf und Ersatz des Schlüssels muss vom primären Hub aus erfolgen. Die genaue Form hierfür wird noch ausgearbeitet, wird aber wahrscheinlich eine Benachrichtigung an alle anderen gebundenen Hubs sein, um „nach Hause zu telefonieren“ (zum primären Hub) und eine Kopie des neuen Schlüssels anzufordern. Diese Mitteilung sollte mit einem Standort- oder Hub-Schlüssel verifiziert werden, da der ursprüngliche Identitätsschlüssel kompromittiert worden sein könnte und nicht vertrauenswürdig ist.
+
+Um Verwirrung zu vermeiden, sollte es für jeden Hub genau eine kanonische Url geben, damit diese indiziert und eindeutig referenziert werden kann.
+
+Um Mehrdeutigkeit des Schemas zu vermeiden, wird dringend empfohlen, dass alle Adressen https mit einem „browser valid“-Zertifikat und einer einzigen gültigen Host-Komponente (entweder www.domain.ext oder domain.ext, aber nicht beides) sein sollten, die in der gesamten Kommunikation verwendet wird. Es können mehrere URLs lokal angegeben werden, aber es sollte nur eine einzige URL für die gesamte Nomad-Kommunikation innerhalb des Grids verwendet werden.
+
+Testinstallationen, die nicht mit dem öffentlichen Netz verbunden sind, können Nicht-SSL verwenden, aber der gesamte Datenverkehr, der über öffentliche Netze fließt, sollte vor Session-Hijacking geschützt sein, vorzugsweise mit einem „browsererkannten“ Zertifikat.
+
+Wenn möglich, empfiehlt das Nomadt die Verwendung von „Batching“, um den Netzwerkverkehr zwischen zwei Hubs zu minimieren. Dies bedeutet, dass Standort A„ mehrere Nachrichten an Standort B“ in einer einzigen Transaktion senden kann und auch die Zustellung identischer Nachrichten an mehrere Empfänger im selben Hub konsolidiert werden kann.
+
+Die Nachrichten selbst können während der Übertragung verschlüsselt werden oder auch nicht, je nach der privaten Natur der Nachrichten. SSL (dringend empfohlen) bietet eine bedingungslose Verschlüsselung des Datenstroms, allerdings macht es wenig Sinn, öffentliche Mitteilungen zu verschlüsseln, die als uneingeschränkt sichtbar eingestuft wurden. Die Verschlüsselung der Datenspeicherung und die sogenannte „Ende-zu-Ende-Verschlüsselung“ liegen außerhalb des Anwendungsbereichs des Nomad. Es wird davon ausgegangen, dass die Betreiber von Knotenpunkten angemessene Sicherheitsvorkehrungen treffen, um die Sicherheit ihrer Datenspeicher zu gewährleisten, und diese sind Funktionen der Anwendungs- und Standortintegrität im Gegensatz zur Protokollintegrität.
+
+
+
+#### Nachrichten
+
+In Anbetracht der zuvor aufgeführten Einschränkungen sollte eine Nomad-Mitteilung daher ein json-Array aus einzelnen Nachrichten sein. Diese können innerhalb der gleichen Übertragung gemischt und kombiniert werden.
+
+Jede Nachricht erfordert dann:
+
+- Typ
+- (optional) Empfängerliste
+
+Das Fehlen einer Empfängerliste würde auf eine unverschlüsselte öffentliche Nachricht oder eine Nachricht auf Standortebene hinweisen. Die Empfängerliste würde ein Array von zot_uid mit einem individuellen Entschlüsselungsschlüssel und einer gemeinsamen iv enthalten. Der Entschlüsselungsschlüssel ist mit dem öffentlichen Schlüssel der Empfängeridentität verschlüsselt. Die iv wird mit dem privaten Schlüssel des Absenders verschlüsselt.
+
+Alle Nachrichten sollten vom Absender digital signiert werden.
+
+Der Typ kann einer der folgenden sein (diese Liste ist erweiterbar):
+
+- Post (oder Aktivität)
+- Mail
+- Identität
+- authentifizieren
+
+Identitätsnachrichten haben keine Empfänger und benachrichtigen den sozialen Graphen des Systems über eine Identitätsaktualisierung, bei der es sich um eine neue oder gelöschte Identität, einen neuen oder gelöschten Standort oder eine Änderung des primären Hubs handeln kann. Die Signatur für diese Nachrichten verwendet Systemschlüssel im Gegensatz zu identitätsspezifischen Schlüsseln.
+
+Posts umfassen viele verschiedene Arten von Aktivitäten, wie z. B. Top-Level-Posts, Likes/Dislikes, Kommentare, Tagging-Aktivitäten usw. Diese Typen werden in der Nachrichtenstruktur angegeben.
+
+Authentifizierungsnachrichten führen zu einer gegenseitigen Authentifizierung und einer Browserumleitung zu geschützten Ressourcen auf dem entfernten Hub, wie z. B. die Möglichkeit, Nachrichten von Wand zu Wand zu posten und private Fotoalben und Ereignisse einzusehen, usw.
+
+
+
+#### Erkundung
+
+Eine bekannte URL wird verwendet, um einen Hub auf Nomad-Fähigkeiten und Identitätsabfragen zu untersuchen, einschließlich der Ermittlung von öffentlichen Schlüsseln, Profilstandorten, Profilfotos und dem primären Hub-Standort.
+
+Der Speicherort für diesen Dienst ist /.well-known/zot-info - und muss im Stammverzeichnis der gewählten Domain verfügbar sein.
+
+Um eine Abfrage durchzuführen, wird eine POST-Anfrage mit den folgenden Parametern an den Erkennungsort gestellt:
+
+Erforderlich:
+
+Adresse => eine Adresse auf dem Zielsystem wie „john“
+
+Optional:
+
+target => die Nomad „guid“ des Beobachters für die Auswertung der Berechtigungen
+
+target_sig => eine RSA-Signatur (base64url-kodiert) der guid
+
+key => Der öffentliche Schlüssel, der zur Verifizierung der Signatur benötigt wird
+
+token => eine vom anfragenden Dienst gewählte Zeichenfolge (möglicherweise zufällig). Falls vorhanden, wird ein Eintrag im entdeckten Paket mit der Bezeichnung „signed_token“ bereitgestellt, der aus der base64url_kodierten RSA-Signatur der Verkettung der Zeichenkette „token.“ und dem bereitgestellten Token unter Verwendung des privaten Schlüssels des entdeckten Kanals besteht. Dies kann mit dem bereitgestellten „key“-Eintrag verifiziert werden und bietet die Sicherheit, dass der Server im Besitz des privaten Schlüssels für die ermittelte Identität ist. Nach dem 01.01.2017 ist es **erforderlich,** dass ein Server ein signed_token bereitstellt *, wenn* ein Token in der Anfrage angegeben wurde.
+
+Wenn kein Ziel angegeben wird, sind die zurückgegebenen Berechtigungen generische Berechtigungen
+für unbekannte oder unauthentifizierte Beobachter
+
+Beispiel eines Erkennungspakets für '`mike@zothub.com`'
+
+```
+ {
+
+ "success": true,
+ "signed_token": "KBJrKTq1qrctNuxF3GwVh3GAGRqmgkirlXANPcJZAeWlvSt_9TMV097slR4AYnYCBEushbVqHEJ9Rb5wHTa0HzMbfRo8cRdl2yAirvvv5d98dtwHddQgX1jB0xEypXtmIYMdPGDLvhI1RNdIBhHkkrRcNreRzoy4xD--HM6m1W0-A8PJJJ9BcNxmGPcBtLzW08wzoP9trJ3M7DQ6Gkk6j7iwVsyApw1ZBaDvabGTdc_SFV-Iegtqw3rjzT_xXWsfzMlKBy-019MYn_KS-gu23YzjvGu5tS_zDfkQb8DMUlPLz5yyxM0yOMlUDtG2qQgIJAU2O0X6T5xDdJ6mtolNyhepg845PvFDEqBQGMIH1nc47CNumeudDi8IWymEALhjG_U8KAK7JVlQTJj2EKUb0au1g6fpiBFab5mmxCMtZEX3Jreyak5GOcFFz-WpxuXJD9TdSoIvaBfBFOoJnXkg2zE4RHXeQzZ2FotmrbBG5dm8B-_6byYGoHBc08ZsWze1K96JIeRnLpBaj6ifUDcVHxZMPcGHHT27dvU2PNbgLiBjlAsxhYqkhN5qOHN8XBcg2KRjcMBaI3V0YMxlzXz5MztmZq3fcB1p-ccIoIyMPMzSj3yMB7J9CEU2LYPSTHMdPkIeDE6GaCkQKviaQQJQde346tK_YjA2k7_SOBmvPYE",
+ "guid": "sebQ-IC4rmFn9d9iu17m4BXO-kHuNutWo2ySjeV2SIW1LzksUkss12xVo3m3fykYxN5HMcc7gUZVYv26asx-Pg",
+ "guid_sig": "Llenlbl4zHo6-g4sa63MlQmTP5dRCrsPmXHHFmoCHG63BLq5CUZJRLS1vRrrr_MNxr7zob_Ykt_m5xPKe5H0_i4pDj-UdP8dPZqH2fqhhx00kuYL4YUMJ8gRr5eO17vsZQ3XxTcyKewtgeW0j7ytwMp6-hFVUx_Cq08MrXas429ZrjzaEwgTfxGnbgeQYQ0R5EXpHpEmoERnZx77VaEahftmdjAUx9R4YKAp13pGYadJOX5xnLfqofHQD8DyRHWeMJ4G1OfWPSOlXfRayrV_jhnFlZjMU7vOdQwHoCMoR5TFsRsHuzd-qepbvo3pzvQZRWnTNu6oPucgbf94p13QbalYRpBXKOxdTXJrGdESNhGvhtaZnpT9c1QVqC46jdfP0LOX2xrVdbvvG2JMWFv7XJUVjLSk_yjzY6or2VD4V6ztYcjpCi9d_WoNHruoxro_br1YO3KatySxJs-LQ7SOkQI60FpysfbphNyvYMkotwUFI59G08IGKTMu3-GPnV1wp7NOQD1yzJbGGEGSEEysmEP0SO9vnN45kp3MiqbffBGc1r4_YM4e7DPmqOGM94qksOcLOJk1HNESw2dQYWxWQTBXPfOJT6jW9_crGLMEOsZ3Jcss0XS9KzBUA2p_9osvvhUKuKXbNztqH0oZIWlg37FEVsDs_hUwUJpv2Ar09k4",
+ "key": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7QCwvuEIwCHjhjbpz3Oc\ntyei/Pz9nDksNbsc44Cm8jxYGMXsTPFXDZYCcCB5rcAhPPdZSlzaPkv4vPVcMIrw\n5cdX0tvbwa3rNTng6uFE7qkt15D3YCTkwF0Y9FVZiZ2Ko+G23QeBt9wqb9dlDN1d\nuPmu9BLYXIT/JXoBwf0vjIPFM9WBi5W/EHGaiuqw7lt0qI7zDGw77yO5yehKE4cu\n7dt3SakrXphL70LGiZh2XGoLg9Gmpz98t+gvPAUEotAJxIUqnoiTA8jlxoiQjeRK\nHlJkwMOGmRNPS33awPos0kcSxAywuBbh2X3aSqUMjcbE4cGJ++/13zoa6RUZRObC\nZnaLYJxqYBh13/N8SfH7d005hecDxWnoYXeYuuMeT3a2hV0J84ztkJX5OoxIwk7S\nWmvBq4+m66usn6LNL+p5IAcs93KbvOxxrjtQrzohBXc6+elfLVSQ1Rr9g5xbgpub\npSc+hvzbB6p0tleDRzwAy9X16NI4DYiTj4nkmVjigNo9v2VPnAle5zSam86eiYLO\nt2u9YRqysMLPKevNdj3CIvst+BaGGQONlQalRdIcq8Lin+BhuX+1TBgqyav4XD9K\nd+JHMb1aBk/rFLI9/f2S3BJ1XqpbjXz7AbYlaCwKiJ836+HS8PmLKxwVOnpLMbfH\nPYM8k83Lip4bEKIyAuf02qkCAwEAAQ==\n-----END PUBLIC KEY-----\n",
+ "name": "Mike Macgirvin",
+ "name_updated": "2012-12-06 04:59:13",
+ "address": "mike@zothub.com",
+ "photo_mimetype": "image/jpeg",
+ "photo": "https://zothub.com/photo/profile/l/1",
+ "photo_updated": "2012-12-06 05:06:11",
+ "url": "https://zothub.com/channel/mike",
+ "connections_url": "https://zothub.com/poco/mike",
+ "target": "",
+ "target_sig": "",
+ "searchable": false,
+ "permissions": {
+ "view_stream": true,
+ "view_profile": true,
+ "view_photos": true,
+ "view_contacts": true,
+ "view_storage": true,
+ "view_pages": true,
+ "send_stream": false,
+ "post_wall": false,
+ "post_comments": false,
+ "post_mail": false,
+ "post_photos": false,
+ "tag_deliver": false,
+ "chat": false,
+ "write_storage": false,
+ "write_pages": false,
+ "delegate": false
+ },
+ "profile": {
+ "description": "Freedom Fighter",
+ "birthday": "0000-05-14",
+ "next_birthday": "2013-05-14 00:00:00",
+ "gender": "Male",
+ "marital": "It's complicated",
+ "sexual": "Females",
+ "locale": "",
+ "region": "",
+ "postcode": "",
+ "country": "Australia"
+ },
+ "locations": [
+ {
+ "host": "zothub.com",
+ "address": "mike@zothub.com",
+ "primary": true,
+ "url": "https://zothub.com",
+ "url_sig": "eqkB_9Z8nduBYyyhaSQPPDN1AhSm5I4R0yfcFxPeFpuu17SYk7jKD7QzvmsyahM5Kq7vDW6VE8nx8kdFYpcNaurqw0_IKI2SWg15pGrhkZfrCnM-g6A6qbCv_gKCYqXvwpSMO8SMIO2mjQItbBrramSbWClUd2yO0ZAceq3Z_zhirCK1gNm6mGRJaDOCuuTQNb6D66TF80G8kGLklv0o8gBfxQTE12Gd0ThpUb5g6_1L3eDHcsArW_RWM2XnNPi_atGNyl9bS_eLI2TYq0fuxkEdcjjYx9Ka0-Ws-lXMGpTnynQNCaSFqy-Fe1aYF7X1JJVJIO01LX6cCs-kfSoz29ywnntj1I8ueYldLB6bUtu4t7eeo__4t2CUWd2PCZkY3PKcoOrrnm3TJP5_yVFV_VpjkcBCRj3skjoCwISPcGYrXDufJxfp6bayGKwgaCO6QoLPtqqjPGLFm-fbn8sVv3fYUDGilaR3sFNxdo9mQ3utxM291XE2Pd0jGgeUtpxZSRzBuhYeOybu9DPusID320QbgNcbEbEImO8DuGIxuVRartzEXQF4WSYRdraZzbOqCzmU0O55P836JAfrWjgxTQkXlYCic-DBk-iE75JeT72smCtZ4AOtoFWCjZAABCw42J7JELY9APixZXWriKtjy6JI0G9d3fs6r7SrXr1JMy0",
+ "callback": "https://zothub.com/post",
+ "sitekey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1IWXwd/BZuevq8jzNFoR\n3VkenduQH2RpR3Wy9n4+ZDpbrUKGJddUGm/zUeWEdKMVkgyllVA/xHdB7jdyKs1X\nuIet9mIdnzvhdLO/JFD5hgbNG2wpSBIUY6aSNeCFTzszqXmuSXMW5U0Ef5pCbzEA\nnhoCoGL1KAgPqyxnGKUlj7q2aDwC9IRNtAqNyFQL67oT91vOQxuMThjlDhbR/29Q\ncYR4i1RzyahgEPCnHCPkT2GbRrkAPjNZAdlnk9UesgP16o8QB3tE2j50TVrbVc/d\nYRbzC56QMPP9UgUsapNeSJBHji75Ip/E5Eg/kfJC/HEQgyCqjCGfb7XeUaeQ7lLO\nqc7CGuMP+Jqr/cE54/aSHg8boTwxkMp11Ykb+ng17fl57MHTM2RJ99qZ1KBkXezR\nuH1lyvjzeJPxEFr9rkUqc4GH74/AgfbgaFvQc8TS7ovEa5I/7Pg04m7vLSEYc6UF\nYJYxXKrzmZT2TDoKeJzaBBx5MFLhW19l68h9dQ8hJXIpTP0hJrpI+Sr6VUAEfFQC\ndIDRiFcgjz6j7T/x8anqh63/hpsyf2PMYph1+4/fxtSCWJdvf+9jCRM8F1IDfluX\n87gm+88KBNaklYpchmGIohbjivJyru41CsSLe0uinQFvA741W00w6JrcrOAX+hkS\nRQuK1dDVwGKoIY85KtTUiMcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
+ }
+ ],
+ "site": {
+ "url": "https://zothub.com",
+ "directory_mode": "primary",
+ "directory_url": "https://zothub.com/dirsearch"
+ }
+
+ }
+```
+
+Discovery gibt ein JSON-Array mit den folgenden Komponenten zurück:
+
+'success' => (true oder false) Operation war erfolgreich, wenn true. Andernfalls kann eine optionale 'message' vorhanden sein, die die Fehlerursache angibt.
+
+'signed_token' => Wenn ein Token-Parameter in der Anfrage angegeben wurde, wird ihm der Text 'token.' vorangestellt und dann mit dem privaten Schlüssel des Kanals RSA-signiert und base64url-kodiert und als 'signed_token' zurückgegeben.
+
+guid' => die guid der Adresse auf dem Zielsystem
+
+guid_sig“ => die mit base64url kodierte RSA-Signatur der guid, signiert mit dem mit dieser guid verbundenen privaten Schlüssel.
+
+'key' => der öffentliche Schlüssel, der mit dieser guid verknüpft ist
+
+name“ => Name des Kanals
+
+name_updated' => Zeitstempel im MySQL-Stil '2012-01-01 00:00:00', als der Name zuletzt geändert wurde (UTC)
+
+address' => „webbie“ oder user@host-Adresse, die mit diesem Kanal verbunden ist
+
+photo' => URL eines Profilfotos für diesen Kanal (idealerweise 175x175)
+
+photo_mimetype“ => Inhaltstyp des Profilfotos
+
+photo_updated' => Zeitstempel im MySQL-Stil, wann das Foto zuletzt aktualisiert wurde (UTC)
+
+'url' => Standort der Kanal-Homepage
+
+'connections_url' => Speicherort der URL für tragbare Kontakte (erweitert für Nomad) für diesen Kanal
+
+target' => wenn ein Berechtigungsziel angegeben wurde, wird es gespiegelt
+
+'target_sig' => wenn ein Berechtigungsziel angegeben wurde, wird die Signatur gespiegelt.
+
+'searchable' => (true oder false) true bedeutet, dass dieser Eintrag in einem Verzeichnis gesucht werden kann
+
+
+
+##### Berechtigungen
+
+'permissions' => erweiterbares Array von Berechtigungen, die für dieses Ziel geeignet sind, Werte sind true oder false
+
+Berechtigungen können beinhalten:
+
+- view_stream
+- view_profile
+- view_photos
+- view_contacts
+- view_storage
+- view_pages
+- send_stream
+- post_wall
+- post_comments
+- post_mail
+- post_photos
+- tag_deliver
+- chat
+- write_storage
+- write_pages
+- delegate
+
+##### Profil
+
+'Profil' => Array mit wichtigen Profilfeldern
+
+- description
+- birthday YYYY-MM-DD , all fields are optional, any field (such as year) may be zero
+- next_birthday => MySQL datetime string representing the next upcoming birthday, converted from the channel's default timezone to UTC.
+- gender (free form)
+- marital (marital status)
+- sexual (preference)
+- locale (city)
+- region (state)
+- postcode
+- country
+
+##### Standorte
+
+'locations' => Array der registrierten Standorte (DNS-Standorte), von denen aus dieser Kanal sichtbar sein kann oder gebucht werden kann
+
+Jeder Standort ist ein Array aus
+
+'host' => DNS-Hostname, z.B. example.com
+
+'address' => die Webbie- oder user@host-Kennung, die mit diesem Standort verbunden ist
+
+'primary' => (true oder false), ob dies der primäre Ort für diesen Kanal ist, an dem Dateien und Webseiten im Allgemeinen zu finden sind
+
+url“ => Url des Stammverzeichnisses dieses DNS-Speicherorts, z. B. `https://example.com`
+
+url_sig' => base64url kodierte RSA-Signatur der URL, signiert mit dem privaten Schlüssel des Kanals
+
+'callback' => Nomad-Kommunikationsendpunkt auf dieser Site, normalerweise `https://example.com/post]`
+
+sitekey' => öffentlicher Schlüssel dieser Seite/Host
+
+
+
+##### Standort
+
+'site' => Array, das die Verzeichnisrolle der Site angibt, die auf diese Anfrage antwortet
+
+url' => url dieser Site z.B. `https://example.com`
+
+directory_mode' => eines von 'primary', 'secondary', 'normal' oder 'standalone'
+
+directory_url' => wenn es sich um einen Verzeichnisserver oder einen Einzelplatz handelt, die URL für das Verzeichnissuchmodul \ No newline at end of file
diff --git a/doc/de/developer/testing.md b/doc/de/developer/testing.md
new file mode 100644
index 000000000..bbe15050f
--- /dev/null
+++ b/doc/de/developer/testing.md
@@ -0,0 +1,131 @@
+### Testen
+
+Hubzilla verwendet [PHPUnit] für automatisierte Tests, oft auch *Unit-Tests* oder *Integrationstests* genannt.
+Die Tests werden als PHP-Klassen geschrieben und befinden sich im Unterverzeichnis `tests/unit` des Hubzilla-Hauptrepositorys. In diesem Leitfaden wird erklärt, wie man die Tests ausführt, wie die Tests strukturiert sind und wie Sie Ihre eigenen Tests schreiben können.
+
+#### Ausführen der Testsuite
+
+##### Installieren der Abhängigkeiten
+
+Um die Tests ausführen zu können, müssen Sie die Entwickler-Abhängigkeiten installieren, die in der `composer.json-Datei` aufgeführt sind. Stellen Sie sicher, dass Sie [composer installiert] auf Ihrem Entwicklungssystem haben, und führen Sie dann aus:
+
+```
+% composer install
+```
+
+Dies wird phpunit und einige andere Abhängigkeiten installieren. Es wird auch einige Dateien im Unterverzeichnis `vendor/composer` aktualisieren. Dies mag ein wenig laut erscheinen, aber es ist wichtig, dass diese Änderungen *nicht* in das Repository übertragen werden!
+
+**Warnung!** Übertragen Sie die Änderungen an den Dateien im Verzeichnis `vendor/composer` nicht in Ihr Projektarchiv!
+
+##### Einrichten der Testdatenbank
+
+Wir haben ein Skript (`tests/create_test_db.sh`) beigefügt, das Ihnen hilft, die Testdatenbank einzurichten. Sie können es wie folgt ausführen:
+
+```
+% HZ_TEST_DB_TYPE=mysql ./tests/create_test_db.sh
+```
+
+Wenn Sie stattdessen PostgreSQL verwenden, erstellen Sie die Testdatenbank wie folgt:
+
+```
+% HZ_TEST_DB_TYPE=postgres ./tests/create_test_db.sh
+```
+
+Das Skript macht einige Annahmen über Ihre Einrichtung, aber alles ist über Umgebungsvariablen konfigurierbar. Falls Sie Anpassungen benötigen, finden Sie die Details im Quelltext des Skripts.
+
+##### Ausführen der Tests
+
+Sobald Sie die Entwicklerabhängigkeiten installiert und die Testdatenbank eingerichtet haben, können Sie die Tests wie folgt ausführen:
+
+```
+% ./vendor/bin/phpunit -c tests/phpunit.xml
+```
+
+Auch hier macht die Konfiguration in `tests/phpunit.xml` standardmäßig einige Annahmen über Ihre Einrichtung, die mit Hilfe von Umgebungsvariablen außer Kraft gesetzt werden können. Siehe `tests/phpunit.xml` für die Details.
+
+Sie können auch einen bestimmten Test oder eine bestimmte Gruppe von Tests ausführen, indem Sie das Argument `--filter` in PHPUnit verwenden:
+
+```
+% ./vendor/bin/phpunit -c tests/phpunit.xml --filter Autoname
+```
+
+Führt jeden Test mit der Bezeichnung „Autoname“ aus.
+
+##### Erzeugen von Abdeckungsberichten
+
+Um Abdeckungsberichte zu generieren, benötigen Sie einen Treiber, der in der Lage ist, die Abdeckungsinformationen zu generieren, die PHPUnit sammelt. Wir empfehlen [Xdebug], aber sehen Sie sich die PHPUnit [Seite zur Codeabdeckungsanalyse](https://docs.phpunit.de/en/9.6/code-coverage-analysis.html) für Alternativen und Details an.
+
+Wenn Xdebug richtig eingestellt ist, können Sie den Abdeckungsbericht wie folgt erzeugen:
+
+```
+% XDEBUG_MODE=coverage ./vendor/bin/phpunit -c ./tests/phpunit.xml
+```
+
+Dies erzeugt eine Reihe von HTML-Dateien in den Verzeichnissen des Unterverzeichnisses `tests/results/coverage/`.
+
+Öffnen Sie die Datei „index.php“ in Ihrem Webbrowser, um die Statistiken zu sehen.
+
+##### Fehlersuche
+
+Wenn Xdebug installiert ist, können Sie auch Step-Debugging und eine Reihe anderer Dinge tun, um die Tests zu debuggen und Informationen über ihre Ausführung zu erhalten. Lesen Sie auf den [Xdebug]-Seiten und in Ihrem bevorzugten Editor nach, wie Sie dies einrichten können.
+
+#### Teststruktur und Organisation
+
+Die Tests befinden sich im Unterverzeichnis `tests/unit` und in weiteren Unterverzeichnissen, die mehr oder weniger das Verzeichnislayout des Core Code Repository widerspiegeln.
+
+Tests werden als PHP-Klassen geschrieben, die die UnitTestCase-Klasse erweitern (zu finden in `tests/unit/UnitTestcase.php`). Der Dateiname und die darin enthaltene Testklasse sollten gleich lauten und *müssen* mit `Test.php` enden.
+
+Beispiele sind:
+
+- `tests/unit/includes/AccountTest.php`
+- `tests/unit/Lib/ActivityTest.php`
+
+Die Testklassen enthalten eine oder mehrere Testfunktionen, diese müssen öffentlich sein und mit dem Präfix `test` versehen werden.
+
+Hier ist ein Beispiel:
+
+```
+use Zotlabs\Tests\Unit\UnitTestCase;
+
+class AccountTest extends UnitTestCase {
+ public function test_get_account_by_id_returns_existing_account() {
+ $Konto = get_account_by_id(42);
+ $this->assertNotFalse($account);
+ $this->assertEquals(
+ $this->fixtures['account'][0]['account_email'],
+ $account['account_email']
+ );
+ }
+```
+
+Beachten Sie, dass wir versuchen, den Namen der Testfunktion so aussagekräftig wie möglich zu gestalten.
+Die Klasse kann auch eine beliebige Anzahl anderer Funktionen enthalten, um die Dinge ordentlich und übersichtlich zu halten. Diese können je nach Bedarf des Testcodes privat, geschützt oder öffentlich sein.
+Die Ergebnisse und Artefakte der Testläufe werden im Verzeichnis `tests/results/` abgelegt. Dazu gehören in der Regel das Testprotokoll, der Code Coverage Report usw.
+
+#### Hubzilla-spezifische Funktionen
+
+##### Testdatenbankzugriff:
+
+Idealerweise sollte es in der Lage sein, jeden Teil des Codes isoliert zu testen, wobei alle Abhängigkeiten durch Stubs, Mocks oder Fakes ersetzt werden sollten.
+
+Dies ist bei einer Legacy-Codebasis wie Hubzilla, die nicht im Hinblick auf Testbarkeit geschrieben wurde, nicht möglich. Aus diesem Grund verwenden wir eine separate Testdatenbank und stellen sie dem zu testenden Code mit denselben Mechanismen zur Verfügung, die auch Hubzilla normalerweise verwendet.
+Das bedeutet, dass jeder Code, der Datenbankabfragen ausführt, sich normal verhält, wenn er von einem Test aufgerufen wird.
+
+Damit dies funktioniert, stellen wir vor der Ausführung jeder Testfunktion eine Verbindung zur Datenbank her. Wir laden auch einige anfängliche Daten in die Datenbank, um sicherzustellen, dass die Dinge wie erwartet funktionieren und dass wir einen bekannten Zustand der Datenbank haben, wenn der Test läuft.
+Nach Beendigung des Tests wird die Testdatenbank gelöscht, so dass bei der nächsten Testdurchführung derselbe konsistente Zustand vorliegt.
+
+All dies wird von der Basisklasse `UnitTestCase` gehandhabt.
+
+##### Datenbank-Fixtures:
+
+Wir benötigen einige vorhersehbare Inhalte in der Datenbank, um Dinge wie die Protokollierung und andere Inhalte, die für die Tests im Allgemeinen nützlich sind, einzurichten. Dies sind Datenbank-Fixtures, die für jeden Testlauf in die Datenbank geladen werden.
+
+Die Datenbankfixtures befinden sich im Verzeichnis `tests/unit/include/dba/_files` und bestehen aus YAML-Dateien, wobei jede Datei den Inhalt einer Datenbanktabelle mit dem gleichen Namen wie die Datei selbst darstellt.
+
+Obwohl Datenbank-Fixtures sehr nützlich sind, versuchen wir, die Anzahl der Fixtures so gering wie möglich zu halten. In der Regel ist es besser, alle für einen bestimmten Test benötigten Inhalte im Test selbst hinzuzufügen.
+
+##### Fakes:
+
+Fakes sind Klassen, die wir an den zu testenden Code übergeben können und die genauso aussehen und sich genauso verhalten wie die Originalklasse (für den zu testenden Code). Sie sind nützlich, wenn wir Objekte an den zu testenden Code übergeben können, da wir sie vom Testcode aus vollständig kontrollieren können.
+
+Fakes befinden sich im Verzeichnis `tests/fakes`. \ No newline at end of file
diff --git a/doc/de/developer/tools_workflows.md b/doc/de/developer/tools_workflows.md
new file mode 100644
index 000000000..406d293a9
--- /dev/null
+++ b/doc/de/developer/tools_workflows.md
@@ -0,0 +1,5 @@
+### Tools und Arbeitsabläufe für Entwickler
+
+#### Hub-Snapshots
+
+Die Seite mit den [Hub-Snapshots](/hlp/de/adminmanual/hub_snapshot_tools.md) enthält Anweisungen und Skripte für die Erstellung vollständiger Snapshots eines Hubs, um den Wechsel zwischen konsistenten und vollständig bekannten Zuständen zu unterstützen. Dies ist nützlich, um Situationen zu vermeiden, in denen der Inhalt oder das Datenbankschema mit dem Code inkompatibel sein könnte. \ No newline at end of file
diff --git a/doc/de/developer/translations.md b/doc/de/developer/translations.md
new file mode 100644
index 000000000..db6730c6c
--- /dev/null
+++ b/doc/de/developer/translations.md
@@ -0,0 +1,57 @@
+### Übersetzungen
+
+Unsere Übersetzungen werden über Transifex verwaltet. Wenn Sie bei der Übersetzung von Hubzilla in eine andere Sprache helfen möchten, melden Sie sich auf transifex.com an, besuchen Sie [Transifex](https://www.transifex.com/Friendica/hubzilla/?f=&zid=pepecyb@hub.hubzilla.hu) und beantragen Sie die Mitgliedschaft in einem der bestehenden Sprachteams oder erstellen Sie ein neues. Benachrichtigen Sie einen der Hauptentwickler, wenn Sie ein Übersetzungsupdate haben, das zusammengeführt werden muss, oder fragen Sie, ob Sie es selbst zusammenführen können, wenn Sie mit Git und PHP vertraut sind. Wir haben eine Zeichenkettendatei namens 'messages.po', die gettext-kompatibel ist, und eine Handvoll E-Mail-Vorlagen, aus denen wir automatisch die Sprachdateien der Anwendung generieren.
+
+#### Der Übersetzungsprozess
+
+Die in der Benutzeroberfläche von Hubzilla verwendeten Strings werden bei [Transifex](https://www.transifex.com/Friendica/hubzilla/?f=&zid=pepecyb@hub.hubzilla.hu) übersetzt und dann
+in das Git-Repository auf github aufgenommen. Wenn Sie bei der Übersetzung helfen möchten
+einer Sprache helfen wollen, sei es bei der Korrektur von Begriffen oder bei der Übersetzung von Hubzilla in eine derzeit nicht unterstützte Sprache übersetzen möchten, registrieren Sie bitte ein Konto auf transifex.com und kontaktieren Sie dort das Übersetzungsteam.
+
+Hubzilla zu übersetzen ist ganz einfach. Verwenden Sie einfach das Online-Tool auf transifex. Wenn Sie
+Wenn Sie sich nicht mit Git & Co. herumschlagen wollen, ist das kein Problem, wir überprüfen den Status der Übersetzungen regelmäßig und importieren sie in den Quellbaum auf github, damit andere sie nutzen können.
+
+Wir nehmen nicht jede Übersetzung von transifex in den Quellbaum auf, um eine eine verstreute und gestörte Gesamterfahrung zu vermeiden. Als ungefähre Schätzung haben wir eine Untergrenze von 50% übersetzter Strings, bevor wir die Sprache einbeziehen. Diese Grenze richtet sich nur nach der Menge der übersetzten Zeichenfolgen, wobei wir davon ausgehen dass die wichtigsten Zeichenfolgen für die Benutzeroberfläche zuerst von einem Übersetzungsteam übersetzt Team übersetzt werden. Wenn Sie der Meinung sind, dass Ihre Übersetzung vor dieser Grenze brauchbar ist, kontaktieren Sie uns bitte und wir werden die Arbeit Ihres Teams wahrscheinlich in den Quellbaum aufnehmen.
+
+Wenn Sie Ihre Arbeit selbst in den Quellbaum aufnehmen möchten, können Sie dies gerne tun und kontaktieren Sie uns bei allen Fragen, die sich ergeben. Der Prozess ist einfach und Hubzilla wird mit allen notwendigen Werkzeugen geliefert.
+
+Der Speicherort der übersetzten Dateien im Quellbaum ist
+
+`/view/LNG-CODE/`
+
+wobei LNG-CODE der Code der verwendeten Sprache ist, z. B. de für Deutsch oder fr für Französisch.
+Für die E-Mail-Vorlagen (die *.tpl-Dateien) legen Sie sie einfach in das Verzeichnis und Sie sind fertig. Die übersetzten Strings kommen als „hmessages.po“-Datei von Transifex, die in die PHP-Datei übersetzt werden muss, die Hubzilla verwendet. Dazu Sie die Datei in das oben genannte Verzeichnis und verwenden Sie das Dienstprogramm „po2php“ aus dem aus dem Verzeichnis util Ihrer Hubzilla-Installation.
+
+Angenommen, Sie möchten die deutsche Lokalisierung konvertieren, die sich in view/de/hmessages.po steht, würden Sie wie folgt vorgehen.
+
+1. Navigieren Sie an der Eingabeaufforderung in das Basisverzeichnis Ihrer
+
+Hubzilla-Installation
+
+2. Führen Sie das po2php-Skript aus, das die Übersetzung
+
+in die Datei hstrings.php einfügt, die von Hubzilla verwendet wird.
+
+`$> php util/po2php.php view/de/hmessages.po`
+
+Die Ausgabe des Skripts wird in der Datei view/de/hstrings.php platziert, wo froemdoca es erwartet, so dass Sie Ihre Übersetzung sofort testen können.
+
+3. Besuchen Sie Ihre Hubzilla-Seite und überprüfen Sie, ob sie in der Sprache ist, die Sie gerade übersetzt haben. Wenn nicht, versuchen Sie, den Fehler zu finden, wahrscheinlich gibt PHP Ihnen einen Hinweis im Log/Warnings.über den Fehler.
+
+Zur Fehlersuche können Sie auch versuchen, die Datei mit PHP „auszuführen“. Dies sollte keine Ausgabe geben, wenn die Datei in Ordnung ist, aber vielleicht einen Hinweis für die Fehler in der Datei zu finden.
+
+`$> php view/de/hstrings.php`
+
+4. Übertragen Sie die beiden Dateien mit einer aussagekräftigen Commit-Nachricht in Ihr Git Repository, pushen Sie es zu Ihrem Fork des Hubzilla Repository auf github und stellen einen Pull Request für diesen Commit.
+
+#### Dienstprogramme
+
+Zusätzlich zum po2php-Skript gibt es einige weitere Hilfsprogramme für die Übersetzung im Verzeichnis „util“ des Hubzilla-Quellbaums. Wenn Sie nur Hubzilla in eine andere Sprache übersetzen wollen, werden Sie wahrscheinlich keines dieser Werkzeuge aber es gibt Ihnen eine Vorstellung davon, wie der Übersetzungsprozess von Hubzilla funktioniert.
+
+Weitere Informationen finden Sie in der Datei utils/README.
+
+#### Bekannte Probleme
+
+- Hubzilla verwendet die Spracheinstellung des Browsers des Besuchers, um die Sprache für die Benutzeroberfläche. Meistens funktioniert dies, aber es gibt einige bekannte Macken.
+
+- Die frühen Übersetzungen basieren auf den Friendica-Übersetzungen, wenn Sie einige grobe Übersetzungen finden, lassen Sie es uns bitte wissen oder korrigieren Sie sie bei Transifex.
diff --git a/doc/de/developer/unorganized.md b/doc/de/developer/unorganized.md
new file mode 100644
index 000000000..fd668f49e
--- /dev/null
+++ b/doc/de/developer/unorganized.md
@@ -0,0 +1,56 @@
+### Noch zu organisierende Informationen
+
+**Hier ist, wie Sie sich uns anschließen können.**
+Richten Sie sich zunächst ein funktionierendes Git-Paket auf dem System ein, auf dem Sie entwickeln werden.
+
+Erstellen Sie Ihr eigenes Github-Konto.
+
+Sie können das Red-Repository von https://framagit.org/hubzilla/core.git forken/klonen.
+Folgen Sie den Anweisungen hier: [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/), um Ihren eigenen Tracking-Fork auf github zu erstellen und zu verwenden.
+
+Gehen Sie dann auf Ihre Github-Seite und erstellen Sie einen „Pull request“, wenn Sie bereit sind, uns mitzuteilen, dass Ihre Arbeit zusammengeführt werden soll.
+
+**Wichtig**
+
+Bitte ziehen Sie alle Änderungen aus dem Projekt-Repository ein und fügen Sie sie mit Ihrer Arbeit zusammen **, bevor** Sie einen Pull-Request erstellen. Wir behalten uns das Recht vor, jeden Patch abzulehnen, der zu einer großen Anzahl von Merge-Konflikten führt. Dies gilt insbesondere für Sprachübersetzungen, bei denen wir möglicherweise nicht in der Lage sind, die feinen Unterschiede zwischen den konfligierenden Versionen zu verstehen.
+
+**Testen** Sie außerdem **Ihre Änderungen**. Gehen Sie nicht davon aus, dass eine einfache Korrektur nicht etwas anderes kaputt macht. Bitten Sie nach Möglichkeit einen erfahrenen Red-Entwickler, den Code zu überprüfen.
+
+**Wie man Hubzilla thematisiert**
+Dies ist eine kurze Dokumentation über das, was ich beim Versuch, das Aussehen von Hubzilla zu ändern, herausgefunden habe.
+
+Zuerst müssen Sie ein neues Thema erstellen. Dieses befindet sich in /view/theme, und ich habe mich für „redbasic“ entschieden, da es im Moment das einzig verfügbare ist. Nehmen wir an, ich nenne es .
+Oh, und vergessen Sie nicht, die Funktion _init in /php/theme.php in _init() statt redbasic_init() umzubenennen.
+
+Wenn Sie nun Javascript- oder CSS-Dateien hinzufügen müssen, fügen Sie sie zu /js oder /css hinzu und „registrieren“ sie dann in `_init()` durch `head_add_js('file.js')` und `head_add_css('file.css')`.
+Jetzt werden Sie wahrscheinlich eine Vorlage ändern wollen. Diese sind in /view/tpl ODER view//tpl zu finden. Alles, was Sie tun müssen, ist, das, was Sie ändern wollen, in das tpl-Verzeichnis Ihres Themes zu kopieren.
+
+Wir sind ziemlich entspannt, wenn es um Entwickler geht. Wir haben nicht viele Regeln. Einige von uns sind überlastet, und wenn Sie helfen wollen, lassen wir Sie gerne helfen. Wenn Sie sich jedoch an ein paar Richtlinien halten, wird der Prozess reibungsloser und die Zusammenarbeit einfacher. Wir haben Entwickler aus der ganzen Welt mit unterschiedlichen Fähigkeiten, unterschiedlichen kulturellen Hintergründen und unterschiedlicher Geduld. Unsere oberste Regel ist es, andere zu respektieren. Manchmal ist das schwierig, und manchmal haben wir sehr unterschiedliche Ansichten darüber, wie die Dinge funktionieren sollten, aber wenn sich jeder bemüht, werden wir gut miteinander auskommen.
+
+**Hier ist, wie Sie sich uns anschließen können.**
+
+Besorgen Sie sich zunächst ein funktionierendes Git-Paket auf dem System, auf dem Sie die Entwicklung durchführen werden.
+
+Erstellen Sie Ihr eigenes Github-Konto.
+
+Du kannst das Hubzilla-Repository von https://framagit.org/hubzilla/core.git forken/klonen.
+Folgen Sie den Anweisungen hier: [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/), um Ihren eigenen Tracking-Fork auf github zu erstellen und zu verwenden.
+
+Gehen Sie dann auf Ihre Github-Seite und erstellen Sie eine „Pull-Anfrage“, wenn Sie bereit sind, uns mitzuteilen, dass Ihre Arbeit zusammengeführt werden soll.
+
+**Wichtig**
+
+Bitte ziehen Sie alle Änderungen aus dem Projekt-Repository ein und fügen Sie sie mit Ihrer Arbeit zusammen **, bevor** Sie einen Pull-Request erstellen. Wir behalten uns das Recht vor, jeden Patch abzulehnen, der zu einer großen Anzahl von Merge-Konflikten führt. Dies gilt insbesondere für Sprachübersetzungen, bei denen wir möglicherweise nicht in der Lage sind, die feinen Unterschiede zwischen den konfligierenden Versionen zu verstehen.
+
+**Testen** Sie außerdem **Ihre Änderungen**. Gehen Sie nicht davon aus, dass eine einfache Korrektur nicht etwas anderes kaputt macht. Bitten Sie nach Möglichkeit einen erfahrenen Red-Entwickler, den Code zu überprüfen.
+
+Weitere Dokumentation finden Sie auf den Github-Wikiseiten unter: https://github.com/friendica/red/wiki
+
+**Konsensbildung**
+
+Codeänderungen, die einen offensichtlichen Fehler beheben, sind ziemlich einfach. Wenn Sie zum Beispiel auf „Speichern“ klicken und das, was Sie zu speichern versuchen, nicht gespeichert wird, ist es ziemlich offensichtlich, was das beabsichtigte Verhalten sein sollte. Bei der Entwicklung von Funktionsanfragen kann es vorkommen, dass eine große Anzahl von Community-Mitgliedern davon betroffen ist und dass andere Mitglieder der Community nicht mit der Notwendigkeit der Funktion oder der von Ihnen vorgeschlagenen Implementierung einverstanden sind. Es kann sein, dass sie etwas nicht als Fehler oder als wünschenswerte Funktion ansehen.
+
+Wir ermutigen zur Konsensbildung innerhalb der Gemeinschaft, wenn es um eine Funktion geht, die als kontrovers angesehen werden könnte oder wenn es keine einstimmige Entscheidung gibt, dass die vorgeschlagene Funktion der richtige Weg ist, um die Aufgabe zu erfüllen. Die erste Anlaufstelle für Ihre Ideen ist [Channel One](https://zothub.com/channel/one)]. Andere haben vielleicht Anregungen oder können Sie auf Aspekte Ihres Konzepts hinweisen, die in unserem Umfeld problematisch sein könnten. Vielleicht stoßen Sie aber auch auf Widerstand gegen Ihren Plan. Das bedeutet nicht, dass Sie aufhören und/oder die Funktion ignorieren sollten. Hören Sie sich die Bedenken der anderen an und versuchen Sie, alle Probleme bei der Umsetzung zu lösen.
+
+Es gibt Stellen, an denen Widerstände nicht ausgeräumt werden können. In diesen Fällen sollten Sie in Erwägung ziehen, Ihre Funktion **optional** zu machen oder ein nicht standardmäßiges Verhalten vorzusehen, das ausdrücklich aktiviert werden muss. Diese Technik kann oft verwendet werden, wenn eine Funktion eine bedeutende, aber nicht einstimmige Unterstützung hat. Diejenigen, die die Funktion wünschen, können sie einschalten, und diejenigen, die sie nicht wollen, können sie ausgeschaltet lassen.
+Wenn eine Funktion andere Netzwerke oder Websites nutzt oder nur von einer kleinen Minderheit der Gemeinschaft als wünschenswert angesehen wird, sollten Sie in Erwägung ziehen, die Funktion über ein Addon oder Plugin verfügbar zu machen. Auch hier gilt, dass diejenigen, die die Funktion nicht wünschen, sie auch nicht installieren müssen. Plugins sind relativ einfach zu erstellen und „Haken“ können leicht hinzugefügt oder geändert werden, wenn die aktuellen Haken nicht das tun, was nötig ist, damit Ihr Plugin funktioniert. \ No newline at end of file
diff --git a/doc/de/developer/versions.md b/doc/de/developer/versions.md
new file mode 100644
index 000000000..d3301e2f9
--- /dev/null
+++ b/doc/de/developer/versions.md
@@ -0,0 +1,23 @@
+### Versionen und Releases
+
+Hubzilla verwendet derzeit eine Standard-Versionsnummerierungssequenz von $x.$y(.$z), zum Beispiel '1.12' oder '1.12.1'. Die erste Ziffer ist die Hauptversionsnummer. Hauptversionen werden „ungefähr“ einmal pro Jahr veröffentlicht, oft im Dezember.
+
+Die zweite Ziffer ist die Nummer der Nebenversion. Wenn diese Zahl ungerade ist, handelt es sich um eine Entwicklungsversion. Ist die Zahl gerade, handelt es sich um eine freigegebene Version. Nebenversionen werden in der Regel einmal pro Monat freigegeben (von der Entwicklungs- in die Hauptversion verschoben), wenn die Entwicklung „stabil“ ist, aber das wird wahrscheinlich zunehmen. Zukünftig werden kleinere Versionen zwischen einem und drei Monaten veröffentlicht; das entspricht einem stabilen Codepunkt und wenn es einen allgemeinen Konsens in der Gemeinschaft gibt, dass die aktuelle Codebasis stabil genug ist, um eine Veröffentlichung in Betracht zu ziehen.
+
+Die letzte Ziffer ist eine Schnittstellen- oder Patch-Kennung.
+
+Der Freigabeprozess beinhaltet die Änderung der Versionsnummer (per Definition ist die Minor-Versionsnummer ungerade, und die Minor-Nummer wird erhöht). Einmal im Jahr wird bei einer Hauptversion die Hauptversionsnummer erhöht und die Nebenversionsnummer auf 0 zurückgesetzt.
+
+Der Versionskandidat wird in einen neuen Zweig verschoben und die Tests beginnen bzw. werden für einen Zeitraum von 1-2 Wochen fortgesetzt, oder bis alle wichtigen Probleme behoben sind. Dieser Zweig wird in der Regel mit RC (Release Candidate) bezeichnet; beispielsweise steht 1.8RC für die bevorstehende Veröffentlichung der Version 1.8. Zu diesem Zeitpunkt wird die Versionsnummer des Entwicklungszweigs auf die nächsthöhere ungerade Zahl erhöht. (Zum Beispiel 1.9). Neue Entwicklungen können dann im Entwicklungszweig stattfinden.
+
+Fehlerkorrekturen sollten immer auf „dev“ angewendet werden und von dort aus (in der Regel mit git cherry-pick) auf den RC-Zweig und, falls erforderlich, auf den Master- oder offiziellen Release-Zweig übertragen werden.
+
+Zum Zeitpunkt der Erstellung eines Release Candidate wird die Sprachstring-Datei eingefroren, bis eine neue Version veröffentlicht wird. Die Übersetzungsarbeit kann fortgesetzt werden, aber alle Übersetzungen sollten an „dev“ übermittelt und in RC zusammengeführt werden.
+
+Sobald die RC-Tests abgeschlossen sind, wird RC mit 'master' zusammengeführt und der RC-Versionsbezeichner entfernt; dies führt zu einem letzten Checkin, um die Versionsnummer zu ändern. Die CHANGELOG-Datei sollte ebenfalls zu diesem Zeitpunkt oder kurz davor aktualisiert werden. Wenn es während dieser letzten Zusammenführung Konflikte gibt, wird die Zusammenführung abgebrochen und „git merge -s ours“ angewendet. Dies führt dazu, dass der Master-Zweig durch den Inhalt des RC-Zweigs ersetzt wird. Konflikte entstehen oft durch String-Updates, die nach der letzten Veröffentlichung an master vorgenommen wurden und nicht einfach ohne manuelle Bearbeitung gelöst werden können. Da es sich hier um eine Veröffentlichung von getesteten Code handelt, wird von einer manuellen Bearbeitung abgeraten und stattdessen sollte die Strategie des Replacement Merge verwendet werden. Es wird davon ausgegangen, dass RC nun den neuesten, gut getesteten Code enthält.
+
+Sobald die Veröffentlichung live ist und in den Master-Zweig überführt wurde, kann der RC-Zweig entfernt werden.
+
+Nach der Veröffentlichung können Korrekturen an Master vorgenommen werden. Wenn möglich, sollten diese in dev vorgenommen werden und „git cherry-pick“ zum Zusammenführen verwendet werden; dadurch bleiben die Commit-Informationen erhalten und Konflikte beim Zusammenführen im nächsten Zyklus werden vermieden. Nur selten gilt ein Patch nur für den Master-Zweig. Falls nötig, kann das gemacht werden. Wenn die Änderung schwerwiegend ist, sollte die Versionsnummer der Schnittstelle erhöht werden. Dies liegt im Ermessen der Gemeinschaft. In jedem Fall sollte ein „git pull“ des Master-Zweigs immer zur neuesten Version führen, auf die alle Patches nach der Veröffentlichung angewendet werden.
+
+Die Schnittstellennummer (das $z in $x.$y.$z) sollte in dev immer dann erhöht werden, wenn eine Änderung vorgenommen wird, die die Schnittstellen oder die API in inkompatibler Weise verändert, so dass alle externen Pakete (insbesondere Addons und API-Clients), die sich auf das aktuelle Verhalten verlassen, ihre eigenen Schnittstellen an dem Punkt, an dem es sich geändert hat, entdecken und entsprechend ändern können. \ No newline at end of file
diff --git a/doc/de/developer/who_is_a_hubzilla_developer.md b/doc/de/developer/who_is_a_hubzilla_developer.md
new file mode 100644
index 000000000..23ffcf6bd
--- /dev/null
+++ b/doc/de/developer/who_is_a_hubzilla_developer.md
@@ -0,0 +1,14 @@
+### Wer ist ein Hubzilla-Entwickler? Sollte ich das lesen?
+
+Jeder, der dazu beiträgt, Hubzilla besser zu machen, ist ein Entwickler. Es gibt viele verschiedene und wichtige Möglichkeiten, wie Sie zu dieser erstaunlichen Technologie beitragen können, *auch wenn Sie nicht wissen, wie man Code schreibt*. Die Software selbst ist nur ein Teil des Hubzilla-Projekts. Sie können beitragen durch
+
+- den Text in Ihre Sprache übersetzen, damit Menschen auf der ganzen Welt die Möglichkeit haben, Hubzilla zu nutzen
+- für Hubzilla werben und das Bewusstsein für die Plattform durch Blogbeiträge, Artikel und Mundpropaganda verbreiten
+- Erstellung von Kunstwerken und Grafiken für Projektressourcen wie Icons und Marketingmaterial
+- Unterstützung der Projektinfrastruktur wie der Projektwebsite und der Demoserver
+
+*Softwareentwickler* sind natürlich willkommen; es gibt so viele großartige Ideen, die umgesetzt werden können, und nicht genug Leute, um sie alle zu verwirklichen! Die Hubzilla-Codebasis ist ein fortschrittliches und ausgereiftes System, aber die Plattform ist immer noch sehr flexibel und offen für neue Ideen.
+
+Wir sind ziemlich entspannt, wenn es um Entwickler geht. Bei uns gibt es nicht viele Regeln. Einige von uns sind überlastet, und wenn Sie helfen wollen, lassen wir Sie gerne helfen. Wenn Sie sich jedoch an ein paar Richtlinien halten, wird der Prozess reibungsloser und die Zusammenarbeit einfacher. Von allen Entwicklern wird erwartet, dass sie sich an unseren [Verhaltenskodex](/help/de/developer/code_of_conduct.md) halten. Wir haben Entwickler aus der ganzen Welt mit unterschiedlichen Fähigkeiten, unterschiedlichen kulturellen Hintergründen und unterschiedlich viel Geduld. Unsere oberste Regel ist es, andere zu respektieren. Manchmal ist das schwierig, und manchmal haben wir sehr unterschiedliche Ansichten darüber, wie die Dinge funktionieren sollten, aber wenn sich jeder bemüht, werden wir gut miteinander auskommen.
+
+Dieses Dokument wird Ihnen dabei helfen, Hubzilla kennenzulernen und mitzugestalten. \ No newline at end of file
diff --git a/doc/de/developer/zot_protocol.md b/doc/de/developer/zot_protocol.md
new file mode 100644
index 000000000..67252eddf
--- /dev/null
+++ b/doc/de/developer/zot_protocol.md
@@ -0,0 +1,47 @@
+### Das Nomad Protokoll
+
+#### Was ist Nomad?
+
+Nomad ist das revolutionäre Protokoll, das Hubzilla antreibt und die **Kommunikation**, das **Identitätsmanagement** und die **Zugangskontrolle** in einem vollständig **dezentralisierten** Netzwerk unabhängiger Websites, oft als „das Grid“ bezeichnet, ermöglicht. Die daraus resultierende Plattform ist ein robustes System, das den Datenschutz und die Sicherheit unterstützt und gleichzeitig die Art von umfangreichen Webdiensten ermöglicht, die normalerweise nur in zentralisierten, proprietären Lösungen zu finden sind.
+
+Betrachten Sie dieses typische Szenario:
+
+Jaquelina möchte Fotos von ihrem Blog unter **jaquelina.org** mit Roberto teilen, aber mit niemandem sonst. Roberto unterhält seinen eigenen Familien-Hub unter **roberto.net** auf einem völlig unabhängigen Server. Nomad erlaubt Jaquelina, ihre Fotos mit einer *Zugriffskontrollliste (ACL)* zu veröffentlichen, die nur Roberto einschließt. Das bedeutet, dass Roberto die Fotos zwar sehen kann, wenn er ihren Blog besucht, sein Bruder Marco jedoch nicht, und auch kein anderes Familienmitglied, das ein Konto auf **roberto.net** hat.
+
+Der Clou an diesem Szenario ist die Tatsache, dass Roberto sich nie auf Jaquelinas Website angemeldet hat. Stattdessen musste er sich nur einmal mit seinem Passwort auf seiner *eigenen* Website **roberto.net** anmelden. Wenn Roberto **jaquelina.org** besucht, wird er von ihrem Hub nahtlos authentifiziert, indem sein Server im Hintergrund abgefragt wird.
+
+Es ist nicht ungewöhnlich, dass Server technische Probleme haben oder aus verschiedenen Gründen unzugänglich werden. Nomad bietet Robustheit für Robertos Online-Aktivitäten, indem es ihm erlaubt, *Klone* seiner Online-Identität oder seines *Kanals* auf mehreren unabhängigen Hubs zu haben. Stellen Sie sich vor, dass Robertos Server aus irgendeinem Grund zusammenbricht und er sich dort nicht mehr einloggen kann. Er meldet sich einfach bei einem seiner Klone auf **gadfly.com** an, einer Website, die von seinem Freund Peter betrieben wird. Sobald er sich bei **gadfly.com** authentifiziert hat, kann Roberto Jaquelinas Blog wie zuvor ansehen, ohne dass Jaquelina zusätzlichen Zugang gewähren muss!
+
+#### Kommunikation
+
+Kommunikation und soziale Netzwerke sind ein wesentlicher Bestandteil des Grids. Jeder Kanal (und jeder Dienst, der von diesem Kanal bereitgestellt wird) kann die funktionsreiche soziale Kommunikation auf globaler Ebene in vollem Umfang nutzen. Diese Kommunikation kann öffentlich oder privat sein - und private Kommunikation umfasst nicht nur einen vollständig verschlüsselten Transport, sondern auch eine verschlüsselte Speicherung zum Schutz vor versehentlichem Schnüffeln und Offenlegung durch unseriöse Systemadministratoren und Internetdienstanbieter.
+
+Nomad unterstützt eine breite Palette von Hintergrunddiensten im Grid, von Freundschaftsvorschlägen bis hin zu Verzeichnisdiensten. Neue Inhalte und Datenaktualisierungen werden im Hintergrund zwischen den Hubs im gesamten Grid gemäß den Zugriffskontrolllisten und den von den *Sender- und* Empfängerkanälen festgelegten Berechtigungen weitergegeben. Die Daten werden auch zwischen einer beliebigen Anzahl von Kanalklonen synchronisiert, so dass Hub-Mitglieder auch dann auf Daten zugreifen und nahtlos weiter zusammenarbeiten können, wenn ihr primärer Hub nicht erreichbar oder offline ist.
+
+#### Identität
+
+Die Identitätsschicht von Nomad ist einzigartig. Sie bietet ein **unsichtbares Single Sign-On** für alle Standorte im Grid.
+
+Sie bietet auch eine **nomadische Identität**, so dass Ihre Kommunikation mit Freunden, Familie oder anderen Personen, mit denen Sie kommunizieren, nicht durch den Verlust Ihres primären Kommunikationsknotens beeinträchtigt wird - weder vorübergehend noch dauerhaft.
+
+Die wichtigen Teile Ihrer Identität und Ihrer Beziehungen können auf einem USB-Stick oder Ihrem Laptop gesichert werden und jederzeit an einem beliebigen Knoten im Netz erscheinen - mit all Ihren Freunden und Vorlieben.
+
+Entscheidend ist, dass diese nomadischen Instanzen synchron gehalten werden, so dass jede Instanz übernehmen kann, wenn eine andere gefährdet oder beschädigt ist. Dies schützt Sie nicht nur vor größeren Systemausfällen, sondern auch vor vorübergehender Überlastung der Website und staatlicher Manipulation oder Zensur.
+
+Wir sind der Meinung, dass die nomadische Identität, die einmalige Anmeldung und die Dezentralisierung von Hubzilla ein hohes Maß an **Widerstandsfähigkeit** und **Beständigkeit** in die Internetkommunikation einbringen, die angesichts der weltweiten Tendenzen zur Zentralisierung von Unternehmen sowie der massenhaften und wahllosen staatlichen Überwachung und Zensur dringend benötigt werden.
+
+Beim Durchsuchen des Netzes, beim Anzeigen von Kanälen und deren einzigartigen Inhalten, werden Sie nahtlos authentifiziert, sogar über völlig unterschiedliche Server-Hubs hinweg. Sie müssen keine Passwörter eingeben. Sie müssen nichts eintippen. Sie werden auf jeder neuen Seite, die Sie besuchen, einfach mit Ihrem Namen begrüßt.
+
+Wie funktioniert das bei Nomad? Wir nennen es **„magic-auth“**, weil Hubzilla die Details der Komplexität von Single-Sign-On-Logins und nomadischen Identitäten vor dem Surfen im Netz verbirgt. Dies ist eines der Designziele von Hubzilla: die Privatsphäre und die Freiheit im Internet zu erhöhen und gleichzeitig die Komplexität und die Langeweile zu reduzieren, die durch die Notwendigkeit entstehen, für jeden Besuch im Internet neue Passwörter und Anmeldenamen einzugeben. Sie melden sich nur einmal auf Ihrem Home-Hub (oder einem von Ihnen gewählten nomadischen Backup-Hub) an. Dadurch können Sie auf alle authentifizierten Dienste zugreifen, die überall im Netz angeboten werden - wie Einkaufen, Blogs, Foren und Zugang zu privaten Informationen. Ihr Passwort wird nicht auf tausend verschiedenen Websites gespeichert, sondern auf Servern, die Sie kontrollieren oder denen Sie vertrauen.
+
+Sie können nicht zum Schweigen gebracht werden. Sie können nicht aus dem Netz entfernt werden, es sei denn, Sie selbst entscheiden sich dafür, es zu verlassen.
+
+#### Zugangskontrolle
+
+Die Identitätsschicht von Nomad ermöglicht es Ihnen, fein abgestufte Berechtigungen für jeden Inhalt zu vergeben, den Sie veröffentlichen möchten - und diese Berechtigungen erstrecken sich über das gesamte Grid. Das ist so, als hätte man eine riesige Website, die aus einer Armee kleiner individueller Websites besteht - und bei der jeder Kanal im Grid seine Datenschutz- und Freigabepräferenzen für alle von ihm erstellten Webressourcen vollständig kontrollieren kann.
+
+Derzeit unterstützt Hubzilla die Zugriffskontrolle für viele Datentypen, darunter Diskussionsbeiträge und -kommentare, Fotoalben, Veranstaltungen, Cloud-Dateien, Webseiten, Wikis und mehr. Jedes Objekt und die Art und Weise, wie und mit wem es geteilt wird, unterliegt vollständig Ihrer Kontrolle.
+
+Diese Art der Kontrolle ist bei großen Unternehmensanbietern trivial, da sie die Benutzerdatenbank besitzen. Im Grid brauchen Sie keine riesige Benutzerdatenbank auf Ihrem Rechner - denn das Grid **ist** Ihre Benutzerdatenbank. Sie verfügt über eine im Grunde unendliche Kapazität (begrenzt durch die Gesamtzahl der Hubs, die im Internet online sind) und ist auf Hunderte, möglicherweise sogar Millionen von Computern verteilt.
+
+Der Zugang kann für jede Ressource, jeden Kanal oder jede Gruppe von Kanälen gewährt oder verweigert werden - überall im Grid. Andere können auf Ihre Inhalte zugreifen, wenn Sie es ihnen erlauben, und sie müssen nicht einmal ein Konto in Ihrem Hub haben. \ No newline at end of file
diff --git a/doc/de/developer/zot_structures.md b/doc/de/developer/zot_structures.md
new file mode 100644
index 000000000..c3f77c8b7
--- /dev/null
+++ b/doc/de/developer/zot_structures.md
@@ -0,0 +1,141 @@
+### Nomad-Strukturen
+
+#### Nomad-Signaturen
+
+Alle signierten Daten in Nomad werden durch eine RSA-Signaturoperation mit dem privaten Schlüssel des Initiators erzeugt. Das binäre Ergebnis wird dann für den Transport mit base64url kodiert.
+
+#### Nomad-Verschlüsselung
+
+Die Verschlüsselung erfolgt derzeit mit AES256CTR. Weitere Algorithmen KÖNNEN unterstützt werden. Ein 32-Oktett-Schlüssel und ein 16-Oktett-Initialisierungsvektor werden nach dem Zufallsprinzip erzeugt. Die gewünschten Daten werden dann mit diesen generierten Zeichenketten verschlüsselt und das Ergebnis base64url-kodiert. Dann wird ein Array erstellt:
+
+- data
+
+ Die base64url-kodierten verschlüsselten Daten
+
+- alg
+
+ Der gewählte Algorithmus, in diesem Fall die Zeichenkette „aes256ctr“.
+
+- key
+
+ Der zufällig erzeugte Schlüssel, RSA-verschlüsselt mit dem öffentlichen Schlüssel des Empfängers, und das Ergebnis base64url-kodiert
+
+- iv
+
+ Der zufällig erzeugte Initialisierungsvektor, RSA-verschlüsselt mit dem öffentlichen Schlüssel des Empfängers, und das Ergebnis base64url-kodiert
+
+#### Grundlegendes Nomad-Paket
+
+Wird verwendet, um einen Dialog mit einer anderen Nomad-Site einzuleiten. Dieses Paket KANN verschlüsselt sein. Das Vorhandensein eines Array-Elements 'iv' zeigt an, dass eine Verschlüsselung vorgenommen wurde. Beim Senden eines 'auth_check'-Pakets MUSS dieses Paket verschlüsselt werden, wobei der öffentliche Schlüssel der Zielsite (der Site-Schlüssel, im Gegensatz zu einem Absenderschlüssel) verwendet wird.
+
+```
+ {
+ "type":"notify",
+ "sender":{
+ "guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
+ "guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
+ "url":"http:\/\/podunk.edu",
+ "url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
+ "sitekey":"-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxTeIwXZWrw/S+Ju6gewh
+LgkKnNNe2uCUqCqMZoYgJar3T5sHCDhvXc4dDCbDkxVIaA/+V1mURtBV60a3IGjn
+OAO0W0XGGLe2ED7G5o9U8T9mVGq8Mauv0v1oQ5wIR1gEAhBavkQ2OUGuF/YKn2nj
+HlKsv9HzUAHpcDMUe3Uklc2RhQbMcnJxEgkyjCkDyrTtCZzISkTAocHvpCG1KSog
+njUZdiz9UWxvM4rCFkCJvQU4RwRZJb7GA9ul+9JrF7NvUQTx8csRP2weBk1E9yyj
+wbe187E0eVj9RXX2Mx3mYhgrTdodxLOVMSXZLg1/SMpeFFl7QBhuM0SiOPg8a7Et
+e2iNA/RD4WiUFqCDfafasRa1TOozOm7LA+08lkAh5PeQPJsJbrX0wVVft++Y+5/z
+BvcUOP73vcbz7j5hJ7HLsbQtye/UUCfODBFybuDqRM84Aet8rjZohX7vukXdMD4I
+2HuB7pjR4uIfyYr0J63ANkvbsn8LR+RnirmHrK5H/OgxxjXcfYbGEQgFxvxhF6lA
+FpMu6Do4dx3CIb6pRmZ8bjSImXexJ0BSo9n3gtrz0XYLecsYFlQ9+QQjm83qxyLb
+M23in0xqMVsyQvzjNkpImrO/QdbEFRIIMee83IHq+adbyjQR49Z2hNEIZhkLPc3U
+2cJJ2HkzkOoF2K37qwIzk68CAwEAAQ==
+-----END PUBLIC KEY-----
+"
+ },
+ "recipients":{
+ {
+ "guid":"lql-1VnxtiO4-WF0h72wLX1Fu8szzHDOXgQaTbELwXW77k8AKFfh-hYr70vqMrc3SSvWN-Flrc5HFhRTWB7ICw",
+ "guid_sig":"PafvEL0VpKfxATxlCqDjfOeSIMdmpr3iU7X-Sysa1h5LzDpjSXsjO37tYZL-accb1M5itLlfnW5epkTa5I4flsW21zSY1A2jCuBQUTLLGV7rNyyBy7lgqJUFvAMRx0TfXzP9lcaPqlM9T1tA6jfWOsOmkdzwofGeXBnsjGfjsO2xdGYe6vwjOU0DSavukvzDMnOayB9DekpvDnaNBTxeGLM45Skzr7ZEMcNF7TeXMbnvpfLaALYEKeQs9bGH-UgAG8fBWgzVAzeBfx_XSR1rdixjyiZGP0kq0h35SlmMPcEjliodOBFwMXqpXFB7Ibp4F6o6te2p2ErViJccQVG8VNKB6SbKNXY6bhP5zVcVsJ-vR-p4xXoYJJvzTN7yTDsGAXHOLF4ZrXbo5yi5gFAlIrTLAF2EdWQwxSGyLRWKxG8PrDkzEzX6cJJ0VRcLh5z6OI5QqQNdeghPZbshMFMJSc_ApCPi9_hI4ZfctCIOi3T6bdgTNKryLm5fhy_eqjwLAZTGP-aUBgLZpb1mf2UojBn6Ey9cCyq-0T2RWyk-FcIcbV4qJ-p_8oODqw13Qs5FYkjLr1bGBq82SuolkYrXEwQClxnrfKa4KYc2_eHAXPL01iS9zVnI1ySOCNJshB97Odpooc4wk7Nb2Fo-Q6THU9zuu0uK_-JbK7IIl6go2qA"
+ },
+ },
+ "callback":"\/post",
+ "version":"1.2",
+ "encryption":{
+ "aes256ctr"
+ },
+ "secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
+ "secret_sig":"0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
+ }
+```
+
+type
+
+Der Nachrichtentyp: **notify, purge, refresh, force_refresh, auth_check, ping** oder **pickup**. Der Inhalt der Pakete variiert je nach Nachrichtentyp. Hier wird das **notify-Paket** beschrieben.
+
+
+
+callback
+
+Eine Zeichenkette, die an die URL angehängt wird und den Nomad-Kommunikationsendpunkt auf diesem System identifiziert. In der Regel ist dies die Zeichenfolge „/post“.
+
+
+
+version
+
+Die Kennung des Nomad-Protokolls, damit künftige Protokollrevisionen nebeneinander bestehen können.
+
+
+
+encryption
+
+Array der unterstützten Verschlüsselungsalgorithmen, geordnet nach abnehmender Präferenz. Wenn keine kompatiblen Verschlüsselungsmethoden angegeben sind, MÜSSEN Anwendungen „aes256cbc“ verwenden.
+
+
+
+secret
+
+Eine 64-stellige Zeichenkette, die von der sendenden Seite zufällig generiert wird.
+
+
+
+secret_sig
+
+Die RSA-Signatur des Geheimnisses, signiert mit dem privaten Schlüssel des Absenders.
+
+
+
+sender
+
+Ein Array aus vier Komponenten, die eine tragbare Identität liefern. Wir können die angegebene URL kontaktieren und ein Nomad-Infopaket herunterladen, um den öffentlichen Schlüssel des Absenders zu erhalten, und diesen zur Überprüfung der Absender-Guid und der Signaturen der Entsende-URL verwenden.
+
+- guid
+
+ In der Regel eine 64 Zeichen lange base64url-kodierte Zeichenfolge. Sie wird erzeugt, wenn eine Identität erstellt wird, und es wird versucht, dass sie eindeutig ist; dies ist jedoch nicht erforderlich.
+
+- guid_sig
+
+ Die RSA-Signatur der guid, signiert mit dem privaten Schlüssel des Absenders und base64url-kodiert.
+
+- url
+
+ Die Basis-URL des Ortes, von dem dieser Beitrag stammt.
+
+- url_sig
+
+ Die RSA-Signatur der url, signiert mit dem privaten Schlüssel des Absenders und base64url kodiert.
+
+- sitekey
+
+ Der öffentliche Schlüssel der in der Url angegebenen Website
+
+recipients
+
+Wird nur für private Nachrichten verwendet. Ein Array von Umschlag-Empfängern. Jeder Empfänger wird durch ein Array aus guid und guid_sig dargestellt. Wenn Empfänger angegeben sind, wird das gesamte Paket auch mit einem ausgehandelten kryptografischen Algorithmus oder 'aes256cbc' gekapselt, wenn keiner ausgehandelt werden konnte.
+
+- guid
+
+ Die guid eines privaten Empfängers.
+
+- guid_sig
+
+ Die RSA-Signatur der guid, signiert mit dem privaten Schlüssel des Empfängers und base64url-kodiert \ No newline at end of file
diff --git a/doc/de/features.bb b/doc/de/features.bb
deleted file mode 100644
index febdc65ee..000000000
--- a/doc/de/features.bb
+++ /dev/null
@@ -1,203 +0,0 @@
-[h1][b]$Projectname-Features[/b][/h1]
-
-[h1]$Projectname kurz zusammengefasst[/h1]
-
-tl;dr
-
-$Projectname stellt verteiltes Web-Publishing und soziale Kommunikation mit [b]dezentraler Rechteverwaltung[/b] zur Verfügung.
-
-Aber was genau ist eine dezentrale Rechteverwaltung? Sie gibt mir die Möglichkeit, etwas auf meiner Website (Fotos, Medien, Dateien, Webseiten etc.) mit bestimmten Personen auf anderen Websites zu teilen – aber nicht unbedingt mit [i]allen[/i] auf diesen Websites. Und: Sie brauchen kein Konto auf meiner Website und müssen sich auf meiner Website nicht extra einloggen, um sich die Dinge anzusehen, die ich mit ihnen geteilt habe. Sie haben ein Konto auf ihrer Heimat-Website, und „Magic Authentication“ zwischen den Websites besorgt den Rest. Da das Netzwerk dezentral aufgebaut ist, gibt es auch keinen einzelnen Betreiber des Netzwerks, der an der Rechteverwaltung vorbei alles sehen kann.
-
-$Projectname kombiniert viele Features von tradionellen Blogs, sozialen Netzwerken und Medien, Content-Management-Systemen und persönlichem Cloud-Speicher auf einer einfach zu nutzenden Plattform. Jeder Hub (Web-Server) im Grid kann isoliert operieren oder sich mit anderen Hubs zu einem Super-Netzwerk vereinen. Die Kontrolle über die Privatsphäre hat immer derjenige, der die Inhalte veröffentlicht.
-
-$Projectname ist eine Open-Source Webserver-Applikation, geschrieben ursprünglich für PHP/MySQL. Mit minimaler Erfahrung als Admin ist sie leicht zu installieren. Sie kann auch durch Plugins und Themes und weitere Angebote von Drittanbietern erweitert werden.
-
-[h1][b]$Projectname-Features[/b][/h1]
-
-$Projectname ist ein Allzweck-Web-Publishing- und Kommunikationsnetzwerk mit einigen einzigartigen Features. Es wurde für eine große Bandbreite von Nutzern entwickelt, von Nutzern sozialer Netzwerke über technisch nicht interessierte Blogger bis hin zu PHP-Experten und erfahrenen Systemadministratoren.
-
-Diese Seite listet einige der Kern-Features von $Projectname auf, die in der offiziellen Distribution enthalten sind. Wie immer bei freier Open-Source-Software sind den Möglichkeiten keine Grenzen gesetzt. Beliebige Erweiterungen, Addons, Themes und Konfigurationen sind möglich.
-
-[h2]Entwickelt für Privatsphäre und Freiheit[/h2]
-
-Eines der Design-Ziele von $Projectname ist einfache Kommunikations über das Web, ohne die Privatsphäre zu vernachlässigen, wenn die Nutzer das wünschen. Um dieses Ziel zu erreichen, verfügt $Projectname über einige Features, die beliebige Stufen des Privatsphäre-Schutzes ermöglichen:
-
-[b]Beziehungs-Tool[/b]
-
-Wenn Du in der $Projectname einen Kontakt hinzufügst (und das Beziehungs-Tool aktiviert hast), hast Du die Möglichkeit, einen „Grad der Freundschaft“ zu bestimmen. Bespiel: Wenn Du ein Blog eines Bekannten hinzufügst, könntest Du ihm den Freundschaftsgrad „Bekannte“ (Acquaintances) geben.
-
-Wenn Du aber den privaten Kanal eines Freundes hinzufügst, wäre der Freundschaftsgrad „Freunde“ vermutlich passender.
-
-Wenn Du allen Kontakten solche Freundschaftsgrade zugeordnet hast, kannst Du mit dem Beziehungs-Tool, das (sofern aktiviert) oben auf Deiner Matrix-Seite erscheint, bestimmen, welche Inhalte Du sehen willst. Indem Du die Schieberegler einstellst, legst Du fest, was angezeigt wird – nur Kanäle mit einem Freundschaftsgrad innerhalb des eingestellten Bereichs werden angezeigt
-
-Das Beziehungs-Tool erlaubt blitzschnelles Filtern von großen Mengen Inhalt, gruppiert nach Freundschaftsgrad.
-
-[b]Filter für Verbindungen[/b]
-
-Du kannst ganz genau kontrollieren, was in Deinem Stream erscheint, wenn Du den optionalen „Filter für Verbindungen“ aktivierst. Dann kannst Du beim Bearbeiten einer Verbindung Kriterien festlegen, nach denen entschieden wird, ob einzelne Beiträge dieser Verbindung importiert werden sollen oder nicht (Einschluss oder Ausschluss möglich). Wurde ein Beitrag einmal importiert, wirst Du auch alle Kommentare dazu sehen, egal ob eines der Kriterien auf sie zutrifft oder nicht. Du könntest einzelne Wörter festlegen, die, wenn sie in einem Beitrag vorkommen, dafür sorgen, dass er geblockt oder eben nicht geblockt wird. Auch reguläre Ausdrüce können benutzt werden, genauso wie Hashtags oder sogar die Sprache, in der der Beitrag verfasst wurde.
-
-[b]Zugriffsrechte[/b]
-
-Wenn Du Inhalte mit anderen teilst, hast Du die Option, den Zugriff darauf einzuschränken. Wenn Du auf das Schloss unterhalb des Beitrags-Editors klickst, kannst Du auswählen, wer diesen Beitrag sehen darf, indem Du einfach auf die Namen klickst.
-
-Diese Nachricht kann dann nur vom Absender und den eingestellten Empfängern betrachtet werden. Mit anderen Worten, sie erscheint nicht öffentlich auf einer Pinnwand.
-
-Solche Zugriffsrechte gibt es bei Beiträgen, Fotos, Terminen, Webseiten, Chat-Räumen und Dateien.
-
-[b]Ein Passwort für alle $Projectname-Server (Single Sign-on)[/b]
-
-Zugriffsrechte funktionieren im gesamten Grid mit allen Kanälen. Die meisten Links, die innerhalb von $Projectname verlinken, enthalten Deine Identität (zid), so dass der Zielserver Dich direkt anmelden kann. Du kannst Dich aber auch so auf jedem $Projectname-Server mit Deiner $Projectname-Identität anmelden und erhältst dann Zugriff auf die Inhalte, die für Dich freigegeben sind.
-
-Du loggst Dich nur einmal auf Deinem Heimat-Hub ein. Ab dann funktioniert die Authentifizierung gegenüber anderen $Projectname-Hubs „magisch“ von selbst.
-
-[b]Dateiablage (Cloud) mit WebDAV-Zugriff[/b]
-
-Du kannst in Deinem persönlichen Speicherbereich Dateien hochladen und ihn sogar als Verzeichnis von Deinem lokalen Betriebssystem anzeigen lassen (via WebDAV). Die Dateien können über Zugriffsrechte bestimmten $Projectname-Mitgliedern (und den Mitgliedern mancher anderer Netze) zugänglich gemacht oder auch komplett öffentlich zur Verfügung gestellt werden.
-
-[b]Fotoalben[/b]
-
-Stelle Deine Fotos online in Alben zur Verfügung. Auch hier kann der Zugriff über die Zugriffsrechte eingeschränkt werden.
-
-[b]Terminkalender[/b]
-
-Im eingebauten Terminkalender kannst Du Termine erstellen und verwalten. Auch hier greifen die Zugriffsrechte für andere. Termine können im vcalendar/iCal-Format importiert/exportiert und in Beiträgen mit anderen geteilt werden. Wenn Deine Kontakte ihren Geburtstag in ihr Profil eingetragen haben, werden diese Geburtstage automatisch zu Deinem Kalender hinzugefügt – mit entsprechender Anpassung der Zeitzone, so dass Du nie zu früh oder zu spät gratulierst. Termine werden normalerweise mit Teilnehmerzählern erstellt, so dass Deine Freunde und Verbindungen sofort zu- oder absagen können.
-
-[b]Chat-Räume[/b]
-
-Du kannst Chaträume erstellen und über die Zugriffsrechte nur bestimmten Nutzern öffnen. Die Nachrichten sind sicherer verschlüsselt als es normalerweise bei Jabber/XMPP, IRC und anderen Instant Messengern üblich ist. Über Plugins ist es aber auch möglich, andere IM-Dienste aus der $Projectname heraus zu nutzen.
-
-[b]Erstellen von Webseiten[/b]
-
-In $Projectname gibt es Werkzeuge für „Content Management“, mit denen Du einfache Webseiten erstellen kannst, aber auch komplexe Layouts, Menüs, Blöcke und Widgets. Auch hier greifen die Zugriffsrechte, so dass die entstandenen Seiten nur von denen betrachtet werden können, denen Du das Recht dazu eingeräumt hast.
-
-[b]Apps[/b]
-
-$Projectname-Mitglieder könnnen Apps erstellen und verteilen. Anders als bei anderen Systemen, bei denen man an den System-Anbieter gebunden ist, werden diese Apps komplett vom App-Anbieter kontrolliert, der auf Wunsch seine eigene Zugriffskontrolle und ein Bezahlsystem einbauen kann. Die meisten Apps in der $Projectname sind kostenlos. Sie sind sehr einfach und ohne große Programmierkenntnisse zu erstellen.
-
-[b]Layout[/b]
-
-Das Seiten-Layout basiert auf eine Beschreibungssprache namens Comanche. $Projectname ist selbst in Comanche-Layouts verfasst, die man verändern kann. Dadurch ist eine sehr starke Anpassung an die eigenen Bedürfnisse möglich, wie man sie so in Multi-User-Umgebungen normalerweise nicht findet.
-
-[b]Lesezeichen[/b]
-
-Du kannst Lesezeichen teilen, speichern und verwalten, direkt aus den Unterhaltungen mit anderen heraus.
-
-[b]Verschlüsselung privater Nachrichten[/b]
-
-Private Nachrichten werden verschlüsselt gespeichert. Das bietet keine absolute Sicherheit, erschwert aber einfaches Herumschnüffeln durch den Administrator oder Internet Provider.
-
-Jeder $Projectname-Kanal hat seinen eigenes 4096-bit-RSA-Schlüsselpaar, das erzeugt wird, wenn der Kanal erstellt wird. Damit werden private Nachrichten und Beiträge mit eingeschränktem Empfängerkreis während der Übermittlung zu anderen Hubs geschützt.
-
-Zusätzlich können Nachrichten mit Ende-zu-Ende-Verschlüsselung versehen werden, so dass weder $Projectname-Hub-Administratoren noch ISPs irgendetwas mitlesen können, solange sie nicht über das Passwort verfügen.
-
-Komplett öffentliche Nachrichten werden weder in der Datenbank noch bei der Übertragung verschlüsselt (abgesehen ggfs. von SSL).
-
-Private Nachrichten und Beiträge können gelöscht (zurückgezogen) werden, aber es kann natürlich nicht garantiert werden, dass der Empfänger sie nicht schon gelesen hat.
-
-Alle Beiträge können mit einem „Verfallsdatum“ versehen werden. Zu diesem Zeitpunkt werden sie dann von den Servern der Empfänger gelöscht.
-
-[b]Verbindung zu anderen Diensten[/b]
-
-Neben Plugins, die das „crossposten“ zu diversen anderen Netzwerk erlauben, wird der Import von RSS/Atom-Feeds nativ unterstützt, auch, um mit diesen Inhalten spezielle Kanäle zu erstellen. Außerdem kann über das Diaspora-Protokoll mit Kontakten in den Netzwerken Friendica und Diaspora kommuniziert werden. Diese Unterstützung ist als experimentell eingestuft, da diese Netzwerke nicht die gleichen Möglichkeiten wie $Projectname in Sachen Privatsphäre und Verschlüsselung bieten, so dass Kommunikation mit ihnen zu Privatsphäreproblemen führen könnte.
-
-Weiterhin wird OpenID auf experimenteller Ebene unterstützt und kann bei den Zugriffsrechten genutzt werden, um Inhalte für per OpenID authentifizierte Nutzer freizugeben. An dieser Funktion wird noch gearbeitet. Jeder $Projectname-Hub kann außerdem als OpenID-Provider dienen.
-
-Die Inhalte von Kanälen können als Quellen für andere Kanäle dienen (wenn der Kanalinhaber das erlaubt), so dass Themen-Kanäle mit den Inhalten von zwei oder mehr Kanälen erstellt werden können.
-
-[b]Sammlungen[/b]
-
-„Sammlungen“ sind unsere Implementierung von Privatsphäregruppen, ähnlich den „Kreisen“ bei Google+ und den „Aspekten“ bei Diaspora. Sammlungen können zur Filterung der angezeigten Nachrichten genutzt werden (nur Threads anzeigen, die von einem Mitglied dieser Sammlung gestartet wurden), aber auch zum Setzen von Zugriffsrechten (bevor der Beitrag abgeschickt wird).
-
-[b]Verzeichnisdienste[/b]
-
-Wir stellen einfachen Zugriff auf ein Mitgliederverzeichnis zur Verfügung, samt einer dezentralen Möglichkeit, sich neue Kontakte basierend auf den eigenen vorschlagen zu lassen. Die Verzeichnis-Server sind normale $Projectname-Server, bei denen der Administrator sich entschieden hat, sie auch als Verzeichnis agieren zu lassen. Das benötigt mehr Ressourcen als eine normale $Projectname-Installation, deshalb ist das nicht voreingestellt. Die Verzeichnis-Server synchronisieren sich miteinander, so dass (abgesehen von einer gewissen Verzögerung bis zur nächsten Synchronisation) alle Verzeichnis-Server aktuelle Informationen über das gesamte Netzwerk bereitstellen können.
-
-[b]TLS/SSL[/b]
-
-$Projectname-Server, die TLS/SSL benutzen, verschlüsseln ihre Kommunikation vom Server zum Nutzer mit SSL. Nach den aktuellen Enthüllungen über das Umgehen von Verschlüsselung durch NSA, GHCQ und andere Dienste, sollte man jedoch nicht mehr davon ausgehen, dass diese Verbindungen nicht mitgelesen werden können. Private Kommunikation (nicht komplett öffentliche Beiträge) wird darüberhinaus zusätzlich verschlüsselt, bevor sie von einem Server zum anderen geschickt wird.
-
-[b]Kanal-Einstellungen[/b]
-
-Wenn ein Kanal erstellt wird, muss eine bestimmte Zugriffsrechte-Kategorie (z.B. öffentliches Forum oder privater Kanal für soziales Netzwerken) ausgewählt werden, die dafür sorgt, dass sinnvolle Privatsphäre-Einstellungen für diese Art von Kanal ausgewählt werden.
-
-Wenn Du die Experten-Kategorie wählst, kannst Du detaillierte Zugriffseinstellungen für verschiedenste Aspekte der Kommunikation festlegen. Unter den „Sicherheits- und Privatsphäre-Einstellungen“ kann für jeden Punkt auf der linken Seite eine von 7-8 möglichen Optionen aus dem Menü gewählt werden. Daneben gibt es diverse weitere Einstellmöglichkeiten zum Thema Privatsphäre.
-
-Die Optionen für die einzelnen Punkte (z.B., wer Deine normalen Beiträge sehen kann) sind:
-[ul][*]Niemand außer Du selbst
-[*]Nur die, denen Du es explizit erlaubst
-[*]Angenommene Verbindungen
-[*]Beliebige Verbindungen
-[*]Jeder auf diesem Website
-[*]Alle $Projectname-Nutzer
-[*]Jeder authentifizierte
-[*]Jeder im Internet[/ul]
-
-[b]Private und öffentliche Foren[/b]
-
-Foren sind Kanäle, in denen mehrere Nutzer als Autoren fungieren können; eine Nachricht eines entsprechend berechtigten Nutzers an das Forum wird an alle Foren-Mitglieder verteilt. Es gibt momentan zwei Arten, um auf diese Weise an ein Forum zu posten: 1) Direktes Posten auf der Kanal-Seite des Forums („wall-to-wall post“) oder 2) über @mention-Tags. Jeder kann Foren erstellen, und sie können für beliebige Zwecke genutzt werden. Das Kanal-Verzeichnis ermöglicht es, direkt nach öffentlichen Foren zu suchen. Private Foren können meist nur von den Mitgliedern beschickt und gelesen werden.
-
-[b]Klone[/b]
-
-Konten in der $Projectname werden auch als [i]nomadische Identitäten[/i] bezeichnet. Nomadisch, weil bei anderen Diensten die Identität eines Nutzers an den Server oder die Plattform gebunden ist, auf der er ursprünglich erstellt wurde. Ein Facebook- oder Gmail-Konto ist and diese Dienste gekettet. Er funktioniert nicht ohne Facebook.com bzw. Gmail.com.
-
-Bei $Projectname ist das anders. Sagen wir, Du hast eine $Projectname-Indentität namens tina@$Projectnamehub.com. Die kannst Du auf einen anderen Server klonen, mit dem gleichen oder einem anderen Namen, zum Beispiel lebtEwig@Anderer$ProjectnameHub.info.
-
-Beide Kanäle sind jetzt miteinander synchronisiert, das heißt, dass alle Kontakte und Einstellungen auf dem Klon immer die gleichen sind wie auf dem ursprünglichen Kanal. Es ist egal, ob Du eine Nachricht von dort aus oder vom Klon aus schickst. Alle Nachrichten sind in beiden Klonen vorhanden.
-
-Das ist ein ziemlich revolutionäres Feature, wenn man sich einige Szenarien dazu ansieht:
-
-[ul][*]Was passiert, wenn ein Server, auf dem sich Deine Identität befindet, plötzlich offline ist (sicher haben viele von Euch den Twitter-„Fail Whale“ gesehen und verflucht)? Ohne Klone ist der Nutzer nicht in der Lage zu kommunizieren, bis der Server wieder online ist. Mit Klonen loggst Du Dich einfach bei Deinem geklonten Kanal ein und lebst glücklich bis an Dein Ende.
-[*]Der Administrator Deines $Projectname-Hubs kann es sich nicht länger leisten, seinen für alle kostenlosen Server zu bezahlen. Er gibt bekannt, dass der Server in zwei Wochen vom Netz gehen wird. Zeit genug, um Deine $Projectname-Kanäle auf andere Server zu klonen und somit Verbindungen und Freunde zu behalten.
-[*]Was, wenn Dein Kanal staatlicher Zensur unterliegt? Dein Server-Admin könnte gezwungen werden, Dein Konto und alle damit verbundenen Kanäle und Daten zu löschen. Durch Klone bietet $Projectname Zensur-Resistenz. Wenn Du willst, kannst Du hunderte von Klonen haben, alle mit unterschiedlichen Namen und auf unterschiedlichen Hubs überall im Internet.[/ul]
-
-$Projectname bietet interessante, neue Möglichkeiten in Bezug auf die Privatsphäre. Mehr dazu unter „Tipps und Tricks zur privaten Kommunikation“.
-
-Klone unterliegen einigen Restriktionen. Eine vollständige Erklärung zum Klonen von Identitäten gibt es unter „Klone“.
-
-[b]Mehrere Profile[/b]
-
-Jeder Kanal kann beliebig viele Profile mit unterschiedlichen Informationen definieren. Dann kannst Du einstellen, wer von Deinen Kontakten welches Profil zu sehen bekommt. Das Default-Profil ist für alle anderen zu sehen und kann so auf nur wenige Informationen beschränkt werden, während Freunde und Bekannte mehr zu sehen bekommen.
-
-[b]Kanal-Backups[/b]
-
-In $Projectname gibt es ein einfaches Ein-Klick-Backup, mit dem Du ein komplettes Backup Deiner Kanal-Einstellungen und Verbindungen herunterladen kannst.
-
-Solche Backups sind ein Weg, um Klone zu erstellen, und können genutzt werden, um einen Kanal wiederherzustellen.
-
-[b]Löschen von Konten[/b]
-
-Konten und Kanäle können sofort gelöscht werden, indem Du einfach auf einen Link klickst. Das wars. Alle damit verbundenen Inhalte werden aus dem Grid gelöscht (inklusiver aller Beiträge und sonstiger Inhalte, die von dem gelöschten Konto/Kanal erzeugt wurden). Je nach Anzahl Deiner Verbindungen kann es etwas dauern, bis die Inhalte auch von allen Servern Deiner Kontakte gelöscht werden, aber die Löschung wird so schnell wie sinnvoll möglich durchgeführt.
-
-[h2]Erstellen von Inhalten[/h2]
-
-[b]Beiträge schreiben[/b]
-
-$Projectname unterstützt diverse verschiedene Wege, um Inhalte mit Auszeichnung (z.B. fett, kursiv, farbig etc.) zu erstellen. Voreinstellung ist die $Projectname-Variante von BBCode (wie in vielen Web-Foren) mit einigen Ergänzungen, die nur hier funktionieren. Du kannst auch Markdown benutzen, wenn Dir das leichter fällt. Bis vor kurzem konnte auch ein grafischer Editor eingesetzt werden, der jedoch große Probleme aufwies und deshalb entfernt wurde. Wir suchen gerade nach einer Alternative.
-
-Webseiten können neben BBCode und Markdown auch in HTML und Plain Text erstellt werden.
-
-[b]Inhalte löschen[/b]
-
-Alle Inhalte in $Projectname bleiben unter der Kontrolle des Mitglieds (bzw. Kanals), der sie ursprünglich erstellt hat. Alle Beiträge können jederzeit gelöscht werden, egal, ob sie auf dem Heimat-Server des Nutzers oder auf einem anderen Server erstellt wurden, an dem der Nutzer via Zot (Kommunikations- und Authentifizierungsprotokoll von $Projectname) angemeldet war.
-
-[b]Medien[/b]
-
-Genau wie jedes andere Blog-System, soziale Netzwerk oder Mikro-Blogging-Dienst unterstützt $Projectname das Hochladen von Dateien, das Einbetten von Bildern und Videos und das Verlinken von Seiten.
-
-[b]Vorschau/Editieren[/b]
-
-Vor dem Absenden kann eine Vorschau von Beiträgen betrachtet werden. Außerdem können Beiträge auch nach dem Absenden noch verändert werden.
-
-[b]Umfragen[/b]
-
-Beiträge können als Umfragen gestaltet werden – die Leser können dann mittels entsprechender Buttons zustimmen, ablehnen oder sich enthalten, was ähnlich wie „Likes“ am Beitrag sichtbar wird. Dadurch kannst Du abschätzen, wie gut neue Ideen ankommen, oder informelle Umfragen starten.
-
-[b]$Projectname erweitern[/b]
-
-Die $Projectname kann auf vielerlei Art erweitert werden: Durch Server-Anpassung, persönliche Anpassung, setzen von Optionen, Themes und Addons/Plugins.
-
-[b]API[/b]
-
-Es existiert eine API, die von beliebigen Programmen/Apps und Diensten genutzt werden kann. Sie basiert auf der ursprünglichen Twitter-API (für die es hunderte von Tools und Apps gibt). Sie wird aktuell erweitert, um Zugriff auf Möglichkeiten zu gewähren, die es nur in $Projectname gibt. Authentifikation erfolgt über Login/Passwort oder OAuth. Eine Client-Registrierung für OAuth-Applikationen ist möglich.
-
-#include doc/macros/main_footer.bb;
diff --git a/doc/de/functions.md b/doc/de/functions.md
new file mode 100644
index 000000000..484c978b9
--- /dev/null
+++ b/doc/de/functions.md
@@ -0,0 +1,229 @@
+### Funktionen
+
+Im Folgenden einige der Hauptfunktionen von Hubzilla, die in der offiziellen Version enthalten sind. Hubzilla ist eine hochgradig erweiterbare Plattform, so dass weitere Funktionen und Möglichkeiten durch zusätzliche Themes und Plugins hinzugefügt werden können.
+
+
+
+#### Affinity-Schieberegler
+
+Beim Hinzufügen von Verbindungen in Hubzilla haben Mitglieder die Möglichkeit, der neuen Verbindung „Affinitätsstufen“ zuzuweisen (wie eng Ihre Freundschaft ist). Wenn Sie z. B. jemanden hinzufügen, dessen Blog Sie verfolgen, können Sie seinem Kanal die Affinitätsstufe „Bekannte“ zuweisen.
+
+Wenn Sie hingegen den Kanal eines Freundes hinzufügen, könnten Sie ihm die Affinitätsstufe „Freunde“ zuweisen.
+
+An diesem Punkt passt das Hubzilla *Affinitäts-Schieberegler-Tool*, das in der Regel oben auf Ihrer „Stream“-Seite erscheint, den Inhalt der Seite so an, dass die Kanäle innerhalb des gewünschten Affinitätsbereichs angezeigt werden. Kanäle außerhalb dieses Bereichs werden nicht angezeigt, es sei denn, Sie passen den Schieberegler so an, dass er sie einschließt.
+
+Der Schieberegler für die Affinität ermöglicht die sofortige Filterung großer Mengen von Inhalten, gruppiert nach Grad der Nähe.
+
+
+
+#### Filterung von Verbindungen
+
+Mit dem optionalen „Verbindungsfilter“ haben Sie die Möglichkeit, genau zu steuern, was in Ihrem Stream angezeigt wird. Wenn er aktiviert ist, bietet der Verbindungseditor Eingaben zur Auswahl von Kriterien, die erfüllt werden müssen, um einen bestimmten Beitrag aus einem bestimmten Kanal aufzunehmen oder auszuschließen. Sobald ein Beitrag zugelassen wurde, sind alle Kommentare zu diesem Beitrag erlaubt, unabhängig davon, ob sie den Auswahlkriterien entsprechen. Sie können Wörter auswählen, die, falls vorhanden, den Beitrag blockieren oder sicherstellen, dass er in Ihren Stream aufgenommen wird. Reguläre Ausdrücke können für eine noch feinere Kontrolle verwendet werden, ebenso wie Hashtags oder sogar die erkannte Sprache des Beitrags.
+
+
+
+#### Zugriffskontrolllisten
+
+Bei der Freigabe von Inhalten haben die Mitglieder die Möglichkeit einzuschränken, wer den Inhalt sehen kann. Durch Anklicken des Vorhängeschlosses unter dem Freigabefeld kann man die gewünschten Empfänger des Beitrags auswählen, indem man auf deren Namen klickt.
+
+Nach dem Versenden kann die Nachricht nur vom Absender und den ausgewählten Empfängern eingesehen werden. Mit anderen Worten, die Nachricht wird nicht auf öffentlichen Wänden erscheinen.
+
+Zugangskontrolllisten können auf Inhalte und Beiträge, Fotos, Ereignisse, Webseiten, Chatrooms und Dateien angewendet werden.
+
+
+
+#### Einmalige Anmeldung
+
+Zugriffskontrolllisten funktionieren dank unserer einzigartigen Single-Sign-On-Technologie für alle Kanäle im Grid. Die meisten internen Links bieten ein Identitäts-Token, das auf anderen Hubzilla-Sites verifiziert und zur Kontrolle des Zugriffs auf private Ressourcen verwendet werden kann. Sie melden sich einmal bei Ihrem Home Hub an. Danach ist die Authentifizierung für alle Hubzilla-Ressourcen „magisch“.
+
+
+
+#### WebDAV-fähiger Dateispeicher
+
+Dateien können mit den Dienstprogrammen Ihres Betriebssystems (in den meisten Fällen per Drag & Drop) in Ihren persönlichen Speicherbereich hochgeladen werden. Sie können diese Dateien mit Zugriffskontrolllisten für eine beliebige Kombination von Hubzilla-Mitgliedern (einschließlich einiger Netzwerkmitglieder von Drittanbietern) schützen oder sie öffentlich zugänglich machen.
+
+
+
+#### Fotoalben
+
+Speichern Sie Fotos in Alben. Alle Ihre Fotos können durch Zugriffskontrolllisten geschützt werden.
+
+
+
+#### Veranstaltungskalender
+
+Erstellen und verwalten Sie Ereignisse und Aufgaben, die ebenfalls mit Zugriffskontrolllisten geschützt werden können. Ereignisse können mit dem Industriestandard vcalendar/iCal-Format in andere Software importiert/exportiert und in Posts mit anderen geteilt werden. Geburtstagsereignisse werden automatisch von Ihren Freunden hinzugefügt und in Ihre korrekte Zeitzone umgewandelt, so dass Sie genau wissen, wann der Geburtstag stattfindet - unabhängig davon, wo auf der Welt Sie sich in Bezug auf die Geburtstagsperson befinden. Veranstaltungen werden normalerweise mit Anwesenheitszählern erstellt, so dass Ihre Freunde und Verbindungen sofort zusagen können.
+
+
+
+#### Chaträume
+
+Sie können eine beliebige Anzahl von persönlichen Chatrooms erstellen und den Zugang über Zugangskontrolllisten erlauben. Diese sind in der Regel sicherer als XMPP, IRC und andere Instant Messaging-Transporte, obwohl wir auch die Nutzung dieser anderen Dienste über Plugins erlauben.
+
+
+
+#### Erstellung von Webseiten
+
+Hubzilla verfügt über zahlreiche „Content Management“-Werkzeuge zur Erstellung von Webseiten, einschließlich Layout-Bearbeitung, Menüs, Blöcke, Widgets und Seiten-/Inhaltsbereiche. Alle diese Tools können zugriffsgesteuert werden, so dass die resultierenden Seiten nur für das vorgesehene Publikum zugänglich sind.
+
+
+
+#### Anwendungen
+
+Apps können von Mitgliedern erstellt und verteilt werden. Diese unterscheiden sich von den traditionellen „Vendor Lockin“-Apps, da sie vollständig vom Autor kontrolliert werden - der den Zugriff auf die Zielseiten der App kontrollieren und entsprechend für diesen Zugriff bezahlen kann. Die meisten Apps in Hubzilla sind kostenlos und können auch von Personen ohne Programmierkenntnisse leicht erstellt werden.
+
+
+
+#### Gestaltung
+
+Das Seitenlayout basiert auf einer Beschreibungssprache namens Comanche. Hubzilla selbst ist in Comanche-Layouts geschrieben, die Sie ändern können. Dies ermöglicht ein Maß an individueller Anpassung, das man in so genannten „Multi-User-Umgebungen“ normalerweise nicht findet.
+
+
+
+#### Lesezeichen
+
+Teilen und speichern/verwalten Sie Lesezeichen von Links, die in Unterhaltungen bereitgestellt werden.
+
+
+
+#### Verschlüsselung privater Nachrichten und Datenschutzaspekte
+
+Private Nachrichten werden in einem verschlüsselten Format gespeichert. Dies ist zwar nicht hundertprozentig sicher, verhindert aber in der Regel das gelegentliche Ausspähen durch den Site-Administrator oder ISP.
+
+Jeder Hubzilla-Channel verfügt über einen eigenen Satz privater und zugehöriger öffentlicher RSA 4096-Bit-Schlüssel, die bei der ersten Erstellung des Channels generiert werden. Dies wird verwendet, um private Nachrichten und Beiträge während der Übertragung zu schützen.
+
+Zusätzlich können Nachrichten mit einer „Ende-zu-Ende-Verschlüsselung“ erstellt werden, die von Hubzilla-Betreibern, ISPs oder anderen Personen, die den Passcode nicht kennen, nicht gelesen werden kann.
+
+Öffentliche Nachrichten werden in der Regel weder bei der Übertragung noch bei der Speicherung verschlüsselt.
+
+Private Nachrichten können zurückgezogen (nicht gesendet) werden, obwohl es keine Garantie gibt, dass der Empfänger sie noch nicht gelesen hat.
+
+Beiträge und Nachrichten können mit einem Verfallsdatum versehen werden, nach dessen Ablauf sie auf der Seite des Empfängers gelöscht/entfernt werden.
+
+
+
+#### Dienst-Föderation
+
+Neben zusätzlichen „Cross-Post-Connectoren“ zu einer Vielzahl von alternativen Netzwerken gibt es eine native Unterstützung für den Import von Inhalten aus RSS/Atom-Feeds und deren Verwendung zur Erstellung spezieller Kanäle. Es sind auch Plugins verfügbar, um mit anderen über die Protokolle Diaspora und GNU-Social (OStatus) zu kommunizieren. Diese Netzwerke unterstützen keine nomadische Identität oder domänenübergreifende Zugriffskontrolle; allerdings wird die grundlegende Kommunikation mit/von Diaspora, Friendica, GNU-Social, Mastodon und anderen Anbietern, die diese Protokolle verwenden, unterstützt.
+
+Es gibt auch eine experimentelle Unterstützung für OpenID-Authentifizierung, die in Zugriffskontrolllisten verwendet werden kann. Dies ist ein Projekt, an dem gearbeitet wird. Ihr Hubzilla-Hub kann als OpenID-Provider verwendet werden, um Sie bei externen Diensten, die diese Technologie nutzen, zu authentifizieren.
+
+Channels können die Berechtigung haben, zu „derivativen Channels“ zu werden, bei denen zwei oder mehr bestehende Channels kombiniert werden, um einen neuen thematischen Channel zu erstellen.
+
+
+
+#### Datenschutz-Gruppen
+
+Unsere Implementierung von Datenschutzgruppen ähnelt den „Circles“ von Google und den „Aspekten“ von Diaspora. Dies ermöglicht Ihnen, Ihren eingehenden Stream nach ausgewählten Gruppen zu filtern und die ausgehende Zugriffskontrollliste automatisch auf die Mitglieder dieser Gruppe zu beschränken, wenn Sie etwas posten. Sie können dies jederzeit (vor dem Senden des Beitrags) rückgängig machen.
+
+
+
+#### Verzeichnisdienste
+
+Wir bieten einen einfachen Zugang zu einem Mitgliederverzeichnis und stellen dezentralisierte Tools zur Verfügung, die „Vorschläge“ für Freunde machen können. Die Verzeichnisse sind normale Hubzilla-Sites, die sich entschieden haben, die Rolle des Verzeichnisservers zu übernehmen. Dies erfordert mehr Ressourcen als die meisten typischen Sites und ist daher nicht die Standardeinstellung. Die Verzeichnisse werden synchronisiert und gespiegelt, so dass sie alle aktuelle Informationen über das gesamte Netzwerk enthalten (vorbehaltlich normaler Ausbreitungsverzögerungen).
+
+
+
+#### TLS/SSL
+
+Bei Hubzilla-Hubs, die TLS/SSL verwenden, wird die Kommunikation zwischen Client und Server über TLS/SSL verschlüsselt. In Anbetracht der jüngsten Enthüllungen in den Medien über die weit verbreitete, weltweite Überwachung und die Umgehung der Verschlüsselung durch die NSA und das GCHQ kann man davon ausgehen, dass HTTPS-geschützte Kommunikation auf verschiedene Weise kompromittiert werden kann. Private Kommunikation wird daher auf einer höheren Ebene verschlüsselt, bevor sie nach außen gesendet wird.
+
+
+
+#### Channel-Einstellungen
+
+Bei der Erstellung eines Channels wird eine Rolle ausgewählt, die eine Reihe von vorkonfigurierten Sicherheits- und Datenschutzeinstellungen anwendet. Diese werden nach bewährten Verfahren ausgewählt, um den Datenschutz auf dem gewünschten Niveau zu halten.
+
+Wenn Sie eine „benutzerdefinierte“ Datenschutzrolle wählen, können Sie für jeden Kanal fein abgestufte Berechtigungen für verschiedene Aspekte der Kommunikation festlegen. Unter der Überschrift „Sicherheits- und Datenschutzeinstellungen“ gibt es zum Beispiel für jeden Aspekt auf der linken Seite sechs (6) mögliche Anzeige-/Zugriffsoptionen, die durch Anklicken des Dropdown-Menüs ausgewählt werden können. Es gibt auch eine Reihe anderer Datenschutzeinstellungen, die Sie bearbeiten können.
+
+Die Optionen sind:
+
+\- Niemand außer Ihnen selbst.
+\- Nur diejenigen, die Sie ausdrücklich zulassen.
+\- Jeder, der in Ihrem Adressbuch steht.
+\- Alle Personen auf dieser Website.
+\- Alle Personen in diesem Netzwerk.
+\- Jeder, der authentifiziert ist.
+\- Jeder, der sich im Internet befindet.
+
+
+
+#### Öffentliche und private Foren
+
+Foren sind in der Regel Kanäle, an denen sich mehrere Autoren beteiligen können. Derzeit gibt es zwei Möglichkeiten, Beiträge in Foren zu veröffentlichen: 1) „Wand-zu-Wand“-Beiträge und 2) über @mention-Tags im Forum. Foren können von jedem erstellt und für jeden Zweck verwendet werden. Das Verzeichnis enthält eine Option zur Suche nach öffentlichen Foren. In privaten Foren können nur Beiträge verfasst werden, die oft nur von Mitgliedern gesehen werden.
+
+
+
+#### Klonen von Accounts
+
+Konten in Hubzilla werden als *nomadische Identitäten* bezeichnet, da die Identität eines Mitglieds nicht an den Hub gebunden ist, in dem die Identität ursprünglich erstellt wurde. Wenn Sie zum Beispiel ein Facebook- oder Gmail-Konto erstellen, ist es an diese Dienste gebunden. Sie können nicht ohne Facebook.com oder Gmail.com funktionieren.
+
+Angenommen, Sie haben eine Hubzilla-Identität namens `tina@Hubzillahub.com` erstellt. Sie können sie auf einen anderen Hubzilla-Hub klonen, indem Sie denselben oder einen anderen Namen wählen: `liveForever@SomeHubzillaHub.info`
+
+Beide Kanäle werden nun synchronisiert, d. h. alle Ihre Kontakte und Einstellungen werden auf Ihrem Klon dupliziert. Es spielt keine Rolle, ob Sie einen Beitrag von Ihrem ursprünglichen Hub oder von dem neuen Hub aus senden. Die Beiträge werden auf beiden Konten gespiegelt.
+
+Dies ist eine ziemlich revolutionäre Funktion, wenn wir uns einige Szenarien vor Augen führen:
+
+\- Was passiert, wenn der Hub, in dem sich eine Identität befindet, plötzlich offline geht? Ohne Klonen ist ein Mitglied nicht in der Lage zu kommunizieren, bis der Hub wieder online ist (zweifellos haben viele von Ihnen den „Fail Whale“ von Twitter gesehen und verflucht). Beim Klonen loggen Sie sich einfach in Ihr geklontes Konto ein, und das Leben geht fröhlich weiter.
+
+\- Der Administrator Ihres Hubs kann es sich nicht mehr leisten, für seinen kostenlosen und öffentlichen Hubzilla-Hub zu bezahlen. Er kündigt an, dass der Hub in zwei Wochen abgeschaltet wird. Das gibt Ihnen reichlich Zeit, Ihre Identität(en) zu klonen und Ihre Hubzilla-Beziehungen, Freunde und Inhalte zu bewahren.
+
+\- Was ist, wenn Ihre Identität der staatlichen Zensur unterliegt? Ihr Hub-Anbieter kann gezwungen sein, Ihr Konto sowie alle Identitäten und zugehörigen Daten zu löschen. Mit Klonen bietet Hubzilla **Widerstand gegen Zensur**. Wenn Sie möchten, können Sie Hunderte von Klonen haben, die alle einen anderen Namen tragen und auf vielen verschiedenen Hubs im Internet verstreut sind.
+
+Hubzilla bietet interessante neue Möglichkeiten für die Privatsphäre. Weitere Informationen finden Sie auf der Seite [Tipps zum Schutz der Privatsphäre](/help/de/member/protection_of_privacy.md).
+
+Es gelten einige Vorbehalte. Eine vollständige Erklärung des Klonens von Identitäten finden Sie auf der Seite [Klonen](/help/de/member/clone.md).
+
+
+
+#### Mehrere Profile
+
+Es kann eine beliebige Anzahl von Profilen mit unterschiedlichen Informationen erstellt werden, die für bestimmte Ihrer Verbindungen/Freunde sichtbar gemacht werden können. Ein „Standard“-Profil kann von jedem eingesehen werden und kann begrenzte Informationen enthalten, während mehr Informationen für ausgewählte Gruppen oder Personen zugänglich sind. Das bedeutet, dass das Profil (und der Inhalt der Website), das Ihre biertrinkenden Freunde sehen, anders sein kann als das, was Ihre Kollegen sehen, und auch völlig anders als das, was für die Allgemeinheit sichtbar ist.
+
+
+
+#### Account-Sicherung
+
+Hubzilla bietet ein einfaches Konto-Backup mit nur einem Klick, bei dem Sie ein vollständiges Backup Ihres Profils herunterladen können. Die Backups können dann zum Klonen oder Wiederherstellen eines Profils verwendet werden.
+
+
+
+#### Konto-Löschung
+
+Konten können sofort gelöscht werden, indem Sie auf einen Link klicken. Das war's. Alle zugehörigen Inhalte werden dann aus dem Netz gelöscht (einschließlich der Beiträge und aller anderen Inhalte, die von dem gelöschten Profil erstellt wurden). Je nach der Anzahl der Verbindungen, die Sie haben, kann der Vorgang des Löschens entfernter Inhalte einige Zeit in Anspruch nehmen, aber er wird so schnell wie möglich durchgeführt.
+
+
+
+#### Löschung von Inhalten
+
+Alle in Hubzilla erstellten Inhalte bleiben unter der Kontrolle des Mitglieds (oder Channels), das sie ursprünglich erstellt hat. Ein Mitglied kann zu jeder Zeit eine Nachricht oder eine Reihe von Nachrichten löschen. Der Löschvorgang stellt sicher, dass der Inhalt gelöscht wird, unabhängig davon, ob er auf dem primären (Heimat-)Hub eines Channels oder auf einem anderen Hub gepostet wurde, bei dem der Channel über Nomad (Hubzilla-Kommunikations- und Authentifizierungsprotokoll) remote authentifiziert wurde.
+
+
+
+#### Medien
+
+Ähnlich wie jedes andere moderne Blogging-System, soziale Netzwerk oder ein Micro-Blogging-Dienst unterstützt Hubzilla das Hochladen von Dateien, das Einbetten von Videos und das Verlinken von Webseiten.
+
+
+
+#### Vorschau/Bearbeitung
+
+Beiträge und Kommentare können vor dem Senden in der Vorschau angezeigt und nach dem Senden bearbeitet werden.
+
+
+
+#### Abstimmen/Umfragen
+
+Beiträge können in „Umfrage“-Elemente umgewandelt werden, die es den Lesern ermöglichen, Feedback zu geben, das in Zählern für „Zustimmung“, „Ablehnung“ und „Enthaltung“ zusammengefasst wird. Auf diese Weise können Sie das Interesse für Ideen messen und informelle Umfragen erstellen.
+
+
+
+#### Erweitern von Hubzilla
+
+Hubzilla kann auf verschiedene Weise erweitert werden: durch Anpassung der Website, persönliche Anpassung, Optionseinstellungen, Themen und Addons/Plugins.
+
+
+
+#### API
+
+Eine API ist für die Nutzung durch Dienste von Drittanbietern verfügbar. Ein Plugin bietet auch eine grundlegende Implementierung der Twitter-API (für die es Hunderte von Drittanbieter-Tools gibt). Der Zugriff kann über Login/Passwort oder OAuth erfolgen, und eine Client-Registrierung von OAuth-Anwendungen ist vorgesehen. \ No newline at end of file
diff --git a/doc/de/general.bb b/doc/de/general.bb
deleted file mode 100644
index 2ce5533f1..000000000
--- a/doc/de/general.bb
+++ /dev/null
@@ -1,17 +0,0 @@
-[h2]Informationen über das Projekt und diesen Hub[/h2]
-
-[zrl=[baseurl]/help/Privacy]Informationen zum Datenschutz[/zrl]
-
-[h3]Externe Ressourcen[/h3]
-[zrl=[baseurl]/help/external-resource-links]Links zu externen Ressourcen[/zrl]
-
-[url=https://framagit.org/hubzilla/core/]Haupt-Website[/url]
-[url=https://framagit.org/hubzilla/addons]Addons-Website[/url]
-
-[url=[baseurl]/help/credits]$Projectname Credits[/url]
-
-[h3]Über diesen $Projectname-Hub[/h3]
-[zrl=[baseurl]/help/TermsOfService]Nutzungsbedingungen dieses Hubs[/zrl]
-[zrl=[baseurl]/siteinfo]Informationen zu diesem Hub und der $Projectname-Version[/zrl]
-[zrl=[baseurl]/siteinfo/json]Detaillierte technische Informationen zu diesem Hub im JSON-Format[/zrl]
-
diff --git a/doc/de/glossary.md b/doc/de/glossary.md
new file mode 100644
index 000000000..32b2c4fe8
--- /dev/null
+++ b/doc/de/glossary.md
@@ -0,0 +1,31 @@
+### Glossar
+
+- **Hub**
+
+ Eine Instanz dieser Software, die auf einem Standard-Webserver läuft.
+
+- **Grid**
+
+ Das globale Netzwerk von Hubs, die mit Hilfe des Zot-Protokolls Informationen untereinander austauschen.
+
+- **Kanal**
+
+ Die grundlegende Identität im Grid. Ein Channel kann eine Person, einen Blog oder ein Forum repräsentieren, um nur einige zu nennen. Channels können Verbindungen mit anderen Channels herstellen, um Informationen mit sehr detaillierten Berechtigungen zu teilen.
+
+- **Klonen**
+
+ Kanäle können Klone haben, die mit separaten und ansonsten nicht verbundenen Konten auf unabhängigen Hubs verbunden sind. Die mit einem Channel geteilte Kommunikation wird zwischen den Channel-Klonen synchronisiert, so dass ein Channel Nachrichten senden und empfangen und auf gemeinsame Inhalte von mehreren Hubs zugreifen kann. Dies bietet Ausfallsicherheit bei Netzwerk- und Hardwareausfällen, was für selbst gehostete oder mit begrenzten Ressourcen ausgestattete Webserver ein großes Problem darstellen kann. Das Klonen ermöglicht es Ihnen, einen Channel vollständig von einem Hub zu einem anderen zu verschieben und dabei Ihre Daten und Verbindungen mitzunehmen. Siehe nomadische Identität.
+
+- **nomadische Identität**
+
+ Die Fähigkeit, eine Identität über unabhängige Hubs und Webdomänen hinweg zu authentifizieren und einfach zu migrieren. Die nomadische Identität bietet echte Eigentumsrechte an einer Online-Identität, da die Identitäten der Kanäle, die von einem Konto auf einem Hub kontrolliert werden, nicht an den Hub selbst gebunden sind. Ein Hub ist eher eine Art „Gastgeber“ für Kanäle. Bei Hubzilla haben Sie kein „Konto“ auf einem Server wie bei typischen Websites, sondern Sie besitzen eine Identität, die Sie über das Netz mitnehmen können, indem Sie Klone verwenden.
+
+- **Nomad**
+
+ Das neuartige JSON-basierte Protokoll zur Implementierung sicherer dezentraler Kommunikation und Dienste. Es unterscheidet sich von vielen anderen Kommunikationsprotokollen, indem es die Kommunikation auf einem dezentralen Identitäts- und Authentifizierungsrahmen aufbaut. Die Authentifizierungskomponente ähnelt dem OpenID-Konzept, ist aber von DNS-basierten Identitäten isoliert. Soweit möglich, erfolgt die Fernauthentifizierung still und unsichtbar. Dies bietet einen Mechanismus für eine verteilte Zugangskontrolle im Internet, der unauffällig ist.
+
+ Ursprünglich trug das Protokoll den Namen Zot. Im Jahr 2021 wurde es von Mike Macgirvin in Nomad umbenannt. Inzwischen wird zwischen dem Protokoll und der Implementierung (Software) des Protokolls unterschieden. Die Implementierung wird bei Hubzilla weiterhin Zot genannt (genauer Zot6, weil es die Implementierung des damals noch gleich benannten Protokolls in der Version 6 fortführt).
+
+ Grundsätzlich wird das Protokoll nur noch als Nomad bezeichnet. Wenn der Begriff Zot oder Zot6 (meist in der Form "Nomad/Zot6") verwendet wird, ist, sofern es um das Protokoll geht, das Nomad-Protokoll gemeint. Zot bzw. Zot6 tauchen eigenständig nur noch im Bereich der Softwareentwicklung bei Hubzilla auf, weil die Routinen und die Programmbibliothek, welche Nomad in der Praxis umsetzen, diesen Namen tragen.
+
+ <u>Hinweis:</u> Die Implementierungen von Nomad in Hubzilla und in (streams) sind in Teilen nicht miteinander kompatibel. Das betrifft insbesondere die nomadische Identität. So ist es nicht möglich, einen Hubzilla-Kanal (Nomad v. Zot6) auf einer (streams) Instanz (Nomad v, Zot12) zu klonen, und umgekehrt.
diff --git a/doc/de/hook/about_hook.bb b/doc/de/hook/about_hook.bb
new file mode 100644
index 000000000..22b60d786
--- /dev/null
+++ b/doc/de/hook/about_hook.bb
@@ -0,0 +1 @@
+[h2]about_hook[/h2]
diff --git a/doc/de/hook/accept_follow.bb b/doc/de/hook/accept_follow.bb
new file mode 100644
index 000000000..e8b1ed0c4
--- /dev/null
+++ b/doc/de/hook/accept_follow.bb
@@ -0,0 +1 @@
+[h2]accept_follow[/h2]
diff --git a/doc/de/hook/account_downgrade.bb b/doc/de/hook/account_downgrade.bb
new file mode 100644
index 000000000..63bae0a58
--- /dev/null
+++ b/doc/de/hook/account_downgrade.bb
@@ -0,0 +1 @@
+[h2]account_downgrade[/h2]
diff --git a/doc/de/hook/account_settings.bb b/doc/de/hook/account_settings.bb
new file mode 100644
index 000000000..91b3a8385
--- /dev/null
+++ b/doc/de/hook/account_settings.bb
@@ -0,0 +1 @@
+[h2]account_settings[/h2]
diff --git a/doc/de/hook/account_settings_post.bb b/doc/de/hook/account_settings_post.bb
new file mode 100644
index 000000000..bbd7a57a8
--- /dev/null
+++ b/doc/de/hook/account_settings_post.bb
@@ -0,0 +1 @@
+[h2]account_settings_post[/h2]
diff --git a/doc/de/hook/activity_decode_mapper.bb b/doc/de/hook/activity_decode_mapper.bb
new file mode 100644
index 000000000..43d08a136
--- /dev/null
+++ b/doc/de/hook/activity_decode_mapper.bb
@@ -0,0 +1 @@
+[h2]activity_decode_mapper[/h2]
diff --git a/doc/de/hook/activity_filter.bb b/doc/de/hook/activity_filter.bb
new file mode 100644
index 000000000..9d0768577
--- /dev/null
+++ b/doc/de/hook/activity_filter.bb
@@ -0,0 +1 @@
+[h2]activity_filter[/h2]
diff --git a/doc/de/hook/activity_mapper.bb b/doc/de/hook/activity_mapper.bb
new file mode 100644
index 000000000..db65fadc4
--- /dev/null
+++ b/doc/de/hook/activity_mapper.bb
@@ -0,0 +1 @@
+[h2]activity_mapper[/h2]
diff --git a/doc/de/hook/activity_obj_decode_mapper.bb b/doc/de/hook/activity_obj_decode_mapper.bb
new file mode 100644
index 000000000..a96b32eee
--- /dev/null
+++ b/doc/de/hook/activity_obj_decode_mapper.bb
@@ -0,0 +1 @@
+[h2]activity_obj_decode_mapper[/h2]
diff --git a/doc/de/hook/activity_obj_mapper.bb b/doc/de/hook/activity_obj_mapper.bb
new file mode 100644
index 000000000..7c14a1b81
--- /dev/null
+++ b/doc/de/hook/activity_obj_mapper.bb
@@ -0,0 +1 @@
+[h2]activity_obj_mapper[/h2]
diff --git a/doc/de/hook/activity_order.bb b/doc/de/hook/activity_order.bb
new file mode 100644
index 000000000..4a4670d03
--- /dev/null
+++ b/doc/de/hook/activity_order.bb
@@ -0,0 +1 @@
+[h2]activity_order[/h2]
diff --git a/doc/de/hook/activity_received.bb b/doc/de/hook/activity_received.bb
new file mode 100644
index 000000000..2e9d68bf3
--- /dev/null
+++ b/doc/de/hook/activity_received.bb
@@ -0,0 +1 @@
+[h2]activity_received[/h2]
diff --git a/doc/de/hook/addon_app_installed_filter.bb b/doc/de/hook/addon_app_installed_filter.bb
new file mode 100644
index 000000000..e610b3205
--- /dev/null
+++ b/doc/de/hook/addon_app_installed_filter.bb
@@ -0,0 +1,18 @@
+[h2]addon_app_installed_filter[/h2]
+
+Allow plugins to filter the result of addon_app_installed.
+
+Code excerpt:
+
+[code]
+ $filter_arr = [
+ 'uid'=>$uid,
+ 'app'=>$app,
+ 'installed'=>$r
+ ];
+ call_hooks('addon_app_installed_filter',$filter_arr);
+ $r = $filter_arr['installed'];
+[/code]
+
+cxref: Zotlabs/Lib/Apps.php
+
diff --git a/doc/de/hook/affinity_labels.bb b/doc/de/hook/affinity_labels.bb
new file mode 100644
index 000000000..7234b7632
--- /dev/null
+++ b/doc/de/hook/affinity_labels.bb
@@ -0,0 +1 @@
+[h2]affinity_labels[/h2]
diff --git a/doc/de/hook/api_perm_is_allowed.bb b/doc/de/hook/api_perm_is_allowed.bb
new file mode 100644
index 000000000..862cbd653
--- /dev/null
+++ b/doc/de/hook/api_perm_is_allowed.bb
@@ -0,0 +1 @@
+[h2]api_perm_is_allowed[/h2]
diff --git a/doc/de/hook/app_destroy.bb b/doc/de/hook/app_destroy.bb
new file mode 100644
index 000000000..386d7af16
--- /dev/null
+++ b/doc/de/hook/app_destroy.bb
@@ -0,0 +1,4 @@
+[h2]app_destroy[/h2]
+
+Allows addons to perform some post delete actions.
+
diff --git a/doc/de/hook/app_installed_filter.bb b/doc/de/hook/app_installed_filter.bb
new file mode 100644
index 000000000..f0d91d6f0
--- /dev/null
+++ b/doc/de/hook/app_installed_filter.bb
@@ -0,0 +1,17 @@
+[h2]app_installed_filter[/h2]
+
+Allow plugins to filter the result of app_installed.
+
+Code excerpt:
+
+[code]
+ $filter_arr = [
+ 'uid'=>$uid,
+ 'app'=>$app,
+ 'installed'=>$r
+ ];
+ call_hooks('app_installed_filter',$filter_arr);
+ $r = $filter_arr['installed'];
+[/code]
+
+cxref: Zotlabs/Lib/Apps.php
diff --git a/doc/de/hook/atom_author.bb b/doc/de/hook/atom_author.bb
new file mode 100644
index 000000000..c9d05a593
--- /dev/null
+++ b/doc/de/hook/atom_author.bb
@@ -0,0 +1 @@
+[h2]atom_author[/h2]
diff --git a/doc/de/hook/atom_entry.bb b/doc/de/hook/atom_entry.bb
new file mode 100644
index 000000000..0aec89f16
--- /dev/null
+++ b/doc/de/hook/atom_entry.bb
@@ -0,0 +1 @@
+[h2]atom_entry[/h2]
diff --git a/doc/de/hook/atom_feed.bb b/doc/de/hook/atom_feed.bb
new file mode 100644
index 000000000..69775ca5e
--- /dev/null
+++ b/doc/de/hook/atom_feed.bb
@@ -0,0 +1 @@
+[h2]atom_feed[/h2]
diff --git a/doc/de/hook/atom_feed_end.bb b/doc/de/hook/atom_feed_end.bb
new file mode 100644
index 000000000..4f019fc8f
--- /dev/null
+++ b/doc/de/hook/atom_feed_end.bb
@@ -0,0 +1 @@
+[h2]atom_feed_end[/h2]
diff --git a/doc/de/hook/attach_delete.bb b/doc/de/hook/attach_delete.bb
new file mode 100644
index 000000000..3b63f28d3
--- /dev/null
+++ b/doc/de/hook/attach_delete.bb
@@ -0,0 +1,11 @@
+[h2]attach_delete[/h2]
+
+Invoked when an attachment is deleted using attach_delete().
+
+[code]
+$arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo'=>$is_photo];
+call_hooks("attach_delete",$arr);
+[/code]
+
+
+See include/attach.php
diff --git a/doc/de/hook/attach_upload_file.bb b/doc/de/hook/attach_upload_file.bb
new file mode 100644
index 000000000..1f8056caa
--- /dev/null
+++ b/doc/de/hook/attach_upload_file.bb
@@ -0,0 +1 @@
+[h2]attach_upload_file[/h2]
diff --git a/doc/de/hook/authenticate.bb b/doc/de/hook/authenticate.bb
new file mode 100644
index 000000000..eb8071e73
--- /dev/null
+++ b/doc/de/hook/authenticate.bb
@@ -0,0 +1,29 @@
+[h2]authenticate[/h2]
+
+Invoked when a POST request is made with non-null $_POST['auth-params'] such as from the login form.
+If the hook handler does not set the 'authenticated' parameter of the passed array, normal login functions continue;
+
+The 'user_record' is in fact an account DB record. To provide automatic provisioning of accounts from other authentication realms, this record should be generated and stored during the verification phase.
+
+
+[code]
+ $addon_auth = array(
+ 'username' => trim($_POST['username']),
+ 'password' => trim($_POST['password']),
+ 'authenticated' => 0,
+ 'user_record' => null
+ );
+
+ /**
+ *
+ * A plugin indicates successful login by setting 'authenticated' to non-zero value and returning a user record
+ * Plugins should never set 'authenticated' except to indicate success - as hooks may be chained
+ * and later plugins should not interfere with an earlier one that succeeded.
+ *
+ */
+
+ call_hooks('authenticate', $addon_auth);
+[/code]
+
+
+See include/auth.php
diff --git a/doc/de/hook/author_is_pmable.bb b/doc/de/hook/author_is_pmable.bb
new file mode 100644
index 000000000..11d1185f3
--- /dev/null
+++ b/doc/de/hook/author_is_pmable.bb
@@ -0,0 +1,14 @@
+[h2]author_is_pmable[/h2]
+
+Called from thread action menu before returning a 'send mail' link for the post author. Not all authors will be able to receive private mail, for instance those on other networks with incompatible mail systems.
+
+By default author_is_pmable() returns true for 'zot' xchans, and false for all others.
+
+The plugin is passed an array
+
+ [ 'xchan' => $author_xchan, 'abook' => abook record, 'result' => 'unset' ]
+
+A plugin which sets the 'result' to something besides 'unset' will over-ride the default behaviour. A value of true will enable the 'send mail' link and the private mail recipient will be set to the author's xchan_hash. A value of false will disable the 'send mail' link.
+
+
+
diff --git a/doc/de/hook/bb2diaspora.bb b/doc/de/hook/bb2diaspora.bb
new file mode 100644
index 000000000..c28f1883e
--- /dev/null
+++ b/doc/de/hook/bb2diaspora.bb
@@ -0,0 +1 @@
+[h2]bb2diaspora[/h2]
diff --git a/doc/de/hook/bbcode.bb b/doc/de/hook/bbcode.bb
new file mode 100644
index 000000000..f6b8711b0
--- /dev/null
+++ b/doc/de/hook/bbcode.bb
@@ -0,0 +1,6 @@
+[h2]bbcode[/h2]
+
+
+Called at end of bbcode to html conversion.
+
+Hook argument contains the converted text string.
diff --git a/doc/de/hook/bbcode_filter.bb b/doc/de/hook/bbcode_filter.bb
new file mode 100644
index 000000000..efeb2e1b0
--- /dev/null
+++ b/doc/de/hook/bbcode_filter.bb
@@ -0,0 +1,7 @@
+[h2]bbcode_filter[/h2]
+
+
+Called at beginning of bbcode to html conversion.
+
+Hook argument contains the text string to be converted.
+
diff --git a/doc/de/hook/build_pagehead.bb b/doc/de/hook/build_pagehead.bb
new file mode 100644
index 000000000..8fc3486c7
--- /dev/null
+++ b/doc/de/hook/build_pagehead.bb
@@ -0,0 +1,2 @@
+[b]build_pagehead[/b]
+
diff --git a/doc/de/hook/can_comment_on_post.bb b/doc/de/hook/can_comment_on_post.bb
new file mode 100644
index 000000000..2cfd3b2da
--- /dev/null
+++ b/doc/de/hook/can_comment_on_post.bb
@@ -0,0 +1,13 @@
+[h3]can_comment_on_post[/h3]
+
+Called when deciding whether or not to display a comment box for a post.
+
+
+Hook data (array):
+ observer_hash => xchan_hash of current observer
+ item => posted item
+ allowed => 'unset'
+
+
+To over-ride the default behaviour, change allowed to true or false
+
diff --git a/doc/de/hook/change_channel.bb b/doc/de/hook/change_channel.bb
new file mode 100644
index 000000000..4514b9265
--- /dev/null
+++ b/doc/de/hook/change_channel.bb
@@ -0,0 +1,11 @@
+[h2]change_channel[/h2]
+
+Called when entering a logged in state in a channel context (as opposed to an account context).
+The hook array provides two arguments, 'channel_id' and 'chanx'. 'chanx' is a union of the channel
+and xchan records for the now active channel.
+
+Use this to capture what would traditionally be known as 'login events'. In this platform, login is
+a separate authentication activity and doesn't necessarily require "connecting to an identity", which
+is what the change_channel activity represents.
+
+
diff --git a/doc/de/hook/channel_links.bb b/doc/de/hook/channel_links.bb
new file mode 100644
index 000000000..c0243dac6
--- /dev/null
+++ b/doc/de/hook/channel_links.bb
@@ -0,0 +1,12 @@
+[h2]channel_links[/h2]
+
+Called when generating the Link HTTP header for the channel page. Different protocol stacks can add links to this header.
+
+Hook data = array
+ 'channel_address' => channel nickname, no checking is done to see if it is valid
+ 'channel_links' => array of channel links in the format
+ 'url' => url of resource
+ 'rel' => link relation
+ 'type' => MIME type
+
+All fields are required \ No newline at end of file
diff --git a/doc/de/hook/channel_remove.bb b/doc/de/hook/channel_remove.bb
new file mode 100644
index 000000000..db9e9dd82
--- /dev/null
+++ b/doc/de/hook/channel_remove.bb
@@ -0,0 +1 @@
+[h2]channel_remove[/h2]
diff --git a/doc/de/hook/chat_message.bb b/doc/de/hook/chat_message.bb
new file mode 100644
index 000000000..ccc93bb2c
--- /dev/null
+++ b/doc/de/hook/chat_message.bb
@@ -0,0 +1 @@
+[h2]chat_message[/h2]
diff --git a/doc/de/hook/chat_post.bb b/doc/de/hook/chat_post.bb
new file mode 100644
index 000000000..7cb3c9fa1
--- /dev/null
+++ b/doc/de/hook/chat_post.bb
@@ -0,0 +1 @@
+[h2]chat_post[/h2]
diff --git a/doc/de/hook/check_account_email.bb b/doc/de/hook/check_account_email.bb
new file mode 100644
index 000000000..b309706a0
--- /dev/null
+++ b/doc/de/hook/check_account_email.bb
@@ -0,0 +1 @@
+[h2]check_account_email[/h2]
diff --git a/doc/de/hook/check_account_invite.bb b/doc/de/hook/check_account_invite.bb
new file mode 100644
index 000000000..8d4a40522
--- /dev/null
+++ b/doc/de/hook/check_account_invite.bb
@@ -0,0 +1 @@
+[h2]check_account_invite[/h2]
diff --git a/doc/de/hook/check_account_password.bb b/doc/de/hook/check_account_password.bb
new file mode 100644
index 000000000..ce5202f48
--- /dev/null
+++ b/doc/de/hook/check_account_password.bb
@@ -0,0 +1,17 @@
+[h2]check_account_password[/h2]
+Use this hook to provide additional checks or validations of the password given when
+registering and account.
+[h3]Arguments:[/h3]
+[code=php]array(
+ 'password' => $password, // The password to check
+ 'result' => array(
+ 'error' => false,
+ 'message' => ''
+ )
+)[/code]
+[h3]Results:[/h3]
+For a failed check set the [code]error[/code] member of the [code]result[/code]
+array to [code]true[/code] and the [code]message[/code] to a short message
+explaining why it failed.
+
+Otherwise, leave it alone.
diff --git a/doc/de/hook/check_channelallowed.bb b/doc/de/hook/check_channelallowed.bb
new file mode 100644
index 000000000..e7559c92f
--- /dev/null
+++ b/doc/de/hook/check_channelallowed.bb
@@ -0,0 +1,11 @@
+[h2]check_channelallowed[/h2]
+
+Called when checking the channel (xchan) black and white lists to see if a channel is blocked.
+
+Hook data
+
+ array('hash' => xchan_hash of xchan to check);
+
+ create and set array element 'allowed' to true or false to override the system checks
+
+
diff --git a/doc/de/hook/check_siteallowed.bb b/doc/de/hook/check_siteallowed.bb
new file mode 100644
index 000000000..28134cbd2
--- /dev/null
+++ b/doc/de/hook/check_siteallowed.bb
@@ -0,0 +1,10 @@
+[h2]check_siteallowed[/h2]
+
+Called when checking the site black and white lists to see if a site is blocked.
+
+Hook data
+
+ array('url' => URL of site to check);
+
+ create and set array element 'allowed' to true or false to override the system checks
+
diff --git a/doc/de/hook/collect_public_recipients.bb b/doc/de/hook/collect_public_recipients.bb
new file mode 100644
index 000000000..de3f4049e
--- /dev/null
+++ b/doc/de/hook/collect_public_recipients.bb
@@ -0,0 +1,42 @@
+[h2]collect_public_recipients[/h2]
+
+Replace the default list of public recipients (i.e., all contacts).
+
+Allow plugins to create a list of recipients for public messages instead of the default
+of all channel connections.
+
+Called with the following array:
+ [
+ 'recipients' => [],
+ 'item' => $item,
+ 'private_envelope' => $private_envelope,
+ 'include_groups' => $include_groups
+ ];
+
+[code]
+ if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') {
+
+ $hookinfo = [
+ 'recipients' => [],
+ 'item' => $item,
+ 'private_envelope' => $private_envelope,
+ 'include_groups' => $include_groups
+ ];
+
+ call_hooks('collect_public_recipients',$hookinfo);
+
+ if ($hookinfo['recipients']) {
+ $r = $hookinfo['recipients'];
+ } else {
+ $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 ",
+ intval($item['uid'])
+ );
+ }
+
+ if($r) {
+
+ . . .
+
+[/code]
+
+see: include/item.php
diff --git a/doc/de/hook/comments_are_now_closed.bb b/doc/de/hook/comments_are_now_closed.bb
new file mode 100644
index 000000000..4d3baa95a
--- /dev/null
+++ b/doc/de/hook/comments_are_now_closed.bb
@@ -0,0 +1,11 @@
+[h3]comments_are_now_closed[/h3]
+
+Called when deciding whether or not commenting is closed for an item.
+
+
+Hook data (array):
+ item => posted item
+ closed => 'unset'
+
+
+To over-ride the default behaviour, change closed to true or false
diff --git a/doc/de/hook/connect_premium.bb b/doc/de/hook/connect_premium.bb
new file mode 100644
index 000000000..ae3aafc66
--- /dev/null
+++ b/doc/de/hook/connect_premium.bb
@@ -0,0 +1 @@
+[h2]connect_premium[/h2]
diff --git a/doc/de/hook/connection_remove.bb b/doc/de/hook/connection_remove.bb
new file mode 100644
index 000000000..bd13ae5f2
--- /dev/null
+++ b/doc/de/hook/connection_remove.bb
@@ -0,0 +1,9 @@
+[h3]connection_remove[/h3]
+
+Called when deleting a connection.
+
+
+Passed parameter array:
+
+ 'channel_id' => channel_id of the channel removing the connection
+ 'abook_id' => abook_id of the connection being removed
diff --git a/doc/de/hook/connector_settings.bb b/doc/de/hook/connector_settings.bb
new file mode 100644
index 000000000..9b59c49da
--- /dev/null
+++ b/doc/de/hook/connector_settings.bb
@@ -0,0 +1 @@
+[h2]connector_settings[/h2]
diff --git a/doc/de/hook/construct_page.bb b/doc/de/hook/construct_page.bb
new file mode 100644
index 000000000..700d9256f
--- /dev/null
+++ b/doc/de/hook/construct_page.bb
@@ -0,0 +1 @@
+[h2]construct_page[/h2]
diff --git a/doc/de/hook/contact_block_end.bb b/doc/de/hook/contact_block_end.bb
new file mode 100644
index 000000000..30a7d2d76
--- /dev/null
+++ b/doc/de/hook/contact_block_end.bb
@@ -0,0 +1 @@
+[h2]contact_block_end[/h2]
diff --git a/doc/de/hook/contact_edit.bb b/doc/de/hook/contact_edit.bb
new file mode 100644
index 000000000..5fd31fb1d
--- /dev/null
+++ b/doc/de/hook/contact_edit.bb
@@ -0,0 +1 @@
+[h2]contact_edit[/h2]
diff --git a/doc/de/hook/contact_edit_post.bb b/doc/de/hook/contact_edit_post.bb
new file mode 100644
index 000000000..bc736f8b8
--- /dev/null
+++ b/doc/de/hook/contact_edit_post.bb
@@ -0,0 +1 @@
+[h2]contact_edit_post[/h2]
diff --git a/doc/de/hook/contact_select_options.bb b/doc/de/hook/contact_select_options.bb
new file mode 100644
index 000000000..65f9154ff
--- /dev/null
+++ b/doc/de/hook/contact_select_options.bb
@@ -0,0 +1 @@
+[h2]contact_select_options[/h2]
diff --git a/doc/de/hook/content_security_policy.bb b/doc/de/hook/content_security_policy.bb
new file mode 100644
index 000000000..96b8095ae
--- /dev/null
+++ b/doc/de/hook/content_security_policy.bb
@@ -0,0 +1,39 @@
+[h2]content_security_policy[/h2]
+
+Called to modify CSP settings prior to the output of the Content-Security-Policy header.
+
+This hook permits addons to modify the content-security-policy if necessary to allow loading of foreign js libraries or css styles.
+
+[code]
+if(App::$config['system']['content_security_policy']) {
+ $cspsettings = Array (
+ 'script-src' => Array ("'self'","'unsafe-inline'","'unsafe-eval'"),
+ 'style-src' => Array ("'self'","'unsafe-inline'")
+ );
+ call_hooks('content_security_policy',$cspsettings);
+
+ // Legitimate CSP directives (cxref: https://content-security-policy.com/)
+ $validcspdirectives=Array(
+ "default-src", "script-src", "style-src",
+ "img-src", "connect-src", "font-src",
+ "object-src", "media-src", 'frame-src',
+ 'sandbox', 'report-uri', 'child-src',
+ 'form-action', 'frame-ancestors', 'plugin-types'
+ );
+ $cspheader = "Content-Security-Policy:";
+ foreach ($cspsettings as $cspdirective => $csp) {
+ if (!in_array($cspdirective,$validcspdirectives)) {
+ logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_DEBUG);
+ continue;
+ }
+ $cspsettingsarray=array_unique($cspsettings[$cspdirective]);
+ $cspsetpolicy = implode(' ',$cspsettingsarray);
+ if ($cspsetpolicy) {
+ $cspheader .= " ".$cspdirective." ".$cspsetpolicy.";";
+ }
+ }
+ header($cspheader);
+}
+[/code]
+
+see: boot.php
diff --git a/doc/de/hook/conversation_start.bb b/doc/de/hook/conversation_start.bb
new file mode 100644
index 000000000..7208c8d8f
--- /dev/null
+++ b/doc/de/hook/conversation_start.bb
@@ -0,0 +1 @@
+[h2]conversation_start[/h2]
diff --git a/doc/de/hook/create_identity.bb b/doc/de/hook/create_identity.bb
new file mode 100644
index 000000000..5c1da2d43
--- /dev/null
+++ b/doc/de/hook/create_identity.bb
@@ -0,0 +1 @@
+[h2]create_identity[/h2]
diff --git a/doc/de/hook/cron.bb b/doc/de/hook/cron.bb
new file mode 100644
index 000000000..55120b6c2
--- /dev/null
+++ b/doc/de/hook/cron.bb
@@ -0,0 +1,5 @@
+[h2]cron[/h2]
+
+Called when cron task (include/poller.php) is executed. The hook data is a string representing the current time (UTC).
+
+
diff --git a/doc/de/hook/cron_daily.bb b/doc/de/hook/cron_daily.bb
new file mode 100644
index 000000000..802bea5e4
--- /dev/null
+++ b/doc/de/hook/cron_daily.bb
@@ -0,0 +1,3 @@
+[h2]cron_daily[/h2]
+
+Called when cron task (include/poller.php) performs a cron_daily operation. The hook data is a string representing the current time (UTC).
diff --git a/doc/de/hook/cron_weekly.bb b/doc/de/hook/cron_weekly.bb
new file mode 100644
index 000000000..c01bf9611
--- /dev/null
+++ b/doc/de/hook/cron_weekly.bb
@@ -0,0 +1,3 @@
+[h2]cron_weekly[/h2]
+
+Called when cron task (include/poller.php) performs a cron_weekly operation. The hook data is a string representing the current time (UTC).
diff --git a/doc/de/hook/crypto_methods.bb b/doc/de/hook/crypto_methods.bb
new file mode 100644
index 000000000..1b16f567d
--- /dev/null
+++ b/doc/de/hook/crypto_methods.bb
@@ -0,0 +1,5 @@
+[h2]crypto_mthods[/h2]
+
+Passed an array of crypto methods in local priority order.
+
+You may change the order and add new methods or disable existing methods. 'aes256cbc' is always supported as a fallback and currently removing this has no effect. \ No newline at end of file
diff --git a/doc/de/hook/daemon_addon.bb b/doc/de/hook/daemon_addon.bb
new file mode 100644
index 000000000..b60b25748
--- /dev/null
+++ b/doc/de/hook/daemon_addon.bb
@@ -0,0 +1,15 @@
+[h2]daemon_addon[/h2]
+
+
+A foreground plugin can create a background process by invoking:
+
+[code]
+\Zotlabs\Daemon\Master::Summon([ 'Addon', 'myplugin', 'something' ]);
+[/code]
+
+This starts up a background process (called 'Addon') specifically for addons to use.
+
+Then if your plugin is also catching the daemon_addon hook that handler will be called with the
+argv array of the background process. In this case [ 'myplugin', 'something' ];
+
+We recommend using this convention so that plugins can share this hook without causing conflicts; that is check to see if your plugin is the first array argument and if not, return from the hook. Otherwise you can initiate background processing. Something to remember is that during background processes there is no session. You are detached from the web page which created the background process. \ No newline at end of file
diff --git a/doc/de/hook/daemon_master_release.bb b/doc/de/hook/daemon_master_release.bb
new file mode 100644
index 000000000..a17216d48
--- /dev/null
+++ b/doc/de/hook/daemon_master_release.bb
@@ -0,0 +1,5 @@
+[h2]daemon_master_release[/h2]
+
+Permit filtering or alternate methods of processing of background processes when [code] \Zotlabs\Daemon\Master::Release() [/code] is called.
+
+Default behavior is for a new PHP process to fire immediately upon a call to Master::Summon(). This hook permits pre-emption and the ability to provide queuing or other alternatives to this procedure.
diff --git a/doc/de/hook/directory_item.bb b/doc/de/hook/directory_item.bb
new file mode 100644
index 000000000..cb710e0b4
--- /dev/null
+++ b/doc/de/hook/directory_item.bb
@@ -0,0 +1 @@
+[h2]directory_item[/h2]
diff --git a/doc/de/hook/discover_channel_webfinger.bb b/doc/de/hook/discover_channel_webfinger.bb
new file mode 100644
index 000000000..b0eb5f2c4
--- /dev/null
+++ b/doc/de/hook/discover_channel_webfinger.bb
@@ -0,0 +1,14 @@
+[h2]discover_channel_webfinger[/h2]
+
+Called after performing channel discovery using RFC7033 webfinger and where the channel is not recognised as zot.
+
+Passed an array:
+
+ address: URL or address that is being discovered
+ success: set to true if the plugin discovers something
+ webfinger: array of webfinger links (output of webfinger_rfc7033())
+
+
+ if your plugin indicates success you are expected to generate and populate an xchan (and hubloc) record prior to returning.
+
+ \ No newline at end of file
diff --git a/doc/de/hook/display_item.bb b/doc/de/hook/display_item.bb
new file mode 100644
index 000000000..a6bfd621d
--- /dev/null
+++ b/doc/de/hook/display_item.bb
@@ -0,0 +1 @@
+[h2]display_item[/h2]
diff --git a/doc/de/hook/display_settings.bb b/doc/de/hook/display_settings.bb
new file mode 100644
index 000000000..1f1e0b491
--- /dev/null
+++ b/doc/de/hook/display_settings.bb
@@ -0,0 +1 @@
+[h2]display_settings[/h2]
diff --git a/doc/de/hook/display_settings_post.bb b/doc/de/hook/display_settings_post.bb
new file mode 100644
index 000000000..d3bb39359
--- /dev/null
+++ b/doc/de/hook/display_settings_post.bb
@@ -0,0 +1 @@
+[h2]display_settings_post[/h2]
diff --git a/doc/de/hook/donate_contributors.bb b/doc/de/hook/donate_contributors.bb
new file mode 100644
index 000000000..f97c77efa
--- /dev/null
+++ b/doc/de/hook/donate_contributors.bb
@@ -0,0 +1 @@
+[h2]donate_contributors[/h2]
diff --git a/doc/de/hook/donate_plugin.bb b/doc/de/hook/donate_plugin.bb
new file mode 100644
index 000000000..db4a6f113
--- /dev/null
+++ b/doc/de/hook/donate_plugin.bb
@@ -0,0 +1 @@
+[h2]donate_plugin[/h2]
diff --git a/doc/de/hook/donate_sponsors.bb b/doc/de/hook/donate_sponsors.bb
new file mode 100644
index 000000000..3abd46d42
--- /dev/null
+++ b/doc/de/hook/donate_sponsors.bb
@@ -0,0 +1 @@
+[h2]donate_sponsors[/h2]
diff --git a/doc/de/hook/dreport_is_storable.bb b/doc/de/hook/dreport_is_storable.bb
new file mode 100644
index 000000000..9ca99b896
--- /dev/null
+++ b/doc/de/hook/dreport_is_storable.bb
@@ -0,0 +1 @@
+[h2]dreport_is_storable[/h2]
diff --git a/doc/de/hook/dreport_process.bb b/doc/de/hook/dreport_process.bb
new file mode 100644
index 000000000..3ad331f41
--- /dev/null
+++ b/doc/de/hook/dreport_process.bb
@@ -0,0 +1,7 @@
+[h2]dreport_process[/h2]
+
+Called for each delivery report received
+
+Passed a delivery_report array.
+
+see: include/zot.php
diff --git a/doc/de/hook/drop_item.bb b/doc/de/hook/drop_item.bb
new file mode 100644
index 000000000..35bb80f82
--- /dev/null
+++ b/doc/de/hook/drop_item.bb
@@ -0,0 +1 @@
+[h2]drop_item[/h2]
diff --git a/doc/de/hook/dropdown_extras.bb b/doc/de/hook/dropdown_extras.bb
new file mode 100644
index 000000000..6d7110a76
--- /dev/null
+++ b/doc/de/hook/dropdown_extras.bb
@@ -0,0 +1,17 @@
+[h2]dropdown_extras[/h2]
+
+Modify the dropdown menu available through the cog of items as displayed by conv_item.tpl
+
+This hook allows plugins to add arbitrary html to the cog dropdown of thread items displayed with the conv_item.tpl template.
+
+It is fed an array of ['item' => $item, 'dropdown_extras' => '']. Any additions to the cog menu should be prepended/appended to
+the ['dropdown_extras'] element.
+
+[code]
+$dropdown_extras_arr = [ 'item' => $item , 'dropdown_extras' => '' ];
+call_hooks('dropdown_extras',$dropdown_extras_arr);
+$dropdown_extras = $dropdown_extras_arr['dropdown_extras'];
+[/code]
+
+see: Zotlabs/Lib/ThreadItem.php
+see: view/tpl/conv_item.tpl
diff --git a/doc/de/hook/encode_object.bb b/doc/de/hook/encode_object.bb
new file mode 100644
index 000000000..0c8e86458
--- /dev/null
+++ b/doc/de/hook/encode_object.bb
@@ -0,0 +1 @@
+[h2]encode_object[/h2]
diff --git a/doc/de/hook/enotify.bb b/doc/de/hook/enotify.bb
new file mode 100644
index 000000000..703a3ffa0
--- /dev/null
+++ b/doc/de/hook/enotify.bb
@@ -0,0 +1 @@
+[h2]enotify[/h2]
diff --git a/doc/de/hook/enotify_mail.bb b/doc/de/hook/enotify_mail.bb
new file mode 100644
index 000000000..adeb8bd30
--- /dev/null
+++ b/doc/de/hook/enotify_mail.bb
@@ -0,0 +1 @@
+[h2]enotify_mail[/h2]
diff --git a/doc/de/hook/enotify_store.bb b/doc/de/hook/enotify_store.bb
new file mode 100644
index 000000000..dc44cc320
--- /dev/null
+++ b/doc/de/hook/enotify_store.bb
@@ -0,0 +1 @@
+[h2]enotify_store[/h2]
diff --git a/doc/de/hook/event_created.bb b/doc/de/hook/event_created.bb
new file mode 100644
index 000000000..222602e77
--- /dev/null
+++ b/doc/de/hook/event_created.bb
@@ -0,0 +1 @@
+[h2]event_created[/h2]
diff --git a/doc/de/hook/event_store_event.bb b/doc/de/hook/event_store_event.bb
new file mode 100644
index 000000000..7015a8322
--- /dev/null
+++ b/doc/de/hook/event_store_event.bb
@@ -0,0 +1,11 @@
+[h2]event_store_event[/h2]
+
+Called from event_store_event() when an event record is being stored.
+
+Hook info is an array
+
+'event' => the passed event details, ready for storage
+'existing_event' => If the event already exists, a copy of the original event record from the database
+'cancel' => false - set to true to cancel the operation.
+
+
diff --git a/doc/de/hook/event_updated.bb b/doc/de/hook/event_updated.bb
new file mode 100644
index 000000000..69e3c72c1
--- /dev/null
+++ b/doc/de/hook/event_updated.bb
@@ -0,0 +1 @@
+[h2]event_updated[/h2]
diff --git a/doc/de/hook/externals_url_select.bb b/doc/de/hook/externals_url_select.bb
new file mode 100644
index 000000000..a542dcb29
--- /dev/null
+++ b/doc/de/hook/externals_url_select.bb
@@ -0,0 +1 @@
+[h2]externals_url_select[/h2]
diff --git a/doc/de/hook/feature_enabled.bb b/doc/de/hook/feature_enabled.bb
new file mode 100644
index 000000000..5630cc768
--- /dev/null
+++ b/doc/de/hook/feature_enabled.bb
@@ -0,0 +1 @@
+[h2]feature_enabled[/h2]
diff --git a/doc/de/hook/feature_settings.bb b/doc/de/hook/feature_settings.bb
new file mode 100644
index 000000000..d1691eb38
--- /dev/null
+++ b/doc/de/hook/feature_settings.bb
@@ -0,0 +1 @@
+[h2]feature_settings[/h2]
diff --git a/doc/de/hook/feature_settings_post.bb b/doc/de/hook/feature_settings_post.bb
new file mode 100644
index 000000000..eecf941ff
--- /dev/null
+++ b/doc/de/hook/feature_settings_post.bb
@@ -0,0 +1 @@
+[h2]feature_settings_post[/h2]
diff --git a/doc/de/hook/fetch_and_store.bb b/doc/de/hook/fetch_and_store.bb
new file mode 100644
index 000000000..afece11a6
--- /dev/null
+++ b/doc/de/hook/fetch_and_store.bb
@@ -0,0 +1 @@
+[h2]fetch_and_store[/h2]
diff --git a/doc/de/hook/follow.bb b/doc/de/hook/follow.bb
new file mode 100644
index 000000000..a97632b06
--- /dev/null
+++ b/doc/de/hook/follow.bb
@@ -0,0 +1 @@
+[h2]follow[/h2]
diff --git a/doc/de/hook/follow_allow.bb b/doc/de/hook/follow_allow.bb
new file mode 100644
index 000000000..fdab1865c
--- /dev/null
+++ b/doc/de/hook/follow_allow.bb
@@ -0,0 +1 @@
+[h2]follow_allow[/h2]
diff --git a/doc/de/hook/gender_selector.bb b/doc/de/hook/gender_selector.bb
new file mode 100644
index 000000000..0b56b5c9b
--- /dev/null
+++ b/doc/de/hook/gender_selector.bb
@@ -0,0 +1 @@
+[h2]gender_selector[/h2]
diff --git a/doc/de/hook/gender_selector_min.bb b/doc/de/hook/gender_selector_min.bb
new file mode 100644
index 000000000..9d143855a
--- /dev/null
+++ b/doc/de/hook/gender_selector_min.bb
@@ -0,0 +1 @@
+[h2]gender_selector_min[/h2]
diff --git a/doc/de/hook/generate_map.bb b/doc/de/hook/generate_map.bb
new file mode 100644
index 000000000..33672d552
--- /dev/null
+++ b/doc/de/hook/generate_map.bb
@@ -0,0 +1 @@
+[h2]generate_map[/h2]
diff --git a/doc/de/hook/generate_named_map.bb b/doc/de/hook/generate_named_map.bb
new file mode 100644
index 000000000..5bacb846d
--- /dev/null
+++ b/doc/de/hook/generate_named_map.bb
@@ -0,0 +1 @@
+[h2]generate_named_map[/h2]
diff --git a/doc/de/hook/get_all_api_perms.bb b/doc/de/hook/get_all_api_perms.bb
new file mode 100644
index 000000000..eb41f8a02
--- /dev/null
+++ b/doc/de/hook/get_all_api_perms.bb
@@ -0,0 +1 @@
+[h2]get_all_api_perms[/h2]
diff --git a/doc/de/hook/get_all_perms.bb b/doc/de/hook/get_all_perms.bb
new file mode 100644
index 000000000..149f8c78c
--- /dev/null
+++ b/doc/de/hook/get_all_perms.bb
@@ -0,0 +1 @@
+[h2]get_all_perms[/h2]
diff --git a/doc/de/hook/get_default_export_sections b/doc/de/hook/get_default_export_sections
new file mode 100644
index 000000000..09b146643
--- /dev/null
+++ b/doc/de/hook/get_default_export_sections
@@ -0,0 +1,10 @@
+[h3]get_default_export_sections[/h3]
+
+The get_default_export_sections call returns the basic functional groups of data to export using channel_export_basic().
+
+The hook is passed an array
+ [
+ 'sections' => [ 'channel', 'connections', 'config', 'apps', 'chatrooms', 'events', 'webpages', 'mail', 'wikis' ]
+ ]
+
+If you desire the export to contain three months of items, add 'items' to the 'sections' array
diff --git a/doc/de/hook/get_features.bb b/doc/de/hook/get_features.bb
new file mode 100644
index 000000000..66e81f13c
--- /dev/null
+++ b/doc/de/hook/get_features.bb
@@ -0,0 +1 @@
+[h2]get_features[/h2]
diff --git a/doc/de/hook/get_photo.bb b/doc/de/hook/get_photo.bb
new file mode 100644
index 000000000..eaf3beffb
--- /dev/null
+++ b/doc/de/hook/get_photo.bb
@@ -0,0 +1,14 @@
+[h2]get_photo[/h2]
+
+Called when fetching the content of photos (except for profile photos) in mod_photo.
+
+
+Hook arguments:
+
+'imgscale' => integer resolution requested
+'resource_id' => resource_id of requested photo
+'photo' => array of matching photo table rows after querying for the photo
+'allowed' => whether or not access to this resource is allowed
+
+
+
diff --git a/doc/de/hook/get_profile_photo.bb b/doc/de/hook/get_profile_photo.bb
new file mode 100644
index 000000000..ab07179ae
--- /dev/null
+++ b/doc/de/hook/get_profile_photo.bb
@@ -0,0 +1,18 @@
+[h2]get_profile_photo[/h2]
+
+Called when fetching the content of the default profile photo for a local channel in mod_photo.
+
+
+Hook arguments:
+
+'imgscale' => integer resolution requested (4, 5, or 6)
+'channel_id' => channel_id of requested profile photo
+'default' => filename of default profile photo of this imgscale
+'data' => empty string
+'mimetype' => empty string
+
+
+If 'data' is set, this data will be used instead of the data obtained from the database search for the profile photo.
+If 'mimetype' is set, this mimetype will be used instead of the mimetype obtained from the database or the default profile photo mimetype.
+
+
diff --git a/doc/de/hook/get_role_perms.bb b/doc/de/hook/get_role_perms.bb
new file mode 100644
index 000000000..87830f8e3
--- /dev/null
+++ b/doc/de/hook/get_role_perms.bb
@@ -0,0 +1 @@
+[h2]get_role_perms[/h2]
diff --git a/doc/de/hook/global_permissions.bb b/doc/de/hook/global_permissions.bb
new file mode 100644
index 000000000..fe998ee9c
--- /dev/null
+++ b/doc/de/hook/global_permissions.bb
@@ -0,0 +1 @@
+[h2]global_permissions[/h2]
diff --git a/doc/de/hook/home_content.bb b/doc/de/hook/home_content.bb
new file mode 100644
index 000000000..7f32b3547
--- /dev/null
+++ b/doc/de/hook/home_content.bb
@@ -0,0 +1 @@
+[h2]home_content[/h2]
diff --git a/doc/de/hook/home_init.bb b/doc/de/hook/home_init.bb
new file mode 100644
index 000000000..25dd72792
--- /dev/null
+++ b/doc/de/hook/home_init.bb
@@ -0,0 +1 @@
+[h2]home_init[/h2]
diff --git a/doc/de/hook/hostxrd.bb b/doc/de/hook/hostxrd.bb
new file mode 100644
index 000000000..2b67320cf
--- /dev/null
+++ b/doc/de/hook/hostxrd.bb
@@ -0,0 +1 @@
+[h2]hostxrd[/h2]
diff --git a/doc/de/hook/html2bbcode.bb b/doc/de/hook/html2bbcode.bb
new file mode 100644
index 000000000..3061f05f0
--- /dev/null
+++ b/doc/de/hook/html2bbcode.bb
@@ -0,0 +1 @@
+[h2]html2bbcode[/h2]
diff --git a/doc/de/hook/identity_basic_export.bb b/doc/de/hook/identity_basic_export.bb
new file mode 100644
index 000000000..71329ba1e
--- /dev/null
+++ b/doc/de/hook/identity_basic_export.bb
@@ -0,0 +1,10 @@
+[h2]identity_basic_export[/h2]
+
+Called when exporting data for a channel
+Passed array contains
+
+ [
+ 'channel_id' => channel_id being exported
+ 'sections' => array of functional export sections which are being exported
+ 'data' => the export data array which has been generated
+ ]
diff --git a/doc/de/hook/import_author_xchan.bb b/doc/de/hook/import_author_xchan.bb
new file mode 100644
index 000000000..e2340469d
--- /dev/null
+++ b/doc/de/hook/import_author_xchan.bb
@@ -0,0 +1 @@
+[h2]import_author_xchan[/h2]
diff --git a/doc/de/hook/import_channel.bb b/doc/de/hook/import_channel.bb
new file mode 100644
index 000000000..b220b7415
--- /dev/null
+++ b/doc/de/hook/import_channel.bb
@@ -0,0 +1 @@
+[h2]import_channel[/h2]
diff --git a/doc/de/hook/import_directory_profile.bb b/doc/de/hook/import_directory_profile.bb
new file mode 100644
index 000000000..e2fac59bc
--- /dev/null
+++ b/doc/de/hook/import_directory_profile.bb
@@ -0,0 +1 @@
+[h2]import_directory_profile[/h2]
diff --git a/doc/de/hook/import_xchan.bb b/doc/de/hook/import_xchan.bb
new file mode 100644
index 000000000..40e0783ce
--- /dev/null
+++ b/doc/de/hook/import_xchan.bb
@@ -0,0 +1 @@
+[h2]import_xchan[/h2]
diff --git a/doc/de/hook/item_custom.bb b/doc/de/hook/item_custom.bb
new file mode 100644
index 000000000..d20c7d76c
--- /dev/null
+++ b/doc/de/hook/item_custom.bb
@@ -0,0 +1,24 @@
+[h2]item_custom[/h2]
+
+Allow addons to create and process custom item types.
+
+Addon authors will need to use iconfig meta data (with sharing on) or some other method
+to specify and determine whether the custom item is destined for their addon.
+
+It is fed an array of ['item' => ${item_array}, 'allow_exec' => {true/false}]
+
+By default $arr['item']['cancel'] is set to TRUE which will abort storage of the
+custom item in the item table unless the addon unsets it or sets it to false.
+
+[code]
+ if ($arr['item_type']==ITEM_TYPE_CUSTOM) {
+ /* Custom items are not stored by default
+ because they require an addon to process. */
+ $d['item']['cancel']=true;
+
+ call_hooks('item_custom',$d);
+ }
+
+[/code]
+
+see: include/items.php
diff --git a/doc/de/hook/item_photo_menu.bb b/doc/de/hook/item_photo_menu.bb
new file mode 100644
index 000000000..8f9860a90
--- /dev/null
+++ b/doc/de/hook/item_photo_menu.bb
@@ -0,0 +1 @@
+[h2]item_photo_menu[/h2]
diff --git a/doc/de/hook/item_store.bb b/doc/de/hook/item_store.bb
new file mode 100644
index 000000000..5d49b725c
--- /dev/null
+++ b/doc/de/hook/item_store.bb
@@ -0,0 +1 @@
+[h2]item_store[/h2]
diff --git a/doc/de/hook/item_store_update.bb b/doc/de/hook/item_store_update.bb
new file mode 100644
index 000000000..a7c58939a
--- /dev/null
+++ b/doc/de/hook/item_store_update.bb
@@ -0,0 +1 @@
+[h2]item_store_update[/h2]
diff --git a/doc/de/hook/item_stored.bb b/doc/de/hook/item_stored.bb
new file mode 100644
index 000000000..8d706cb4e
--- /dev/null
+++ b/doc/de/hook/item_stored.bb
@@ -0,0 +1,18 @@
+[h2]item_stored[/h2]
+
+Allow addons to continue processing after an item has been stored in the event
+that they need access to the item_id or other data that gets assigned during
+the storage process.
+
+It is fed an array of type item (including terms and iconfig data).
+
+[code]
+ /**
+ * @hooks item_stored
+ * Called after new item is stored in the database.
+ * (By this time we have an item_id and other frequently needed info.)
+ */
+ call_hooks('item_stored',$arr);
+[/code]
+
+see: include/items.php
diff --git a/doc/de/hook/item_stored_update.bb b/doc/de/hook/item_stored_update.bb
new file mode 100644
index 000000000..4532a347c
--- /dev/null
+++ b/doc/de/hook/item_stored_update.bb
@@ -0,0 +1,15 @@
+[h2]item_stored_update[/h2]
+
+Allow addons to continue processing after an item update has been stored
+
+It is fed an array of type item (including terms and iconfig data).
+
+[code]
+ /**
+ * @hooks item_stored_update
+ * Called after updated item is stored in the database.
+ */
+ call_hooks('item_stored_update',$arr);
+[/code]
+
+see: include/items.php
diff --git a/doc/de/hook/item_translate.bb b/doc/de/hook/item_translate.bb
new file mode 100644
index 000000000..695494b9c
--- /dev/null
+++ b/doc/de/hook/item_translate.bb
@@ -0,0 +1 @@
+[h2]item_translate[/h2]
diff --git a/doc/de/hook/jot_header_tpl_filter.bb b/doc/de/hook/jot_header_tpl_filter.bb
new file mode 100644
index 000000000..b17d81d03
--- /dev/null
+++ b/doc/de/hook/jot_header_tpl_filter.bb
@@ -0,0 +1,5 @@
+[h2]jot_header_tpl_filter[/h2]
+
+Allows addon developers to modify the values of replacements fed into jot-header.tpl
+
+cxref: include/conversation.php
diff --git a/doc/de/hook/jot_networks.bb b/doc/de/hook/jot_networks.bb
new file mode 100644
index 000000000..4c1629ba7
--- /dev/null
+++ b/doc/de/hook/jot_networks.bb
@@ -0,0 +1 @@
+[h2]jot_networks[/h2]
diff --git a/doc/de/hook/jot_tool.bb b/doc/de/hook/jot_tool.bb
new file mode 100644
index 000000000..22ba9701e
--- /dev/null
+++ b/doc/de/hook/jot_tool.bb
@@ -0,0 +1 @@
+[h2]jot_tool[/h2]
diff --git a/doc/de/hook/jot_tpl_filter.bb b/doc/de/hook/jot_tpl_filter.bb
new file mode 100644
index 000000000..426da3c56
--- /dev/null
+++ b/doc/de/hook/jot_tpl_filter.bb
@@ -0,0 +1,5 @@
+[h2]jot_tpl_filter[/h2]
+
+Allows addon developers to alter the macro replacements prior to being fed into jot.tpl
+
+cxref: include/conversation.php
diff --git a/doc/de/hook/legal_webbie.bb b/doc/de/hook/legal_webbie.bb
new file mode 100644
index 000000000..8c7d32d56
--- /dev/null
+++ b/doc/de/hook/legal_webbie.bb
@@ -0,0 +1,10 @@
+[h2]legal_webbie[/h2]
+
+Called when validating a channel address. By default the valid characters are
+a-z,0-9,-,_, and . Uppercase ASCII characters are folded to lower and any invalid characters are stripped.
+
+Some federated networks require more restrictive rules.
+
+The hook is called with an array [ 'input' => (supplied text), 'output' => (validated text) ]
+
+A plugin will generally perform a regex filter or text operation on 'input' and provide the results in 'output'. \ No newline at end of file
diff --git a/doc/de/hook/legal_webbie_text.bb b/doc/de/hook/legal_webbie_text.bb
new file mode 100644
index 000000000..32c74c93b
--- /dev/null
+++ b/doc/de/hook/legal_webbie_text.bb
@@ -0,0 +1,7 @@
+[h2]legal_webbie_text[/h2]
+
+Returns a string describing the text rules applied to legal_webbie().
+
+Called with an array [ 'text' => (descriptive text describing text character limitations) ]
+
+A plugin should return the description of the allowed characters and operation performed in the 'legal_webbie' hook to assist people when creating a new channel. \ No newline at end of file
diff --git a/doc/de/hook/load_pdl.bb b/doc/de/hook/load_pdl.bb
new file mode 100644
index 000000000..149a3e766
--- /dev/null
+++ b/doc/de/hook/load_pdl.bb
@@ -0,0 +1 @@
+[h2]load_pdl[/h2]
diff --git a/doc/de/hook/local_dir_update.bb b/doc/de/hook/local_dir_update.bb
new file mode 100644
index 000000000..d0b0f8ac1
--- /dev/null
+++ b/doc/de/hook/local_dir_update.bb
@@ -0,0 +1 @@
+[h2]local_dir_update[/h2]
diff --git a/doc/de/hook/logged_in.bb b/doc/de/hook/logged_in.bb
new file mode 100644
index 000000000..b01041576
--- /dev/null
+++ b/doc/de/hook/logged_in.bb
@@ -0,0 +1 @@
+[h2]logged_in[/h2]
diff --git a/doc/de/hook/logger.bb b/doc/de/hook/logger.bb
new file mode 100644
index 000000000..8fe989abd
--- /dev/null
+++ b/doc/de/hook/logger.bb
@@ -0,0 +1,16 @@
+[h2]logger[/h2]
+
+Called when making an entry to the application logfile
+
+Hook data:
+
+ array(
+ 'filename' => name of logfile relative to application basedir. String.
+ 'loglevel' => the log level of this log entry, if this is higher than the configured maximum loglevel
+ this hook will not be called. Integer.
+ 'message' => The formatted log message, ready for logging. String.
+ 'logged' => boolean, default is false. Set to true to prevent the normal logfile entry to be made
+ (e.g. if the plugin is configured to handle this aspect of the function, or if it is determined
+ that this log entry should not be made)
+ )
+
diff --git a/doc/de/hook/logging_out.bb b/doc/de/hook/logging_out.bb
new file mode 100644
index 000000000..d47b9f1df
--- /dev/null
+++ b/doc/de/hook/logging_out.bb
@@ -0,0 +1 @@
+[h2]logging_out[/h2]
diff --git a/doc/de/hook/login_hook.bb b/doc/de/hook/login_hook.bb
new file mode 100644
index 000000000..156a0afcd
--- /dev/null
+++ b/doc/de/hook/login_hook.bb
@@ -0,0 +1 @@
+[h2]login_hook[/h2]
diff --git a/doc/de/hook/magic_auth.bb b/doc/de/hook/magic_auth.bb
new file mode 100644
index 000000000..80d6edb27
--- /dev/null
+++ b/doc/de/hook/magic_auth.bb
@@ -0,0 +1 @@
+[h2]magic_auth[/h2]
diff --git a/doc/de/hook/magic_auth_openid_success.bb b/doc/de/hook/magic_auth_openid_success.bb
new file mode 100644
index 000000000..810f2e06d
--- /dev/null
+++ b/doc/de/hook/magic_auth_openid_success.bb
@@ -0,0 +1 @@
+[h2]magic_auth_openid_success[/h2]
diff --git a/doc/de/hook/magic_auth_success.bb b/doc/de/hook/magic_auth_success.bb
new file mode 100644
index 000000000..d795e43e5
--- /dev/null
+++ b/doc/de/hook/magic_auth_success.bb
@@ -0,0 +1 @@
+[h2]magic_auth_success[/h2]
diff --git a/doc/de/hook/main_slider.bb b/doc/de/hook/main_slider.bb
new file mode 100644
index 000000000..a63c2170a
--- /dev/null
+++ b/doc/de/hook/main_slider.bb
@@ -0,0 +1 @@
+[h2]main_slider[/h2]
diff --git a/doc/de/hook/marital_selector.bb b/doc/de/hook/marital_selector.bb
new file mode 100644
index 000000000..0f76c3f5a
--- /dev/null
+++ b/doc/de/hook/marital_selector.bb
@@ -0,0 +1 @@
+[h2]marital_selector[/h2]
diff --git a/doc/de/hook/marital_selector_min.bb b/doc/de/hook/marital_selector_min.bb
new file mode 100644
index 000000000..f02d21f20
--- /dev/null
+++ b/doc/de/hook/marital_selector_min.bb
@@ -0,0 +1 @@
+[h2]marital_selector_min[/h2]
diff --git a/doc/de/hook/markdown_to_bb.bb b/doc/de/hook/markdown_to_bb.bb
new file mode 100644
index 000000000..8af637c8c
--- /dev/null
+++ b/doc/de/hook/markdown_to_bb.bb
@@ -0,0 +1,5 @@
+[h2]markdown_to_bb[/h2]
+
+Called when processing markdown to bbcode conversion such as when importing Diaspora protocol source or other markdown sources. The plugin is called post conversion.
+
+The function takes one argument which is the string being converted. It may be additionally processed by the plugin.
diff --git a/doc/de/hook/module_loaded.bb b/doc/de/hook/module_loaded.bb
new file mode 100644
index 000000000..cb0d2302d
--- /dev/null
+++ b/doc/de/hook/module_loaded.bb
@@ -0,0 +1 @@
+[h2]module_loaded[/h2]
diff --git a/doc/de/hook/module_mod_aftercontent.bb b/doc/de/hook/module_mod_aftercontent.bb
new file mode 100644
index 000000000..04e3c8d88
--- /dev/null
+++ b/doc/de/hook/module_mod_aftercontent.bb
@@ -0,0 +1,12 @@
+[h2]module_mod_aftercontent[/h2]
+
+
+
+The hook data for this call consists of an array
+
+ $arr['content']
+
+This element contains the HTML content which was prepared for this page by calling the module_content() function. It is invoked after the content has been created. It does not contain the result of AJAX or asynchronous page load calls.
+
+ The current module may be determined by lookin at App::$module
+
diff --git a/doc/de/hook/module_mod_content.bb b/doc/de/hook/module_mod_content.bb
new file mode 100644
index 000000000..eef5b7ba5
--- /dev/null
+++ b/doc/de/hook/module_mod_content.bb
@@ -0,0 +1,10 @@
+[h2]module_mod_content[/h2]
+
+The hook data for this call consists of an array
+
+ $arr['content']
+
+This element contains the HTML content before calling the module_content() function. It is invoked before the content region has been populated. This may or may not be empty as there may be other processes or addons generating content prior to your hook handler is run. Be certain to preserve any current content. Typically anything you add here will be placed at the top of the content region of the page, but in any event prior to the main content region being generated.
+
+ The current module may be determined by lookin at App::$module
+
diff --git a/doc/de/hook/module_mod_init.bb b/doc/de/hook/module_mod_init.bb
new file mode 100644
index 000000000..52fe5a616
--- /dev/null
+++ b/doc/de/hook/module_mod_init.bb
@@ -0,0 +1 @@
+[h2]module_mod_init[/h2]
diff --git a/doc/de/hook/module_mod_post.bb b/doc/de/hook/module_mod_post.bb
new file mode 100644
index 000000000..3adb0e737
--- /dev/null
+++ b/doc/de/hook/module_mod_post.bb
@@ -0,0 +1 @@
+[h2]module_mod_post[/h2]
diff --git a/doc/de/hook/mood_verbs.bb b/doc/de/hook/mood_verbs.bb
new file mode 100644
index 000000000..67fb719dd
--- /dev/null
+++ b/doc/de/hook/mood_verbs.bb
@@ -0,0 +1 @@
+[h2]mood_verbs[/h2]
diff --git a/doc/de/hook/nav.bb b/doc/de/hook/nav.bb
new file mode 100644
index 000000000..b52f90602
--- /dev/null
+++ b/doc/de/hook/nav.bb
@@ -0,0 +1,28 @@
+[h2]nav[/h2]
+
+Called when generating the main navigation bar and menu for a page
+
+Hook data:
+
+ array(
+ 'usermenu' => array( 'icon' => photo URL, 'name' => channel name )
+ 'nav' => array(
+ 'usermenu' => usermenu (photo menu) link array
+ (channel home, profiles, photos, cloud, chats, webapges ...)
+ 'loginmenu' => login menu link array
+ 'network' => grid link and grid-notify
+ 'home' => home link and home-notify
+ 'intros' => intros link and intros-notify
+ 'notifications' => notifications link and notifications-notify
+ 'messages' => PM link and PM-notify
+ 'all_events' => events link and events notfiy
+ 'manage' => manage channels link
+ 'settings' => settings link
+ 'register' => registration link
+ 'help' => help/doc link
+ 'apps' => apps link
+ 'search' => search link and form
+ 'directory' => directory link
+ )
+
+
diff --git a/doc/de/hook/network_content_init.bb b/doc/de/hook/network_content_init.bb
new file mode 100644
index 000000000..224da393a
--- /dev/null
+++ b/doc/de/hook/network_content_init.bb
@@ -0,0 +1 @@
+[h2]network_content_init[/h2]
diff --git a/doc/de/hook/network_ping.bb b/doc/de/hook/network_ping.bb
new file mode 100644
index 000000000..78deefe78
--- /dev/null
+++ b/doc/de/hook/network_ping.bb
@@ -0,0 +1 @@
+[h2]network_ping[/h2]
diff --git a/doc/de/hook/network_to_name.bb b/doc/de/hook/network_to_name.bb
new file mode 100644
index 000000000..eea4a1841
--- /dev/null
+++ b/doc/de/hook/network_to_name.bb
@@ -0,0 +1 @@
+[h2]network_to_name[/h2]
diff --git a/doc/de/hook/notifier_end.bb b/doc/de/hook/notifier_end.bb
new file mode 100644
index 000000000..df9d852bd
--- /dev/null
+++ b/doc/de/hook/notifier_end.bb
@@ -0,0 +1 @@
+[h2]notifier_end[/h2]
diff --git a/doc/de/hook/notifier_hub.bb b/doc/de/hook/notifier_hub.bb
new file mode 100644
index 000000000..4255ce446
--- /dev/null
+++ b/doc/de/hook/notifier_hub.bb
@@ -0,0 +1 @@
+[h2]notifier_hub[/h2]
diff --git a/doc/de/hook/notifier_normal.bb b/doc/de/hook/notifier_normal.bb
new file mode 100644
index 000000000..0059baa47
--- /dev/null
+++ b/doc/de/hook/notifier_normal.bb
@@ -0,0 +1 @@
+[h2]notifier_normal[/h2]
diff --git a/doc/de/hook/obj_verbs.bb b/doc/de/hook/obj_verbs.bb
new file mode 100644
index 000000000..ca98229aa
--- /dev/null
+++ b/doc/de/hook/obj_verbs.bb
@@ -0,0 +1 @@
+[h2]obj_verbs[/h2]
diff --git a/doc/de/hook/oembed_probe.bb b/doc/de/hook/oembed_probe.bb
new file mode 100644
index 000000000..4f32ac267
--- /dev/null
+++ b/doc/de/hook/oembed_probe.bb
@@ -0,0 +1 @@
+[h2]oembed_probe[/h2]
diff --git a/doc/de/hook/other_encapsulate.bb b/doc/de/hook/other_encapsulate.bb
new file mode 100644
index 000000000..ea0cdf622
--- /dev/null
+++ b/doc/de/hook/other_encapsulate.bb
@@ -0,0 +1,7 @@
+[h2]other_encapsulate[/h2]
+
+Passed an array of 'data', 'pubkey', 'alg', 'result' when encrypting data with an algorithm (alg) which is unknown to the system. Hooks are expected to identify their algorithm, encrypt data with pubkey and place the result in 'result'.
+
+
+
+
diff --git a/doc/de/hook/other_unencapsulate.bb b/doc/de/hook/other_unencapsulate.bb
new file mode 100644
index 000000000..c8b0b617f
--- /dev/null
+++ b/doc/de/hook/other_unencapsulate.bb
@@ -0,0 +1,5 @@
+[h2]other_unencapsulate[/h2]
+
+Passed an array of 'data', 'prvkey', 'alg', 'result' when decrypting data with an algorithm (alg) which is unknown to the system. Hooks are expected to identify their algorithm, decrypt data with prvkey and place the result in 'result'.
+
+
diff --git a/doc/de/hook/page_content_top.bb b/doc/de/hook/page_content_top.bb
new file mode 100644
index 000000000..137e3abfd
--- /dev/null
+++ b/doc/de/hook/page_content_top.bb
@@ -0,0 +1 @@
+[h2]page_content_top[/h2]
diff --git a/doc/de/hook/page_end.bb b/doc/de/hook/page_end.bb
new file mode 100644
index 000000000..09293cf50
--- /dev/null
+++ b/doc/de/hook/page_end.bb
@@ -0,0 +1 @@
+[h2]page_end[/h2]
diff --git a/doc/de/hook/page_header.bb b/doc/de/hook/page_header.bb
new file mode 100644
index 000000000..ffaa791c9
--- /dev/null
+++ b/doc/de/hook/page_header.bb
@@ -0,0 +1 @@
+[h2]page_header[/h2]
diff --git a/doc/de/hook/page_meta.bb b/doc/de/hook/page_meta.bb
new file mode 100644
index 000000000..30a8f9440
--- /dev/null
+++ b/doc/de/hook/page_meta.bb
@@ -0,0 +1,13 @@
+[h2]page_meta[/h2]
+
+Called before generating the page header.
+
+[code]
+ $pagemeta = [ 'og:title' => self::$page['title'] ];
+
+ call_hooks('page_meta',$pagemeta);
+ foreach ($pagemeta as $metaproperty => $metavalue) {
+ self::$meta->set($metaproperty,$metavalue);
+ }
+
+[/code]
diff --git a/doc/de/hook/parse_atom.bb b/doc/de/hook/parse_atom.bb
new file mode 100644
index 000000000..c8037317f
--- /dev/null
+++ b/doc/de/hook/parse_atom.bb
@@ -0,0 +1 @@
+[h2]parse_atom[/h2]
diff --git a/doc/de/hook/parse_link.bb b/doc/de/hook/parse_link.bb
new file mode 100644
index 000000000..1c328059c
--- /dev/null
+++ b/doc/de/hook/parse_link.bb
@@ -0,0 +1 @@
+[h2]parse_link[/h2]
diff --git a/doc/de/hook/pdl_selector.bb b/doc/de/hook/pdl_selector.bb
new file mode 100644
index 000000000..bee01c843
--- /dev/null
+++ b/doc/de/hook/pdl_selector.bb
@@ -0,0 +1 @@
+[h2]pdl_selector[/h2]
diff --git a/doc/de/hook/perm_is_allowed.bb b/doc/de/hook/perm_is_allowed.bb
new file mode 100644
index 000000000..aac647609
--- /dev/null
+++ b/doc/de/hook/perm_is_allowed.bb
@@ -0,0 +1 @@
+[h2]perm_is_allowed[/h2]
diff --git a/doc/de/hook/permissions_create.bb b/doc/de/hook/permissions_create.bb
new file mode 100644
index 000000000..22f80a80e
--- /dev/null
+++ b/doc/de/hook/permissions_create.bb
@@ -0,0 +1 @@
+[h2]permissions_create[/h2]
diff --git a/doc/de/hook/permissions_update.bb b/doc/de/hook/permissions_update.bb
new file mode 100644
index 000000000..40366b33d
--- /dev/null
+++ b/doc/de/hook/permissions_update.bb
@@ -0,0 +1 @@
+[h2]permissions_update[/h2]
diff --git a/doc/de/about/.gitkeep b/doc/de/hook/permit_hook.bb
index e69de29bb..e69de29bb 100644
--- a/doc/de/about/.gitkeep
+++ b/doc/de/hook/permit_hook.bb
diff --git a/doc/de/hook/personal_xrd.bb b/doc/de/hook/personal_xrd.bb
new file mode 100644
index 000000000..71d4bd8dd
--- /dev/null
+++ b/doc/de/hook/personal_xrd.bb
@@ -0,0 +1 @@
+[h2]personal_xrd[/h2]
diff --git a/doc/de/hook/photo_post_end.bb b/doc/de/hook/photo_post_end.bb
new file mode 100644
index 000000000..8a3291763
--- /dev/null
+++ b/doc/de/hook/photo_post_end.bb
@@ -0,0 +1 @@
+[h2]photo_post_end[/h2]
diff --git a/doc/de/hook/photo_upload_begin.bb b/doc/de/hook/photo_upload_begin.bb
new file mode 100644
index 000000000..5e441a12a
--- /dev/null
+++ b/doc/de/hook/photo_upload_begin.bb
@@ -0,0 +1 @@
+[h2]photo_upload_begin[/h2]
diff --git a/doc/de/hook/photo_upload_end.bb b/doc/de/hook/photo_upload_end.bb
new file mode 100644
index 000000000..956175f1d
--- /dev/null
+++ b/doc/de/hook/photo_upload_end.bb
@@ -0,0 +1 @@
+[h2]photo_upload_end[/h2]
diff --git a/doc/de/hook/photo_upload_file.bb b/doc/de/hook/photo_upload_file.bb
new file mode 100644
index 000000000..726622ac0
--- /dev/null
+++ b/doc/de/hook/photo_upload_file.bb
@@ -0,0 +1 @@
+[h2]photo_upload_file[/h2]
diff --git a/doc/de/hook/photo_upload_form.bb b/doc/de/hook/photo_upload_form.bb
new file mode 100644
index 000000000..70b8318b8
--- /dev/null
+++ b/doc/de/hook/photo_upload_form.bb
@@ -0,0 +1 @@
+[h2]photo_upload_form[/h2]
diff --git a/doc/de/hook/photo_view_filter.bb b/doc/de/hook/photo_view_filter.bb
new file mode 100644
index 000000000..0780c1edc
--- /dev/null
+++ b/doc/de/hook/photo_view_filter.bb
@@ -0,0 +1 @@
+[h2]photo_view_filter[/h2]
diff --git a/doc/de/hook/poke_verbs.bb b/doc/de/hook/poke_verbs.bb
new file mode 100644
index 000000000..54d68c3a2
--- /dev/null
+++ b/doc/de/hook/poke_verbs.bb
@@ -0,0 +1 @@
+[h2]poke_verbs[/h2]
diff --git a/doc/de/hook/post_local.bb b/doc/de/hook/post_local.bb
new file mode 100644
index 000000000..5aa723cb9
--- /dev/null
+++ b/doc/de/hook/post_local.bb
@@ -0,0 +1 @@
+[h2]post_local[/h2]
diff --git a/doc/de/hook/post_local_end.bb b/doc/de/hook/post_local_end.bb
new file mode 100644
index 000000000..380166fdb
--- /dev/null
+++ b/doc/de/hook/post_local_end.bb
@@ -0,0 +1 @@
+[h2]post_local_end[/h2]
diff --git a/doc/de/hook/post_local_start.bb b/doc/de/hook/post_local_start.bb
new file mode 100644
index 000000000..2f684f67a
--- /dev/null
+++ b/doc/de/hook/post_local_start.bb
@@ -0,0 +1 @@
+[h2]post_local_start[/h2]
diff --git a/doc/de/hook/post_mail.bb b/doc/de/hook/post_mail.bb
new file mode 100644
index 000000000..8f67ad4f0
--- /dev/null
+++ b/doc/de/hook/post_mail.bb
@@ -0,0 +1 @@
+[h2]post_mail[/h2]
diff --git a/doc/de/hook/post_mail_end.bb b/doc/de/hook/post_mail_end.bb
new file mode 100644
index 000000000..7f0085773
--- /dev/null
+++ b/doc/de/hook/post_mail_end.bb
@@ -0,0 +1 @@
+[h2]post_mail_end[/h2]
diff --git a/doc/de/hook/post_remote.bb b/doc/de/hook/post_remote.bb
new file mode 100644
index 000000000..f8e087eee
--- /dev/null
+++ b/doc/de/hook/post_remote.bb
@@ -0,0 +1 @@
+[h2]post_remote[/h2]
diff --git a/doc/de/hook/post_remote_end.bb b/doc/de/hook/post_remote_end.bb
new file mode 100644
index 000000000..0fef20cbf
--- /dev/null
+++ b/doc/de/hook/post_remote_end.bb
@@ -0,0 +1 @@
+[h2]post_remote_end[/h2]
diff --git a/doc/de/hook/post_remote_update.bb b/doc/de/hook/post_remote_update.bb
new file mode 100644
index 000000000..fd358db28
--- /dev/null
+++ b/doc/de/hook/post_remote_update.bb
@@ -0,0 +1 @@
+[h2]post_remote_update[/h2]
diff --git a/doc/de/hook/post_remote_update_end.bb b/doc/de/hook/post_remote_update_end.bb
new file mode 100644
index 000000000..95f1e6f78
--- /dev/null
+++ b/doc/de/hook/post_remote_update_end.bb
@@ -0,0 +1 @@
+[h2]post_remote_update_end[/h2]
diff --git a/doc/de/hook/prepare_body.bb b/doc/de/hook/prepare_body.bb
new file mode 100644
index 000000000..3f1eaef85
--- /dev/null
+++ b/doc/de/hook/prepare_body.bb
@@ -0,0 +1 @@
+[h2]prepare_body[/h2]
diff --git a/doc/de/hook/prepare_body_final.bb b/doc/de/hook/prepare_body_final.bb
new file mode 100644
index 000000000..96d1ae389
--- /dev/null
+++ b/doc/de/hook/prepare_body_final.bb
@@ -0,0 +1 @@
+[h2]prepare_body_final[/h2]
diff --git a/doc/de/hook/prepare_body_init.bb b/doc/de/hook/prepare_body_init.bb
new file mode 100644
index 000000000..f3de79970
--- /dev/null
+++ b/doc/de/hook/prepare_body_init.bb
@@ -0,0 +1 @@
+[h2]prepare_body_init[/h2]
diff --git a/doc/de/hook/privacygroup_extras.bb b/doc/de/hook/privacygroup_extras.bb
new file mode 100644
index 000000000..bd67f2470
--- /dev/null
+++ b/doc/de/hook/privacygroup_extras.bb
@@ -0,0 +1,12 @@
+[h2]privacygroup_extras[/h2]
+
+Add items to the Privacy Group edit form
+
+[code]
+ $hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(1) ];
+ call_hooks ('privacygroup_extras',$hookinfo);
+ $pgrp_extras = $hookinfo['pgrp_extras'];
+[/code]
+
+see: Zotlabs/Module/Group.php
+see: view/tpl/privacy_groups.tpl
diff --git a/doc/de/hook/privacygroup_extras_drop.bb b/doc/de/hook/privacygroup_extras_drop.bb
new file mode 100644
index 000000000..fd27ab255
--- /dev/null
+++ b/doc/de/hook/privacygroup_extras_drop.bb
@@ -0,0 +1,11 @@
+[h2]privacygroup_extras_drop[/h2]
+
+Called after privacy group is dropped
+
+[code]
+ $hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(2) ];
+ call_hooks ('privacygroup_extras_drop',$hookinfo);
+[/code]
+
+see: Zotlabs/Module/Group.php
+see: view/tpl/privacy_groups.tpl
diff --git a/doc/de/hook/privacygroup_extras_post.bb b/doc/de/hook/privacygroup_extras_post.bb
new file mode 100644
index 000000000..704db1997
--- /dev/null
+++ b/doc/de/hook/privacygroup_extras_post.bb
@@ -0,0 +1,11 @@
+[h2]privacygroup_extras_post[/h2]
+
+Called as privacy group edit form is edited.
+
+[code]
+ $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
+ call_hooks ('privacygroup_extras_post',$hookinfo);
+[/code]
+
+see: Zotlabs/Module/Group.php
+see: view/tpl/privacy_groups.tpl
diff --git a/doc/de/hook/proc_run.bb b/doc/de/hook/proc_run.bb
new file mode 100644
index 000000000..a3759794a
--- /dev/null
+++ b/doc/de/hook/proc_run.bb
@@ -0,0 +1 @@
+[h2]proc_run[/h2]
diff --git a/doc/de/hook/process_channel_sync_delivery.bb b/doc/de/hook/process_channel_sync_delivery.bb
new file mode 100644
index 000000000..c0416c8cb
--- /dev/null
+++ b/doc/de/hook/process_channel_sync_delivery.bb
@@ -0,0 +1 @@
+[h2]process_channel_sync_delivery[/h2]
diff --git a/doc/de/hook/profile_advanced.bb b/doc/de/hook/profile_advanced.bb
new file mode 100644
index 000000000..65e56afd6
--- /dev/null
+++ b/doc/de/hook/profile_advanced.bb
@@ -0,0 +1 @@
+[h2]profile_advanced[/h2]
diff --git a/doc/de/hook/profile_edit.bb b/doc/de/hook/profile_edit.bb
new file mode 100644
index 000000000..e60663d4a
--- /dev/null
+++ b/doc/de/hook/profile_edit.bb
@@ -0,0 +1 @@
+[h2]profile_edit[/h2]
diff --git a/doc/de/hook/profile_photo_content_end.bb b/doc/de/hook/profile_photo_content_end.bb
new file mode 100644
index 000000000..518415c4d
--- /dev/null
+++ b/doc/de/hook/profile_photo_content_end.bb
@@ -0,0 +1 @@
+[h2]profile_photo_content_end[/h2]
diff --git a/doc/de/hook/profile_post.bb b/doc/de/hook/profile_post.bb
new file mode 100644
index 000000000..d22d8fbc7
--- /dev/null
+++ b/doc/de/hook/profile_post.bb
@@ -0,0 +1 @@
+[h2]profile_post[/h2]
diff --git a/doc/de/hook/profile_sidebar.bb b/doc/de/hook/profile_sidebar.bb
new file mode 100644
index 000000000..bfd059e4b
--- /dev/null
+++ b/doc/de/hook/profile_sidebar.bb
@@ -0,0 +1 @@
+[h2]profile_sidebar[/h2]
diff --git a/doc/de/hook/profile_sidebar_enter.bb b/doc/de/hook/profile_sidebar_enter.bb
new file mode 100644
index 000000000..9d6726a30
--- /dev/null
+++ b/doc/de/hook/profile_sidebar_enter.bb
@@ -0,0 +1 @@
+[h2]profile_sidebar_enter[/h2]
diff --git a/doc/de/hook/register_account.bb b/doc/de/hook/register_account.bb
new file mode 100644
index 000000000..df4de2b30
--- /dev/null
+++ b/doc/de/hook/register_account.bb
@@ -0,0 +1 @@
+[h2]register_account[/h2]
diff --git a/doc/de/hook/render_location.bb b/doc/de/hook/render_location.bb
new file mode 100644
index 000000000..41501c087
--- /dev/null
+++ b/doc/de/hook/render_location.bb
@@ -0,0 +1 @@
+[h2]render_location[/h2]
diff --git a/doc/de/hook/replace_macros.bb b/doc/de/hook/replace_macros.bb
new file mode 100644
index 000000000..fac39dd7b
--- /dev/null
+++ b/doc/de/hook/replace_macros.bb
@@ -0,0 +1 @@
+[h2]replace_macros[/h2]
diff --git a/doc/de/hook/reverse_magic_auth.bb b/doc/de/hook/reverse_magic_auth.bb
new file mode 100644
index 000000000..4cbd84b93
--- /dev/null
+++ b/doc/de/hook/reverse_magic_auth.bb
@@ -0,0 +1 @@
+[h2]reverse_magic_auth[/h2]
diff --git a/doc/de/hook/settings_form.bb b/doc/de/hook/settings_form.bb
new file mode 100644
index 000000000..d65341181
--- /dev/null
+++ b/doc/de/hook/settings_form.bb
@@ -0,0 +1 @@
+[h2]settings_form[/h2]
diff --git a/doc/de/hook/settings_post.bb b/doc/de/hook/settings_post.bb
new file mode 100644
index 000000000..f72546c11
--- /dev/null
+++ b/doc/de/hook/settings_post.bb
@@ -0,0 +1 @@
+[h2]settings_post[/h2]
diff --git a/doc/de/hook/sexpref_selector.bb b/doc/de/hook/sexpref_selector.bb
new file mode 100644
index 000000000..b4dad6b38
--- /dev/null
+++ b/doc/de/hook/sexpref_selector.bb
@@ -0,0 +1 @@
+[h2]sexpref_selector[/h2]
diff --git a/doc/de/hook/sexpref_selector_min.bb b/doc/de/hook/sexpref_selector_min.bb
new file mode 100644
index 000000000..6f49946af
--- /dev/null
+++ b/doc/de/hook/sexpref_selector_min.bb
@@ -0,0 +1 @@
+[h2]sexpref_selector_min[/h2]
diff --git a/doc/de/hook/smilie.bb b/doc/de/hook/smilie.bb
new file mode 100644
index 000000000..575acc178
--- /dev/null
+++ b/doc/de/hook/smilie.bb
@@ -0,0 +1,19 @@
+[h2]smilie[/h2]
+
+
+Called when processing translation of emoticons. It is passed an array containing two sub-arrays:
+
+ array(
+ 'texts' => array('text1','text2',...),
+ 'icons' => array('icon1','icon2',...)
+ );
+
+ texts is the emoticon text - for example ':-)' for a traditional smile face.
+ icons is the HTML used as a replacement. For example
+ '&lt;img class="smiley" src="https://localhost/images/smiley-smile.gif" alt=":-)" /&gt;'
+
+ If adding or removing an entry from either array, the corresponding element from the matching array must also
+ be added or removed. Emoticons less than three characters in length or not recommended as they get triggered
+ incorrectly quite often. Extended emoticons are indicated by convention using a preceding colon, for example
+
+ :walrus_kissing_a_baby \ No newline at end of file
diff --git a/doc/de/hook/status_editor.bb b/doc/de/hook/status_editor.bb
new file mode 100644
index 000000000..00e97a7c9
--- /dev/null
+++ b/doc/de/hook/status_editor.bb
@@ -0,0 +1,31 @@
+[h2]status_editor[/h2]
+
+Replace the default status_editor (jot).
+
+Allow plugins to replace the default status editor in a context dependent manner.
+
+It is fed an array of ['editor_html' => '', 'x' => $x, 'popup' => $popup, 'module' => $module].
+
+All calls to the status_editor at the time of the creation of this hook have been updated
+to set $module at invocation. This allows addon developers to have a context dependent editor
+based on the Hubzilla module/addon.
+
+Calls to status_editor() are in the form of:
+ status_editor($a, $x, $popup, $module).
+
+Future module/addon developers are encouraged to set $popup and $module when invoking the
+status_editor.
+
+
+[code]
+ $hook_info = ['editor_html' => '', 'x' => $x, 'popup' => $popup, 'module' => $module];
+ call_hooks('status_editor',$hook_info);
+ if ($hook_info['editor_html'] == '') {
+ return hz_status_editor($a, $x, $popup);
+ } else {
+ return $hook_info['editor_html'];
+ }
+
+[/code]
+
+see: include/conversation.php
diff --git a/doc/de/hook/stream_item.bb b/doc/de/hook/stream_item.bb
new file mode 100644
index 000000000..30086961d
--- /dev/null
+++ b/doc/de/hook/stream_item.bb
@@ -0,0 +1,13 @@
+[h2]stream_item[/h2]
+
+
+Called for each item processed for viewing by conversation();
+
+The hook data consists of an array
+
+ array(
+ 'mode' => current mode of conversation()
+ 'item' => item being processed
+ );
+
+ Set item['blocked'] to block the item from viewing. This action will not affect comment or sub-thread counts, so if there are three comments in a conversation and you block one, three comments will still be reported even though only two are visible.
diff --git a/doc/de/hook/system_app_installed_filter.bb b/doc/de/hook/system_app_installed_filter.bb
new file mode 100644
index 000000000..a269a79a8
--- /dev/null
+++ b/doc/de/hook/system_app_installed_filter.bb
@@ -0,0 +1,18 @@
+[h2]system_app_installed_filter[/h2]
+
+Allow plugins to filter the result of system_app_installed.
+
+Code excerpt:
+
+[code]
+ $filter_arr = [
+ 'uid'=>$uid,
+ 'app'=>$app,
+ 'installed'=>$r
+ ];
+ call_hooks('system_app_installed_filter',$filter_arr);
+ $r = $filter_arr['installed'];
+[/code]
+
+cxref: Zotlabs/Lib/Apps.php
+
diff --git a/doc/de/hook/tagged.bb b/doc/de/hook/tagged.bb
new file mode 100644
index 000000000..05d081d07
--- /dev/null
+++ b/doc/de/hook/tagged.bb
@@ -0,0 +1,16 @@
+[h2]tagged[/h2]
+
+
+This hook is called when a delivery is made which results in the recipient being tagged.
+
+The hook data is an array containing
+
+ array(
+ 'channel_id' => int,
+ 'item' => item structure of the delivered item from database,
+ 'body' => the body of the referenced item
+
+ );
+
+ Note: This hook is called before secondary delivery chains are invoked in the case of tagging a forum. This means that permissions and some item attributes will be those of the item before being re-packaged and before ownership of this item is given to the forum.
+
diff --git a/doc/de/hook/update_unseen.bb b/doc/de/hook/update_unseen.bb
new file mode 100644
index 000000000..8fb02c239
--- /dev/null
+++ b/doc/de/hook/update_unseen.bb
@@ -0,0 +1,9 @@
+[h3]update_unseen[/h3]
+
+Called prior to automatically marking items 'seen'; allowing a plugin the choice to not perform this action.
+
+hook data
+
+[ 'channel_id' => local_channel(), 'update' => 'unset' ];
+
+If 'update' is set to 0 or false on return, the update operation is not performed. \ No newline at end of file
diff --git a/doc/de/hook/validate_channelname.bb b/doc/de/hook/validate_channelname.bb
new file mode 100644
index 000000000..2ab12bbec
--- /dev/null
+++ b/doc/de/hook/validate_channelname.bb
@@ -0,0 +1,23 @@
+[h2]validate_channelname[/h2]
+
+Called when creating a new channel or changing the channel name in mod/settings.php
+
+Hook data consists of an array
+
+ array(
+ 'name' => supplied name
+ );
+
+ If the hook handler determines the name is valid, do nothing. If there is an issue with the name,
+ set $hook_data['message'] to the message text which should be displayed to the member - and the name will
+ not be accepted.
+
+
+ Example:
+ [code]
+ if(mb_strlen($hook_data['name']) < 3)
+ $hook_data['message'] = t('Name too short.');
+ [/code]
+
+
+ \ No newline at end of file
diff --git a/doc/de/hook/webfinger.bb b/doc/de/hook/webfinger.bb
new file mode 100644
index 000000000..7cc24322f
--- /dev/null
+++ b/doc/de/hook/webfinger.bb
@@ -0,0 +1 @@
+[h2]webfinger[/h2]
diff --git a/doc/de/hook/well_known.bb b/doc/de/hook/well_known.bb
new file mode 100644
index 000000000..778b27a02
--- /dev/null
+++ b/doc/de/hook/well_known.bb
@@ -0,0 +1 @@
+[h2]well_known[/h2]
diff --git a/doc/de/hook/wiki_preprocess.bb b/doc/de/hook/wiki_preprocess.bb
new file mode 100644
index 000000000..913b601ba
--- /dev/null
+++ b/doc/de/hook/wiki_preprocess.bb
@@ -0,0 +1,11 @@
+[h3]wiki_preprocess[/h3]
+
+Called before markdown/bbcode processors are run for wiki pages
+
+Passed parameter array:
+
+ 'content' => wiki page content
+ 'mimetype' => page mimetype
+
+
+see: Zotlabs/Module/Wiki.php
diff --git a/doc/de/hook/zid.bb b/doc/de/hook/zid.bb
new file mode 100644
index 000000000..2210c1342
--- /dev/null
+++ b/doc/de/hook/zid.bb
@@ -0,0 +1 @@
+[h2]zid[/h2]
diff --git a/doc/de/hook/zid_init.bb b/doc/de/hook/zid_init.bb
new file mode 100644
index 000000000..131dd8f72
--- /dev/null
+++ b/doc/de/hook/zid_init.bb
@@ -0,0 +1 @@
+[h2]zid_init[/h2]
diff --git a/doc/de/hook/zot_best_algorithm.bb b/doc/de/hook/zot_best_algorithm.bb
new file mode 100644
index 000000000..ccde505cb
--- /dev/null
+++ b/doc/de/hook/zot_best_algorithm.bb
@@ -0,0 +1,3 @@
+[h2]zot_best_algorithm[/h2]
+
+
diff --git a/doc/de/hook/zot_finger.bb b/doc/de/hook/zot_finger.bb
new file mode 100644
index 000000000..9383b4c31
--- /dev/null
+++ b/doc/de/hook/zot_finger.bb
@@ -0,0 +1 @@
+[h2]zot_finger[/h2]
diff --git a/doc/de/hooks.html b/doc/de/hooks.html
new file mode 100644
index 000000000..a7ee314e7
--- /dev/null
+++ b/doc/de/hooks.html
@@ -0,0 +1 @@
+<div><h3>Hooks</h3><table><tr><td>Function</td><td>Source File</td><td>Arg</td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>$a-&gt;module . _mod_aftercontent</td><td>index.php</td><td>$arr</td></tr><tr><td>$a-&gt;module . _mod_content</td><td>index.php</td><td>$arr</td></tr><tr><td>$a-&gt;module . _mod_init</td><td>index.php</td><td>$placeholder</td></tr><tr><td>$a-&gt;module . _mod_post</td><td>index.php</td><td>$_POST</td></tr><tr><td>$a-&gt;module . _post_ . $selname</td><td>include/acl_selectors.php</td><td>$o</td></tr><tr><td>$a-&gt;module . _post_ . $selname</td><td>include/acl_selectors.php</td><td>$o</td></tr><tr><td>$a-&gt;module . _post_ . $selname</td><td>include/acl_selectors.php</td><td>$o</td></tr><tr><td>$a-&gt;module . _pre_ . $selname</td><td>include/acl_selectors.php</td><td>$arr</td></tr><tr><td>$a-&gt;module . _pre_ . $selname</td><td>include/acl_selectors.php</td><td>$arr</td></tr><tr><td>$a-&gt;module . _pre_ . $selname</td><td>include/acl_selectors.php</td><td>$arr</td></tr><tr><td>$name</td><td>include/plugin.php</td><td>&amp;$data = null</td></tr><tr><td>about_hook</td><td>mod/siteinfo.php</td><td>$o</td></tr><tr><td>accept_follow</td><td>mod/connedit.php</td><td>$arr</td></tr><tr><td>account_downgrade</td><td>include/account.php</td><td>$ret</td></tr><tr><td>account_downgrade</td><td>include/account.php</td><td>$ret</td></tr><tr><td>account_settings</td><td>mod/settings.php</td><td>$account_settings</td></tr><tr><td>activity_received</td><td>include/zot.php</td><td>$parr</td></tr><tr><td>affinity_labels</td><td>include/widgets.php</td><td>$labels</td></tr><tr><td>affinity_labels</td><td>mod/connedit.php</td><td>$labels</td></tr><tr><td>api_perm_is_allowed</td><td>include/permissions.php</td><td>$arr</td></tr><tr><td>app_menu</td><td>index.php</td><td>$arr</td></tr><tr><td>atom_author</td><td>include/items.php</td><td>$o</td></tr><tr><td>atom_entry</td><td>include/items.php</td><td>$o</td></tr><tr><td>atom_feed</td><td>include/items.php</td><td>$atom</td></tr><tr><td>atom_feed_end</td><td>include/items.php</td><td>$atom</td></tr><tr><td>attach_upload_file</td><td>include/attach.php</td><td>$f</td></tr><tr><td>authenticate</td><td>include/auth.php</td><td>$addon_auth</td></tr><tr><td>avatar_lookup</td><td>include/network.php</td><td>$avatar</td></tr><tr><td>bb2diaspora</td><td>include/markdown.php</td><td>$Text</td></tr><tr><td>bbcode</td><td>include/bbcode.php</td><td>$Text</td></tr><tr><td>channel_remove</td><td>include/Contact.php</td><td>$r[0]</td></tr><tr><td>chat_message</td><td>include/chat.php</td><td>$arr</td></tr><tr><td>chat_post</td><td>mod/chatsvc.php</td><td>$arr</td></tr><tr><td>check_account_email</td><td>include/account.php</td><td>$arr</td></tr><tr><td>check_account_invite</td><td>include/account.php</td><td>$arr</td></tr><tr><td>check_account_password</td><td>include/account.php</td><td>$arr</td></tr><tr><td>connect_premium</td><td>mod/connect.php</td><td>$arr</td></tr><tr><td>connector_settings</td><td>mod/settings.php</td><td>$settings_connectors</td></tr><tr><td>construct_page</td><td>boot.php</td><td>$arr</td></tr><tr><td>contact_block_end</td><td>include/text.php</td><td>$arr</td></tr><tr><td>contact_edit</td><td>mod/connedit.php</td><td>$arr</td></tr><tr><td>contact_edit_post</td><td>mod/connedit.php</td><td>$_POST</td></tr><tr><td>contact_select_options</td><td>include/acl_selectors.php</td><td>$x</td></tr><tr><td>conversation_start</td><td>include/conversation.php</td><td>$cb</td></tr><tr><td>create_identity</td><td>include/channel.php</td><td>$newuid</td></tr><tr><td>cron</td><td>include/cronhooks.php</td><td>$d</td></tr><tr><td>cron_daily</td><td>include/poller.php</td><td>datetime_convert()</td></tr><tr><td>cron_weekly</td><td>include/poller.php</td><td>datetime_convert()</td></tr><tr><td>directory_item</td><td>mod/directory.php</td><td>$arr</td></tr><tr><td>discover_by_webbie</td><td>include/network.php</td><td>$arr</td></tr><tr><td>display_item</td><td>include/ItemObject.php</td><td>$arr</td></tr><tr><td>display_item</td><td>include/conversation.php</td><td>$arr</td></tr><tr><td>display_settings</td><td>mod/settings.php</td><td>$o</td></tr><tr><td>display_settings_post</td><td>mod/settings.php</td><td>$_POST</td></tr><tr><td>donate_contributors</td><td>extend/addon/matrix/donate/donate.php</td><td>$contributors</td></tr><tr><td>donate_plugin</td><td>extend/addon/matrix/donate/donate.php</td><td>$o</td></tr><tr><td>donate_sponsors</td><td>extend/addon/matrix/donate/donate.php</td><td>$sponsors</td></tr><tr><td>dreport_is_storable</td><td>include/zot.php</td><td>$dr</td></tr><tr><td>drop_item</td><td>include/items.php</td><td>$arr</td></tr><tr><td>enotify</td><td>include/enotify.php</td><td>$h</td></tr><tr><td>enotify_mail</td><td>include/enotify.php</td><td>$datarray</td></tr><tr><td>enotify_store</td><td>include/enotify.php</td><td>$datarray</td></tr><tr><td>event_created</td><td>include/event.php</td><td>$event[id]</td></tr><tr><td>event_updated</td><td>include/event.php</td><td>$event[id]</td></tr><tr><td>externals_url_select</td><td>include/externals.php</td><td>$arr</td></tr><tr><td>feature_enabled</td><td>include/features.php</td><td>$arr</td></tr><tr><td>feature_settings</td><td>mod/settings.php</td><td>$settings_addons</td></tr><tr><td>feature_settings_post</td><td>mod/settings.php</td><td>$_POST</td></tr><tr><td>follow</td><td>include/follow.php</td><td>$arr</td></tr><tr><td>follow</td><td>include/follow.php</td><td>$arr</td></tr><tr><td>follow_allow</td><td>include/follow.php</td><td>$x</td></tr><tr><td>gender_selector</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>gender_selector_min</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>generate_map</td><td>include/text.php</td><td>$arr</td></tr><tr><td>generate_named_map</td><td>include/text.php</td><td>$arr</td></tr><tr><td>get_all_api_perms</td><td>include/permissions.php</td><td>$arr</td></tr><tr><td>get_all_perms</td><td>include/permissions.php</td><td>$arr</td></tr><tr><td>get_features</td><td>include/features.php</td><td>$arr</td></tr><tr><td>get_role_perms</td><td>include/permissions.php</td><td>$ret</td></tr><tr><td>get_widgets</td><td>boot.php</td><td>$arr</td></tr><tr><td>get_widgets</td><td>boot.php</td><td>$arr</td></tr><tr><td>global_permissions</td><td>include/permissions.php</td><td>$ret</td></tr><tr><td>home_content</td><td>mod/home.php</td><td>$o</td></tr><tr><td>home_init</td><td>mod/home.php</td><td>$ret</td></tr><tr><td>hostxrd</td><td>mod/hostxrd.php</td><td>$arr</td></tr><tr><td>html2bbcode</td><td>include/html2bbcode.php</td><td>$message</td></tr><tr><td>identity_basic_export</td><td>include/channel.php</td><td>$addon</td></tr><tr><td>import_author_xchan</td><td>include/items.php</td><td>$arr</td></tr><tr><td>import_channel</td><td>mod/import.php</td><td>$addon</td></tr><tr><td>import_directory_profile</td><td>include/zot.php</td><td>$d</td></tr><tr><td>import_xchan</td><td>include/zot.php</td><td>$arr</td></tr><tr><td>item_photo_menu</td><td>include/conversation.php</td><td>$args</td></tr><tr><td>item_store</td><td>include/items.php</td><td>$d</td></tr><tr><td>item_store</td><td>include/items.php</td><td>$arr</td></tr><tr><td>item_store_update</td><td>include/items.php</td><td>$d</td></tr><tr><td>item_translate</td><td>include/items.php</td><td>$translate</td></tr><tr><td>item_translate</td><td>include/items.php</td><td>$translate</td></tr><tr><td>jot_networks</td><td>include/acl_selectors.php</td><td>$jotnets</td></tr><tr><td>jot_networks</td><td>include/conversation.php</td><td>$jotnets</td></tr><tr><td>jot_networks</td><td>mod/editblock.php</td><td>$jotnets</td></tr><tr><td>jot_networks</td><td>mod/editpost.php</td><td>$jotnets</td></tr><tr><td>jot_networks</td><td>mod/editwebpage.php</td><td>$jotnets</td></tr><tr><td>jot_networks</td><td>mod/editlayout.php</td><td>$jotnets</td></tr><tr><td>jot_tool</td><td>include/conversation.php</td><td>$jotplugins</td></tr><tr><td>jot_tool</td><td>mod/editblock.php</td><td>$jotplugins</td></tr><tr><td>jot_tool</td><td>mod/editpost.php</td><td>$jotplugins</td></tr><tr><td>jot_tool</td><td>mod/editwebpage.php</td><td>$jotplugins</td></tr><tr><td>jot_tool</td><td>mod/editlayout.php</td><td>$jotplugins</td></tr><tr><td>load_pdl</td><td>boot.php</td><td>$arr</td></tr><tr><td>local_dir_update</td><td>include/dir_fns.php</td><td>$arr</td></tr><tr><td>logged_in</td><td>include/oauth.php</td><td>$a-&gt;user</td></tr><tr><td>logged_in</td><td>include/api.php</td><td>$a-&gt;user</td></tr><tr><td>logged_in</td><td>include/security.php</td><td>$a-&gt;account</td></tr><tr><td>logged_in</td><td>include/security.php</td><td>$user_record</td></tr><tr><td>logging_out</td><td>include/auth.php</td><td>$args</td></tr><tr><td>login_hook</td><td>boot.php</td><td>$o</td></tr><tr><td>magic_auth</td><td>mod/magic.php</td><td>$arr</td></tr><tr><td>magic_auth_openid_success</td><td>mod/openid.php</td><td>$arr</td></tr><tr><td>magic_auth_openid_success</td><td>mod/openid.php</td><td>$arr</td></tr><tr><td>magic_auth_success</td><td>mod/post.php</td><td>$arr</td></tr><tr><td>main_slider</td><td>include/widgets.php</td><td>$arr</td></tr><tr><td>marital_selector</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>marital_selector_min</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>module_loaded</td><td>index.php</td><td>$x</td></tr><tr><td>mood_verbs</td><td>include/text.php</td><td>$arr</td></tr><tr><td>nav</td><td>include/nav.php</td><td>$x</td></tr><tr><td>network_content_init</td><td>mod/network.php</td><td>$arr</td></tr><tr><td>network_ping</td><td>mod/ping.php</td><td>$arr</td></tr><tr><td>network_tabs</td><td>include/conversation.php</td><td>$arr</td></tr><tr><td>network_to_name</td><td>include/contact_selectors.php</td><td>$nets</td></tr><tr><td>notifier_end</td><td>include/notifier.php</td><td>$target_item</td></tr><tr><td>notifier_hub</td><td>include/notifier.php</td><td>$narr</td></tr><tr><td>notifier_normal</td><td>include/deliver_hooks.php</td><td>$r[0]</td></tr><tr><td>obj_verbs</td><td>include/taxonomy.php</td><td>$arr</td></tr><tr><td>oembed_probe</td><td>include/oembed.php</td><td>$x</td></tr><tr><td>page_content_top</td><td>index.php</td><td>$a-&gt;page[content]</td></tr><tr><td>page_end</td><td>index.php</td><td>$a-&gt;page[content]</td></tr><tr><td>page_header</td><td>include/nav.php</td><td>$a-&gt;page[nav]</td></tr><tr><td>parse_atom</td><td>include/items.php</td><td>$arr</td></tr><tr><td>parse_link</td><td>mod/linkinfo.php</td><td>$arr</td></tr><tr><td>pdl_selector</td><td>include/comanche.php</td><td>$arr</td></tr><tr><td>perm_is_allowed</td><td>include/permissions.php</td><td>$arr</td></tr><tr><td>permissions_create</td><td>include/notifier.php</td><td>$perm_update</td></tr><tr><td>permissions_update</td><td>include/notifier.php</td><td>$perm_update</td></tr><tr><td>personal_xrd</td><td>mod/xrd.php</td><td>$arr</td></tr><tr><td>photo_post_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_post_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_upload_begin</td><td>include/attach.php</td><td>$arr</td></tr><tr><td>photo_upload_begin</td><td>include/photos.php</td><td>$args</td></tr><tr><td>photo_upload_end</td><td>include/attach.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/attach.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/attach.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/attach.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/attach.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_upload_end</td><td>include/photos.php</td><td>$ret</td></tr><tr><td>photo_upload_file</td><td>include/attach.php</td><td>$f</td></tr><tr><td>photo_upload_file</td><td>include/photos.php</td><td>$f</td></tr><tr><td>photo_upload_form</td><td>mod/photos.php</td><td>$ret</td></tr><tr><td>poke_verbs</td><td>include/text.php</td><td>$arr</td></tr><tr><td>post_local</td><td>include/zot.php</td><td>$arr</td></tr><tr><td>post_local</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_local</td><td>mod/item.php</td><td>$datarray</td></tr><tr><td>post_local_end</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_local_end</td><td>include/attach.php</td><td>$arr</td></tr><tr><td>post_local_end</td><td>include/attach.php</td><td>$arr</td></tr><tr><td>post_local_end</td><td>extend/addon/matrix/randpost/randpost.php</td><td>$x</td></tr><tr><td>post_local_end</td><td>extend/addon/matrix/randpost/randpost.php</td><td>$x</td></tr><tr><td>post_local_end</td><td>mod/mood.php</td><td>$arr</td></tr><tr><td>post_local_end</td><td>mod/like.php</td><td>$arr</td></tr><tr><td>post_local_end</td><td>mod/item.php</td><td>$datarray</td></tr><tr><td>post_local_end</td><td>mod/subthread.php</td><td>$arr</td></tr><tr><td>post_local_start</td><td>mod/item.php</td><td>$_REQUEST</td></tr><tr><td>post_mail</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_mail_end</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_remote</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_remote_end</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_remote_update</td><td>include/items.php</td><td>$arr</td></tr><tr><td>post_remote_update_end</td><td>include/items.php</td><td>$arr</td></tr><tr><td>prepare_body</td><td>include/text.php</td><td>$prep_arr</td></tr><tr><td>prepare_body_final</td><td>include/text.php</td><td>$prep_arr</td></tr><tr><td>prepare_body_init</td><td>include/text.php</td><td>$item</td></tr><tr><td>probe_well_known</td><td>include/probe.php</td><td>$ret</td></tr><tr><td>proc_run</td><td>boot.php</td><td>$arr</td></tr><tr><td>process_channel_sync_delivery</td><td>include/zot.php</td><td>$addon</td></tr><tr><td>profile_advanced</td><td>mod/profile.php</td><td>$o</td></tr><tr><td>profile_edit</td><td>mod/profiles.php</td><td>$arr</td></tr><tr><td>profile_photo_content_end</td><td>mod/profile_photo.php</td><td>$o</td></tr><tr><td>profile_post</td><td>mod/profiles.php</td><td>$_POST</td></tr><tr><td>profile_sidebar</td><td>include/channel.php</td><td>$arr</td></tr><tr><td>profile_sidebar_enter</td><td>include/channel.php</td><td>$profile</td></tr><tr><td>register_account</td><td>include/account.php</td><td>$result</td></tr><tr><td>render_location</td><td>include/conversation.php</td><td>$locate</td></tr><tr><td>replace_macros</td><td>include/text.php</td><td>$arr</td></tr><tr><td>reverse_magic_auth</td><td>mod/rmagic.php</td><td>$arr</td></tr><tr><td>settings_account</td><td>mod/settings.php</td><td>$_POST</td></tr><tr><td>settings_form</td><td>mod/settings.php</td><td>$o</td></tr><tr><td>settings_post</td><td>mod/settings.php</td><td>$_POST</td></tr><tr><td>sexpref_selector</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>sexpref_selector_min</td><td>include/profile_selectors.php</td><td>$select</td></tr><tr><td>smilie</td><td>include/text.php</td><td>$params</td></tr><tr><td>smilie</td><td>extend/addon/matrix/smileybutton/smileybutton.php</td><td>$params</td></tr><tr><td>tagged</td><td>include/items.php</td><td>$arr</td></tr><tr><td>validate_channelname</td><td>include/channel.php</td><td>$arr</td></tr><tr><td>webfinger</td><td>mod/wfinger.php</td><td>$arr</td></tr><tr><td>well_known</td><td>mod/_well_known.php</td><td>$arr</td></tr><tr><td>zid</td><td>include/channel.php</td><td>$arr</td></tr><tr><td>zid_init</td><td>include/channel.php</td><td>$arr</td></tr><tr><td>zot_finger</td><td>include/zot.php</td><td>$ret</td></tr></table><p>Generated Tue Nov 03 21:19:02 PST 2015</p></div> \ No newline at end of file
diff --git a/doc/de/main.bb b/doc/de/main.bb
deleted file mode 100644
index eee2e85fe..000000000
--- a/doc/de/main.bb
+++ /dev/null
@@ -1,11 +0,0 @@
-[img][baseurl]/images/hubzilla-banner.png[/img]
-
-[zrl=[baseurl]/help/about]Was ist $Projectname?[/zrl]
-$Projectname ist eine dezentrale Kommunikations- und Publishing-Plattform. Es ermöglicht Dir die volle Kontrolle über all Deine Kommunikation mit Hilfe von automatischer Verschlüsselung und detaillierter Zugriffskontrolle. Du, und [i]nur[/i] Du, entscheidest, wer Deine Beiträge sehen darf. $Projectname ist der Nachfolger, der seit einigen Jahren erfolgreichen Plattformen Friendica und RedMatrix.
-
-[zrl=[baseurl]/help/features]Features von $Projectname[/zrl]
-$Projectname funktioniert schon heute als ein globales verteiltes Netzwerk und beweist täglich seine Vielseitigkeit und Skalierbarkeit - auf kleinen Privatservern wie auch auf riesigen Sites.
-Kommunikationsplattformen für Familien, verteilte Online-Communities, Support-Foren, Blogs und Homepages. Oder auch professionelle Inhalte-Anbieter mit kommerziellen Premium-Kanälen und eingeschränktem Zugriff – was immer Du willst, $Projectname unterstützt Dich in Deinem kreativen Schaffen.
-
-[zrl=[baseurl]/help/what_is_zot]Got Zot? Hast Du schon Zot? Wenn nicht wird es Zeit.[/zrl]
-Zot ist ein großartiges neues Kommunikationsprotokoll, das für $Projectname entwickelt wurde. Als Mitglied bist Du dank „Nomadischer Identität“ nicht länger an einen einzigen Server oder einen einzigen Anbieter gebunden. Ziehe einfach auf einen anderen Server um und behalte dabei alle Deine Kontakte, oder klone Deinen Kanal und lasse ihn auf mehreren Servern gleichzeitig laufen – sollte einer davon plötzlich geschlossen werden, ist das kein Problem für Dich. Und bist Du erst Teil des $Projectname-Netzwerkes, musst Du Dich nie wieder mehrfach anmelden, selbst wenn Du Seiten auf einem andere Hub (den $Projectname-Servern) betrachtest. Zot ist, was das $Projectname-Netzwerk besonders macht.
diff --git a/doc/de/member/AdvancedSearch.md b/doc/de/member/AdvancedSearch.md
new file mode 100644
index 000000000..35479dba1
--- /dev/null
+++ b/doc/de/member/AdvancedSearch.md
@@ -0,0 +1,30 @@
+### Erweiterte Verzeichnissuche
+
+Die erweiterte Verzeichnissuche wird im „Expertenmodus“ auf der Seite Einstellungen => Zusätzliche Funktionen aktiviert.
+
+Auf der Verzeichnisseite wird eine Option namens „Erweitert“ im Widget „Kanäle suchen“ (normalerweise in der Seitenleiste) angezeigt. Wenn Sie auf „Erweitert“ klicken, wird ein weiteres Suchfeld geöffnet, in das Sie erweiterte Suchanfragen eingeben können.
+Zu den erweiterten Suchanfragen gehören
+
+- name=xxx [Kanalname enthält xxx]
+- address=xxx [Kanaladresse (Webbie) enthält xxx]
+- locale=xxx [Gebietsschema (typischerweise 'Stadt') enthält xxx]
+- region=xxx [Region (Bundesland/Territorium) enthält xxx]
+- postcode=xxx [Postleitzahl enthält xxx]
+- country=xxx [Ländername enthält xxx]
+- gender=xxx [Geschlecht enthält xxx]
+- marital=xxx [Familienstand enthält xxx]
+- sexual=xxx [Sexuelle Präferenz enthält xxx]
+- keywords=xxx [Schlüsselwörter enthalten xxx]
+
+Es gibt viele Gründe, warum ein Treffer nicht das liefert, wonach Sie suchen, da viele Kanäle in ihrem (öffentlichen) Standardprofil keine detaillierten Informationen bereitstellen und viele dieser Felder Freitext-Eingaben in mehreren Sprachen zulassen - und dies kann schwierig sein, einen genauen Treffer zu erzielen. Zum Beispiel können Sie bessere Ergebnisse erzielen, wenn Sie jemanden in den USA mit 'country=u' finden (zusammen mit einigen seltsamen Kanälen aus Deutschland, Bulgarien und Australien), da dies in einem Profil als US, U.S.A, USA, Vereinigte Staaten, usw. dargestellt werden könnte.
+
+Künftige Überarbeitungen dieses Tools könnten versuchen, einige dieser Schwierigkeiten zu beseitigen.
+Anfragen können mit 'und', 'oder' und 'und nicht' verbunden werden.
+
+Begriffe, die Leerzeichen enthalten, müssen in Anführungszeichen gesetzt werden.
+
+Beispiel:
+
+```
+name=„charlie brown“ und country=canada und nicht gender=female
+``` \ No newline at end of file
diff --git a/doc/de/member/NSFW.md b/doc/de/member/NSFW.md
new file mode 100644
index 000000000..031bc0f47
--- /dev/null
+++ b/doc/de/member/NSFW.md
@@ -0,0 +1,21 @@
+### Inhaltswarnung/NSFW
+
+Inhaltswarnungen bzw. das Verbergen von bestimmten Inhalten wird bei Hubzilla mit der App "NSFW" realisiert.
+
+Während Sie bei anderen Diensten im Fediverse darauf angewiesen sind, dass die Verfasser von Postings möglicherweise "sensible" Inhalte hinter einem Contentwarning (einer Inhalts- bzw. Triggerwarnung) verbergen, haben Sie bei Hubzilla diese Funktionalität als Rezipient selbst in der Hand. Mit der App NSFW können Sie Filter erstellen, die dafür sorgen, dass Beiträge, die unter die Filterregeln fallen, eingeklappt angezeigt werden. Erst ein Klick auf den Button zeigt den Inhalt des Postings.
+
+Starten Sie die App, so erscheint die Eingabemaske für die Filter.
+
+![NSFW 01](/help/de/member/pic/nsfw01.png)
+
+Hier können Sie nun Schlüsselwörter und sogar [reguläre Ausdrücke](https://de.wikipedia.org/wiki/Regulärer_Ausdruck) eingeben, auf welche das Posting durchsucht wird. Wird eines der Wörter oder ein Textmuster gefunden, so wird der Inhalt im Stream zugeklappt angezeigt.
+
+Es ist auch möglich, nach Sprachen zu filtern (lang=xx oder lang!=xx).
+
+Findet sich nun ein Ausdruck, welcher zu einem der eingegebenen Filtern passt, in einem Posting, so wird das Posting für Sie hinter einer Inhaltswarnung zunächst verborgen.
+
+![NSFW 02](/help/de/member/pic/nsfw02.png)
+
+![NSFW 03](/help/de/member/pic/nsfw03.png)
+
+![NSFW 04](/help/de/member/pic/nsfw04.png)
diff --git a/doc/de/member/account_settings.md b/doc/de/member/account_settings.md
new file mode 100644
index 000000000..ac7263925
--- /dev/null
+++ b/doc/de/member/account_settings.md
@@ -0,0 +1,5 @@
+#### Konto-Einstellungen
+
+Mit den Konto-Einstellungen können Sie die Daten Ihres Accounts ändern.
+
+![Konto-Einstellungen](/help/de/member/pic/ktoeinst01.png)
diff --git a/doc/de/member/accounts_profiles_channels_basics.md b/doc/de/member/accounts_profiles_channels_basics.md
new file mode 100644
index 000000000..fd5fac77b
--- /dev/null
+++ b/doc/de/member/accounts_profiles_channels_basics.md
@@ -0,0 +1,30 @@
+### Konten, Profile und Kanäle
+
+Sobald Sie ein *Konto* im Grid registriert haben, haben Sie auch ein *Profil* und einen *Kanal* erstellt.
+
+**Konto**
+
+Sie haben *ein* Konto. Dieses besteht aus Ihrem E-Mail-Konto und Ihrem Passwort. Mit Ihrem Konto haben Sie Zugang zu Ihrem Profil und Ihrem Kanal.
+
+*Betrachten Sie Ihr Konto als die Art und Weise, wie Sie sich bei einer Hubzilla-Website authentifizieren.* *Damit können Sie Dinge tun, wie z. B. Profile und Kanäle erstellen, mit denen Sie sich mit anderen Personen verbinden können.*
+
+**Profil**
+
+Sicherlich haben Sie sich auch bei anderen Internetdiensten registriert, z. B. bei Foren oder Online-Communities. Bei all diesen Diensten haben Sie einige Informationen über sich selbst angegeben, z. B. Geburtsdatum, Land, Alter und Ähnliches.
+
+Wenn Sie möchten, können Sie Ihr Profil hier einsehen: `[baseurl]/profile/[webname]` und bearbeiten, indem Sie auf das Bleistiftsymbol neben Ihrem Avatarbild klicken.
+
+Im Gegensatz zu anderen Diensten bietet dir hubzilla den Vorteil, dass du *viele weitere Profile* anlegen kannst. Auf diese Weise können Sie zwischen Profilen unterscheiden, die sich speziell an jedermann (Ihr öffentliches Profil), Ihre Arbeitskollegen, Ihre Familie und Ihren Partner richten.
+
+*Betrachten Sie Ihr Profil als die grundlegenden Informationen über sich selbst, die Sie anderen Menschen mitteilen.*
+
+**Kanal**
+
+Bei der Registrierung haben Sie Ihren ersten *Kanal* erstellt. Ja, neben mehreren Profilen können Sie auch mehrere Channels haben. Das mag am Anfang etwas verwirrend sein, aber lassen Sie uns die Dinge klarstellen. Sie haben bereits einen Kanal erstellt. Diesen können Sie für die Öffentlichkeit nutzen, um mit Leuten über das tägliche Leben zu kommunizieren. Aber vielleicht sind Sie ein begeisterter Leser von Büchern und das langweilt viele Leute. Also eröffnen Sie einen *zweiten Kanal* nur für Buchliebhaber, in dem Sie alle so viel über Bücher reden können, wie Sie wollen. Natürlich ist dies ein neuer Strom von Beiträgen, mit einem neuen Profil (... oder neuen*Profilen*...) und völlig anderen Kontakten. Einige Verbindungen können in beiden Kanälen bestehen, aber es wird auch welche geben, die nur in einem der beiden Kanäle bestehen. Sie selbst wechseln einfach zwischen den beiden Kanälen hin und her, so wie Sie auch im wirklichen Leben zwischen den Kanälen hin und her wechseln würden, wenn Sie sich mit Leuten unterhalten, die Sie auf der Straße treffen, oder mit Leuten, die Sie speziell für ein Gespräch über Bücher treffen. Sie können sich sogar mit sich selbst verbinden, oder besser: mit Ihrem anderen Kanal :)
+
+*Stellen Sie sich einen Kanal als verschiedene Bereiche vor, die verschiedenen Themen gewidmet sind und in denen Sie sich mit verschiedenen Menschen treffen.*
+
+#include doc/de/member/channels.md;
+#include doc/de/member/profiles.md;
+#include doc/de/member/settings.md;
+#include doc/de/member/connecting_with_channels.md;
diff --git a/doc/de/member/additional_features.md b/doc/de/member/additional_features.md
new file mode 100644
index 000000000..554c52af1
--- /dev/null
+++ b/doc/de/member/additional_features.md
@@ -0,0 +1,31 @@
+#### Zusätzliche Funktionen (verborgene Einstellungen)
+
+Die Einstellungen "Zusätzliche-Funktionen" sind in der Gesamtheit weder über das Menü, noch über ein Symbol/Icon erreichbar. Sämtliche einzelnen Funktions-Einstellungen kann man aber auch in der jeweiligen App über das Zahnrad neben dem Hauptmenü (Avatar-Bild) erreichen.
+
+Es handelt sich um Einstellungen zu weiteren Funktionen in allen mögliche Bereichen von Hubzilla. Um die Einstellungen aufzurufen, muss man im Browser an die URL des Hubs `/settings/features` anhängen, also z.B. `https://klacker.org/settings/features`.
+
+![Features Einstellungen 01](/help/de/member/pic/feateinst01.png)
+
+Die Voreinstellungen für sämtliche dieser Optionen werden vom Administrator des Hubs vorgenommen. Diese Voreinstellung kann durch den Nutzer in den "Zusätzlichen Funktionen" überschrieben werden.
+
+Adlerdings hat der Administrator bei jeder Option auch die Möglichkeit, die Voreinstellung gegen Änderung zu sperren. Der Nutzer kann den Schalter für die Option zwar weiterhin umlegen, die Auswahl wird aber nicht gespeichert, sondern die Option wieder auf die Voreinstellung zurückgesetzt.
+
+![Features Einstellungen 02](/help/de/member/pic/feateinst02.png)
+
+![Features Einstellungen 03](/help/de/member/pic/feateinst03.png)
+
+![Features Einstellungen 04](/help/de/member/pic/feateinst04.png)
+
+![Features Einstellungen 05](/help/de/member/pic/feateinst05.png)
+
+![Features Einstellungen 06](/help/de/member/pic/feateinst06.png)
+
+![Features Einstellungen 07](/help/de/member/pic/feateinst07.png)
+
+![Features Einstellungen 08](/help/de/member/pic/feateinst08.png)
+
+![Features Einstellungen 09](/help/de/member/pic/feateinst09.png)
+
+![Features Einstellungen 10](/help/de/member/pic/feateinst10.png)
+
+![Features Einstellungen 11](/help/de/member/pic/feateinst11.png)
diff --git a/doc/de/member/addressbook.md b/doc/de/member/addressbook.md
new file mode 100644
index 000000000..fa53339fe
--- /dev/null
+++ b/doc/de/member/addressbook.md
@@ -0,0 +1,23 @@
+### Adressbuch (CardDAV)
+
+Hubzilla bietet Ihnen mit der App "CardDAV" eine Adressverwaltung. Sie können beliebig viele Adressbücher anlegen.
+
+![carddav01](/help/de/member/pic/carddav01.png)
+
+Die Einträge sind im vCards-Format abgelegt.
+
+![carddav02](/help/de/member/pic/carddav02.png)
+
+![carddav03](/help/de/member/pic/carddav03.png)
+
+![carddav04](/help/de/member/pic/carddav04.png)
+
+![carddav05](/help/de/member/pic/carddav05.png)
+
+![carddav06](/help/de/member/pic/carddav06.png)
+
+Die App ermöglicht auch das Importieren von Adressbüchern oder einzelnen vCards aus einer Datei.
+
+![carddav07](/help/de/member/pic/carddav07.png)
+
+Adressbücher sind generell privat und können - auch nicht mittels Fernautorisierung - geteilt werden.
diff --git a/doc/de/member/apps.md b/doc/de/member/apps.md
new file mode 100644
index 000000000..8715e8a68
--- /dev/null
+++ b/doc/de/member/apps.md
@@ -0,0 +1,59 @@
+### Apps
+
+Im frisch installierten Zustand verfügt eine Hubzilla-Instanz über etliche Grundfunktionalitäten. Es gibt jedoch viele Features, die nicht zur Basis-Installation gehören und auch nicht für einen neu erstellten Kanal sofort zur Verfügung stehen.
+
+Die überwiegende Zahl der Funktionen sind als "Applikationen" (kurz "Apps") realisiert.
+
+Die Apps erreichen Sie mit dem "App-Menü", das mit der Schaltfläche "⋮" symbolisiert wird (und sich bei den meisten Hubs ganz rechts in der Navigationsleiste befindet). Häufig benutzte Appskönnen Sie auch an der Navigationsleiste anpinnen, so dass Sie nicht bei jedem Aufruf erst das App-Menü öffnen müssen.
+
+Wie viele und welche Apps Ihnen zur Verfügung stehen, hängt davon ab, wie der Administrator den Hub konfiguriert hat.
+
+#### Appverwaltung
+
+Sie können die Apps für Ihren Kanal mit der Appverwaltung verwalten. Diese erreichen Sie ebenfalls im App-Menü unter dem untersten Menüpunkt "+ Apps"
+
+Nach dem Aufrufen der Appverwaltung werden Ihnen die bereits installierten Apps angezeigt.
+
+In der linken Seitenleiste können Sie nun zwischen "Installierte Apps" und "Verfügbare Apps" umschalten.
+
+##### Verfügbare Apps
+
+Bei den verfügbaren Apps finden Sie sämtliche auf Ihrem Hub zur Verfügung stehenden Apps. Einige davon sind bereits installiert. Über den Button neben der App kann man Apps installieren oder bereits installierte Apps aktualisieren.
+
+![verfügbare Apps](/help/de/member/pic/apps02.png)
+
+##### Installierte Apps
+
+Im Tab der installierten Apps finden Sie sämtliche Apps, die für Ihren Kanal installiert sind. Rechts von jeder App befinden sich zwei oder drei Buttons mit Symbolen: ein Sternsymbol, ein Pinnadelsymbol und ggf. ein Zahnradsymbol.
+
+![verfügbare Apps](/help/de/member/pic/apps01.png)
+
+Das Sternsymbol dient dazu, die App im App-Menü als Menüeintrag zugänglich zu machen.
+
+![verfügbare Apps](/help/de/member/pic/apps03.png)
+
+![verfügbare Apps](/help/de/member/pic/apps04.png)
+
+Mit dem Pinnadelsymbol kann man die App in der Navigationsleiste anpinnen.
+
+![verfügbare Apps](/help/de/member/pic/apps05.png)
+
+Gibt es für eine App einen Dialog für app-spezifische Einstellungen, so erreichen Sie diesen über den Button mit dem Zahnradsymbol.
+
+![verfügbare Apps](/help/de/member/pic/apps06.png)
+
+##### Apps verwalten
+
+Im Tab der installierten Apps ist genz oben auch ein Button vorhanden, welcher mit "Apps verwalten" beschriftet ist. Mit diesem Button gelangen Sie auf die Seite "Apps verwalten", auf welcher Sie Apps wieder deinstallieren und installierte in gewissen Grenzen auch bearbeiten können.
+
+![verfügbare Apps](/help/de/member/pic/apps08.png)
+
+Dort ist es auch möglich eigene Apps zu erstellen (Nur für fortgeschrittene Nutzer!).
+
+##### Apps im Menü sortieren
+
+Die Apps, welche Sie dem App-Menü zugefügt haben, können sie dort ganz einfach per Drag-and-drop sortieren und Ihre bevorzugte Reihenfolge festlegen.
+
+![verfügbare Apps](/help/de/member/pic/apps07.png)
+
+#include doc/de/member/important_apps.md;
diff --git a/doc/de/member/article.md b/doc/de/member/article.md
new file mode 100644
index 000000000..81fee5881
--- /dev/null
+++ b/doc/de/member/article.md
@@ -0,0 +1,27 @@
+### Artikel
+
+Der Artikel ist eine Macroblogging-Beitragsform bei Hubzilla und z.B. geeignet für echte Blogbeiträge. Im Gegensatz zu normalen Beiträgen, die im gesamten Netzwerk (inklusive Fediverse) verteilt werden, verbleiben Artikel beim eigenen Hub. Sie sind für Nutzer anderer Instanzen und Nutzer, die keinen Account im Fediverse haben also nur über ihre URL erreichbar. Die URL kann selbstverständlich geteilt werden, so dass der Artikel trotzdem im Fediverse bekannt wird und abgerufen werden kann.
+
+Einen Artikel erstellt man über die App (App-Menü ⋮) "Artikel". Ruft man diese auf, werden sämtliche erstellten Artikel angezeigt und man hat die Möglichkeit, einen neuen Artikel anzulegen ("Artikel hinzufügen").
+
+![Artikel 01](/help/de/member/pic/artikel01.png)
+
+Das Erstellen eines Artikels ähnelt dem Erstellen eines normalen Beitrags. Das Eingabe-Formular weist aber ein zusätzliches Feld auf: "Link zur Seite".
+
+Hier kann man eine leicht lesbare Linkbezeichnung eingeben. Lässt man das Feld frei, wird eine Bezeichnung automatisch vergeben (länger und "kryptischer").
+
+![Artikel 02](/help/de/member/pic/artikel02.png)
+
+Hat man das optionale Feld Summary ("Zusammenfassung") ausgefüllt, wird bei einem Artikel, genauso wie bei normalen Beiträgen, zunächst nur die Zusammenfassung angezeigt.
+
+![Artikel 03](/help/de/member/pic/artikel03.png)
+
+Wenn Sie auf "Artikel ansehen" klicken, wir der Artikel selbst angezeigt.
+
+![Artikel 04](/help/de/member/pic/artikel04.png)
+
+Der Direktlink zum Artikel setzt sich so zusammen:
+
+`URL-Ihres-Hubs/articles/Kanalname/Link-zurSeite`
+
+![Artikel Link](/help/de/member/pic/artikel05.png)
diff --git a/doc/de/member/bbcode.md b/doc/de/member/bbcode.md
new file mode 100644
index 000000000..b25168323
--- /dev/null
+++ b/doc/de/member/bbcode.md
@@ -0,0 +1,79 @@
+#### Text Dekoration
+
+| BBcode syntax | Rendered text |
+| ------------------------------------------------------------ | --------------------------------- |
+| `[b]bold[/b]` | **bold** |
+| `[i]italic[/i]` | *italic* |
+| `[u]underlined[/u]` | underlined |
+| `[s]strike[/s]` | ~~strike~~ |
+| `[color=red]red[/color]` | ![red](/help/de/member/pic/red.png) |
+| `[hl]highlighted[/hl]` | ![highlited](/help/de/member/pic/highlited.png) |
+| `[font=courier]some text[/font] ` | ![font](/help/de/member/pic/font.png) |
+| `[quote]quote[/quote]` | ![quote](/help/de/member/pic/quote.png) |
+| `[quote=Author]Author? Me? No, no, no...[/quote]` | ![author](/help/de/member/pic/author.png) |
+| ` [size=small]small text[/size]`<br />`[size=xx-large]xx-large text[/size]`<br />` [size=20]20px exactly[/size] `<br />Size options include: **xx-small, small, medium, large, xx-large** | ![size](/help/de/member/pic/size.png) |
+| `Add a horizontal bar [hr] Like this ` | ![hbar](/help/de/member/pic/hbar.png) |
+| `This is [center]centered[/center] text` | ![center](/help/de/member/pic/center.png) |
+
+#### Code-Blöcke
+
+Code kann generisch in einem Block- oder Inline-Format dargestellt werden (je nachdem, ob es neue Zeilenzeichen im Text gibt), oder Sie können eine unterstützte Sprache für erweiterte Syntaxhervorhebung angeben. Die Syntaxhervorhebung erfordert ein geeignetes Rendering-Plugin wie **hilite**. Unterstützte Sprachen mit dem hilite-Plugin sind **php, css, mysql, sql, abap, diff, html, perl, ruby, vbscript, avrc, dtd, java, xml, cpp, python, javascript, js, json, sh**.
+
+Wenn kein Rendering-Plugin installiert ist oder eine nicht unterstützte Sprache angegeben wird, entspricht die Ausgabe für syntaxmarkierte Codeblöcke dem Code-Tag des Blockformats.
+
+| BBcode syntax | Output |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| `[code]function bbcode() { }[/code]` | `function bbcode() { }` |
+| `[code=php]function bbcode() { $variable = true; if( $variable ) { echo "true"; } }[/code]` | ![code](/help/de/member/pic/code.png) |
+| `[nobb][nobb]This is how [i]you[/i] can [u]show[/u] how to use [hl]BBcode[/hl] syntax[/nobb][/nobb]` | This is how [i]you[/i] can [u]show[/u] how to use [hl]BBcode[/hl] syntax |
+
+#### Listen
+
+| BBcode syntax | Rendered list |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| `[ul] [*] First list element [*] Second list element [/ul]` | - First list element<br />- Second list element |
+| `[ol] [*] First list element [*] Second list element [/ol]` | 1. First list element<br />2. Second list element |
+| `[list=A] [*] First list element [*] Second list element [/list]` The list type options are `1, i, I, a, A`. | A. First list element<br />B. Second list element |
+| `[dl terms="b"] [*= First element term] First element description [*= Second element term] Second element description [/dl]` The **terms** style options can be any combination of: bbold iitalic uunderline mmonospace llarge hhorizontal — like *this* defintion list | First element term<br />First element description <br />Second element term<br />Second element description |
+
+#### Tabellen
+
+| BBcode syntax | Rendered table |
+| ------------------------------------------------------------ | --------------------------- |
+| `[table border=0] [tr] [th]Header 1[/th][th]Header 2[/th] [/tr] [tr][td]Content[/td][td]Content[/td][/tr] [tr][td]Content[/td][td]Content[/td][/tr] [/table]` | ![table1](/help/de/member/pic/table1.png) |
+| `[table border=1] [tr] [th]Header 1[/th][th]Header 2[/th] [/tr] [tr][td]Content[/td][td]Content[/td][/tr] [tr][td]Content[/td][td]Content[/td][/tr] [/table]` | ![table2](/help/de/member/pic/table2.png) |
+| `[table] [tr] [th]Header 1[/th][th]Header 2[/th] [/tr] [tr][td]Content[/td][td]Content[/td][/tr] [tr][td]Content[/td][td]Content[/td][/tr] [/table]` | ![table3](/help/de/member/pic/table3.png) |
+
+#### Links und eingebettete Inhalte
+
+| BBcode syntax | Output |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| `[video]video URL[/video] [audio]audio URL[/audio]` | VIDEO<br />AUDIO |
+| `[video='URL_TO_POSTER']video_link[/video]` | <img src="/help/de/member/pic/video_poster.png" alt="image" style="zoom:100%;" /> |
+| `[url=https://hubzilla.org]Hubzilla[/url]` | [Hubzilla](https://hubzilla.org) |
+| `An image [img]https://example.org/image.jpg[/img] in some text ` | <img src="/help/de/member/pic/image.png" alt="image" style="zoom:80%;" /> |
+
+#### Hubzilla-spezifische codes
+
+| BBcode syntax | Output |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| Magic-auth version of [url] tag `[zrl=https://hubzilla.org]Identity-aware link[/zrl]` | ![mauth](/help/de/member/pic/mauth.png) |
+| Magic-auth version of [img] tag `[zmg]https://hubzilla.org/some/photo.jpg[/zmg]` | Image is only viewable by those authenticated and with permission. |
+| Observer-dependent output:`[observer=1]Text to display if observer IS authenticated[/observer]` | |
+| ` [observer=0]Text to display if observer IS NOT authenticated[/observer]` | |
+| `[observer.language=en]Text to display if observer language is English[/observer]` | |
+| `[observer.language!=de]Text to display if observer language is not German[/observer]` | |
+| `[observer.url]` | channel URL of observer |
+| `[observer.baseurl]` | website of observer |
+| `[observer.name]` | name of observer |
+| ` [observer.webname]` | short name in the url of the observer |
+| `[observer.address]` | address (Nomad/Zot-id) of observer |
+| `[observer.photo]` | profile photo of observer |
+| `What is a spoiler? [spoiler]Text you want to hide.[/spoiler]` | What is a spoiler? Click to open/close |
+| `[toc data-toc='div.page-body' data-toc-headings='h1,h2']` Create a table of content in a webpage or wiki page. Please refer to the [original jQuery toc](http://ndabas.github.io/toc/) to get more explanations. Optional param: 'data-toc'. If omitted the default is 'body' Optional param: 'data-toc-headings'. If omitted the default is 'h1,h2,h3' | |
+| `[rpost=title]Text to post[/rpost]` The observer will be returned to their home hub to enter a post with the specified title and body. Both are optional | [[baseurl\]/rpost?f=&title=title&body=Text+to+post](file:///home/daniel/Fediverse/orig doc/member/[baseurl]/rpost?f=&title=title&body=Text+to+post) |
+| This requires the [**qrator**](https://framagit.org/hubzilla/addons/tree/master/qrator) plugin. `[qr]text to post[/qr]` | ![qrcode](/help/de/member/pic/qrcode.png) |
+| This requires a suitable map plugin such as **[openstreetmap](https://framagit.org/hubzilla/addons/tree/master/openstreetmap)**. `[map]` | Generate an inline map using the current browser coordinates of the poster, if browser location is enabled |
+| This requires a suitable map plugin such as **[openstreetmap](https://framagit.org/hubzilla/addons/tree/master/openstreetmap)**. `[map=latitude,longitude]` | Generate a map using global coordinates. |
+| This requires a suitable map plugin such as **[openstreetmap](https://framagit.org/hubzilla/addons/tree/master/openstreetmap)**. `[map]Place Name[/map]` | Generate a map for a given named location. The first matching location is returned. For instance "Sydney" will usually return Sydney, Australia and not Sydney, Nova Scotia, Canada unless the more precise location is specified. It is highly recommended to use the post preview utility to ensure you have the correct location before submitting the post. |
+| `[©]` | © |
diff --git a/doc/de/member/blocking_channels.md b/doc/de/member/blocking_channels.md
new file mode 100644
index 000000000..467017c11
--- /dev/null
+++ b/doc/de/member/blocking_channels.md
@@ -0,0 +1,19 @@
+### Kanäle blockieren/ignorieren/archivieren/verstecken
+
+Kanäle in Ihrem Adressbuch können den Status „ *blockiert*“, „ *ignoriert“*, „ *archiviert“* oder „ *versteckt“* haben. Auf Ihrer Verbindungsseite gibt es einen Filter, der die Kanäle mit diesen Status anzeigen.
+
+![Statusfilter](/help/de/member/pic/statusfilter.png)
+
+Auf den Seiten zum Bearbeiten von Verbindungen können Sie den Status eines Kanals ändern.
+
+<u>Die Bedeutung:</u>
+
+**Blockiert:** Der Kanal kann Ihre Elemente nicht lesen, unabhängig von den Berechtigungen, und er kann auch nicht in Ihren Kanal schreiben.
+
+**Ignoriert**: Der Kanal kann Ihre Elemente lesen, wenn er dazu berechtigt ist, aber er kann nicht in Ihren Kanal schreiben.
+
+**Versteckt:** Der Kanal wird nicht in der Verbindungsliste Ihres Profils angezeigt. Niemand kann sehen, dass Sie mit ihm verbunden sind. Beachte: Er ist trotzdem für deine anderen Verbindungen sichtbar, zum Beispiel in Antworten auf Beiträge.
+
+**Archiviert:** Wenn ein Kanal 30 Tage lang nicht erreicht werden kann, wird er automatisch als archiviert markiert. Dadurch bleiben alle Daten erhalten, aber der Kanal wird nicht mehr nach neuen Informationen abgefragt und aus der automatischen Vervollständigung entfernt. Wenn Sie später erfahren, dass der Kanal wieder online ist, können Sie ihn manuell aus dem Archiv entfernen.
+
+#include doc/de/member/superblock.md;
diff --git a/doc/de/member/bookmarks.md b/doc/de/member/bookmarks.md
new file mode 100644
index 000000000..f1483add2
--- /dev/null
+++ b/doc/de/member/bookmarks.md
@@ -0,0 +1,27 @@
+### Bookmarks (Lesezeichen))
+
+Lesezeichen geben einen Link an, der in Ihrem Lesezeichenordner gespeichert werden kann. Sie verwenden die Zeichenfolge `#^` gefolgt von dem Link. Oft werden diese automatisch generiert. Wenn der Administrator des Hub das Addon "bookmarker" installiert hat, wird diese Sequenz beim Online-Ansehen des Beitrags oder Kommentars in ein Lesezeichensymbol konvertiert.
+
+![Bookmarks01](/help/de/member/pic/bookm01.png)
+
+![Bookmarks02](/help/de/member/pic/bookm02.png)
+
+Wenn Sie auf das Symbol klicken, wird das Lesezeichen gespeichert. Wenn das Add-In für das Lesezeichen nicht installiert ist, enthält das Post-Dropdown-Menü einen Link zum Speichern des Lesezeichens oder der Lesezeichen.
+
+Um Lesezeichen zu verwenden, müssen Sie die App "Lesezeichen" installieren.
+
+Die App führt dann alle von Ihnen gesetzten Lesezeichen auf.
+
+Um ein Lesezeichen unabhängig von einem Link im Stream oder einem Beitrag hinzuzufügen, können Sie die Seite `<URL_Ihres_hub>/rbmark` aufrufen, welche Ihnen eine Maske zur manuellen Eingabe eines Lesezeichens zur Verfügung stellt.
+
+![Bookmarks03](/help/de/member/pic/bookm03.png)
+
+Sie können auch ein [Bookmarklet](https://de.wikipedia.org/wiki/Bookmarklet) erstellen und z.B. in der Lesezeichenleiste Ihres Webbrowsers ablegen:
+
+```javascript
+javascript:javascript:(function(){var%20url=location.href;var%20title=document.title||url;window.open('[observer.baseurl]/rbmark?&url='+encodeURIComponent(url)+'&title='+encodeURIComponent(title)+'&source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();
+```
+
+<u>Wichtig:</u> Ersetzen Sie den Ausdruck `[observer.baseurl]` durch die URL Ihres Hub, also wenn Sie z.B. Ihren Kanal bei Klackerhub haben, müssen Sie für `[observer.baseurl]` einfach `https://klacker.org` eintragen.
+
+Nun können Sie jede Webseite, welche Sie aufsuchen mit einem Klick auf das Bookmarklet in die Lesezeichen Ihres Kanals übernehmen.
diff --git a/doc/de/member/calendar.md b/doc/de/member/calendar.md
new file mode 100644
index 000000000..5a15747f3
--- /dev/null
+++ b/doc/de/member/calendar.md
@@ -0,0 +1,27 @@
+### Kalender
+
+Mit der App "Kalender" kann man seine Termine verwalten.
+
+Nach Aufruf der App erscheint eine Kalender-Übersicht (ein Monat).
+
+![Kalender 01](/help/de/member/pic/kalender01.png)
+
+Durch Klicken auf einen Tag können Sie ein Ereignis erzeugen. In der Eingabemaske (verkürzte Ansicht, lässt sich durch Klicken auf "mehr" erweitern) kann man nun die wesentlichen Inhalte eingeben.
+
+![Kalender 02](/help/de/member/pic/kalender02.png)
+
+![Kalender 03](/help/de/member/pic/kalender02.png)
+
+![Kalender 04](/help/de/member/pic/kalender03.png)
+
+Für diese Einträge können Sie ebenfalls wieder detaillierte [Berechtigungen](/help/de/member/permissions_content.md) festlegen, so dass man private Termine und öffentliche / gemeinsame Termine in ein und demselben Kalender erfassen kann.
+
+![Kalender 05](/help/de/member/pic/kalender05.png)
+
+##### CalDAV-Zugang mit Android
+
+Sie können Ihren Kalender auf Android mit Ihrem Hub synchronisieren.
+
+Verwenden Sie "URL" und "Benutzername" zur Anmeldung. Die Basis-URL ist `<URL-Ihres-Hubs>/cdav`, der Benutzername ist Ihr Kanalname (ohne führendes"@" und ohne Hub-Adresse "`@<Ihr-Hub>`").
+
+Um Ihren Kalender zu teilen, besuchen Sie `<URL-Ihres-Hubs>/cdav/calendar`.
diff --git a/doc/de/member/channel_locations.md b/doc/de/member/channel_locations.md
new file mode 100644
index 000000000..93b5ab871
--- /dev/null
+++ b/doc/de/member/channel_locations.md
@@ -0,0 +1,7 @@
+#### Klon-Adressen verwalten
+
+Existieren vom aktuellen Kanal Klone auf anderen Hubs, so wird als weiterer Menüeintrag "Klon-Adressen verwalten" angezeigt. Damit können Sie festlegen, auf welchem Hub der der Hauptkanal (primärer Kanal) liegt (das legt auch den Teil des Handles hinter dem "@" fest).
+
+Außerdem kann man Klone an dieser Stelle löschen. Für Kanäle auf fremden Servern empfiehlt es sich allerdings, den Kanal auf dem eigentlichen Hub zu löschen. Das Löschen aus der Klon-Verwaltung sollte nur genutzt werden, wenn der Hub des Klons nicht mehr existiert.
+
+![Klon-Einstellungen 01](/help/de/member/pic/kloneinst01.png)
diff --git a/doc/de/member/channel_roles.md b/doc/de/member/channel_roles.md
new file mode 100644
index 000000000..f793b7c4e
--- /dev/null
+++ b/doc/de/member/channel_roles.md
@@ -0,0 +1,41 @@
+### Kanalrollen
+
+Wenn Sie einen neuen Kanal erstellen, werden Sie aufgefordert, eine Berechtigungsrolle auszuwählen, je nachdem, wie Sie diesen Kanal nutzen möchten. Die beliebtesten Berechtigungsrollen sind die Rollen für soziale Netzwerke. Sie haben viele weitere Auswahlmöglichkeiten, die mit Facebook-Gruppen und -Seiten, kollaborativen Bereichen, Newsfeeds und mehr vergleichbar sind. Diese Rollen konfigurieren automatisch verschiedene Systemvariablen, von den Berechtigungen, die Freunden gewährt werden, bis hin zu den Standardeinstellungen für Privatsphäre und Sichtbarkeit. Es stehen erweiterte Konfigurationen zur Verfügung, mit denen Sie jeden dieser Parameter an Ihre Bedürfnisse anpassen können, aber wir haben die Erfahrung gemacht, dass die meisten Nutzer es vorziehen, die Einstellungen vorzunehmen und sie zu vergessen. Im Folgenden werden einige der verschiedenen Rollen beschrieben, die derzeit verfügbar sind, und wie sie sich auf Ihre Privatsphäre und Ihre Interaktionsmöglichkeiten auswirken.
+
+**Es gibt vier Kanal-Rollen:**
+
+- Öffentlich
+- Persönlich
+- Community Forum
+- Benutzerdefiniert
+
+#### Öffentlich
+
+Der Kanal ist ein sehr freizügiges soziales Netzwerkprofil, das mit anderen föderierten sozialen Netzwerken kompatibel ist. Die Privatsphäre hat eine geringere Priorität als der einfache Zugang und die Verbindung mit anderen. Jeder im Netzwerk kann Ihre öffentlichen Beiträge kommentieren und Ihnen private Nachrichten schicken. Standardmäßig sind Beiträge und veröffentlichte Artikel öffentlich, aber Sie können dies bei der Erstellung des Artikels überschreiben und einschränken. Sie sind im Verzeichnis aufgeführt. Ihre Online-Präsenz und Ihre Verbindungen sind für andere sichtbar. Dieser Modus kann Ihre Anfälligkeit für unerwünschte Mitteilungen und Spam erhöhen. Der "klassische" Social-Media-Account.
+
+#### Persönlich
+
+Standardmäßig sind Beiträge und veröffentlichte Elemente öffentlich, aber Sie können dies bei der Erstellung des Elements außer Kraft setzen und einschränken. Sie sind im Verzeichnis aufgeführt. Ihre Online-Präsenz und Ihre Verbindungen sind für andere sichtbar. Nur Ihre unmittelbaren Verbindungen können Ihre öffentlichen Beiträge kommentieren und Ihnen private Nachrichten schicken.
+
+#### Community Forum
+
+Der Kanal ist ein typisches Forum. Standardmäßig sind Beiträge und veröffentlichte Artikel öffentlich. Mitglieder können Beiträge per !mention oder wall-to-wall posten. Das Einstellen von Fotos und anderen veröffentlichten Beiträgen ist gesperrt. Der Kanal ist im Verzeichnis sichtbar. Mitglieder werden automatisch hinzugefügt.
+
+Um als Nutzer eines Forums Medien, die zum Teil eventuell mit eingeschränkten Berechtigungen versehen sind, sehen zu können, ist es erforderlich, im eigenen Nutzer-Kanal unter Einstellungen → Privacy-Einstellungen (`<hub>/settings/privacy`) die Option "Enable OCAP access") einzuschalten.
+
+#### Benutzerdefiniert
+
+Dies ist die genaueste Einstellung für Kanal-Rechte. Alle Rechte können fein-granular festgelegt werden. Vorsicht: Wer hier die falschen Einstellungen wählt, kann seinen Kanal unbrauchbar machen. Zum Glück lassen sich die Rechte auch wieder ändern, so dass man solche Fehlfunktionen beheben kann. Es ist sinnvoll, sich bei jedem einzelnen Recht zu überlegen, welche Auswirkungen es für einen selbst, als Kanalbesitzer, aber auch für andere Nutzer hat.
+
+**Für jeden Regelungspunkt sind folgende Einstellungen möglich:**
+
+- Nur ich
+- Nur die, denen Du es explizit erlaubst
+- Angenommene Verbindungen
+- Beliebige Verbindungen
+- Jeder auf dieser Webseite
+- Alle Hubzilla-Mitglieder
+- Jeder authentifizierte
+- Jeder im Internet
+
+Um die **benutzerdefinierte Rolle** zu bearbeiten, wählt man in den Einstellungen den Punkt "Privacy-Einstellungen". Rechts unten findet man den Button "Benutzerdefinierte Konfiguration der Kanal-Rolle". Klickt man darauf, erscheint ein Warn-Dialog, der auf die Risiken einer fehlerhaften Konfiguration aufmerksam macht. Bestätigt man, dass man die Rechte bearbeiten möchte, öffnet sich der Einstellungs-Dialog für die benutzerdefinierten Rollenrechte. \ No newline at end of file
diff --git a/doc/de/member/channel_settings.md b/doc/de/member/channel_settings.md
new file mode 100644
index 000000000..3cb0d6f7f
--- /dev/null
+++ b/doc/de/member/channel_settings.md
@@ -0,0 +1,15 @@
+#### Kanal-Einstellungen
+
+Rufen Sie die Einstellungen über den Menüeintrag auf, so werden als Standard die Kanal-Einstellungen gezeigt. Die Grundeinstellungen dienen dazu, die Eigenschaften und Funktionen des aktuell ausgewählten (genutzten) Kanals einzustellen. Neben der Kanalrolle können Sie hier auch den Standard für automatisch erstellte Verzeichnisse in der Cloud (diese werden z.B. erzeugt, wenn Sie in einem Beitrag ein Bild als Anhang hochladen) festlegen.
+
+Außerdem ist es möglich, den Kanal zu löschen (roter Button "Kanal löschen").
+
+<u>Wichtiger Hinweis:</u> Es ist nicht ohne weiteres möglich, einen Kanal unter dem Namen des gelöschten Kanals neu auf diesem Hub zu erstellen (auch nicht durch Klonen). Das dient dem Schutz vor "Identitäts-Missbrauch". Möchte man auch wichtigen Gründen dennoch wieder einen Kanal mit diesem Namen installieren, dann kann man mit dem Admin des Hub Kontakt aufnehmen, ihm die Gründe darlegen (so dass er sicher sein kann, dass man ein berechtigtes Interesse hat) und bitten, die Sperre aufzuheben. Das kann nur er mit wenigen Handgriffen in der Datenbank tun.
+
+Sie können außerdem den Verfallszeitraum für importierte Inhalte anderer Kanäle festlegen und diesen Import über zwei Filter regeln.
+
+![Kanaleinstellungen 01](/help/de/member/pic/kaneinst01.png)
+
+Die Benachrichtigungs-Einstellungen erlauben es, ganz genau festzulegen, ob man über bestimmte Ereignisse und Vorgänge benachrichtigt wird. Und ob die Benachrichtigung auch per E-Mail erfolgen soll.
+
+![Kanaleinstellungen 02](/help/de/member/pic/kaneinst02.png)
diff --git a/doc/de/member/channels.md b/doc/de/member/channels.md
new file mode 100644
index 000000000..48f3292af
--- /dev/null
+++ b/doc/de/member/channels.md
@@ -0,0 +1,22 @@
+### Kanäle
+
+Kanäle sind einfach Sammlungen von Inhalten, die an einem Ort gespeichert werden. Ein Kanal kann für alles Mögliche stehen. Er kann für Sie stehen, für eine Website, ein Forum, Fotoalben, einfach für alles. Für die meisten Menschen ist ihr erster Kanal „Ich“.
+
+Die wichtigsten Funktionen für einen Kanal, der „mich“ repräsentiert, sind:
+
+- Sichere und private „spamfreie“ Kommunikation
+
+- Identität und „Single-Signon“ über das gesamte Netzwerk
+
+- Datenschutzkontrollen und Berechtigungen, die sich auf das gesamte Netz erstrecken
+
+- Verzeichnisdienste (wie ein Telefonbuch)
+
+
+
+Kurz gesagt, ein Kanal, der Sie repräsentiert, ist „ich im Internet“.
+
+Mit einem Account bei einem Hub können mehrere verschiedene Kanäle mit jeweils individueller Konfiguration angelegt und genutzt werden.
+
+#include doc/de/member/create_channels.md;
+#include doc/de/member/channel_roles.md;
diff --git a/doc/de/member/chat_rooms.md b/doc/de/member/chat_rooms.md
new file mode 100644
index 000000000..ff5757608
--- /dev/null
+++ b/doc/de/member/chat_rooms.md
@@ -0,0 +1,17 @@
+#### Chaträume
+
+Die App "Chaträume" erlaubt es, Chaträume für Instant-Messaging innerhalb eines Hubs einzurichten und mit anderen Kanälen des Hubs zu chatten.
+
+Ruft man die App aus dem Hamburger-Menü auf, werden die eigenen Chaträume angezeigt.
+
+Um einen neuen Chatraum anzulegen, kickt man auf den Button "Raum hinzufügen".
+
+![Chatraum anlegen](/help/de/member/pic/chat01.png)
+
+Hier muss man einen Namen für den Taum vergeben und man kann wählen, nach wie viel Minuten die Chatinhalte verfallen. Außerdem ist es möglich, über das [Privacy-Tool](/help/de/member/permissions_content.md) (🔒) festzulegen, wer berechtigt ist, den Chatraum zu sehen und zu nutzen.
+
+Die URL zum Chatraum wird im Browser angezeigt und ist auch über den Verweis auf den Chatraum in der linken Seitenleiste verfügbar. Nun kann man einem anderen Nutzer des eigenen Hubs die URL mitteilen und einen Chat mit diesem beginnen.
+
+![Chat Nutzer 1](/help/de/member/pic/chat02.png)
+
+![Chat Nutzer 2](/help/de/member/pic/chat03.png)
diff --git a/doc/de/member/clone.md b/doc/de/member/clone.md
new file mode 100644
index 000000000..50db62f8a
--- /dev/null
+++ b/doc/de/member/clone.md
@@ -0,0 +1,53 @@
+### Klonen
+
+Hubzilla-Kanäle verfügen über eine sogenannten "nomadische Identität". Das ist eine Spezialität des Nomad-Protokolls, auf welchem Hubzilla basiert und mit welchem Hubzilla-Hubs untereinander kommunizieren.
+
+Durch die nomadische Identität ist es möglich, Klone des eigenen Kanals zu erzeugen, was die Zensur- und Ausfallresistenz enorm erhöht.
+
+Wenn Sie Ihren Kanal geklont haben, ist es kein Problem, wenn Ihr "Heimat-Hub" einmal ausfällt oder nicht korrekt funktioniert. Sie können übergangslos mit einem Kanal-Klon, welcher auf einem anderen Hub liegt, weiter am Fediverse teilnehmen.
+
+Sämtliche Kanal-Klone werden automatisch im Hintergrund synchronisiert.
+
+Um einen Klon Ihres Kanals anzulegen, benötigen sie einen Account auf einem weiteren (anderen) Hubzilla-Hub.
+
+Es gibt nun mehrere Möglichkeiten, auf diesem anderen Hub, einen Klon Ihres Kanals anzulegen.
+
+Sie können mit der App "Kanalexport" den Kanal auf Ihrem "Heimat-Hub" exportieren.
+
+![Klone 01](/help/de/member/pic/klone01.png)
+
+Durch einen Klick auf den Button "Kanal exportieren", exportieren Sie Ihre Identität und Ihren sozialen Graph in eine Datei, welche sie herunterladen können.
+
+Da Dateien, Webseiten, Wikis, Kalender und Chaträume immer lokal auf den eigenen Hub (also den jeweiligen Server) beschränkt sind, können sie mit der App "Kanalexport" auch Archive dieser Daten lokal herunterladen.
+
+Loggen Sie sich nun auf dem neuen Hub ein und wählen Sie entweder im Hauptmenü (Ihr Profilbild; links oben) den Menüpunkt "Kanäle" und auf der erscheinenden Kanalauswahl-Seite den Button "+ Neu anlegen" oder rufen Sie direkt die Kanalerstellungs-Seite unter `<URL-Ihres-Hubs>/new_channel` auf.
+
+![Klone 02](/help/de/member/pic/klone02.png)
+
+![Klone 03](/help/de/member/pic/klone03.png)
+
+Auf dieser Seite geben Sie nun aber keine Informationen für das Erstellen eines neuen Kanals ein, sondern wählen am unteren Ende des Dialogs den Link "importiere einen bestehenden Kanal von einem anderen Server".
+
+![Klone 04](/help/de/member/pic/klone04.png)
+
+Nun öffnet sich der Dialog für den Kanal-Import.
+
+![Klone 05](/help/de/member/pic/klone05.png)
+
+Mit dem Button "Hochzuladende Datei: Durchsuchen..." öffnen Sie einen Dateidialog, mit welchem Sie die zuvor gespeicherte Kanal-Datei auswählen können.
+
+Alternativ zu dieser Methode können Sie Ihren Kanal auch direkt von der Quelle, also dem Ursprungs-Hub klonen. Dafür müssen Sie im Kanal-Import-Dialog das Handle des zu klonenden Kanals, die Mailadresse für den Login beim Ursprungs-Hub, sowie das dazugehörige Passwort angeben. Zusätzlich können sie per Schalter auswählen, ob Dateien und Objekte des Ursprungs-Hubs mit importiert werden sollen (sofern Ihr neuer Hub das zulässt und das Speicherlimit ausreicht).
+
+Im Dialog können Sie außerdem noch festlegen, ob der neue Hub Ihr "primärer Hub sein soll". Das bedeutet, dass der neue Kanal (also der nun anzulegende) Ihr primärer Kanal wird. Das hat Auswirkungen auf das Handle Ihres Kanals, welches damit künftig mit der URL des neuen Hubs endet.
+
+In der Regel, sofern man nicht komplett auf einen anderen Hub umziehen möchte, lässt man den Schalter bei "Nein" und der Primäre Hub bleibt derjenige, der es aktuell ist (das Handle bleibt unverändert).
+
+Sollten Sie eine veränderte Kanalbezeichnung (Kurzname) wünschen, können Sie diese in einem weiteren Eingabefeld eingeben. Lassen Sie das Feld leer, bleibt der Kanal-Kurzname unverändert.
+
+*<u>Hinweis:</u> Sollte der Kanal-Kurzname auf dem neuen Hub bereits vergeben sein (oder gesperrt, weil ein gleichlautender Kanal dort bereits existierte, aber wieder gelöscht wurde), wird der Kurzname vom System automatisch modifiziert.*
+
+Schließlich klicken Sie auf "Absenden" und verlassen Sie die Seite NICHT, bis der Import abgeschlossen ist. Dies kann, je nach Umfang des Ursprungskanals einige Zeit in Anspruch nehmen.
+
+Über Einstellungen → Klon-Adressen verwalten können Sie jederzeit Ihre Klone verwalten. Sie können festlegen, welches der "primäre Hub" ist und Sie können Klone löschen, wobei empfohlen wird, geklonte Kanäle besser direkt auf dem jeweiligen Hub zu löschen.
+
+![Klone 06](/help/de/member/pic/klone06.png)
diff --git a/doc/de/member/cloud_storage.md b/doc/de/member/cloud_storage.md
new file mode 100644
index 000000000..02914d1dd
--- /dev/null
+++ b/doc/de/member/cloud_storage.md
@@ -0,0 +1,10 @@
+#### Cloudspeicher
+
+Ihre Dateien sind im Internet unter der Adresse `<URL-Ihres-Hubs>/cloud/<Ihr-Kanalname>` für jeden sichtbar, der sie einsehen darf. Wenn der Betrachter über ausreichende Berechtigungen verfügt, kann er auch neue Dateien und Ordner/Verzeichnisse erstellen. Diese Möglichkeit sollte nur für kleinere Dateien und Fotos (bis zu einigen Megabyte) genutzt werden, da sie den internen Speicher beansprucht. Größere Dateien (Videos, Musik usw.) laden Sie bitte mit WebDAV hoch. Diese Dateien können weiterhin über den Web-Zugang abgerufen werden.
+
+Mit WebDAV können Sie Dateien direkt in das Betriebssystem Ihres Computers oder aus diesem heraus kopieren, wobei Ihre Cloud-Dateien wie ein virtuelles Laufwerk erscheinen. Dies sollte zum Hochladen großer Dateien wie Video- und Audiodateien verwendet werden.
+
+Die URL für das Cloud-Verzeichnis lautet `<URL-Ihres-Hubs>/dav/<kanalname>`.
+
+Sollte, je nach Einbindung des DAV-Dateisystems (abhängig vom verwendeten Betriebssystem und ggf. der Anwendung) die Angabe eine Benutzernamens erforderlich sein, so ist dies der Kanal-Kurzname (also ohne führendes "@" und ohne folgende "@Hub-Adresse"). Ein eventuell erforderliches Passwort entspricht Ihrem Login-Passwort.
+
diff --git a/doc/de/member/comanche.md b/doc/de/member/comanche.md
new file mode 100644
index 000000000..8e449e939
--- /dev/null
+++ b/doc/de/member/comanche.md
@@ -0,0 +1,278 @@
+### Comanche Seitenbeschreibungssprache
+
+Comanche ist eine bbCode-ähnliche Auszeichnungssprache, mit der aufwendige und komplexe Webseiten erstellt werden können, indem sie aus einer Reihe von Komponenten zusammengesetzt werden, von denen einige vorgefertigt sind und andere spontan definiert werden können. Comanche verwendet eine Seitenbeschreibungssprache, um diese Seiten zu erstellen.
+
+
+Comanche wählt in erster Linie aus, welche Inhalte in den verschiedenen Bereichen der Seite erscheinen sollen. Die verschiedenen Bereiche haben Namen, und diese Namen können sich je nach gewählter Layoutvorlage ändern.
+
+#### Seitenvorlagen
+
+Derzeit gibt es fünf Layout-Vorlagen, es sei denn, Ihre Website bietet zusätzliche Layouts.
+
+
+**Standardvorlage**
+
+Die Standardvorlage definiert einen "nav"-Bereich am oberen Rand, "aside" als Seitenleiste mit fester Breite, "content" für den Hauptinhaltsbereich und "footer" für einen Seitenfuß.
+
+
+**Vollständig**
+
+Die vollständige Vorlage entspricht der Standardvorlage mit der Ausnahme, dass es keinen "aside"-Bereich gibt.
+
+**Choklet**
+
+Die Choklet-Vorlage bietet eine Reihe von fließenden Layoutstilen, die nach Geschmack festgelegt werden können:
+
+- (default flavour) - ein zweispaltiges Layout, das der "default"-Vorlage ähnelt, aber flexibler ist
+- bannertwo - ein zweispaltiges Layout mit einem Bannerbereich, kompatibel mit der "default"-Vorlage auf kleinen Displays
+- three - dreispaltiges Layout (ergänzt die Standardvorlage um einen "right_aside"-Bereich)
+- edgestwo - zweispaltiges Layout mit festen Seitenrändern
+- edgesthree - dreispaltiges Layout mit festen Seitenrändern
+- full - dreispaltiges Layout mit festen Seitenrändern und Hinzufügen eines "Header"-Bereichs unter der Navigationsleiste
+
+
+**Redable**
+
+Eine Vorlage zum Lesen längerer Texte im Vollbildmodus (also ohne Navigationsleiste). Drei Spalten: aside, content und right_aside.
+
+Für maximale Lesbarkeit ist es ratsam, nur die mittlere Inhaltsspalte zu verwenden.
+
+
+**Zen**
+
+Gibt Ihnen die Freiheit, alles selbst zu machen. Nur eine leere Seite mit einem Inhaltsbereich.
+
+
+Um eine Layout-Vorlage auszuwählen, verwenden Sie den Tag 'template'.
+
+```
+[template]full[/template]
+```
+
+So wählen Sie die Vorlage "choklet" mit der Geschmacksrichtung "three":
+
+```
+[template=three]choklet[/template]
+```
+
+
+Die Standardvorlage wird verwendet, wenn keine andere Vorlage angegeben wird. Die Vorlage kann beliebige Namen für die Inhaltsbereiche verwenden. Sie werden "region"-Tags verwenden, um zu entscheiden, welche Inhalte in den jeweiligen Regionen platziert werden sollen.
+
+Es wurden drei "Makros" für Ihre Verwendung definiert.
+
+```
+$htmlhead - replaced with the site head content.
+$nav - replaced with the site navigation bar content.
+$content - replaced with the main page content.
+```
+
+Standardmäßig wird `$nav` in den Seitenbereich "nav" und `$content` in den Bereich "content" eingefügt. Sie brauchen diese Makros nur zu verwenden, wenn Sie die Reihenfolge der Elemente ändern oder sie in andere Bereiche verschieben möchten.
+
+
+Um ein Thema für Ihre Seite auszuwählen, verwenden Sie den Tag "theme".
+
+```
+[theme]suckerberg[/theme]
+```
+
+Dadurch wird das Thema "suckerberg" ausgewählt. Standardmäßig wird das von Ihrem Kanal bevorzugte Thema verwendet.
+
+```
+[theme=passion]suckerberg[/theme]
+```
+
+Dadurch wird das Thema mit dem Namen "suckerberg" ausgewählt und das Schema "passion" (Themenvariante) gewählt. Alternativ ist es auch möglich, eine komprimierte Themennotation zu verwenden.
+
+```
+[theme]suckerberg:passion[/theme]
+```
+
+Die komprimierte Notation ist nicht Teil von Comanche selbst, wird aber von der Hubzilla-Plattform als Themenspezifizierer erkannt.
+
+
+**Navbar**
+
+```
+[navbar]tucson[/navbar]
+```
+
+Verwenden Sie die Vorlage "tucson" für die Navigationsleiste und die CSS-Regeln. Standardmäßig wird die Vorlage "default" für die Navigationsleiste verwendet.
+
+
+**Regions**
+
+Jede Region hat, wie oben erwähnt, einen Namen. Sie spezifizieren die Region, die Sie interessiert, mit einem "region"-Tag, der den Namen enthält. Alle Inhalte, die Sie in diesem Bereich platzieren möchten, sollten zwischen dem öffnenden Region-Tag und dem schließenden Tag platziert werden.
+
+```
+[region=htmlhead]....content goes here....[/region]
+[region=aside]....content goes here....[/region]
+[region=nav]....content goes here....[/region]
+[region=content]....content goes here....[/region]
+```
+
+
+**CSS und Javascript**
+
+Wir haben die Möglichkeit, Javascript- und CSS-Bibliotheken in den htmlhead-Bereich einzubinden. Zurzeit verwenden wir jquery (js), bootstrap (css/js) und foundation (css/js).
+
+Dies überschreibt den htmlhead des ausgewählten Themas.
+
+```
+[region=htmlhead]
+ [css]bootstrap[/css]
+ [js]jquery[/js]
+ [js]bootstrap[/js]
+[/region]
+```
+
+
+**Menüs und Blöcke**
+
+Mit den Werkzeugen zur Erstellung von Webseiten können Sie neben dem Seiteninhalt auch Menüs und Blöcke erstellen. Diese bieten einen Teil des vorhandenen Inhalts, der in den von Ihnen festgelegten Bereichen und in der von Ihnen festgelegten Reihenfolge platziert werden kann. Jedes dieser Elemente hat einen Namen, den Sie bei der Erstellung des Menüs oder Blocks festlegen.
+
+```
+[menu]mymenu[/menu]
+```
+
+Dadurch wird das Menü "mymenu" an dieser Stelle auf der Seite platziert, die sich innerhalb eines Bereichs befinden muss.
+
+```
+[menu=horizontal]mymenu[/menu]
+```
+
+Dadurch wird das Menü mit dem Namen "mymenu" an dieser Stelle auf der Seite platziert, die sich innerhalb eines Bereichs befinden muss. Außerdem wird dem Menü die Klasse "horizontal" zugewiesen. Die Klasse "horizontal" ist im redbasic-Theme definiert. Sie kann in anderen Themes verfügbar sein, muss es aber nicht.
+
+```
+[menu][var=wrap]none[/var]mymenu[/menu]
+```
+
+Die Variable `[var=wrap]none[/var]` in einem Block entfernt das umschließende div-Element aus dem Menü.
+
+```
+[block]contributors[/block]
+```
+
+Dadurch wird ein Block mit dem Namen "contributors" in dieser Region platziert.
+
+```
+[block=someclass]contributors[/block]
+```
+
+Dadurch wird ein Block mit dem Namen "contributors" in dieser Region platziert. Zusätzlich wird die Klasse "someclass" auf den Block angewendet. Dies ersetzt die Standard-Blockklassen "bblock widget".
+
+```
+[block][var=wrap]none[/var]contributors[/block]
+```
+
+Die Variable `[var=wrap]none[/var]` in einem Block entfernt das umschließende div-Element aus dem Block.
+
+
+**Widgets**
+
+Widgets sind ausführbare Anwendungen, die vom System bereitgestellt werden und die Sie auf Ihrer Seite platzieren können. Einige Widgets benötigen Argumente, mit denen Sie das Widget an Ihren Zweck anpassen können. System-Widgets sind hier aufgelistet. Widgets können auch von Plugins, Themes oder Ihrem Website-Administrator erstellt werden, um zusätzliche Funktionen bereitzustellen.
+
+Widgets und Argumente werden mit den Tags "widget" und "var" angegeben.
+
+```
+[widget=recent_visitors][var=count]24[/var][/widget]
+```
+
+Damit wird das Widget "recent_visitors" geladen und mit dem Argument "count" auf "24" gesetzt.
+
+
+**Kommentare**
+
+Das Tag "comment" wird zur Abgrenzung von Kommentaren verwendet. Diese Kommentare werden auf der gerenderten Seite nicht angezeigt.
+
+```
+[comment]This is a comment[/comment]
+```
+
+
+**Bedingte Ausführung**
+
+Sie können ein 'if'-Konstrukt verwenden, um Entscheidungen zu treffen. Diese basieren derzeit auf der Systemkonfigurationsvariable oder dem aktuellen Beobachter.
+
+```
+[if $config.system.foo]
+ ... the configuration variable system.foo evaluates to 'true'.
+[else]
+ ... the configuration variable system.foo evaluates to 'false'.
+[/if]
+
+[if $observer]
+ ... this content will only be show to authenticated viewers
+[/if]
+```
+
+Die 'else'-Klausel ist optional.
+
+Neben der booleschen Auswertung werden mehrere Tests unterstützt.
+
+```
+[if $config.system.foo == bar]
+ ... the configuration variable system.foo is equal to the string 'bar'
+[/if]
+[if $config.system.foo != bar]
+ ... the configuration variable system.foo is not equal to the string 'bar'
+[/if]
+[if $config.system.foo {} bar ]
+ ... the configuration variable system.foo is a simple array containing a value 'bar'
+[/if]
+[if $config.system.foo {*} bar]
+ ... the configuration variable system.foo is a simple array containing a key named 'bar'
+[/if]
+```
+
+
+**Komplexes Beispiel**
+
+```
+[comment]use an existing page template which provides a banner region plus 3 columns beneath it[/comment]
+
+[template]3-column-with-header[/template]
+
+[comment]Use the "darknight" theme[/comment]
+
+[theme]darkknight[/theme]
+
+[comment]Use the existing site navigation menu[/comment]
+
+[region=nav]$nav[/region]
+
+[region=side]
+
+ [comment]Use my chosen menu and a couple of widgets[/comment]
+
+ [menu]myfavouritemenu[/menu]
+
+ [widget=recent_visitors]
+ [var=count]24[/var]
+ [var=names_only]1[/var]
+ [/widget]
+
+ [widget=tagcloud][/widget]
+ [block]donate[/block]
+
+[/region]
+
+
+
+[region=middle]
+
+ [comment]Show the normal page content[/comment]
+
+ $content
+
+[/region]
+
+
+
+[region=right]
+
+ [comment]Show my condensed channel "wall" feed and allow interaction if the observer is allowed to interact[/comment]
+
+ [widget]channel[/widget]
+
+[/region]
+``` \ No newline at end of file
diff --git a/doc/de/member/commenting.md b/doc/de/member/commenting.md
new file mode 100644
index 000000000..74c80c954
--- /dev/null
+++ b/doc/de/member/commenting.md
@@ -0,0 +1,9 @@
+### Kommentieren
+
+Möchte man einen Beitrag kommentieren, also auf diesen antworten, klickt man in das Feld am unteren Rand des Beitrag ("Kommentar"). Es öffnet sich an dieser Stelle der Kommentar-Editor, welcher dem Beitrags-Editor ähnelt. Es gibt hier aber keine Felder für einen Titel, eine Zusammenfassung und Kategorien. Unter dem Eingabe-Feld für den Inhalt des Kommentars befinden sich wiederum Schaltflächen für bestimmte Formatierungen (nicht alle, die es im Beitrags-Editor gibt, weil nicht alles in einem Kommentar möglich ist) und rechts wieder eine Schaltfläche für eine Vorschau, sowie einen Button "Absenden", um den Kommentar zu veröffentlichen.
+
+![kommentieren](/help/de/member/pic/kommentieren.png)
+
+Auch im Feld für den Kommentar-Inhalt kann man den Text mit Markdown, bbCode und HTML auszeichnen.
+
+Da es kein Feld für eine Zusammenfassung gibt, ist es nicht möglich, diese für eine Inhaltswarnung bezüglich des Kommentars zu verwenden. Man kann das aber durch bbCode erreichen, indem man die Inhaltswarnung in `[summary][/summary]` einschießt. Dadurch wird der gesamte nachfolgende Inhalt zunächst verborgen und kann per Klick angezeigt werden.
diff --git a/doc/de/member/connecting_with_channels.md b/doc/de/member/connecting_with_channels.md
new file mode 100644
index 000000000..1e98977b9
--- /dev/null
+++ b/doc/de/member/connecting_with_channels.md
@@ -0,0 +1,33 @@
+### Verbinden mit Kanälen
+
+Verbindungen in Hubzilla können sehr viele verschiedene Bedeutungen haben. Eine Verbindung ist genauer definiert als eine Reihe von Berechtigungen, die Sie einer anderen Person erteilt haben. In herkömmlichen sozialen Netzwerken erhalten alle Verbindungen die gleichen Berechtigungen oder höchstens zwei Stufen (Freunde und „Follower“). In Hubzilla kann eine Reihe von separaten Berechtigungen festgelegt/angepasst werden, abhängig von der Situation und der Beziehung, die Sie mit dem anderen Kanal haben. Sie können jemandem erlauben, Ihre Beiträge zu sehen, aber nicht Ihre Fotos. Sie können ihnen auch die Erlaubnis verweigern, Ihre Beiträge zu kommentieren oder private Nachrichten an Sie zu senden. Aber machen wir es uns einfach: Sie wollen mit jemandem befreundet sein, den Sie aus sozialen Netzwerken kennen. Wie machen Sie das?
+
+Sie können sich das Verzeichnis ansehen. Das Verzeichnis ist auf allen Hubzilla-Websites verfügbar, d. h., wenn Sie von Ihrer eigenen Seite aus suchen, erhalten Sie Ergebnisse aus dem gesamten Netzwerk. Sie können nach Name, Interesse, Standort und Schlüsselwort suchen.
+
+Wenn Sie das „Handle“ von jemandem bereits kennen, können Sie direkt mit ihm in Verbindung treten. Ein Handle sieht genauso aus wie eine E-Mail-Adresse (z. B. `bob@example.com`), verweist aber auf eine Person im offenen sozialen Netz. Um eine Verbindung herstellen zu können, muss ein kompatibles Netzwerkprotokoll verwendet werden. Standardmäßig unterstützt diese Software das Nomad-Protokoll, weitere Protokolle können jedoch über Plugins/Addons bereitgestellt werden. Weitere Informationen zur Verbindung mit Kanälen in anderen Netzwerken finden Sie weiter unten.
+
+#### So verbinden Sie sich mit anderen Hubzilla-Kanälen:
+
+Besuchen Sie das Profil des gewünschten Kanals, indem Sie auf ihr Foto im Verzeichnis, im Stream oder in den Kommentaren klicken, und es öffnet sich ihre Kanal-Homepage im Kanal-Betrachter. Auf der linken Seite des Bildschirms sehen Sie normalerweise einen Link mit der Bezeichnung „Verbinden“. Klicken Sie darauf, und schon sind Sie fertig. Je nach den Einstellungen des Kanals, mit dem Sie eine Verbindung herstellen möchten, müssen Sie möglicherweise warten, bis der Kanal Ihre Verbindung genehmigt hat, aber es sind keine weiteren Aktionen Ihrerseits erforderlich. Sobald Sie die Verbindung initiiert haben, werden Sie zum Verbindungseditor weitergeleitet. Hier können Sie bestimmte Berechtigungen für diesen Kanal zuweisen, wenn Sie Änderungen vornehmen möchten.
+
+Sie können auch eine Verbindung zu einem beliebigen Kanal herstellen, indem Sie die Seite „Verbindungen“ Ihrer Website oder des Verzeichnisses aufrufen und den „Handle“ in das Feld „Neue Verbindung hinzufügen“ eingeben. Verwenden Sie diese Methode, wenn Ihnen jemand sein Handle mitteilt und Sie sich mit ihm verbinden möchten. Der Vorgang ist derselbe wie bei der Verbindung über die Schaltfläche „Verbinden“ - Sie werden dann zum Verbindungseditor weitergeleitet, um die Berechtigungen festzulegen.
+
+#### So stellen Sie eine Verbindung zu Kanälen in anderen Netzwerken her:
+
+Das Verfahren zum Verbinden mit "Kanälen" in anderen Netzwerken (wie z.B GNU-Social, Mastodon, Misskey, Pleroma und Diaspora) ist ähnlich - geben Sie deren „Handle“ in das Feld „+Add“ auf der Seite „Verbindungen“ ein. Bevor Sie dies jedoch tun, besuchen Sie bitte die App-Verwaltung im App-Menü und vergewissern Sie sich, dass das entsprechende Protokoll (Diaspora, GNU-Social/OStatus oder ActivityPub) in Ihrem Hub bereitgestellt und **für Ihren Kanal** ***aktiviert*** ist. Diese Netzwerke/Protokolle unterstützen keine Kontomigration und Standortunabhängigkeit. Wenn Sie also den Standort wechseln oder Ihren Kanal anderswo klonen, kann die Kommunikation mit diesen Verbindungen fehlschlagen. Aus diesem Grund sind diese Protokolle nicht standardmäßig aktiviert, sondern nur mit Ihrer Zustimmung. Die Aktivierung dieser Protokolle ist eine wichtige Entscheidung zwischen der Kommunikation mit Freunden in diesen Netzwerken und der Ausfallsicherheit des Kontos, falls Ihr Server ausfällt.
+
+Einige Kommunikationsnetze bieten mehr als ein Protokoll an. So können Sie sich beispielsweise mit jemandem verbinden, der sowohl das „ostatus“- alsauch das „activitypub“-Protokoll für die Kommunikation verwendet. Im Allgemeinen bietet das 'activitypub'-Protokoll eine bessere Erfahrung als das 'ostatus'-Protokoll, aber Hubzilla wählt oft das erste Protokoll, das es entdeckt, und das ist vielleicht nicht das, was Sie wollen. Sie können sich mit jemandem über ein bestimmtes Protokoll verbinden, indem Sie den Protokollnamen in eckigen Klammern vor dessen „Handle“ setzen. Zum Beispiel
+
+`[activitypub]https://foo.bar/foobar`
+
+`[ostatus]foobar@foo.bar`
+
+`[diaspora]foobar@foo.bar`
+
+`[zot]foobar@foo.bar`
+
+`[feed]https://foo.bar/foobar`
+
+#### So verbinden Sie sich mit RSS-Feeds:
+
+Ihr Hub-Administrator kann die Verbindung mit RSS-Feeds erlauben. Das Verfahren für die Verbindung mit einem RSS-Feed ist dasselbe, außer dass Sie die URL des Feeds in das Feld „Neue Verbindung hinzufügen“ eingeben (oder einfügen). Die Möglichkeiten können durch den Administrator Ihres Hubs eingeschränkt sein, weil Verbindungen mit Feeds teilweise für hohe Systemlast sorgen. \ No newline at end of file
diff --git a/doc/de/member/connection_editor.md b/doc/de/member/connection_editor.md
new file mode 100644
index 000000000..c96f5e9b3
--- /dev/null
+++ b/doc/de/member/connection_editor.md
@@ -0,0 +1,32 @@
+#### Verbindungs-Editor
+
+Klicken Sie in der App "Verbindungen" auf den Button "Bearbeiten" an einem Kontakt, öffnet sich der Verbindungs-Editor.
+
+Mit dem Editor können Sie einem Kontakt eine bestimmte Kontaktrolle zuweisen. Bei Bedarf können Sie sich über den Button "Contact Roles" die vorhandenen Rollen anzeigen lassen und auch neue Rollen erstellen. Ein weiterer Button (Berechtigungen vergleichen) können Sie die zugewiesenen Berechtigungen mit denen der Standard-Berechtigungsrolle vergleichen.
+
+![Verbindungen 02](/help/de/member/pic/verbindungen02.png)
+
+Mit dem Tab "Privacy Gruppen" können Sie einen Kontakt einer oder mehreren Privacy-Gruppen zuordnen.
+
+![Verbindungen 06](/help/de/member/pic/verbindungen06.png)
+
+Über den Tab "Profile" können Sie festlegen, welches Ihrer Profile (sofern Sie mehrere angelegt haben) dem Kontakt angezeigt wird.
+
+![Verbindungen 03](/help/de/member/pic/verbindungen03.png)
+
+Mit den Inhaltsfiltern ist es Ihnen möglich, Beiträge eines Kontakts mit bestimmten Inhalten herauszufiltern bzw. über Filter festzulegen, dass nur Beiträge mit definierten Inhalten im Stream landen.
+
+![Verbindungen 04](/help/de/member/pic/verbindungen04.png)
+
+Mit den Kontakt-Tools können sie den Kontakt
+
+- blockieren,
+- ignorieren,
+- archivieren,
+- verstecken
+
+oder
+
+- löschen.
+
+![Verbindungen 05](/help/de/member/pic/verbindungen05.png)
diff --git a/doc/de/member/connections.md b/doc/de/member/connections.md
new file mode 100644
index 000000000..7d03bfd48
--- /dev/null
+++ b/doc/de/member/connections.md
@@ -0,0 +1,24 @@
+### Verbindungen
+
+Mit der App "Verbindungen" können Sie sich alle Ihre Verbindungen anzeigen lassen.
+
+![Verbindungen 01](/help/de/member/pic/verbindungen01.png)
+
+In der Übersicht kann man für jede Verbindung
+
+- den Kanalnamen
+- das Datum der Verbindung
+- die Kanaladresse (Handle)
+- das Netzwerk des Kontakts (ActivityPub, Zot (Nomad), RSS, Diaspora...)
+ - man kann sich über einen daneben stehenden Filter die kürzlichen Aktivitäten des Kanals im Stream anzeigen lassen
+- das Profilbild
+- und durch einen farbigen Punkt (Ampelfarben) die vom Kontakt eingeräumten Rechte (lassen Sie den Mauszeiger über dem farbigen Punkt ruhen, werden Ihnen die eingeräumten Rechte angezeigt)
+
+sehen.
+
+Außerdem ist wird ein Label angezeigt, welches den Typ der Verbindung anzeigt oder warnt, dass (bei Klonen) an diesem Ort noch keine Verbindung besteht.
+
+Rechts befindet sich an jedem Kontakteintrag ein Button "Bearbeiten", mit welchem man die Verbindung mittels Verbindungs-Editor bearbeiten kann.
+
+#include doc/de/member/connection_editor.md;
+#include doc/de/member/diaspora_compat.md;
diff --git a/doc/de/member/content_filters.md b/doc/de/member/content_filters.md
new file mode 100644
index 000000000..0e44b63ae
--- /dev/null
+++ b/doc/de/member/content_filters.md
@@ -0,0 +1,141 @@
+### Inhaltsfilter
+
+Mit dem Inhaltsfilter können Sie eingehende Inhalte aus allen Quellen oder von bestimmten Verbindungen filtern. Die Filterung kann auf Wörtern, Tags, regulären Ausdrücken oder Sprache basieren.
+
+In den Stream-Einstellungen können Sie globale Filter für alle eingehenden Inhalte festlegen.
+
+![contfilter01](/help/de/member/pic/contfilter01.png)
+
+Sie können auch individuelle Filter für jede Ihrer Verbindungen festlegen. Wählen Sie „Bearbeiten“ für eine beliebige Verbindung und suchen Sie dann unter „Filter für den Inhalt“.
+
+![contfilter02](/help/de/member/pic/contfilter02.png)
+
+Wenn Sie Filter unter **„Beiträge mit diesem Text nicht importieren**“ („Filterung ablehnen“) hinzufügen, werden diese zuerst angewendet. Alle übereinstimmenden Inhalte werden entfernt.
+
+Anschließend werden alle Filter unter **„Nur Beiträge mit diesem Text importieren“** („Filterung akzeptieren“) angewendet. Nur übereinstimmende Inhalte werden beibehalten und alles, was nicht übereinstimmt, wird entfernt.
+
+Im Allgemeinen werden Sie den einen oder den anderen verwenden und selten, wenn überhaupt, beide gleichzeitig.
+
+
+
+#### Grundlegende Filter
+
+**TEXT**
+
+Jeder Text, der nicht **mit einem der folgenden Zeichen beginnt**: '#', '$', '?', '/', '@', 'lang=', 'lang!=' führt eine Textübereinstimmung durch, bei der die Groß-/Kleinschreibung nicht berücksichtigt wird.
+
+Beispiel: `apple` (stimmt mit "apple", "APPLE", "Apples", "pineapple", "applesauce", etc. überein)
+
+Beispiel: `low sub` (stimmt mit "low sub", "low sub-zero", "Yellow Submarine", etc. überein)
+
+HINWEIS: Achten Sie darauf, keine kurzen Zeichenfolgen zu verwenden, die mit vielen unterschiedlichen Wörtern übereinstimmen. Sie können Reguläre Ausdrücke verwenden (siehe unten).
+
+
+
+**SPRACHE**
+
+**lang=** Übereinstimmung der Sprache (sofern diese identifiziert werden kann) Beispiel: `lang=de` (stimmt mit deutschsprachigen Inhalten überein)
+
+**lang!=** Alles außer dieser Sprache (sofern diese identifiziert werden kann) Beispiel: `lang!=en` (stimmt mit nicht-englischen Inhalten überein)
+
+
+
+**HASHTAG**
+
+**#**
+
+Hashtag abgleichen
+
+Beispiel: `#cats`
+
+**#>n**
+
+Größer als die Gesamtzahl der Hashtags abgleichen
+
+Beispiel: `#>10`
+
+
+
+**ERWÄHNUNG**
+
+**@**
+Erwähnung stimmt überein
+Beispiel: `@alice`
+
+**@>n**
+Größer als die Gesamtzahl der Erwähnungen
+Beispiel: `@>10`
+
+
+
+**KATEGORIE**
+
+**$**
+Übereinstimmung der Kategorie
+Beispiel: `$Science`
+
+
+
+**ITEMS UND FELDER**
+
+**?**
+Nachstehend unter „**Erweiterte Filter“** erläutert
+
+
+
+**REGULÄRE AUSDRÜCKE (REGEX)**
+
+**/**
+Übereinstimmung einer "regular expression". Zahlreiche Online-Hilfeseiten wie [Regular-Expressions.info](https://www.regular-expressions.info/) und [regexr.com](https://regexr.com/) bieten Unterstützung bei der Verwendung von regulären Ausdrücken.
+
+Beispiel: `/gr[ae]y/` (entspricht "gray" and "grey")
+Beipiel: `/\b[Ww]ar\b/` (entspricht dem gesamten Worten "war" und "War", jedoch nicht "wars", "warning", "software", etc.)
+
+
+
+#### Erweiterte Filter
+
+**ITEMS**
+
+**?**
+Sie können eine Zeichenketten-/Zahlen-/Array-/Boolesche-Abgleichung für die Datenbankfelder eines Elements (eines Beitrags, eines Kommentars usw.) durchführen. Eine vollständige Auflistung würde den Rahmen dieses Dokuments sprengen, aber sehen Sie sich `install/schema_mysql.sql` an und suchen Sie nach `CREATE TABLE IF NOT EXISTS `item``. Hier ist ein paar Beispiele:
+
+- `body` (Inhalt der Nachricht)
+- `verb` (in der Regel identisch mit dem ActivityPub-Aktivitätstyp, z. B. Create, Listen)
+- `obj_type` (normalerweise dasselbe wie das ActivityPub-Objekt „object.type“, z. B. Note, Event)
+- `item_thread_top` (erster Beitrag in einem Thread, boolean)
+- `item_private` (0 = öffentliche Nachricht, 1 = eingeschränkte Nachricht, 2 = Direktnachricht)
+- `ua` (Benutzeragenten-Zeichenfolge des Zustellungsagenten – benutzerdefiniertes Artikelattribut, das nicht in der Datenbank enthalten ist)
+- ...etc...
+
+Verfügbare Vergleichsoperatoren sind:
+
+- `?foo ~= baz` -- item.foo enthält den String 'baz'
+- `?foo == baz` -- item.foo ist der String 'baz'
+- `?foo != baz` -- item.foo ist nicht der String 'baz'
+- `?foo // regex` -- item.foo erfüllt die regular expression 'regex' - automatisch in Schrägstriche eingeschlossen, es sei denn, Sie schließen es mit einem anderen Zeichen ein, z. B. '=regex='.
+- `?foo >= 3` -- item.foo ist größer oder gleich 3
+- `?foo > 3` -- item.foo ist größer 3
+- `?foo <= 3` -- item.foo ist kleiner oder gleich 3
+- `?foo < 3` -- item.foo ist kleiner 3
+- `?foo {} baz` -- 'baz' ist ein Array-Element in item.foo
+- `?foo {*} baz` -- 'baz' ist ein Array-Schlüssel in item.foo
+- `?foo` -- wahre Bedingung für item.foo
+- `?!foo` -- falsche Bedingung für item.foo (Die Werte 0, '', ein leeres Array, und ein ungesetzter Wert werden alle zu falsch aufgelöst)
+
+Beispiel: `?verb == Announce` (entspricht ActivityPub "boosts")
+
+
+
+**FELDER**
+
+**?+**
+Übereinstimmung von ActivityPub/ActivityStreams-Objekten. Dies ist buchstäblich das kopierte Objektattribut aus der eingehenden Aktivität.
+
+Unterstützt alle oben aufgeführten Operatoren.
+
+Beispiele:
+
+`?+type == Question` -- entspricht ActivityPub Umfragen
+
+`?+attributedTo ~= spammer` -- entspricht ActivityPub attributedTo: https://o3r56t3c.example.com/u/cryptospammer2355
diff --git a/doc/de/member/conversation_features.md b/doc/de/member/conversation_features.md
new file mode 100644
index 000000000..86dff8b3d
--- /dev/null
+++ b/doc/de/member/conversation_features.md
@@ -0,0 +1,9 @@
+#### Konversationsmerkmale
+
+Über diesen Dialog ist es möglich, bestimmte Konversationsmerkmale für einen Beitrag festzulegen:
+
+- Zulassen von Emoji-Reaktionen
+- Zulassen von Dislikes
+- Zulassen des lokalen Markierens (Sternchen)
+- Zulassen von Antworten auf Kommentare
+
diff --git a/doc/de/member/create_channels.md b/doc/de/member/create_channels.md
new file mode 100644
index 000000000..47cdbe6d6
--- /dev/null
+++ b/doc/de/member/create_channels.md
@@ -0,0 +1,9 @@
+### Kanäle erstellen
+
+Nachdem Sie Ihr Konto erstellt haben, wird Ihnen der Bildschirm „Kanal hinzufügen“ angezeigt. Normalerweise wird Ihr erster Kanal einer sein, der Sie repräsentiert - es ist also eine gute Idee, Ihren eigenen Namen (oder ein Pseudonym) als Kanalnamen zu verwenden. Der Kanalname sollte als Titel oder kurze Beschreibung deines Kanals betrachtet werden. Das Feld „Wählen Sie einen kurzen Spitznamen“ ist vergleichbar mit einem „Benutzernamen“. Mit dem, was Sie hier eingeben, erstellen Sie eine Kanaladresse (im Fediverse auch als "Handle" bezeichnet), mit der sich andere Personen mit Ihnen verbinden können und mit der Sie sich auf anderen Websites anmelden können. Diese Adresse sieht aus wie eine E-Mail-Adresse und hat die Form `<nickname>@<ihr_hub>`.
+
+**Hinweis**: ***Bei anderen Diensten im Fediverse wird dem Handle ein "@" vorangestellt. Bei Hubzilla muss dieses Zeichen weggelassen werden, wenn man sich z.B. mit einem anderen Nutzer verbinden oder nach einem Handle suchen möchte.***
+
+Sie können weitere Kanäle über den Link „Kanalmanager“ erstellen.
+
+Sobald Sie dies getan haben, ist Ihr Kanal einsatzbereit. Unter `<ihr_hub>/channel/<Nickname>` finden Sie Ihren Kanal „Stream“. Hier werden Ihre jüngsten Aktivitäten in umgekehrter chronologischer Reihenfolge angezeigt. \ No newline at end of file
diff --git a/doc/de/member/delete.md b/doc/de/member/delete.md
new file mode 100644
index 000000000..2700cee7a
--- /dev/null
+++ b/doc/de/member/delete.md
@@ -0,0 +1,3 @@
+#### Löschen
+
+Mit dieser Funktion kann man Beiträge aus dem Stream löschen. Ein normaler Nutzer hat diese Möglichkeit nicht beim globalen Stream. In diesem kann nur ein Administrator Beiträge entfernen. \ No newline at end of file
diff --git a/doc/de/member/delete_account.md b/doc/de/member/delete_account.md
new file mode 100644
index 000000000..7e9c44e8a
--- /dev/null
+++ b/doc/de/member/delete_account.md
@@ -0,0 +1,8 @@
+### Account löschen
+
+Möchten Sie Ihren Account, also den gesamten Zugang zum Hub, löschen, nutzen Sie die Einstellungen im Hauptmenü (oben links; Pofilbild): Einstellungen → Konto-Einstellungen.
+
+Dort befindet sich ganz oben ein Button mit der Beschriftung "Konto entfernen". Ein Klick darauf und Ihr Account (Konto) wird, nach Eingabe Ihres Account-Passworts (zur Absicherung) inklusive aller Inhalte gelöscht.
+
+![Account löschen](/help/de/member/pic/ktoloe.png)
+
diff --git a/doc/de/member/deleting_channel.md b/doc/de/member/deleting_channel.md
new file mode 100644
index 000000000..4fe3aaf39
--- /dev/null
+++ b/doc/de/member/deleting_channel.md
@@ -0,0 +1,9 @@
+### Kanal löschen
+
+Möchten Sie Ihren Kanal löschen, nutzen Sie die Einstellungen im Hauptmenü (oben links; Pofilbild): Einstellungen → Kanal-Einstellungen.
+
+Dort befindet sich ganz oben ein Button mit der Beschriftung "Kanal löschen". Ein Klick darauf und Ihr Kanal wird, nach Eingabe Ihres Account-Passworts (zur Absicherung) inklusive aller Inhalte gelöscht.
+
+![Kanal löschen](/help/de/member/pic/kanloe.png)
+
+***Beachte:*** Es ist nun nicht mehr möglich, einen neuen Kanal mit der selben Kanalbezeichnung auf diesem Hub anzulegen. Der Grund dafür ist, dass der Kanal-Kurzname in der Datenbank gesperrt ist, um eine Identitätsübernahme durch dritte zu verhindern. Sollte es aber dennoch erforderlich sein, den alten Kanal (durch Klonen) auf dem Hub wiederherzustellen, müssen Sie den Administrator darum bitte, den gesperrten Kurznamen aus der Hubzilla-Datenbank zu löschen.
diff --git a/doc/de/member/diaspora_compat.md b/doc/de/member/diaspora_compat.md
new file mode 100644
index 000000000..36d5075be
--- /dev/null
+++ b/doc/de/member/diaspora_compat.md
@@ -0,0 +1,33 @@
+### Diaspora-Kompatibilität
+
+Das Diaspora Protocol Addon ermöglicht es einer Website, mit dem Diaspora-Protokoll zu kommunizieren, was die Kommunikation und Verbindungen mit Diaspora-Mitgliedern (und auch Friendica-Mitgliedern, da dieses Netzwerk ebenfalls das Diaspora-Protokoll anbietet) ermöglicht.
+Dieses Addon ist in den Serverkonfigurationen 'Basic' und 'Standard' verfügbar. Es ist nicht verfügbar mit und das Plugin ist komplett deaktiviert, wenn Sie die 'pro' Serverkonfiguration verwenden. Der Grund dafür ist, dass das Diaspora-Protokoll nicht sehr ausgereift ist und viele Funktionen von $projectname nicht gut damit funktionieren.
+Die Mitglieder müssen sich der Einschränkungen des Protokolls bewusst sein eigenen Aktivitäten auf diejenigen beschränken, die mit Diaspora kompatibel sind. Die „Pro“-Serverkonfiguration ist frei von diesen Einschränkungen und Sie können alle Projektfunktionen und -fähigkeiten nutzen, ohne sich Gedanken darüber zu machen, wie sie sich auf andere Netzwerke übertragen lassen. Viele Funktionen sind einzigartig für Hubzilla und werden durch das Nomad-Protokoll unterstützt, das unsere native Kommunikationssprache zwischen Servern/Hubs ist.
+Wenn Sie eine Konfiguration verwenden, die direkte Diaspora-Kommunikation erlaubt, sollten Sie sich der hier dargestellten Einschränkungen bewusst sein.
+
+- Das Zurückziehen von privaten Nachrichten (unsend) ist bei Diaspora-Verbindungen nicht möglich.
+- Private Nachrichten und die dazugehörigen Kommentare werden in Diaspora und Friendica als E-Mail-Benachrichtigungen im Klartext gesendet. Dies ist ein großes Problem für die Privatsphäre und betrifft alle privaten Kommunikationen, bei denen sich *ein* Mitglied der Konversation in einem anderen Netzwerk befindet. Sei dir dessen bewusst.
+- Die Zugangskontrolle funktioniert nur für Beiträge und Kommentare. Diaspora-Mitgliedern wird die Erlaubnis verweigert, wenn sie versuchen, auf andere zugriffskontrollierte hubzilla-Objekte wie Dateien, Fotos, Webseiten, Chatrooms usw. zuzugreifen. Im Falle von privaten Fotos, die mit Beiträgen verlinkt sind, sehen sie ein „Verbotszeichen“ anstelle des Fotos. Diaspora hat kein Konzept für private Medien und bietet eine Illusion von Fotoprivatsphäre, indem es unkenntliche URLs verwendet, anstatt das Foto vor dem Schnüffeln durch unbefugte Betrachter zu schützen.
+
+Es gibt keine andere Möglichkeit, als Ihre Medienressourcen öffentlich zu machen (für jeden im Internet).
+
+- Bearbeitete Beiträge werden nicht zugestellt. Die Diaspora-Mitglieder sehen den ursprünglichen Beitrag/Kommentar ohne Bearbeitungen. Das Protokoll enthält keinen Mechanismus, um einen bestehenden Beitrag zu aktualisieren. Es ist nicht möglich, einen Beitrag zu löschen und einen neuen unsichtbar zu übermitteln, da sich die Message-ID ändert und wir dieselbe Message-ID in unserem eigenen Netzwerk behalten müssen. Die einzige Abhilfe besteht darin, den Beitrag/Kommentar zu löschen und ihn erneut zu übermitteln. (Wenn es sich um einen Beitrag handelt, werden dabei alle vorhandenen Likes/Kommentare gelöscht). Eventuell werden wir eine Möglichkeit anbieten, die veraltete Kopie nur in Diaspora zu löschen und sie in Netzwerken, die Änderungen verarbeiten können, intakt zu lassen.
+- Nomadische Identität (nur $projectname 'standard') wird nicht mit Diaspora funktionieren. Wir werden eventuell eine **Option** bereitstellen, die es Ihnen ermöglicht, die Freigabe von allen Ihren Klonen zu starten, wenn Sie die erste Verbindung herstellen. Die Diaspora-Person muss dies nicht akzeptieren, aber wenn sie die Verbindung akzeptiert, kann die Kommunikation fortgesetzt werden. Ohne diese Option müssen Sie, wenn Sie zu einem anderen Server gehen, von dem aus Sie die Verbindung ursprünglich hergestellt haben, oder wenn Sie die Verbindung herstellen, bevor Sie den Klon erstellt haben, die Verbindung von dem neuen Ort aus erneut herstellen.
+- Das Ablaufen der Post wird auf Diaspora nicht unterstützt. Wir können Ihnen eine Option anbieten, um ablaufende Beiträge nicht an das Netzwerk zu senden. In Zukunft kann dies mit einer Remote-Löschanforderung bereitgestellt werden.
+- Eine Ende-zu-Ende-Verschlüsselung wird nicht unterstützt. Wir werden diese Beiträge in ein Schlosssymbol übersetzen, das von der Diaspora-Seite aus nicht entsperrt werden kann.
+- Die Überprüfung von Nachrichten wird in Zukunft unterstützt werden.
+- Mehrere Profile werden nicht unterstützt. Diaspora-Mitglieder können nur Ihr Standardprofil sehen.
+- Geburtstagsereignisse werden in Diaspora nicht angezeigt. Andere Ereignisse werden übersetzt und als Post verschickt, aber alle Zeiten werden entweder in der Zeitzone des Ursprungskanals oder in GMT angegeben. Wir kennen die Zeitzone des Empfängers nicht, da Diaspora dieses Konzept nicht hat.
+- Wir erlauben derzeit standardmäßig, dass Tags gekapert werden können. Es gibt eine Option, mit der Sie verhindern können, dass das andere Ende des Netzwerks Ihre Tags entführt und sie auf seine eigenen Ressourcen zeigt.
+- Community-Tags werden nicht funktionieren. Wir werden eine Tagging-Aktivität als Kommentar senden. Das wird nichts bewirken.
+- Privatsphären-Tags (@!somebody) werden für Diaspora-Mitglieder nicht verfügbar sein. Diese Tags müssen möglicherweise entfernt oder unkenntlich gemacht werden, um zu verhindern, dass sie gekapert werden - was zu Datenschutzproblemen führen könnte.
+- Plus-getaggte hubzilla-Foren sollten von Diaspora aus funktionieren.
+- Du kannst keine Diaspora-Kanäle als Kanalquellen verwenden.
+- Ablehnungen von Beiträgen werden in Kommentare umgewandelt und Du wirst die Möglichkeit haben, diese als Kommentare zu senden oder sie nicht an Diaspora zu senden (das keine Ablehnungen bietet). Derzeit werden sie nicht gesendet.
+- Wir werden das Gleiche für Likes und Dislikes von ***Kommentaren*** tun. Sie können entweder als Kommentar gesendet werden oder Sie werden die Möglichkeit haben, zu verhindern, dass sie an Diaspora übermittelt werden. Derzeit werden sie nicht gesendet.
+- Emojis sind derzeit nicht übersetzt.
+- „Beobachter-Tags“ werden in leeren Text umgewandelt.
+- Eingebettete Apps werden in Links umgewandelt.
+- Eingebettete Seitendesign-Elemente (in Arbeit) werden entweder entfernt oder in eine Fehlermeldung umgewandelt.
+- Diaspora-Mitglieder werden nicht im Verzeichnis angezeigt.
+- Es gibt Unterschiede in der Kompatibilität von Oembed zwischen den Netzwerken. Einige eingebettete Ressourcen werden auf der einen oder anderen Seite in einen Link umgewandelt. \ No newline at end of file
diff --git a/doc/de/member/direct_messages.md b/doc/de/member/direct_messages.md
new file mode 100644
index 000000000..dc0f77775
--- /dev/null
+++ b/doc/de/member/direct_messages.md
@@ -0,0 +1,9 @@
+### Direktnachrichten
+
+Direktnachrichten sind Nachrichten, die an eine oder mehrere Einzelverbindungen adressiert sind. Sie sind über den Netzwerkstream zugänglich. Ein Filter für Direktnachrichten wurde dem Stream-Filter-Widget für den schnellen Zugriff hinzugefügt.
+
+Möchte man eine Direktnachricht an einen (oder mehrere) andere Nutzer versenden (Direktnachrichten können nur von den Adressaten und vom Versender gelesen werden), so verfasst man einen normalen Beitrag und adressiert diesen per spezieller Erwähnung ausschließlich an den / die Empfänger. Dies geschieht mit der privaten Erwähnung (Privacy-Tag). Ein Privacy-Tag ist ein Name, dem die beiden Zeichen `@!` vorangestellt sind und der zusätzlich zur Markierung dieser Kanäle auch die Datenschutzberechtigungen des Beitrags so ändert, dass nur diese berücksichtigt werden.
+
+Um auf eine DN auch wieder "privat", also als DN zu antworten, muss man kein Privacy-Tag nutzen. Man antwortet einfach direkt auf die eingegangene DN, wodurch die Antwort an alle ursprünglichen Adressaten verteilt wird.
+
+Alternativ zum Privacy-Tag kann man auch Kanäle oder Privacygruppen aus dem [Privacy-Tool](/help/de/member/permissions_content.md) (🔒) auswählen. Dies ist der umständlichere Weg, der aber auch funktioniert. Die Nutzung eines Privacy-Tags überschreibt allerdings eine ggf. getroffene Auswahl im Privacy-Tool. Schreibt man also einen Beitrag, der als Direktnachricht verschickt werden soll, kann man den Privacy-Tag weglassen und stattdessen auf das Vorhangschloss-Symbol neben dem Button "Teilen" klicken, so dass man in die Berechtigungs-Einstellungen gelangt. \ No newline at end of file
diff --git a/doc/de/member/directory.md b/doc/de/member/directory.md
new file mode 100644
index 000000000..d8732f0ee
--- /dev/null
+++ b/doc/de/member/directory.md
@@ -0,0 +1,15 @@
+### Verzeichnis
+
+Hubzilla bietet über die App (im App-Menü ⋮) ein Kanalverzeichnis an. Im Verzeichnis werden die Kanäle des Hubzilla-Grid aufgeführt.
+
+![verzeichnis](/help/de/member/pic/verzeichnis.png)
+
+Über die Verzeichnisoptionen in der linken Seitenleiste kann der Umfang des Verzeichnisses gefiltert werden. So können Sie z.B. die Auflistung auf Kanäle des eigenen Hub beschränken.
+
+Außerdem gibt es in der linken Seitenleiste einen Schlüsselwörter-Wolke, über welche Sie Kanäle mit entsprechenden Interessen/Schwerpunkten finden können.
+
+Ebenfalls in der linken Seitenleiste befindet sich ein Suchfeld, um Kanäle durch Namen / Namensbestandteile oder Interessen (Schlagwörter) zu finden.
+
+Sie können sich dann durch Klick auf den entsprechenden Button direkt mit einem gefundenen Kanal verbinden.
+
+#include doc/de/member/AdvancedSearch.md;
diff --git a/doc/de/member/display_settings.md b/doc/de/member/display_settings.md
new file mode 100644
index 000000000..b484b4012
--- /dev/null
+++ b/doc/de/member/display_settings.md
@@ -0,0 +1,19 @@
+#### Anzeige-Einstellungen
+
+Mit den Anzeige-Einstellungen kann das Design des Kanals eingestellt werden. Überdies kann in gewissen Grenzen festgelegt werden, welche Inhalte dargestellt werden.
+
+In den Design-Einstellungen kann man aus den installierten Themes auswählen und dein Design-Schema für das Thema festlegen.
+
+![Anzeige-Einstellungen 01](/help/de/member/pic/anzeinst01.png)
+
+Mit den Benutzerdefinierten Design-Einstellungen ist es möglich, das Farbschema den eigenen Vorstellungen anzupassen und Eckenradien, Standardgrößen sowie Standardmaße für Avatare festzulegen. Es werden als Standard zunächst die vereinfachten Einstellungen angezeigt, mit denen es nur möglich ist, den dunklen Modus festzulegen, eine schmale Navigationsleiste zu wählen, sowie die Breite des Inhaltsbereichs und die Schriftgröße.
+
+![Anzeige-Einstellungen 02](/help/de/member/pic/anzeinst02.png)
+
+Wenn Sie den Schalter "Erweiterte Einstellungen anzeigen" auf "Ja" stellen und die Auswahl absenden, werden die erweiterten Einstellungen angezeigt, mit denen man Farben, Avatarmaße und Hintergrundbilder festlegen kann.
+
+![Anzeige-Einstellungen 03](/help/de/member/pic/anzeinst03.png)
+
+Mit den Inhalts-Einstellungen lassen sich verschiedene Parameter auswählen (z.B. die Zeit bis zur Aktualisierung der Ansicht) und die Anzeige der "Links für neue Mitglieder", die bei neu angelegten Kanälen angezeigt werden, auszuschalten.
+
+![Anzeige-Einstellungen 04](/help/de/member/pic/anzeinst04.png)
diff --git a/doc/de/member/encryption.md b/doc/de/member/encryption.md
new file mode 100644
index 000000000..b8c238b07
--- /dev/null
+++ b/doc/de/member/encryption.md
@@ -0,0 +1,15 @@
+### Eingebaute automatische Verschlüsselung
+
+Vollständige Offenlegung: Die Verschlüsselung, die hubzilla standardmäßig verwendet, ist nicht absolut wasserdicht. Es *gibt* bekannte Verfahren, um sie zu umgehen. Das ist*aber* sehr aufwändig und muss für jeden Kanal einzeln durchgeführt werden. Und um das klarzustellen: Andere Dienste speichern Ihre Nachrichten im Klartext, daher sehen wir diesen Ansatz als eine *deutliche* Verbesserung für Ihre Privatsphäre an. Außerdem steht es Ihnen jederzeit frei, weitere Verschlüsselungen und Passwortschutz zu verwenden, wenn Sie dies wünschen.
+Um dies näher zu erläutern:
+
+- jeder Kanal hat sein Schlüsselpaar
+- jeder nicht-öffentliche Beitrag wird automatisch verschlüsselt
+- optionaler Passwortschutz für Inhalte über Krypto-Javascript, Browser-zu-Browser-Verschlüsselung (muss in den Einstellungen aktiviert werden) Vollständige Offenlegung: Ein böswilliger Hub-Administrator könnte bösartigen Javascript-Code (z.B. Keylogging-Fähigkeiten) in den Code einschleusen. Verschlüsseln Sie unsere Daten mit GPG, werden Sie selbst Hub-Administrator oder verwenden Sie andere Kommunikationsmittel, wenn Sie das stört.
+
+Was ist also der Umfang der Sicherheit? Um es ganz offen zu sagen: Das mag toll sein, aber es ist nicht perfekt.
+
+- Jeder nicht öffentliche Beitrag wird automatisch verschlüsselt, aber Personen, die Zugriff auf die Datenbank und die Dateien der Website haben *, können* mit Hilfe dieser Schlüssel, die natürlich auf dem Server gespeichert werden müssen, alles entschlüsseln. Um es klar zu sagen: Die Verschlüsselungsschlüssel sind für jeden Kanal unterschiedlich und es ist ein *ziemlicher Aufwand*, dies zu tun. Und nochmals: Andere Dienste speichern Ihre Nachrichten im Klartext und unverschlüsselt. Das ist also *ein* ganz erheblicher Gewinn für Ihre Privatsphäre.
+
+Wir glauben, dass eine Massenüberwachung auf NSA-Niveau mit Klartextextraktion aufgrund des Designs des Nomad-Protokolls wahrscheinlich nicht möglich ist. Gezielte Angriffe, wie z. B. das Hacken eines Hubs, um an die Serverprotokolle und die Datenbank zu gelangen, geben nur teilweise Aufschluss darüber, was zwischen der Kommunikation von Personen zwischen verschiedenen Hubs vor sich geht. Wir glauben, dass dies es für Angreifer auf staatlicher Ebene viel teurer macht, auf Ihre Inhalte in hubzilla zuzugreifen.
+Wir nehmen gerne Hilfe an, um die Sicherheit des Systems zu verbessern und es auch zu überprüfen. \ No newline at end of file
diff --git a/doc/de/member/files.md b/doc/de/member/files.md
new file mode 100644
index 000000000..3a1b3206e
--- /dev/null
+++ b/doc/de/member/files.md
@@ -0,0 +1,31 @@
+### Dateien
+
+Hubzilla bietet Cloud-Funktionalität. Das bedeutet, dass man für jeden Kanal ein Verzeichnis besitzt, in dem man weitere Unterverzeichnisse erzeugen und Dateien ablegen kann. Für jedes Verzeichnis, ja sogar für jede einzelne Datei kann man genaue Zugriffsrechte festlegen. Das geht von einer Sichtbarkeit für die Allgemeinheit, eine Sichtbarkeit für Mitglieder bestimmter Gruppen bis hin zur einzelnen Freigabe für einzelne Mitglieder aus den eigenen Verbindungen. Es ist sogar möglich, Dateien mit Leuten zu teilen, die keine Hubzilla-Identität haben. Dazu verwendet man Gastzugangs-Token.
+
+Das Anlegen und Löschen von Verzeichnissen und das Anlegen bzw. Löschen von Dateien ist wirklich einfach.
+
+Seinen Cloudspeicher erreicht man über das „App-Menü“ (⋮) → Dateien. Man kann auch Bilder über den Dateien-Bereich hochladen, was ebenfalls über den Bereich „Fotos“ möglich ist.
+
+Die Dateien können in einer Listenansicht
+
+![Dateien - Listenansicht](/help/de/member/pic/dateien01.png)
+
+oder einer Kachelansicht
+
+![Dateien - Kachelansicht](/help/de/member/pic/dateien02.png)
+
+dargestellt werden.
+
+Neue Verzeichnisse / Ordner erstellt man mit dem Button "Erstelle". Beim Erstellen kann man auch sofort die [Berechtigungen](/help/de/member/permissions_content.md) für den neuen Ordner festlegen (🔓).
+
+Mit dem Button "+ Dateien hinzufügen" kann man Dateien in seine Cloud hochladen. Auch hier ist es wieder möglich die Zugriffsberechtigungen direkt festzulegen.
+
+Um Zugriffsrechte (Berechtigungen) für Verzeichnisse oder Dateien nachträglich festzulegen oder zu ändern, klickt man auf das Kontextmenü (︙) der Datei oder des Verzeichnisses. Hier lassen sich außerdem noch verschiedene Dateioperationen durchführen.
+
+![Dateioperationen](/help/de/member/pic/dateien03.png)
+
+Fügt man beim Erstellen eines Beitrags oder eines Kommentars eine Datei mit dem Button "📎" an, so wird diese Datei in einem ggf. neu erstellten Verzeichnis (Muster: `Jahr-Monat`) in der Cloud abgelegt.
+
+#include doc/de/member/photos.md;
+#include doc/de/member/gallery.md;
+#include doc/de/member/cloud_storage.md;
diff --git a/doc/de/member/follow_conversation.md b/doc/de/member/follow_conversation.md
new file mode 100644
index 000000000..0c5581613
--- /dev/null
+++ b/doc/de/member/follow_conversation.md
@@ -0,0 +1,3 @@
+#### Unterhaltung folgen / nicht mehr folgen
+
+Mittels Unterhaltung folgen / Unterhaltung nicht mehr folgen kann man umschalten, ob man einem Thread folgen möchte, also ob man Benachrichtigungen über Kommentare/Antworten, über Likes, Dislikes, Emoji-Reaktionen oder darüber, dass der Beitrag geteilt oder wiederholt wurde, erhalten möchte.
diff --git a/doc/de/member/gallery.md b/doc/de/member/gallery.md
new file mode 100644
index 000000000..764bd5959
--- /dev/null
+++ b/doc/de/member/gallery.md
@@ -0,0 +1,7 @@
+#### Galerie
+
+Bei der App "Galerie" handelt es sich um eine einfache Fotogalerie mit welcher alle Ihre Bilder aus dem Cloudspeicher dargestellt werden.
+
+Die dargestellten Bilder werden passend skaliert, was bei kleineren Bildern zu einer etwas unscharfen Darstellung führen kann. Praktischer ist die Nutzung der App "[Fotos](/help/de/member/photos.md)".
+
+![Galerie](/help/de/member/pic/galerie01.png)
diff --git a/doc/de/member/guest_access.md b/doc/de/member/guest_access.md
new file mode 100644
index 000000000..29d5c4ddd
--- /dev/null
+++ b/doc/de/member/guest_access.md
@@ -0,0 +1,15 @@
+### Gastzugang
+
+Wenn Sie private (also für die Öffentlichkeit nicht zugängliche) Inhalte mit Menschen teilen möchten, die über keinen Hubzilla-Account verfügen, so haben Sie die Möglichkeit dies mittels eines Gastzugangs zu verwirklichen.
+
+Mit dem Gastzugang erstellen Sie einen (ggf. temporären) Zugang, der es dem Nutzer, welcher sich mit diesen Daten einloggt, ermöglicht, auf ihre öffentlich zugänglichen Inhalte, aber auch nicht-öffentliche Inhalte, welche sie speziell für den Gastzugang freigeben, zuzugreifen.
+
+Rufen Sie die App "Gastzugang" auf, so wird ein Webformular angezeigt, mit welchem Sie einen solchen Gastzugang einrichten können. Sie geben einen selbst gewählten Anmeldenamen ein. Hubzilla hat bereits automatisch ein Kennwort für den Gastzugang generiert. Diese beiden Informationen können Sie nun demjenigen weitergeben, dem sie Zugriff auf Inhalte geben möchten.
+
+Im Feld "Läuft ab" können Sie außerdem ein Verfallsdatum eingeben, ab welchem der Gastzugang automatisch wieder gelöscht wird. Wenn Sie dieses Feld leer lassen, wird der Gastzugang ohne zeitliche Beschränkung erstellt. Er läuft dann nie automatisch ab und muss ggf. per Hand gelöscht werden.
+
+Außerdem können Sie für den Gastzugang eine [Kontakt-Rolle](/help/de/member/permissions_contact_roles.md) festlegen.
+
+In der linken Seitenleiste werden sämtliche Gastzugänge aufgelistet. Wählen Sie dort einen Gastzugang auf, so können sie ihn bearbeiten oder wieder löschen (auch vor Ablauf der Frist).
+
+Sobald ein Gastzugang angelegt ist, taucht er auch in den "[Berechtigungs-Einstellungen](/help/de/member/permissions_content.md)" (Privacy-Tool) unter "Benutzerdefinierte Auswahl" auf. Sie können damit einzelne private Inhalte explizit für den Gastzugang (aber natürlich zusätzlich auch für andere Kontakte) freigeben, so dass der Gast auf die Inhalte zugreifen kann. \ No newline at end of file
diff --git a/doc/de/member/important_apps.md b/doc/de/member/important_apps.md
new file mode 100644
index 000000000..fa2c22693
--- /dev/null
+++ b/doc/de/member/important_apps.md
@@ -0,0 +1,17 @@
+#### Wichtige Apps
+
+Wenn Sie Ihren Kanal hauptsächlich für social Networking nutzen möchten, gibt es einige Apps, die nicht als Standard installiert oder aktiviert sind, welche teilweise essenziell wichtig sind.
+
+Um am gesamten Fediverse teilzunehmen, müssen Sie die App "**ActivityPub-Protokoll**" installieren.
+
+Um einfacher lohnende Kontakte zu finden und zu erleben, was im Fediverse gerade passiert, kann man die App "**[Öffentlicher Beitragsstream](/hlp/de/member/public_stream.md)**" installieren und nutzen.
+
+Ebenfalls sinnvoll und empfehlenswert ist die App "**[NSFW](/help/de/member/NSFW.md)**", mit welcher man eigenen Contentwarning-Filter erstellen und nutzen kann.
+
+Außerdem sollten Sie die App "**[Superblock](/help/de/member/superblock.md)**" installieren, welche es Ihnen ermöglicht, bestimmte Nutzer komplett zu blockieren.
+
+Als ebenfalls wichtig ist die App "**[Privacy Groups](/help/de/member/privacy_groups.md)**" zu erachten. Mit dieser ist es möglich, Kontaktgruppen zu erstellen und sowohl nach diesen zu filtern, als auch mit den Kontakten aus den Gruppen geschlossene Kommunikation zu betreiben.
+
+----
+
+**<u>Achtung:</u>** Die App "Öffentlicher Beitragsstream" steht zur Installation nur zur Verfügung, wenn der Administrator des Hub diesen Stream aktiviert hat.
diff --git a/doc/de/member/insert_images.md b/doc/de/member/insert_images.md
new file mode 100644
index 000000000..6b713e0a1
--- /dev/null
+++ b/doc/de/member/insert_images.md
@@ -0,0 +1,53 @@
+#### Bilder einfügen
+
+Es gibt verschiedene Möglichkeiten, Bilder in Beiträge und Kommentare einzufügen.
+
+##### Im Beitragseditor
+
+Im Beitragseditor existieren zwei Schaltflächen, die es ermöglichen, Bilder einzufügen: "Bild einbetten" und "Datei anhängen/hochladen".
+
+Mit "Bild einbetten" kann man ein bereits in der Cloud existierendes Bild in den Beitrag einfügen. Das Bild muss also entweder vorhanden sein, oder mal lädt es zu genau diesem Zweck z.B. mit der App "Dateien" hoch.
+
+![bild01](/help/de/member/pic/bild01.png)
+
+Wählt man diese Schaltfläche, öffnet sich eine Übersicht mit den vorhandenen ´Bilddateien und man muss nur das passende Bild auswählen und anklicken. <u>Beachte:</u> Nach der Auswahl des einzufügenden Bildes muss das Auswahlfenster wieder geschlossen werden. Es schließt sich nicht automatisch nach dem Einfügen eines Bildes, um die Möglichkeit zu haben, in einem Durchgang ggf. mehrere Bilder einzufügen.
+
+Mit dem Auswählen wird das Bild, auf Originalgröße skaliert, als anklickbarer Link zur Quelldatei mittels der bbCode-Tags `[zrl][zmg] [/zmg][/zrl]` am Ende des bisherigen Beitrags (nicht an der aktuellen Textcursor-Position) eingefügt.
+
+![bild03](/help/de/member/pic/bild03.png)
+
+Mit "Datei anhängen/hochladen" kann ein Bild direkt vom eigenen Gerät hochgeladen und am Ende des Beitrags eingebettet.
+
+![bild02](/help/de/member/pic/bild02.png)
+
+Es öffnet sich ein Dateiauswahl-Dialog mit dem man das Bild auf dem eigenen Gerät auswählen und hochladen kann. Es wird in ein ggf. automatisch erstelltes Unterverzeichnis in der Cloud hochgeladen und wieder mit den bbCode-Tags `[zrl][zmg] [/zmg][/zrl]` am Ende des bisherigen Beitrags eingefügt.
+
+<u>Beachte:</u> Bei dieser Methode wird das Bild, sofern kein Fließtext vorhanden ist, nicht auf Originalgröße skaliert, sondern als kleiners Vorschaubild dargestellt. Das führt dazu, dass um das Bild herum ggf. ein Karomuster-Hintergrund zu sehen ist.
+
+![bild04](/help/de/member/pic/bild04.png)
+
+##### Im Kommentareditor
+
+Im Kommentareditor existiert lediglich die Möglichkeit, ein Bild hochzuladen und einzubetten. Die Schaltfläche für das Einfügen eines vorhandenen Bildes existiert dort nicht.
+
+![bild05](/help/de/member/pic/bild05.png)
+
+Möchte man dennoch ein bereits vorhandenes Bild einfügen, muss man das per Hand mit den Tags `[img][/img]` oder `[img=URL][/img]` tun. Dafür muss man die URL des Bildes kennen. Auf diese Weise kann man auch Bilder aus externen Quellen im Web einfügen.
+
+Verwendet man `[img=BREITExHÖHE][/img]`, kann man das Bild skalieren.
+
+##### Alt-Text
+
+Möchte man einen Alternativtext (Alt-Text), welcher angezeigt wird, wenn das Bild nicht dargestellt werden kann, oder - als Popup - wenn man mit dem Mauszeiger über das Bild fährt, so kann man diesen zwischen die beiden Tags `[img=URL]ALT_TEXT[/img]` tun. Hat man das Bild in einen Beitrag nach der erstem Methode ("Bild einbetten"), muss man selbst Hand anlegen. Man muss die Skalierung, die im öffnenden zmg-Tag eingefügt wurde mit der URL des Bildes ersetzen und die automatisch eingefügte Bild-URL zwischen dem öffnenden und dem schließenden zmg-Tag durch den Alt-Text ersetzen.
+
+Beispiel:
+
+Das Bild wurde eingebettet und es wurde damit der Code
+
+`[zrl=https://klacker.org/photos/tutorial01/image/cd747cd9-3f05-42cd-94cc-91c7368c5a18][zmg=520x520]https://klacker.org/photo/cd747cd9-3f05-42cd-94cc-91c7368c5a18-2.png[/zmg][/zrl]`
+
+automatisch erstellt, dann muss er, um z.B. den Alt-Text "Hubzilla-Symbol auf hellgrauem Hintergrund" einzufügen, so geändert werden:
+
+`[zrl=https://klacker.org/photos/tutorial01/image/cd747cd9-3f05-42cd-94cc-91c7368c5a18][zmg=https://klacker.org/photo/cd747cd9-3f05-42cd-94cc-91c7368c5a18-2.png520x520]Hubzilla-Symbol auf hellgrauem Hintergrund[/zmg][/zrl]`
+
+![bild06](/help/de/member/pic/bild06.png)
diff --git a/doc/de/member/interact.md b/doc/de/member/interact.md
new file mode 100644
index 000000000..9e10e2a59
--- /dev/null
+++ b/doc/de/member/interact.md
@@ -0,0 +1,25 @@
+### Mit Postings interagieren
+
+Mit Postings, die man im Stream sieht, kann man interagieren. Das ist einer der Zwecke von Sozialen Netzwerken. Man kann einen solchen Beitrag kommentieren, es sind aber noch weitere Möglichkeiten der Interaktion möglich.
+
+Die Schaltflächen (Buttons) dafür findet man im Beitrag unten rechts.
+
+![Schaltflächen](/help/de/member/pic/interag01.png)
+
+Man kann einen Beitrag "mögen" 🖒 oder "nicht mögen" 🖓 oder man kann mit einem Emoji auf einen Beitrag reagieren:
+
+![Emoji-Reaktion](/help/de/member/pic/interag02.png)
+
+Weitere Funktionen befinden sich im Menü **⚙**.
+
+![Menü](/help/de/member/pic/interag03.png)
+
+#include doc/de/member/repeat.md;
+#include doc/de/member/share.md;
+#include doc/de/member/link_to_source.md;
+#include doc/de/member/save_to_folder.md;
+#include doc/de/member/toggle_star_status.md;
+#include doc/de/member/show_source_code.md;
+#include doc/de/member/follow_conversation.md;
+#include doc/de/member/delete.md;
+#include doc/de/member/conversation_features.md;
diff --git a/doc/de/member/link_to_source.md b/doc/de/member/link_to_source.md
new file mode 100644
index 000000000..39b344f2c
--- /dev/null
+++ b/doc/de/member/link_to_source.md
@@ -0,0 +1,3 @@
+#### Link zur Quelle
+
+Mit "Link zur Quelle" gelangt man zur eigentlichen Quelle eines Beitrags. Man landet beim Original-Beitrag auf der Instanz des Beitragserstellers. \ No newline at end of file
diff --git a/doc/de/member/mentions.md b/doc/de/member/mentions.md
new file mode 100644
index 000000000..5f1c714c1
--- /dev/null
+++ b/doc/de/member/mentions.md
@@ -0,0 +1,9 @@
+### Erwähnungen
+
+Kanäle (Nutzer) werden gekennzeichnet, indem ihrem Namen (Handle) einfach das @ -Zeichen vorangestellt wird. `@Jack`
+
+Wenn Sie jemanden erwähnen, wird ein Feld für die automatische Vervollständigung erstellt, aus dem Sie Ihre unmittelbaren Verbindungen auswählen können. Wählen Sie entsprechend aus.
+
+Ist der Kontakt berechtigt, Ihr Posting zu empfangen, erhält er eine Tagbenachrichtigung.
+
+Befindet sich der Adressat nicht in Ihrer Kontaktliste, so müssen sie das Handle hinter dem "@"-Zeichen ausschreiben. Sofern der Empfänger auch Erwähnungen Fremder erlaubt, wird er ebenfalls über die Erwähnung benachrichtigt. \ No newline at end of file
diff --git a/doc/de/member/overview.md b/doc/de/member/overview.md
new file mode 100644
index 000000000..9bd2f6c0c
--- /dev/null
+++ b/doc/de/member/overview.md
@@ -0,0 +1,3 @@
+### Überblick
+
+Während viele Funktionen und Möglichkeiten von Hubzilla denjenigen vertraut sind, die bereits Social-Networking-Websites und Blogging-Software verwendet haben, gibt es auch einige neue Konzepte und Funktionen, die den meisten Menschen noch nicht begegnet sind. Einige der neuen Ideen hängen mit der dezentralen Natur des Grids zusammen, andere mit dem fortschrittlichen Berechtigungssystem, das zum Schutz Ihrer Daten notwendig ist. Dieser Leitfaden soll Ihnen helfen zu verstehen, wie Sie Ihre nomadische Identität erstellen, konfigurieren und nutzen können. \ No newline at end of file
diff --git a/doc/de/member/permissions.md b/doc/de/member/permissions.md
new file mode 100644
index 000000000..f5d9fb33c
--- /dev/null
+++ b/doc/de/member/permissions.md
@@ -0,0 +1,7 @@
+### Berechtigungen
+
+Berechtigungen sind ein Kernelement von Hubzilla. Sie erlauben sehr fein abgestufte Möglichkeiten, Inhalte zugänglich zu machen, zu verbergen oder in der Nutzung zu beschränken. So dienen sie auch dazu, Direktnachrichten möglich zu machen, indem über Berechtigungen festgelegt wird, wer den Beitrag (nichts anderes sind Direktnachrichten) sehen kann, und wer nicht.
+
+#include doc/de/member/permissions_content.md;
+#include doc/de/member/permissions_channel_roles.md;
+#include doc/de/member/permissions_contact_roles.md;
diff --git a/doc/de/member/permissions_channel_roles.md b/doc/de/member/permissions_channel_roles.md
new file mode 100644
index 000000000..db0cbe062
--- /dev/null
+++ b/doc/de/member/permissions_channel_roles.md
@@ -0,0 +1,45 @@
+#### Berechtigungen - Benutzerdefinierte Kanalrollen
+
+[Kanal-Rollen](/help/de/member/channel_roles.md) legen fest, welche Rechte bei der Interaktion mit einem Kanal gegeben sind. Sie sind unter "Einstellungen" → "Kanal Einstellungen" erreichbar.
+
+Hier kann die Rolle für einen Kanal festgelegt werden. Kanal-Rollen haben auch Einfluss auf Kontakt-Rollen, weil individuelle Rechte, die von den Kanal-Rollen vorgegeben sind und vererbt werden, dort die eigenen Einstellungen überschreiben.
+
+Für eine wirklich individuelle Anpassung der Rollenberechtigungen Ihres Kanals, müssen Sie als Kanal-Rolle "Benutzerdefiniert" auswählen. Die anderen Rollen ("Öffentlich", "Persönlich", "Community Forum") sind vordefinierte Berechtigungsrollen (siehe: [Kanal-Rollen](/help/de/member/channel_roles.md)).
+
+Mit den benutzerdefinierten Kanal-Rollen können Sie für folgende Interaktionen festlegen, wer sie wie ausüben kann:
+
+- Kann meinen Kanal-Stream und meine Beiträge sehen
+- Kann mir die Beiträge aus seinem Kanal schicken
+- Kann mein Standardprofil sehen
+- Kann meine Verbindungen sehen
+- Kann meine Datei- und Bilderordner sehen
+- Kann in meine Datei- und Bilderordner hochladen/ändern
+- Kann die Webseiten meines Kanals sehen
+- Kann meine Wiki-Seiten sehen
+- Kann Webseiten in meinem Kanal erstellen/ändern
+- Kann meine Wiki-Seiten bearbeiten
+- Kann auf meiner Kanal-Seite ("wall") Beiträge veröffentlichen
+- Kann mir direkte Nachrichten schicken
+- Kann Profile und Profilsachen mögen/nicht mögen
+- Kann mit mir chatten
+- Kann meine öffentlichen Beiträge in anderen Kanälen zitieren/spiegeln
+- Kann meinen Kanal administrieren
+
+Für diese Interaktionen stehen dann folgende Berechtigungen zur Wahl:
+
+- Nur ich
+- Nur die, denen Du es explizit erlaubst
+- Angenommene Verbindungen
+- Beliebige Verbindungen
+- Jeder auf dieser Webseite
+- Alle Hubzilla-Mitglieder
+- Jeder authentifizierte
+- Jeder im Internet
+
+Um die **benutzerdefinierte Rolle** zu bearbeiten, wählt man in den Einstellungen den Punkt "Privacy-Einstellungen". Rechts unten findet man den Button "Benutzerdefinierte Konfiguration der Channel Role". Klickt man darauf, erscheint ein Warn-Dialog, der auf die Risiken einer fehlerhaften Konfiguration aufmerksam macht. Bestätigt man, dass man die Rechte bearbeiten möchte, öffnet sich der Einstellungs-Dialog für die benutzerdefinierten Rollenrechte.
+
+----
+
+**<u>Wichtiger Hinweis:</u>**
+
+Die benutzerdefinierten Rollen sollten mit Bedacht eingestellt werden und bergen die Gefahr, dass sich bei bestimmten Konfigurationen der Kanal nicht mehr wie gewünscht verhält. \ No newline at end of file
diff --git a/doc/de/member/permissions_contact_roles.md b/doc/de/member/permissions_contact_roles.md
new file mode 100644
index 000000000..fae1981ae
--- /dev/null
+++ b/doc/de/member/permissions_contact_roles.md
@@ -0,0 +1,17 @@
+#### Berechtigungen - Kontaktrollen
+
+Kontakt-Rollen dient dazu Rollen (also eine Sammlung von Rechten und Möglichkeiten) für Kontakte zu erstellen. Diese Rollen können dann einem Kontakt oder allen Kontakten einer Privacy Gruppe (nicht der Gruppe selbst) zugewiesen werden. Damit werden die Möglichkeiten von Kontakten eingeschränkt oder erweitert.
+
+Mittels der App "Kontakt-Rollen" kann man Rollen, die den [Kanal-Rollen](/help/de/member/permissions_channel_roles.md) entsprechen, zusammenstellen. Im Kontakt-Editor oder im Privacy Gruppen Editor kann dann diese Berechtigungsrolle einzelnen Kontakten oder allen Kontakten einer Privacy Gruppe zuweisen.
+
+Jeder Kanal verfügt nach Erstellung automatisch über die Kontakt-Rolle "Standard" ("Systemrolle - nicht editierbar"). Neuen Kontakten wird automatisch diese Kontaktrolle zugewiesen (es sei denn, man legt eine eigene Kontaktrolle an, ändert diese Standard-Einstellung und weist neuen Kontakten künftig die neue, eigene Rolle zu). Die Standard-Kontaktrolle umfasst Berechtigungen aufgrund der gewählten Kanal-Rolle. Zusätzlich zu den durch die Kanal-Rolle gewährten Rechte, werden noch einige andere Rechte eingeräumt, damit sich der Kanal so verhält, wie man es aufgrund der Kanal-Rolle erwarten würde (so entspricht "Öffentlich" am ehesten einem "normalen" Social-Network-Kanal).
+
+![croles1](/help/de/member/pic/croles1.png)
+
+<u>Beachte:</u> Etliche der Rechte einer Kanalrolle (ob Standard oder selbst erstellt) werden von der Kanalrolle geerbt. Diese Rechte können in der Kontakt-Rolle nicht wieder entzogen werden. Es handelt sich bei der Kontakt-Rolle um eine Whitelist, bei welcher nur zusätzliche Rechte gewährt werden können.
+
+Im [Verbindungs-Editor](/help/de/member/connection_editor.md) kann man einem Kontakt eine Kontakt-Rolle zuweisen. Dieser Dialog erscheint auch, wenn man einen neuen Kontakt zufügt. Als Standard ist hier dann die Kontakt-Rolle ausgewählt, bei welcher man den Schalter "Neuen Kontakten automatisch diese Rolle zuweisen" eingeschaltet hat.
+
+![croles2](/help/de/member/pic/croles2.png)
+
+Bei Kanälen ohne selbst definierte Kontakt-Rollen ist dies immer "Standard". Im Verbindungs-Editor befindet sich auch ein Button "Contact Roles" mit welchem man zum Kontaktrollen-Editor gelangt, falls man für den Kontakt eine neue Kontaktrolle erstellen möchte.
diff --git a/doc/de/member/permissions_content.md b/doc/de/member/permissions_content.md
new file mode 100644
index 000000000..dc16477d9
--- /dev/null
+++ b/doc/de/member/permissions_content.md
@@ -0,0 +1,38 @@
+#### Berechtigungen für Inhalte
+
+Wenn Sie Inhalte bei Hubzilla teilen, also Postings veröffentlichen, Bilder oder Texte hochladen, Termine im Kalender eintragen, können Sie ganz exakt festlegen, wer Zugriff auf diese Inhalte hat.
+
+Die Berechtigungs-Einstellungen für Inhalte erreichen Sie über einen Button ("Privacy Tool") mit einem Vorhangschloss-Symbol 🔒bzw.🔓.
+
+Für das Teilen von Beiträgen: ![Beitragsberechtigung](/help/de/member/pic/inhber01.png)
+
+Für das Erstellen von Ordnern/Verzeichnissen im Cloud-Speicher: ![Ordnerberechtigung](/help/de/member/pic/inhber02.png)
+
+Für das Hochladen von Dateien: ![Dateiberechtigung](/help/de/member/pic/inhber03.png)
+
+Für das Teilen von Terminen/Ereignissen: ![Terminberechtigung](/help/de/member/pic/inhber04.png)
+
+Auch für Webseiten, Wikiseiten und verschiedene andere Inhalte gibt es entsprechende Einstellungsmöglichkeiten für die Berechtigung.
+
+Wenn Sie auf das Symbol klicken, öffnet sich der Berechtigungsdialog, mit welchem Sie die Berechtigung für andere Nutzer festlegen können (hier geht es in der Regel um die Sichtbarkeit der Inhalte).
+
+![Berechtigungsdialog](/help/de/member/pic/inhber05.png)
+
+Sie haben die Wahl zwischen
+
+- **Öffentlich** - Wie die Bezeichnung schon nahelegt, ist der Inhalt für jeden im Internet sichtbar. Also selbst für Nutzer, die gar keinen Fediverse-Dienst nutzen.
+
+- **Nur ich** - Hier kann nur der erstellende Nutzer den Inhalt sehen. Sie "teilen" ihn mit sich selbst.
+
+- **Privacy Gruppen** - Der Inhalt ist für alle Nutzer sichtbar, die sich in einer Ihrer Privacy Gruppen befinden.
+
+- **Benutzerdefinierte Auswahl** - Hier kann exakt festgelegt werden, wer den Inhalt sehen kann. Es sind auch Kobinationen von Privacy Gruppen und einzelnen Kontakten möglich, indem man bei dem jeweiligen Eintrag "Erlauben" oder "Verweigern" anwählt.
+
+ ![Benutzerdefiniert](/help/de/member/pic/inhber06.png)
+
+----
+
+<u>**Wichtiger Hinweis:**</u>
+
+Die einmal festgelegten Berechtigungen für Postings können nachträglich nicht mehr geändert werden! Ein Posting wird sofort an eine nicht wirklich bestimmbare Zahl von anderen Servern verteilt, so dass Berechtigungen nachträglich nicht erteilt oder entzogen werden können.Die Berechtigungen für andere Inhalte, wie Dateien, Bilder etc. können hingegen nachträglich bearbeitet werden, weil diese Inhalte nur auf der eigenen Instanz (Hub) gespeichert werden und beim Teilen lediglich die Referenz zum Inhalt weitergegeben wird.
+
diff --git a/doc/de/member/photos.md b/doc/de/member/photos.md
new file mode 100644
index 000000000..a52c23017
--- /dev/null
+++ b/doc/de/member/photos.md
@@ -0,0 +1,17 @@
+#### Fotos
+
+Die App "Fotos" ist eine spezielle Verwaltung/Ansicht für in die eigene Cloud hochgeladene Bilder. Es werden Vorschaubilder, statt Kacheln oder Dateibezeichnungen angezeigt, was das Auffinden bestimmter Bilder einfacher macht.
+
+![Foto-App](/help/de/member/pic/fotos01.png)
+
+Ein Klick auf das Bild führt zur Bildansicht. Hier gibt es zwei Steuerungssymbole, um zum nächsten oder vorherigen Foto zu wechseln.
+
+![Foto-App Steuerung](/help/de/member/pic/fotos02.png)
+
+Außerdem gibt es einen Button "Fotowerkzeuge", der es mittels eines Menüs möglich macht, Das Bild als Profilbild oder Banner festzulegen, sowie das Bild zu bearbeiten.
+
+![Foto-App Bearbeiten](/help/de/member/pic/fotos03.png)
+
+Ein weiterer Klick auf das Bild führt zur Vollansicht.
+
+![Foto-App Volansicht](/help/de/member/pic/fotos04.png)
diff --git a/doc/de/member/pic/anzeinst01.png b/doc/de/member/pic/anzeinst01.png
new file mode 100644
index 000000000..0ed30700a
--- /dev/null
+++ b/doc/de/member/pic/anzeinst01.png
Binary files differ
diff --git a/doc/de/member/pic/anzeinst02.png b/doc/de/member/pic/anzeinst02.png
new file mode 100644
index 000000000..00c7369ea
--- /dev/null
+++ b/doc/de/member/pic/anzeinst02.png
Binary files differ
diff --git a/doc/de/member/pic/anzeinst03.png b/doc/de/member/pic/anzeinst03.png
new file mode 100644
index 000000000..fe82a0a0f
--- /dev/null
+++ b/doc/de/member/pic/anzeinst03.png
Binary files differ
diff --git a/doc/de/member/pic/anzeinst04.png b/doc/de/member/pic/anzeinst04.png
new file mode 100644
index 000000000..6c0e36d75
--- /dev/null
+++ b/doc/de/member/pic/anzeinst04.png
Binary files differ
diff --git a/doc/de/member/pic/apps01.png b/doc/de/member/pic/apps01.png
new file mode 100644
index 000000000..cd36e7f3a
--- /dev/null
+++ b/doc/de/member/pic/apps01.png
Binary files differ
diff --git a/doc/de/member/pic/apps02.png b/doc/de/member/pic/apps02.png
new file mode 100644
index 000000000..99c8211d3
--- /dev/null
+++ b/doc/de/member/pic/apps02.png
Binary files differ
diff --git a/doc/de/member/pic/apps03.png b/doc/de/member/pic/apps03.png
new file mode 100644
index 000000000..3db58133c
--- /dev/null
+++ b/doc/de/member/pic/apps03.png
Binary files differ
diff --git a/doc/de/member/pic/apps04.png b/doc/de/member/pic/apps04.png
new file mode 100644
index 000000000..adc8ca5e9
--- /dev/null
+++ b/doc/de/member/pic/apps04.png
Binary files differ
diff --git a/doc/de/member/pic/apps05.png b/doc/de/member/pic/apps05.png
new file mode 100644
index 000000000..7dcf8f69c
--- /dev/null
+++ b/doc/de/member/pic/apps05.png
Binary files differ
diff --git a/doc/de/member/pic/apps06.png b/doc/de/member/pic/apps06.png
new file mode 100644
index 000000000..4662aef0b
--- /dev/null
+++ b/doc/de/member/pic/apps06.png
Binary files differ
diff --git a/doc/de/member/pic/apps07.png b/doc/de/member/pic/apps07.png
new file mode 100644
index 000000000..e32fb4bb9
--- /dev/null
+++ b/doc/de/member/pic/apps07.png
Binary files differ
diff --git a/doc/de/member/pic/apps08.png b/doc/de/member/pic/apps08.png
new file mode 100644
index 000000000..db3c0c8c8
--- /dev/null
+++ b/doc/de/member/pic/apps08.png
Binary files differ
diff --git a/doc/de/member/pic/artikel01.png b/doc/de/member/pic/artikel01.png
new file mode 100644
index 000000000..f92f00eb3
--- /dev/null
+++ b/doc/de/member/pic/artikel01.png
Binary files differ
diff --git a/doc/de/member/pic/artikel02.png b/doc/de/member/pic/artikel02.png
new file mode 100644
index 000000000..60a4902cd
--- /dev/null
+++ b/doc/de/member/pic/artikel02.png
Binary files differ
diff --git a/doc/de/member/pic/artikel03.png b/doc/de/member/pic/artikel03.png
new file mode 100644
index 000000000..21ecfccec
--- /dev/null
+++ b/doc/de/member/pic/artikel03.png
Binary files differ
diff --git a/doc/de/member/pic/artikel04.png b/doc/de/member/pic/artikel04.png
new file mode 100644
index 000000000..514722303
--- /dev/null
+++ b/doc/de/member/pic/artikel04.png
Binary files differ
diff --git a/doc/de/member/pic/artikel05.png b/doc/de/member/pic/artikel05.png
new file mode 100644
index 000000000..feb6b44f9
--- /dev/null
+++ b/doc/de/member/pic/artikel05.png
Binary files differ
diff --git a/doc/de/member/pic/author.png b/doc/de/member/pic/author.png
new file mode 100644
index 000000000..75ec9e636
--- /dev/null
+++ b/doc/de/member/pic/author.png
Binary files differ
diff --git a/doc/de/member/pic/bild01.png b/doc/de/member/pic/bild01.png
new file mode 100644
index 000000000..b6c9df8f0
--- /dev/null
+++ b/doc/de/member/pic/bild01.png
Binary files differ
diff --git a/doc/de/member/pic/bild02.png b/doc/de/member/pic/bild02.png
new file mode 100644
index 000000000..35fd8b549
--- /dev/null
+++ b/doc/de/member/pic/bild02.png
Binary files differ
diff --git a/doc/de/member/pic/bild03.png b/doc/de/member/pic/bild03.png
new file mode 100644
index 000000000..ea47a710c
--- /dev/null
+++ b/doc/de/member/pic/bild03.png
Binary files differ
diff --git a/doc/de/member/pic/bild04.png b/doc/de/member/pic/bild04.png
new file mode 100644
index 000000000..b5ad1eaf6
--- /dev/null
+++ b/doc/de/member/pic/bild04.png
Binary files differ
diff --git a/doc/de/member/pic/bild05.png b/doc/de/member/pic/bild05.png
new file mode 100644
index 000000000..b2be326c2
--- /dev/null
+++ b/doc/de/member/pic/bild05.png
Binary files differ
diff --git a/doc/de/member/pic/bild06.png b/doc/de/member/pic/bild06.png
new file mode 100644
index 000000000..f7020dc1e
--- /dev/null
+++ b/doc/de/member/pic/bild06.png
Binary files differ
diff --git a/doc/de/member/pic/bookm01.png b/doc/de/member/pic/bookm01.png
new file mode 100644
index 000000000..97cdcab11
--- /dev/null
+++ b/doc/de/member/pic/bookm01.png
Binary files differ
diff --git a/doc/de/member/pic/bookm02.png b/doc/de/member/pic/bookm02.png
new file mode 100644
index 000000000..b57aa435e
--- /dev/null
+++ b/doc/de/member/pic/bookm02.png
Binary files differ
diff --git a/doc/de/member/pic/bookm03.png b/doc/de/member/pic/bookm03.png
new file mode 100644
index 000000000..69ddbff7a
--- /dev/null
+++ b/doc/de/member/pic/bookm03.png
Binary files differ
diff --git a/doc/de/member/pic/carddav01.png b/doc/de/member/pic/carddav01.png
new file mode 100644
index 000000000..174c57c92
--- /dev/null
+++ b/doc/de/member/pic/carddav01.png
Binary files differ
diff --git a/doc/de/member/pic/carddav02.png b/doc/de/member/pic/carddav02.png
new file mode 100644
index 000000000..af1de5720
--- /dev/null
+++ b/doc/de/member/pic/carddav02.png
Binary files differ
diff --git a/doc/de/member/pic/carddav03.png b/doc/de/member/pic/carddav03.png
new file mode 100644
index 000000000..dbbd0028a
--- /dev/null
+++ b/doc/de/member/pic/carddav03.png
Binary files differ
diff --git a/doc/de/member/pic/carddav04.png b/doc/de/member/pic/carddav04.png
new file mode 100644
index 000000000..5ce71e5c9
--- /dev/null
+++ b/doc/de/member/pic/carddav04.png
Binary files differ
diff --git a/doc/de/member/pic/carddav05.png b/doc/de/member/pic/carddav05.png
new file mode 100644
index 000000000..9292cb5e9
--- /dev/null
+++ b/doc/de/member/pic/carddav05.png
Binary files differ
diff --git a/doc/de/member/pic/carddav06.png b/doc/de/member/pic/carddav06.png
new file mode 100644
index 000000000..a329be64b
--- /dev/null
+++ b/doc/de/member/pic/carddav06.png
Binary files differ
diff --git a/doc/de/member/pic/carddav07.png b/doc/de/member/pic/carddav07.png
new file mode 100644
index 000000000..828a9aa71
--- /dev/null
+++ b/doc/de/member/pic/carddav07.png
Binary files differ
diff --git a/doc/de/member/pic/center.png b/doc/de/member/pic/center.png
new file mode 100644
index 000000000..c3ce7b125
--- /dev/null
+++ b/doc/de/member/pic/center.png
Binary files differ
diff --git a/doc/de/member/pic/chat01.png b/doc/de/member/pic/chat01.png
new file mode 100644
index 000000000..1ed934cac
--- /dev/null
+++ b/doc/de/member/pic/chat01.png
Binary files differ
diff --git a/doc/de/member/pic/chat02.png b/doc/de/member/pic/chat02.png
new file mode 100644
index 000000000..070d6d544
--- /dev/null
+++ b/doc/de/member/pic/chat02.png
Binary files differ
diff --git a/doc/de/member/pic/chat03.png b/doc/de/member/pic/chat03.png
new file mode 100644
index 000000000..e92c19272
--- /dev/null
+++ b/doc/de/member/pic/chat03.png
Binary files differ
diff --git a/doc/de/member/pic/code.png b/doc/de/member/pic/code.png
new file mode 100644
index 000000000..2bed8e01a
--- /dev/null
+++ b/doc/de/member/pic/code.png
Binary files differ
diff --git a/doc/de/member/pic/contfilter01.png b/doc/de/member/pic/contfilter01.png
new file mode 100644
index 000000000..e6510c75e
--- /dev/null
+++ b/doc/de/member/pic/contfilter01.png
Binary files differ
diff --git a/doc/de/member/pic/contfilter02.png b/doc/de/member/pic/contfilter02.png
new file mode 100644
index 000000000..84c7726d2
--- /dev/null
+++ b/doc/de/member/pic/contfilter02.png
Binary files differ
diff --git a/doc/de/member/pic/croles1.png b/doc/de/member/pic/croles1.png
new file mode 100644
index 000000000..d1d6d6fed
--- /dev/null
+++ b/doc/de/member/pic/croles1.png
Binary files differ
diff --git a/doc/de/member/pic/croles2.png b/doc/de/member/pic/croles2.png
new file mode 100644
index 000000000..39f5c0d4f
--- /dev/null
+++ b/doc/de/member/pic/croles2.png
Binary files differ
diff --git a/doc/de/member/pic/dateien01.png b/doc/de/member/pic/dateien01.png
new file mode 100644
index 000000000..bd5792ce8
--- /dev/null
+++ b/doc/de/member/pic/dateien01.png
Binary files differ
diff --git a/doc/de/member/pic/dateien02.png b/doc/de/member/pic/dateien02.png
new file mode 100644
index 000000000..1891bbdf5
--- /dev/null
+++ b/doc/de/member/pic/dateien02.png
Binary files differ
diff --git a/doc/de/member/pic/dateien03.png b/doc/de/member/pic/dateien03.png
new file mode 100644
index 000000000..6a9cd8f49
--- /dev/null
+++ b/doc/de/member/pic/dateien03.png
Binary files differ
diff --git a/doc/de/member/pic/einst01.png b/doc/de/member/pic/einst01.png
new file mode 100644
index 000000000..aa3855cc4
--- /dev/null
+++ b/doc/de/member/pic/einst01.png
Binary files differ
diff --git a/doc/de/member/pic/einst02.png b/doc/de/member/pic/einst02.png
new file mode 100644
index 000000000..c2661f3f0
--- /dev/null
+++ b/doc/de/member/pic/einst02.png
Binary files differ
diff --git a/doc/de/member/pic/einst03.png b/doc/de/member/pic/einst03.png
new file mode 100644
index 000000000..2409c006d
--- /dev/null
+++ b/doc/de/member/pic/einst03.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst01.png b/doc/de/member/pic/feateinst01.png
new file mode 100644
index 000000000..133c3543a
--- /dev/null
+++ b/doc/de/member/pic/feateinst01.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst02.png b/doc/de/member/pic/feateinst02.png
new file mode 100644
index 000000000..317d2704d
--- /dev/null
+++ b/doc/de/member/pic/feateinst02.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst03.png b/doc/de/member/pic/feateinst03.png
new file mode 100644
index 000000000..860fd3802
--- /dev/null
+++ b/doc/de/member/pic/feateinst03.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst04.png b/doc/de/member/pic/feateinst04.png
new file mode 100644
index 000000000..ea5235fd8
--- /dev/null
+++ b/doc/de/member/pic/feateinst04.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst05.png b/doc/de/member/pic/feateinst05.png
new file mode 100644
index 000000000..3aacf571a
--- /dev/null
+++ b/doc/de/member/pic/feateinst05.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst06.png b/doc/de/member/pic/feateinst06.png
new file mode 100644
index 000000000..0de2cc05a
--- /dev/null
+++ b/doc/de/member/pic/feateinst06.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst07.png b/doc/de/member/pic/feateinst07.png
new file mode 100644
index 000000000..434c98ffe
--- /dev/null
+++ b/doc/de/member/pic/feateinst07.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst08.png b/doc/de/member/pic/feateinst08.png
new file mode 100644
index 000000000..1dd96e460
--- /dev/null
+++ b/doc/de/member/pic/feateinst08.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst09.png b/doc/de/member/pic/feateinst09.png
new file mode 100644
index 000000000..8babe1599
--- /dev/null
+++ b/doc/de/member/pic/feateinst09.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst10.png b/doc/de/member/pic/feateinst10.png
new file mode 100644
index 000000000..12a87d72a
--- /dev/null
+++ b/doc/de/member/pic/feateinst10.png
Binary files differ
diff --git a/doc/de/member/pic/feateinst11.png b/doc/de/member/pic/feateinst11.png
new file mode 100644
index 000000000..26d1d371f
--- /dev/null
+++ b/doc/de/member/pic/feateinst11.png
Binary files differ
diff --git a/doc/de/member/pic/font.png b/doc/de/member/pic/font.png
new file mode 100644
index 000000000..598448ce7
--- /dev/null
+++ b/doc/de/member/pic/font.png
Binary files differ
diff --git a/doc/de/member/pic/fotos01.png b/doc/de/member/pic/fotos01.png
new file mode 100644
index 000000000..2a617e74a
--- /dev/null
+++ b/doc/de/member/pic/fotos01.png
Binary files differ
diff --git a/doc/de/member/pic/fotos02.png b/doc/de/member/pic/fotos02.png
new file mode 100644
index 000000000..ead6a7b82
--- /dev/null
+++ b/doc/de/member/pic/fotos02.png
Binary files differ
diff --git a/doc/de/member/pic/fotos03.png b/doc/de/member/pic/fotos03.png
new file mode 100644
index 000000000..541ebecb4
--- /dev/null
+++ b/doc/de/member/pic/fotos03.png
Binary files differ
diff --git a/doc/de/member/pic/fotos04.png b/doc/de/member/pic/fotos04.png
new file mode 100644
index 000000000..2f180346c
--- /dev/null
+++ b/doc/de/member/pic/fotos04.png
Binary files differ
diff --git a/doc/de/member/pic/galerie01.png b/doc/de/member/pic/galerie01.png
new file mode 100644
index 000000000..6746cc154
--- /dev/null
+++ b/doc/de/member/pic/galerie01.png
Binary files differ
diff --git a/doc/de/member/pic/hbar.png b/doc/de/member/pic/hbar.png
new file mode 100644
index 000000000..10594f231
--- /dev/null
+++ b/doc/de/member/pic/hbar.png
Binary files differ
diff --git a/doc/de/member/pic/highlited.png b/doc/de/member/pic/highlited.png
new file mode 100644
index 000000000..4dc0224f1
--- /dev/null
+++ b/doc/de/member/pic/highlited.png
Binary files differ
diff --git a/doc/de/member/pic/image.png b/doc/de/member/pic/image.png
new file mode 100644
index 000000000..ecb300cd2
--- /dev/null
+++ b/doc/de/member/pic/image.png
Binary files differ
diff --git a/doc/de/member/pic/inhber01.png b/doc/de/member/pic/inhber01.png
new file mode 100644
index 000000000..03f7f7113
--- /dev/null
+++ b/doc/de/member/pic/inhber01.png
Binary files differ
diff --git a/doc/de/member/pic/inhber02.png b/doc/de/member/pic/inhber02.png
new file mode 100644
index 000000000..d302f8730
--- /dev/null
+++ b/doc/de/member/pic/inhber02.png
Binary files differ
diff --git a/doc/de/member/pic/inhber03.png b/doc/de/member/pic/inhber03.png
new file mode 100644
index 000000000..5910fbb44
--- /dev/null
+++ b/doc/de/member/pic/inhber03.png
Binary files differ
diff --git a/doc/de/member/pic/inhber04.png b/doc/de/member/pic/inhber04.png
new file mode 100644
index 000000000..e77d8e27a
--- /dev/null
+++ b/doc/de/member/pic/inhber04.png
Binary files differ
diff --git a/doc/de/member/pic/inhber05.png b/doc/de/member/pic/inhber05.png
new file mode 100644
index 000000000..a407f2d92
--- /dev/null
+++ b/doc/de/member/pic/inhber05.png
Binary files differ
diff --git a/doc/de/member/pic/inhber06.png b/doc/de/member/pic/inhber06.png
new file mode 100644
index 000000000..9b740ffd1
--- /dev/null
+++ b/doc/de/member/pic/inhber06.png
Binary files differ
diff --git a/doc/de/member/pic/interag01.png b/doc/de/member/pic/interag01.png
new file mode 100644
index 000000000..3a0eeac65
--- /dev/null
+++ b/doc/de/member/pic/interag01.png
Binary files differ
diff --git a/doc/de/member/pic/interag02.png b/doc/de/member/pic/interag02.png
new file mode 100644
index 000000000..94cd80cad
--- /dev/null
+++ b/doc/de/member/pic/interag02.png
Binary files differ
diff --git a/doc/de/member/pic/interag03.png b/doc/de/member/pic/interag03.png
new file mode 100644
index 000000000..c97f4eaab
--- /dev/null
+++ b/doc/de/member/pic/interag03.png
Binary files differ
diff --git a/doc/de/member/pic/kalender01.png b/doc/de/member/pic/kalender01.png
new file mode 100644
index 000000000..8fbd2a75a
--- /dev/null
+++ b/doc/de/member/pic/kalender01.png
Binary files differ
diff --git a/doc/de/member/pic/kalender02.png b/doc/de/member/pic/kalender02.png
new file mode 100644
index 000000000..f09858820
--- /dev/null
+++ b/doc/de/member/pic/kalender02.png
Binary files differ
diff --git a/doc/de/member/pic/kalender03.png b/doc/de/member/pic/kalender03.png
new file mode 100644
index 000000000..88009d2f7
--- /dev/null
+++ b/doc/de/member/pic/kalender03.png
Binary files differ
diff --git a/doc/de/member/pic/kalender04.png b/doc/de/member/pic/kalender04.png
new file mode 100644
index 000000000..1d91f90d9
--- /dev/null
+++ b/doc/de/member/pic/kalender04.png
Binary files differ
diff --git a/doc/de/member/pic/kalender05.png b/doc/de/member/pic/kalender05.png
new file mode 100644
index 000000000..965e43e7c
--- /dev/null
+++ b/doc/de/member/pic/kalender05.png
Binary files differ
diff --git a/doc/de/member/pic/kaneinst01.png b/doc/de/member/pic/kaneinst01.png
new file mode 100644
index 000000000..943cb1bf5
--- /dev/null
+++ b/doc/de/member/pic/kaneinst01.png
Binary files differ
diff --git a/doc/de/member/pic/kaneinst02.png b/doc/de/member/pic/kaneinst02.png
new file mode 100644
index 000000000..5ef0f309f
--- /dev/null
+++ b/doc/de/member/pic/kaneinst02.png
Binary files differ
diff --git a/doc/de/member/pic/kanloe.png b/doc/de/member/pic/kanloe.png
new file mode 100644
index 000000000..0cf340d88
--- /dev/null
+++ b/doc/de/member/pic/kanloe.png
Binary files differ
diff --git a/doc/de/member/pic/klone01.png b/doc/de/member/pic/klone01.png
new file mode 100644
index 000000000..33092c473
--- /dev/null
+++ b/doc/de/member/pic/klone01.png
Binary files differ
diff --git a/doc/de/member/pic/klone02.png b/doc/de/member/pic/klone02.png
new file mode 100644
index 000000000..648f5d431
--- /dev/null
+++ b/doc/de/member/pic/klone02.png
Binary files differ
diff --git a/doc/de/member/pic/klone03.png b/doc/de/member/pic/klone03.png
new file mode 100644
index 000000000..5d05b08cb
--- /dev/null
+++ b/doc/de/member/pic/klone03.png
Binary files differ
diff --git a/doc/de/member/pic/klone04.png b/doc/de/member/pic/klone04.png
new file mode 100644
index 000000000..a4347a608
--- /dev/null
+++ b/doc/de/member/pic/klone04.png
Binary files differ
diff --git a/doc/de/member/pic/klone05.png b/doc/de/member/pic/klone05.png
new file mode 100644
index 000000000..49c08b4ef
--- /dev/null
+++ b/doc/de/member/pic/klone05.png
Binary files differ
diff --git a/doc/de/member/pic/klone06.png b/doc/de/member/pic/klone06.png
new file mode 100644
index 000000000..0d3ad85d7
--- /dev/null
+++ b/doc/de/member/pic/klone06.png
Binary files differ
diff --git a/doc/de/member/pic/kloneinst01.png b/doc/de/member/pic/kloneinst01.png
new file mode 100644
index 000000000..f87a25b00
--- /dev/null
+++ b/doc/de/member/pic/kloneinst01.png
Binary files differ
diff --git a/doc/de/member/pic/kmerkmale.png b/doc/de/member/pic/kmerkmale.png
new file mode 100644
index 000000000..f9d603f5c
--- /dev/null
+++ b/doc/de/member/pic/kmerkmale.png
Binary files differ
diff --git a/doc/de/member/pic/kommentieren.png b/doc/de/member/pic/kommentieren.png
new file mode 100644
index 000000000..1f8bb790e
--- /dev/null
+++ b/doc/de/member/pic/kommentieren.png
Binary files differ
diff --git a/doc/de/member/pic/ktoeinst01.png b/doc/de/member/pic/ktoeinst01.png
new file mode 100644
index 000000000..4081e7b58
--- /dev/null
+++ b/doc/de/member/pic/ktoeinst01.png
Binary files differ
diff --git a/doc/de/member/pic/ktoloe.png b/doc/de/member/pic/ktoloe.png
new file mode 100644
index 000000000..51e82e7b7
--- /dev/null
+++ b/doc/de/member/pic/ktoloe.png
Binary files differ
diff --git a/doc/de/member/pic/mauth.png b/doc/de/member/pic/mauth.png
new file mode 100644
index 000000000..5e624124b
--- /dev/null
+++ b/doc/de/member/pic/mauth.png
Binary files differ
diff --git a/doc/de/member/pic/nsfw01.png b/doc/de/member/pic/nsfw01.png
new file mode 100644
index 000000000..17509786f
--- /dev/null
+++ b/doc/de/member/pic/nsfw01.png
Binary files differ
diff --git a/doc/de/member/pic/nsfw02.png b/doc/de/member/pic/nsfw02.png
new file mode 100644
index 000000000..9b5285a27
--- /dev/null
+++ b/doc/de/member/pic/nsfw02.png
Binary files differ
diff --git a/doc/de/member/pic/nsfw03.png b/doc/de/member/pic/nsfw03.png
new file mode 100644
index 000000000..6777a5b5a
--- /dev/null
+++ b/doc/de/member/pic/nsfw03.png
Binary files differ
diff --git a/doc/de/member/pic/nsfw04.png b/doc/de/member/pic/nsfw04.png
new file mode 100644
index 000000000..578776870
--- /dev/null
+++ b/doc/de/member/pic/nsfw04.png
Binary files differ
diff --git a/doc/de/member/pic/ordner01.png b/doc/de/member/pic/ordner01.png
new file mode 100644
index 000000000..3c5c5d181
--- /dev/null
+++ b/doc/de/member/pic/ordner01.png
Binary files differ
diff --git a/doc/de/member/pic/ordner02.png b/doc/de/member/pic/ordner02.png
new file mode 100644
index 000000000..d7340c2a1
--- /dev/null
+++ b/doc/de/member/pic/ordner02.png
Binary files differ
diff --git a/doc/de/member/pic/ordner03.png b/doc/de/member/pic/ordner03.png
new file mode 100644
index 000000000..81577e40a
--- /dev/null
+++ b/doc/de/member/pic/ordner03.png
Binary files differ
diff --git a/doc/de/member/pic/ordner04.png b/doc/de/member/pic/ordner04.png
new file mode 100644
index 000000000..f20440b85
--- /dev/null
+++ b/doc/de/member/pic/ordner04.png
Binary files differ
diff --git a/doc/de/member/pic/pgroups01.png b/doc/de/member/pic/pgroups01.png
new file mode 100644
index 000000000..2c2654f40
--- /dev/null
+++ b/doc/de/member/pic/pgroups01.png
Binary files differ
diff --git a/doc/de/member/pic/pgroups02.png b/doc/de/member/pic/pgroups02.png
new file mode 100644
index 000000000..0a35c3ded
--- /dev/null
+++ b/doc/de/member/pic/pgroups02.png
Binary files differ
diff --git a/doc/de/member/pic/pgroups03.png b/doc/de/member/pic/pgroups03.png
new file mode 100644
index 000000000..fba19a8cc
--- /dev/null
+++ b/doc/de/member/pic/pgroups03.png
Binary files differ
diff --git a/doc/de/member/pic/priveinst01.png b/doc/de/member/pic/priveinst01.png
new file mode 100644
index 000000000..bdf645d07
--- /dev/null
+++ b/doc/de/member/pic/priveinst01.png
Binary files differ
diff --git a/doc/de/member/pic/qrcode.png b/doc/de/member/pic/qrcode.png
new file mode 100644
index 000000000..0db383feb
--- /dev/null
+++ b/doc/de/member/pic/qrcode.png
Binary files differ
diff --git a/doc/de/member/pic/quellcode.png b/doc/de/member/pic/quellcode.png
new file mode 100644
index 000000000..d21df87f5
--- /dev/null
+++ b/doc/de/member/pic/quellcode.png
Binary files differ
diff --git a/doc/de/member/pic/quote.png b/doc/de/member/pic/quote.png
new file mode 100644
index 000000000..cef0df619
--- /dev/null
+++ b/doc/de/member/pic/quote.png
Binary files differ
diff --git a/doc/de/member/pic/red.png b/doc/de/member/pic/red.png
new file mode 100644
index 000000000..227f8ccda
--- /dev/null
+++ b/doc/de/member/pic/red.png
Binary files differ
diff --git a/doc/de/member/pic/size.png b/doc/de/member/pic/size.png
new file mode 100644
index 000000000..f87ab74cf
--- /dev/null
+++ b/doc/de/member/pic/size.png
Binary files differ
diff --git a/doc/de/member/pic/statusfilter.png b/doc/de/member/pic/statusfilter.png
new file mode 100644
index 000000000..3e5225535
--- /dev/null
+++ b/doc/de/member/pic/statusfilter.png
Binary files differ
diff --git a/doc/de/member/pic/stern.png b/doc/de/member/pic/stern.png
new file mode 100644
index 000000000..f289ba55d
--- /dev/null
+++ b/doc/de/member/pic/stern.png
Binary files differ
diff --git a/doc/de/member/pic/streinst01.png b/doc/de/member/pic/streinst01.png
new file mode 100644
index 000000000..79499a782
--- /dev/null
+++ b/doc/de/member/pic/streinst01.png
Binary files differ
diff --git a/doc/de/member/pic/suche01.png b/doc/de/member/pic/suche01.png
new file mode 100644
index 000000000..5c35a1e0e
--- /dev/null
+++ b/doc/de/member/pic/suche01.png
Binary files differ
diff --git a/doc/de/member/pic/suche02.png b/doc/de/member/pic/suche02.png
new file mode 100644
index 000000000..5ec69f0f0
--- /dev/null
+++ b/doc/de/member/pic/suche02.png
Binary files differ
diff --git a/doc/de/member/pic/suche03.png b/doc/de/member/pic/suche03.png
new file mode 100644
index 000000000..45373bddf
--- /dev/null
+++ b/doc/de/member/pic/suche03.png
Binary files differ
diff --git a/doc/de/member/pic/table1.png b/doc/de/member/pic/table1.png
new file mode 100644
index 000000000..99a4fb9a0
--- /dev/null
+++ b/doc/de/member/pic/table1.png
Binary files differ
diff --git a/doc/de/member/pic/table2.png b/doc/de/member/pic/table2.png
new file mode 100644
index 000000000..ed241a6d6
--- /dev/null
+++ b/doc/de/member/pic/table2.png
Binary files differ
diff --git a/doc/de/member/pic/table3.png b/doc/de/member/pic/table3.png
new file mode 100644
index 000000000..9b7acceb3
--- /dev/null
+++ b/doc/de/member/pic/table3.png
Binary files differ
diff --git a/doc/de/member/pic/teilen.png b/doc/de/member/pic/teilen.png
new file mode 100644
index 000000000..1481c9ccf
--- /dev/null
+++ b/doc/de/member/pic/teilen.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen01.png b/doc/de/member/pic/verbindungen01.png
new file mode 100644
index 000000000..519370df2
--- /dev/null
+++ b/doc/de/member/pic/verbindungen01.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen02.png b/doc/de/member/pic/verbindungen02.png
new file mode 100644
index 000000000..37333e87f
--- /dev/null
+++ b/doc/de/member/pic/verbindungen02.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen03.png b/doc/de/member/pic/verbindungen03.png
new file mode 100644
index 000000000..69e32a688
--- /dev/null
+++ b/doc/de/member/pic/verbindungen03.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen04.png b/doc/de/member/pic/verbindungen04.png
new file mode 100644
index 000000000..0b032ef3d
--- /dev/null
+++ b/doc/de/member/pic/verbindungen04.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen05.png b/doc/de/member/pic/verbindungen05.png
new file mode 100644
index 000000000..0a48cd425
--- /dev/null
+++ b/doc/de/member/pic/verbindungen05.png
Binary files differ
diff --git a/doc/de/member/pic/verbindungen06.png b/doc/de/member/pic/verbindungen06.png
new file mode 100644
index 000000000..3ac1ba628
--- /dev/null
+++ b/doc/de/member/pic/verbindungen06.png
Binary files differ
diff --git a/doc/de/member/pic/verzeichnis.png b/doc/de/member/pic/verzeichnis.png
new file mode 100644
index 000000000..397d42efb
--- /dev/null
+++ b/doc/de/member/pic/verzeichnis.png
Binary files differ
diff --git a/doc/de/member/pic/video_poster.png b/doc/de/member/pic/video_poster.png
new file mode 100644
index 000000000..f2151abd1
--- /dev/null
+++ b/doc/de/member/pic/video_poster.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten01.png b/doc/de/member/pic/webseiten01.png
new file mode 100644
index 000000000..9ab33fe6e
--- /dev/null
+++ b/doc/de/member/pic/webseiten01.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten02.png b/doc/de/member/pic/webseiten02.png
new file mode 100644
index 000000000..aab2db3ce
--- /dev/null
+++ b/doc/de/member/pic/webseiten02.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten03.png b/doc/de/member/pic/webseiten03.png
new file mode 100644
index 000000000..424aa702e
--- /dev/null
+++ b/doc/de/member/pic/webseiten03.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten04.png b/doc/de/member/pic/webseiten04.png
new file mode 100644
index 000000000..c0c34bc5c
--- /dev/null
+++ b/doc/de/member/pic/webseiten04.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten05.png b/doc/de/member/pic/webseiten05.png
new file mode 100644
index 000000000..7f16b1e35
--- /dev/null
+++ b/doc/de/member/pic/webseiten05.png
Binary files differ
diff --git a/doc/de/member/pic/webseiten06.png b/doc/de/member/pic/webseiten06.png
new file mode 100644
index 000000000..dd22b6ed7
--- /dev/null
+++ b/doc/de/member/pic/webseiten06.png
Binary files differ
diff --git a/doc/de/member/pic/wiki01.png b/doc/de/member/pic/wiki01.png
new file mode 100644
index 000000000..bd59ed0cf
--- /dev/null
+++ b/doc/de/member/pic/wiki01.png
Binary files differ
diff --git a/doc/de/member/pic/wiki02.png b/doc/de/member/pic/wiki02.png
new file mode 100644
index 000000000..f55a66706
--- /dev/null
+++ b/doc/de/member/pic/wiki02.png
Binary files differ
diff --git a/doc/de/member/pic/wiki03.png b/doc/de/member/pic/wiki03.png
new file mode 100644
index 000000000..97dfad139
--- /dev/null
+++ b/doc/de/member/pic/wiki03.png
Binary files differ
diff --git a/doc/de/member/pic/wiki04.png b/doc/de/member/pic/wiki04.png
new file mode 100644
index 000000000..a48aec8b4
--- /dev/null
+++ b/doc/de/member/pic/wiki04.png
Binary files differ
diff --git a/doc/de/member/posting.md b/doc/de/member/posting.md
new file mode 100644
index 000000000..81a6ed99b
--- /dev/null
+++ b/doc/de/member/posting.md
@@ -0,0 +1,17 @@
+### Posten
+
+Möchten Sie einen Beitrag verfassen und teilen (veröffentlichen, wobei der Kreis der Empfänger bzw. derer, welche den Beitrag sehen können, eingeschränkt sein kann), so tun Sie dies in der Regel über das Feld "Teilen", das sich über dem Stream befindet. Klicken Sie in dieses Feld, öffnet sich der Beitragseditor.
+
+Zu oberst gibt es das Feld für den Beitragstitel (optional), darunter befindet sich das Feld für die Zusammenfassung (Summary, ebenfalls optional nutzbar), sofern der Administrator Ihres Hubs diese Funktion erlaubt. Die Zusammenfassung kann auch für den Zweck einer Inhaltswarnung verwendet werden. Unter dem Feld für die Zusammenfassung befindet sich (sofern vom Admin aktiviert) ein Feld für Kategorien.
+
+Darunter folgt das Textfeld, in welchem Sie den Beitragsinhalt erstellen können. Sie können, je nach Einstellung des Hubs, einfachen Text, Markdown, bbCode oder HTML für Formatierungen des Textes nutzen.
+
+Am unteren Rand des Beitragseditors befinden sich einige Schaltflächen zur einfacheren Formatierung des Inhalts und für das einfügen von Elementen und das Nutzen zusätzlicher Funktionen: fett, kursiv, unterstrichen, Zitat, Code, Datei anhängen/hochladen, Link einfügen, Bild einfügen (ein unter Dateien bereits existierendes Bild), Standort einfügen, Verfallsdatum für den Beitrag festlegen, Veröffentlichungsdatum festlegen, Text verschlüsseln, Abstimmung (Umfrage) ein/aus, Kommentare deaktivieren. Rechts daneben befindet sich ein weiterer Block mit Schaltflächen. Hier kann man sich eine Vorschau des Beitrags anzeigen lassen, festlegen, ob der Beitrag ggf. noch bei anderen Netzwerken veröffentlicht wird, die Berechtigungs-Einstellungen (wer kann das Posting sehen) vornehmen und mit dem Button "Teilen" schließlich veröffentlichen.
+
+![teilen](/help/de/member/pic/teilen.png)
+
+Man erreicht den Beitrags-Editor auch, wenn man den entsprechenden Menüpunkt im App-Menü (oben rechts ⋮) auswählt oder das entsprechende Icon in der Navigationsleiste (sofern man die App "Beitrag schreiben" angepinnt hat).
+
+#include doc/de/member/commenting.md;
+#include doc/de/member/insert_images.md;
+#include doc/de/member/bbcode.md;
diff --git a/doc/de/member/privacy_groups.md b/doc/de/member/privacy_groups.md
new file mode 100644
index 000000000..4a398b8e9
--- /dev/null
+++ b/doc/de/member/privacy_groups.md
@@ -0,0 +1,23 @@
+### Privacy Gruppen
+
+Die App "Privacy Gruppen" erlaubt Ihnen das Anlegen von Gruppen, denen Sie Kontakte zuordnen können. Sie dienen einerseits dem Filtern des Streams (so kann man sich nur Beiträge von Nutzern anzeigen lassen, welche sich in einer Privacy Gruppe befinden) und andererseits erlauben sie es, in Bezug auf [Berechtigungen](/help/de/member/permissions_contact_roles.md), bestimmten Gruppen Rechte an Inhalten einzuräumen.
+
+Die erste Funktion ist einfach zu verstehen. Hat man Kontakte (ein Kontakt kann durchaus auch in mehreren Gruppen vorhanden sein) in einer Gruppe und wählt man in der linken Seitenleiste bei der Stream-Ansicht eine bestimmte Gruppe aus, so werden nur Beiträge von Kontakten angezeigt, die sich in der Gruppe befinden. Diese Funktion fungiert also als Stream-Filter.
+
+Die zweite Funktion ist auch einfach zu begreifen, aber eher ungewohnt für viele Fediverse-Nutzer, da es sie in dieser Form nur bei Hubzilla und verwandten Diensten (Streams, Friendica etc.) gibt. Wie die Bezeichnung "Privacy Gruppen" nahelegt, geht es hier auch um eine beschränkte Kommunikation. Wählt man beim Verfassen eines Beitrags als Berechtigung eine Gruppe aus, so wird das Posting nur an die Kontakte, die in dieser Gruppe enthalten sind, verteilt und nur diese können es sehen. Es ist den Empfängern (Gruppenmitglieder) auch nicht möglich, ein solches Posting öffentlich zu teilen. Damit kann eine geschlossene Gruppenkommunikation realisiert werden.
+
+Ruft man die App auf, werden in der linken Seitenleiste bereits vorhandene Gruppen angezeigt und in der Hauptansicht das Eingabeformular zum Erstellen einer neuen Gruppe.
+
+![Privacy Gruppen 01](/help/de/member/pic/pgroups01.png)
+
+Wählt man in der Seitenleiste eine der Gruppen auf, so kann man diese bearbeiten.
+
+![Privacy Gruppen 02](/help/de/member/pic/pgroups02.png)
+
+Hier kann auch die Mitgliedschaft für Kontakte festgelegt werden. Ein Klick auf einen Eintrag wechselt die Mitgliedschaft zwischen "Nicht in der Gruppe" und "Gruppenmitgliedschaft". So kann man Mitglieder aus einer Gruppe entfernen oder Nutzer als Gruppenmitglieder hinzufügen.
+
+Das Hinzufügen eines Kontakt zu einer Gruppe kann auch in der App "Verbindungen" mit dem Kontakt-Tool erfolgen:
+
+![Privacy Gruppen 03](/help/de/member/pic/pgroups03.png)
+
+Um eine neue Gruppe hinzuzufügen, klickt man in der Seitenleiste auf den Eintrag "+ Neue Gruppe hinzufügen".
diff --git a/doc/de/member/privacy_settings.md b/doc/de/member/privacy_settings.md
new file mode 100644
index 000000000..ad5aed9c7
--- /dev/null
+++ b/doc/de/member/privacy_settings.md
@@ -0,0 +1,6 @@
+#### Privacy Einstellungen
+
+In den Privacy-Einstellungen können Sie festlegen, ob Ihre eigenen Beiträge durch Suchmaschinen indiziert werden dürfen, ob Sie Kontaktanfragen automatisch (ohne manuelle Genehmigung) akzeptieren, ob alle Nachrichten in denen Sie erwähnt werden, automatisch akzeptiert werden, ob Kommentare von Nutzern, die nicht zu Ihren Kontakten gehören, zur Moderation (freigeben / verwerfen) gestellt oder gelöscht werden und ob Sie OCAP-Zugriff erlauben.
+
+![Privacy-Einstellungen 01](/help/de/member/pic/priveinst01.png)
+
diff --git a/doc/de/member/profiles.md b/doc/de/member/profiles.md
new file mode 100644
index 000000000..0fbcebe7b
--- /dev/null
+++ b/doc/de/member/profiles.md
@@ -0,0 +1,23 @@
+### Profile
+
+Hubzilla hat unbegrenzte Profile. Sie können verschiedene Profile verwenden, um verschiedene „Seiten von sich selbst“ für verschiedene Zielgruppen zu zeigen. Das ist etwas anderes als verschiedene Kanäle zu haben. Verschiedene Kanäle ermöglichen völlig unterschiedliche Informationen. Sie können einen Kanal für sich selbst, einen Kanal für Ihr Sportteam, einen Kanal für Ihre Website oder etwas anderes haben. Ein Profil ermöglicht fein abgestufte „Seiten“ eines jeden Kanals. Verschiedene Profile könnte man mit verschiedenen Visitenkarten einer Person vergleichen. Je nach Verwendungszweck werden auf der jeweiligen Visitenkarte unterschiedliche Informationen gegeben. Ihr öffentliches Standardprofil könnte zum Beispiel lauten: „Hallo, ich bin Fred und ich lache gerne“. Ihren engen Freunden können Sie ein Profil zeigen, auf dem steht „und ich werfe auch gerne Zwerge“.
+
+Sie haben immer ein Profil, das als Ihr „Standard-“ oder „öffentliches“ Profil bezeichnet wird. Dieses Profil ist immer für die Allgemeinheit zugänglich und kann nicht versteckt werden (es kann seltene Ausnahmen auf privat betriebenen oder nicht angeschlossenen Sites geben). Sie können und sollten die Informationen, die Sie in Ihrem öffentlichen Profil zur Verfügung stellen, einschränken.
+
+Wenn Sie möchten, dass Ihre Freunde Sie finden können, ist es hilfreich, wenn Sie die folgenden Informationen in Ihrem öffentlichen Profil angeben...
+
+- Ihr richtiger Name oder zumindest ein Spitzname, den jeder kennt
+- Ein Foto von Ihnen
+- Ihr Standort auf der Erde, zumindest auf Länderebene.
+
+Wenn Sie außerdem Leute treffen möchten, die allgemeine Interessen mit Ihnen teilen, nehmen Sie sich bitte einen Moment Zeit und fügen Sie Ihrem Profil einige „Schlüsselwörter“ hinzu. Zum Beispiel „Musik, Linux, Fotografie“ oder ähnliches. Sie können so viele Stichwörter hinzufügen, wie Sie möchten.
+
+Wählen Sie „Profile bearbeiten“ aus dem Menü Ihrer Hubzilla-Site. Sie können ein bestehendes Profil bearbeiten, das Profilfoto ändern, Dinge zu einem Profil hinzufügen oder ein neues Profil erstellen. Sie können auch einen „Klon“ eines bestehenden Profils erstellen, wenn Sie nur ein paar Dinge ändern möchten, aber nicht alle Informationen erneut eingeben wollen. Klicken Sie dazu auf das Profil, das Sie klonen möchten, und wählen Sie dort „Dieses Profil klonen“.
+
+In der Liste Ihrer Profile können Sie auch die Kontakte auswählen, die ein bestimmtes Profil sehen können. Klicken Sie einfach auf „Sichtbarkeit bearbeiten“ neben dem betreffenden Profil (nur verfügbar für Profile, die nicht Ihr Standardprofil sind) und klicken Sie dann auf bestimmte Verbindungen, um sie zu der Gruppe von Personen, die dieses Profil sehen können, hinzuzufügen oder sie daraus zu entfernen.
+
+Sobald ein Profil ausgewählt wurde, sieht die Person, die Ihr Profil anschaut, das private Profil, das Sie zugewiesen haben. Wenn die Person nicht authentifiziert ist, wird sie Ihr öffentliches Profil sehen.
+
+Es gibt eine Einstellung, die es Ihnen ermöglicht, Ihr Profil in einem Verzeichnis zu veröffentlichen und sicherzustellen, dass es von anderen gefunden werden kann. Sie können diese Einstellung auf der Seite „Einstellungen“ ändern.
+
+Wenn Sie nicht möchten, dass Sie von anderen gefunden werden, ohne ihnen Ihre Kanal-Adresse mitzuteilen, können Sie Ihr Profil unveröffentlicht lassen. \ No newline at end of file
diff --git a/doc/de/member/protection_of_privacy.md b/doc/de/member/protection_of_privacy.md
new file mode 100644
index 000000000..e28b61da5
--- /dev/null
+++ b/doc/de/member/protection_of_privacy.md
@@ -0,0 +1,67 @@
+### Tipps zum Schutz der Privatsphäre
+
+Wenn Sie großen Wert auf Ihre Privatsphäre legen und trotzdem am Fediverse teilnehmen möchten, ist es erforderlich, dass Sie sich vor und während der Erstellung eines persönlichen Kanals konkrete Gedanken darüber machen, was Sie von sich preisgeben wollen. Das ist bei jedem Fediverse-Dienst so. Bei Hubzilla kommt aber ein anderer, wesentlicher Aspekt dazu. Sie müssen sich nicht nur die Frage über das "Was?" stellen, sondern auch noch über das "Wem?" und das "Welche?".
+
+Mit Hubzilla legen Sie also nicht nur fest, was Sie von sich preisgeben, sondern auch, wem Sie es erlauben, die Informationen und Inhalte zu sehen. Und wem Sie welche Interaktion erlauben.
+
+Der Vorteil ist, dass Sie nicht auf einen "Regelsatz" angewiesen sind, sondern verschiedene Regeln für verschiedene Anwendungszwecke und verschiedenen Kontakte festlegen können.
+
+Ein typischer Anwendungsfall wäre, dass Sie am Fediverse ganz normal so teilnehmen möchten,wie man es von anderen Sozialen Netzwerken kennt.
+
+Beim Erstellen eines Kanals müssen Sie die erste relevante Entscheidung treffen: **die Kanalrolle**.
+
+Hier haben Sie die Wahl zwischen "Öffentlich", "Persönlich", "Community Forum" und "Benutzerdefiniert".
+
+Abgesehen vom "Community Forum", das für andere Anwendungszwecke gedacht ist, haben Sie also die Wahl zwischen drei Rollen.
+
+Bei der Rolle "Öffentlich" erlauben Sie, dass andere
+
+1. Ihren Kanal-Stream (also die Beiträge, welche Sie öffentlich teilen) und allgemein Ihre Beiträge sehen können,
+2. Ihr Standard-Profil sehen können,
+3. Ihre Verbindungen sehen können,
+4. Ihre Datei- und Bilderordner sehen können,
+5. die Webseiten Ihres Kanals sehen können,
+6. die Wiki-Seiten Ihres Kanals sehen können,
+7. Ihre Beiträge kommentieren, liken oder disliken dürfen,
+8. Ihnen Direktnachrichten schicken dürfen,
+9. Ihre Profile und Profilinhalte liken oder disliken dürfen und
+10. mit Ihnen chatten dürfen.
+
+Diese rollenbasierten Regeln bilden recht gut die "normale" Nutzung eines Sozialen Netzwerks ab.
+
+Die Rolle "Persönlich" ist ähnlich und verweigert nur einige Erlaubnisse der Rolle "Öffentlich". Sie erlaubt, dass andere
+
+1. Ihren Kanal-Stream (also die Beiträge, welche Sie öffentlich teilen) und allgemein Ihre Beiträge sehen können,
+2. Ihr Standard-Profil sehen können,
+3. ./.
+4. Ihre Datei- und Bilderordner sehen können,
+5. die Webseiten Ihres Kanals sehen können,
+6. die Wiki-Seiten Ihres Kanals sehen können,
+7. ./.
+8. ./.
+9. ./.
+10. ./.
+
+Mit diesem Profil ist die Interaktion durch andere Nutzer eingeschränkt, denn diese dürfen Ihre Beiträge nicht kommentieren, liken, disliken (letzteres auch nicht in Bezug auf Ihre Profile/Profilinhalte). Sie dürfen Ihnen auch keine Direktnachrichten schicken oder mit Ihnen chatten.
+
+Die Rolle "Benutzerdefiniert" erlaubt es Ihnen, sämtliche Berechtigungen einzeln festzulegen. Hier ist Vorsicht geboten, weil unpassende Regeln einen Kanal halbwegs "unbenutzbar" machen können.
+
+Für die angedachte Nutzung als "typischer Social Network Account" empfiehlt sich die Wahl der Rolle "Öffentlich" oder "Persönlich".
+
+Wenn Sie sich nun eher für die Rolle "persönlich" entscheiden würden, aber trotzdem gerne weitergehende Interaktion mit bestimmten Nutzern (Freund, Familie, Kollegen, o.ä.) erlauben würden, müssen Sie deshalb nicht auf die Rolle "Öffentlich" zurückgreifen.
+
+Hubzilla funktioniert bei den Berechtigungen mit Whitelists (Erlaubnislisten). Die Kanalrolle legt also die grundsätzlichen Berechtigungen fest. Diese können Sie nachträglich (abgesehen von der Festlegung von Zugriffsrechten im speziellen Einzelfall eines Inhalts) nicht mit anderen Mechanismen wieder entziehen.
+
+Sie können aber mittels verschiedener **Kontaktrollen** die Whitelist um verschiedene Berechtigungen erweitern. So könnten Sie z.B. eine Kontaktrolle "Familie" erstellen, in welcher - zusätzlich zu den Berechtigungen, welche die Rolle "Persönlich" einräumt - weitere Berechtigungen (z.B. das Kommentieren, Linke, Disliken und das Senden von Direktnachrichten) gewährt werden. Wenn Sie nun in diesem Beispiel Ihren Kontakten, die Sie als "Familie" definieren, diese Kontaktrolle zuordnen, können Ihre Familienmitglieder - im Gegensatz zu allen anderen - Kommentare verfassen, den Daumen nach oben oder unten zeigen lassen und nicht-öffentlich (Direktnachricht) mit Ihnen kommunizieren.
+
+Sie können beliebig viele Kontaktrollen für die verschiedensten Zwecke und Kontakte anlegen und dort weitere Berechtigungen, zusätzlich zu denen der Kanalrolle, erteilen. Aber(!): Sie können keine Berechtigung aus der Kanalrolle dort entziehen.
+
+Es empfiehlt sich also, bei der Kanalrolle nicht zu großzügig zu sein und diese - je nach dem Nutzungszweck des Kanals - entsprechend auszuwählen. Mit der Auswahl "Benutzerdefiniert" könnten Sie z.B. eine noch restriktivere Kanalrolle als die "Persönlich" festlegen und dann für bestimmte Nutzer weitere Berechtigungen mit den Kontaktrollen festlegen (empfehlenswert erst, wenn Sie mit dem Berechtigungssystem wirklich vertraut sind).
+
+Ein weiterer Aspekt bezüglich der Privatsphäre sind die **Profil**-Informationen. Für eine "typische" Nutzung als Social Network Account, sollten im Profil einige Informationen preisgegeben werden. Ansonsten werden andere Nutzer nicht auf die Idee kommen, sich mit Ihnen zu verbinden. Oder sie wollen eine Verbindung zu einem anderen Nutzer herstellen, doch dieser lehnt dies ab, weil er keinerlei Informationen zu Ihnen hat (es sei denn, er kennt Sie und ihren Kanalnamen). Einige Informationen sollten dort also hinein.
+
+So viele wie nötig, so wenige wie möglich.
+
+Genau nach diesem Prinzip sollten sie Ihr Standard-Profil, über welches jeder Kanal verfügt, mit Informationen bestücken.
+
+Hubzilla erlaubt es aber, mehrere Profile anzulegen. In solchen Profilen können Sie dann weitergehende Informationen angeben, die für bestimmte Verbindungen interessant sein könnten. Sie haben dann die Möglichkeit, solche speziellen Profile für bestimmte Verbindungen freizugeben. Die Informationen tauchen also nicht für jeden sichtbar und öffentlich auf, sondern sie sind nur für die ausgewählten Nutzer verfügbar. \ No newline at end of file
diff --git a/doc/de/member/public_stream.md b/doc/de/member/public_stream.md
new file mode 100644
index 000000000..1b9b7774f
--- /dev/null
+++ b/doc/de/member/public_stream.md
@@ -0,0 +1,12 @@
+### Öffentlicher Beitrags-Stream
+
+Sofern der Administrator eines Hub den öffentlichen Beitrags-Stream aktiviert hat, können Sie als Nutzer die App "Öffentlicher Beitrags-Stream" installieren und aktivieren.
+
+Während im "normalen" Stream alle Beiträge und Aktivitäten von Ihnen selbst und all Ihren Verbindungen erscheinen, ist der öffentliche Beitrags-Stream umfassender.
+
+Es gibt zwei Möglichkeiten:
+
+1. Hat der Administrator den öffentlichen Beitrags-Stream auf den eigenen Hub beschränkt, so erscheinen dort sämtliche öffentlichen Beiträge und Aktivitäten aus den Streams aller Nutzer, die einen Account auf eben diesem Hub haben.
+2. Hat der Administrator den öffentlichen Beitrags-Stream nicht auf den eigenen Hub beschränkt, so werden alle öffentlichen Beiträge aller Kanäle des eigene Hub, öffentliche Inhalte, die beim eigenen Hub eintreffen (also .B. Kommentare auf Beiträge der Nutzer des Hubs, die von anderen Instanzen stammen), sowie zufällig eingesammelte Inhalte von Kanälen, die dem eigenen Hub bekannt sind (also alle Kontakte aller Kanäle auf dem eigenen Hub).
+
+Der öffentliche Beitrags-Stream ist nicht unmoderiert. Der Administrator eines Hub hat die Möglichkeit, Beiträge aus dem öffentlichen Beitrags-Stream zu löschen ("Admin delete"). Diese Beiträge sind dann tatsächlich aus dem öffentlichen Beitragsstream des eigenen Hub gelöscht. Sie erscheinen auch nicht in der öffentlichen Stream-Ansicht anderer Kanäle des Hub. \ No newline at end of file
diff --git a/doc/de/member/registration.md b/doc/de/member/registration.md
new file mode 100644
index 000000000..96cbdc574
--- /dev/null
+++ b/doc/de/member/registration.md
@@ -0,0 +1,19 @@
+### Anmeldung / Registrierung
+
+Nicht alle Hubzilla-Sites erlauben eine offene Registrierung. Wenn die Registrierung erlaubt ist, sehen Sie einen Link „Registrieren“ direkt neben der Anmeldeaufforderung auf der Homepage der Site. Wenn Sie diesem Link folgen, gelangen Sie auf die Registrierungsseite der Site. Auf einigen Sites werden Sie möglicherweise zu einer anderen Site weitergeleitet, auf der Hubs aufgeführt sind, bei denen eine Registrierung möglich ist. Da alle Hubzilla-Sites miteinander verbunden sind, spielt es keine Rolle, wo sich Ihr Account befindet.
+
+**Ihre E-Mail-Adresse**
+
+Bitte geben Sie eine gültige E-Mail-Adresse an. Ihre E-Mail-Adresse wird niemals veröffentlicht. Diese Adresse wird verwendet, um Ihr Konto zu aktivieren, um (optional) E-Mail-Benachrichtigungen für eingehende Nachrichten oder Artikel zu versenden *und um verlorene Passwörter wiederherzustellen*.
+
+**Kennwort**
+
+Geben Sie ein Passwort Ihrer Wahl ein und wiederholen Sie es im zweiten Feld, um sicherzustellen, dass es richtig eingegeben wurde. Da Hubzilla eine dezentrale Identität bietet, können Sie sich mit Ihrem Konto bei vielen anderen Websites anmelden.
+
+**Nutzungsbedingungen**
+
+Klicken Sie auf den Link, um die <u>Nutzungsbedingungen</u> der Website zu lesen. Wenn Sie sie gelesen haben, bestätigen Sie sie durch Anklicken des Kästchens im Anmeldeformular.
+
+**Anmelden**
+
+Sobald Sie die erforderlichen Angaben gemacht haben, klicken Sie auf die Schaltfläche „Registrieren“. Bei einigen Websites ist möglicherweise die Zustimmung des Administrators erforderlich, bevor die Registrierung bearbeitet werden kann; in diesem Fall werden Sie benachrichtigt. Bitte achten Sie auf Ihre E-Mail (einschließlich Spam-Ordner), um die Genehmigung für Ihre Registrierung zu erhalten. \ No newline at end of file
diff --git a/doc/de/member/repeat.md b/doc/de/member/repeat.md
new file mode 100644
index 000000000..e354f8059
--- /dev/null
+++ b/doc/de/member/repeat.md
@@ -0,0 +1,5 @@
+#### Repeat
+
+Mit dem Repeaten (Wiederholen) von Beiträgen, wird der Beitrag an die eigenen Verbindungen verteilt. Kommentare landen (im Gegensatz zu [geteilten Beiträgen](/help/de/member/share.md)) beim Original-Beitrag.
+
+Dieses Verhalten entspricht dem "Boosten", wie man es z.B. von Mastodon oder anderen Fediverse-Diensten kennt. \ No newline at end of file
diff --git a/doc/de/member/save_to_folder.md b/doc/de/member/save_to_folder.md
new file mode 100644
index 000000000..483959bd2
--- /dev/null
+++ b/doc/de/member/save_to_folder.md
@@ -0,0 +1,25 @@
+#### In Ordner speichern
+
+Möchte man sich Postings für später merken, so kann man das durch [Markierung](/help/de/member/toggle_star_status.md) tun. Bei einer großen Zahl solchermaßen gemerkter Postings wird es aber leicht unübersichtlich.
+
+Praktischer ist es, solche Beiträge in verschiedenen "Ordner" zu speichern. Das sind kategorisierte Markierungen.
+
+Wählt man "In Ordner speichern", so öffnet sich ein Dialogfenster zur Auswahl des Ordners.
+
+![Ordnername](/help/de/member/pic/ordner01.png)
+
+In dem Textfeld kann man einen Ordnernamen vergeben. Sind schon Ordner vorhanden, sorgt ein zweifacher Kick in das Textfeld dafür, dass eine Auswahlliste der vorhandenen Ordner angezeigt wird, um einen davon auszuwählen.
+
+![Ordnerauswahl](/help/de/member/pic/ordner02.png)
+
+Hat man einen Beitrag in einem Ordner abgelegt, erkennt man dies an Ordersymbol (inklusive Ordnernamen) unten links am Beitrag.
+
+![Ordnerauswahl](/help/de/member/pic/ordner03.png)
+
+Dieses Symbol dient auch dazu, einen Beitrag wieder aus eienm Ordner zu entfernen, indem man auf das "X" im Symbol klickt.
+
+![Ordnerauswahl](/help/de/member/pic/ordner04.png)
+
+In der Stream-Ansicht findet man in der linken Seitenleiste den Eintrag "Gespeicherte Ordner". Klickt man darauf werden sämtliche vorhandenen Ordner angezeigt. Wählt man nun einen Ordner aus, so werden sämtliche Postings, die man in diesem Ordner abgelegt hat in (umgekehrter) chronologischer Reihenfolge im Stream angezeigt.
+
+Dieses Feature ist vergleichbar mit den "Clips" (= kategorisierte Lesezeichen), wie man sie von Misskey und den Forkeys kennt.
diff --git a/doc/de/member/search.md b/doc/de/member/search.md
new file mode 100644
index 000000000..0fb0a4a6d
--- /dev/null
+++ b/doc/de/member/search.md
@@ -0,0 +1,15 @@
+### Suche
+
+Um schnell Informationen zu finden, kann die Suche verwendet werden.
+
+Eine Suche ist über das Symbol in der Navigationsleiste möglich.
+
+![Suche Nav-Leiste 01](/help/de/member/pic/suche01.png)
+
+![Suche Nav-Leiste 02](/help/de/member/pic/suche02.png)
+
+Hier wird der gesamte Hub durchsucht. Es kann nach Hashtags, Handles und nach Texten gesucht werden.
+
+In der Kanalansicht ist in der linken Seitenleiste ebenfalls ein Suchfeld vorhanden. Es durchsucht nur den Stream des eigenen Kanals. Suchen, welche in diesem Widget durchgeführt wurden, können auch gespeichert werden, indem man auf das Diskettensymbol neben dem Suchfeld klickt. Die gespeicherte Suche wird dann in einer Liste von Suchbegriffen unter dem Suchfeld angezeigt und kann jederzeit durch einen einfachen Klick wiederholt werden.
+
+![Such-Widget](/help/de/member/pic/suche03.png)
diff --git a/doc/de/member/settings.md b/doc/de/member/settings.md
new file mode 100644
index 000000000..910c6e03d
--- /dev/null
+++ b/doc/de/member/settings.md
@@ -0,0 +1,37 @@
+### Einstellungen
+
+Hubzilla erlaubt vielfältige Einstellungen zu Verhalten, Optik, Features,Kanälen etc.
+
+Sie erreichen die meisten Einstellungen über das Hauptmenü, wo Sie den Menüpunkt Einstellungen finden.
+
+![Einstellungen 01](/help/de/member/pic/einst01.png)
+
+![Einstellungen 02](/help/de/member/pic/einst02.png)
+
+Es werden verschiedene Einstellungs-Kategorien zur Verfügung gestellt:
+
+- Konto-Einstellungen
+- Kanal-Einstellungen
+- Privacy-Einstellungen
+- Anzeige-Einstellungen
+- sofern Klone des eigenen Kanals existieren: Klon-Adressen verwalten
+
+Befinden Sie sich in der Stream Ansicht, ist ein neben dem Hauptmenü ein kleines Zahnrad (⚙)zu sehen,über welches Sie die
+
+- Stream-Einstellungen
+
+erreichen.
+
+Außerdem gibt es noch verborgene Einstellungen
+
+- Zusätzliche Funktionen
+
+welche Sie jedoch nicht über das Menü oder ein Icon erreichen können.
+
+#include doc/de/usermanual/account_settings.md;
+#include doc/de/usermanual/channel_settings.md;
+#include doc/de/usermanual/privacy_settings.md;
+#include doc/de/usermanual/display_settings.md;
+#include doc/de/usermanual/channel_locations.md;
+#include doc/de/usermanual/stream_settings.md;
+#include doc/de/usermanual/additional_features.md;
diff --git a/doc/de/member/share.md b/doc/de/member/share.md
new file mode 100644
index 000000000..a7148f731
--- /dev/null
+++ b/doc/de/member/share.md
@@ -0,0 +1,5 @@
+#### Teilen
+
+Mit dem Teilen (Weitersagen) von Beiträgen wird ein Beitrag eines anderen Nutzers auf dem eigenen Kanal erneut gepostet. Es wird eine neue Konversation im eigenen Kanal erzeugt. Kommentare dazu landen bei der neuen Konversation und nicht bei der ursprünglichen.
+
+Das Teilen von Beiträgen funktioniert nur aus dem Stream oder dem eigenen Kanal, nicht jedoch aus einem „fremden“ Kanal. \ No newline at end of file
diff --git a/doc/de/member/show_source_code.md b/doc/de/member/show_source_code.md
new file mode 100644
index 000000000..b5c7139a8
--- /dev/null
+++ b/doc/de/member/show_source_code.md
@@ -0,0 +1,15 @@
+#### Quellcode anzeigen
+
+Mit dieser Funktion kann man sich den Quelltext eines Beitrags anzeigen lassen. Der Inhalt wird also nicht in formatierter Form gerendert, sondern umfasst den Text inklusive aller Auszeichnungs-Tags (Markdown, bbCode, HTML).
+
+Die Funktion scheint deshalb eher etwas für fortgeschrittene oder sehr neugierige Nutzer zu sein.
+
+Allerdings enthält sie ein Feature, das für jeden hilfreich sein kann.
+
+Neben der internen Beitrags-Id findet man zwei Hyperlinks: "plink" und "llink".
+
+![Quellcode](/help/de/member/pic/quellcode.png)
+
+"plink" bedeutet "Permalink" und entspricht dem [Link zur Quelle](/help/de/member/link_to_source.md).
+
+"llink" bedeutet "lokaler Link" und verweist auf den Ort des Postings auf der eigenen Instanz (Hub). Ein Klick darauf führt nicht dazu, dass man seine eigene Instanz verlässt, sondern zeigt den Beitrag in der Einzelansicht an.
diff --git a/doc/de/member/stream_settings.md b/doc/de/member/stream_settings.md
new file mode 100644
index 000000000..0b4aeb673
--- /dev/null
+++ b/doc/de/member/stream_settings.md
@@ -0,0 +1,9 @@
+#### Stream-Einstellungen
+
+Die Stream-Einstellungen erreicht man nicht über Hauptmenü → Einstellungen, sonder über das kleine Zahnradsymbol (⚙) neben dem Hauptmenü, das dort erscheint, sobald man die Stream-Ansicht aufruft.
+
+![Einstellungen 03](/help/de/member/pic/einst03.png)
+
+Mit den Stream-Einstellungen kann man die Ansicht des Streams und dort zur Verfügung stehende Features (z.B. Stream-Filter, das Speichern von Suchanfragen etc.) auswählen.
+
+![Stream-Einstellungen 01](/help/de/member/pic/streinst01.png)
diff --git a/doc/de/member/superblock.md b/doc/de/member/superblock.md
new file mode 100644
index 000000000..e3833c890
--- /dev/null
+++ b/doc/de/member/superblock.md
@@ -0,0 +1,15 @@
+### Superblock
+
+Die App "Superblock" ist eine Moderationsmethode für den eigenen Stream. Während man mit der normalen Funktionalität von Hubzilla mittels Kontakt-Tool ausschließlich Nutzer, mit denen man verbunden ist, blockieren kann, funktioniert Superblock unabhängig davon, ob man mit einem Kontakt verbunden ist, oder nicht.
+
+Bemerkt man im Stream einen Nutzer, mit welchem man nicht verbunden ist (weil dessen Beiträge von einem Kontakt aus dem eigenen Adressbuch geteilt werden), und möchte man - aus welchem Grund auch immer - keinerlei Beiträge von diesem Fediversenutzer im Stream haben, so kann man dies mit der App "Superblock" erreichen.
+
+Dafür klicken Sie man auf das kleine weiße Dreieck im Avatar des zu blockenden Nutzers.
+
+Es öffnet sich ein Pulldown-Menü, welches zu unterst den Menüpunkt "Vollständig blockieren" enthält. Mit einem Klick auf diesen Menüpunkt landet der Nutzer in der Superblock-Liste. Beiträge dieses Nutzers erscheinen künftig nicht mehr im eigenen Stream. Betroffene Beiträge werden sofort im Stream ausgeblendet. Außerdem ist es diesem Nutzer nicht mehr möglich, Ihre Beiträge zu lesen, unabhängig von den Berechtigungen, noch kann er in Ihren Kanal schreiben.
+
+Wählt man im App-Menü (oben rechts ⋮) die App "Superblock", so wird die Liste sämtlicher blockierter Kontakte angezeigt.
+
+Neben jedem Kontakt ist ein "Mülltonnen-Icon" zu sehen. Ein Klick auf dieses Icon entfernt den Nutzer von der Blockliste. Anschließend kann der Nutzer Ihnen wieder folgen, Ihre Beiträge sehen und auch bei Ihnen kommentieren und seine Postings erscheinen (z.B. durch Teilen von einem Kontakt) auch wieder im Stream.
+
+Superblock ist bei neuen Kanälen nicht per Standard installiert und aktiviert. \ No newline at end of file
diff --git a/doc/de/member/tags.md b/doc/de/member/tags.md
new file mode 100644
index 000000000..1f2fd79f4
--- /dev/null
+++ b/doc/de/member/tags.md
@@ -0,0 +1,5 @@
+### Tags
+
+Tags (auch thematische Tags, Hashtags oder topical tags genannt) Tags werden angezeigt, indem Sie dem Tagnamen das Zeichen "#" voranstellen. Dadurch wird im Beitrag ein Link zu einer verallgemeinerten Website-Suche nach dem angegebenen Begriff erstellt. Zum Beispiel, #cars stellt einen Suchlink für alle Beiträge bereit, die „Autos“ auf Ihrer Website erwähnen. Aktuelle Tags sind in der Regel mindestens drei Zeichen lang. Kürzere Suchbegriffe führen wahrscheinlich nicht zu Suchergebnissen, dies hängt jedoch von der Datenbankkonfiguration ab.
+
+Thematische Tags werden normalerweise auch nicht verknüpft, wenn sie rein numerisch sind, z. #1. Wenn Sie einen numerischen Hashtag verwenden möchten, fügen Sie bitte einen beschreibenden Text wie z.B. #2012-Wahlen oder das gesamte Tag in doppelte Anführungszeichen setzen (z. B. #“2012″). Doppelte Anführungszeichen sind auch erforderlich, wenn das Tag Leerzeichen enthält (# „Mein Tag“) und möglicherweise erforderlich, wenn das Tag Satzzeichen enthält (# „EndsWithPeriod.“ Oder # „Exciting !!!“).
diff --git a/doc/de/member/the_grid.md b/doc/de/member/the_grid.md
new file mode 100644
index 000000000..d49b199f6
--- /dev/null
+++ b/doc/de/member/the_grid.md
@@ -0,0 +1,3 @@
+### Das Grid
+
+Als das "Grid" wird das Netzwerk aller Hubzilla-Hubs bezeichnet, die miteinander über das Nomad-Protokoll kommunizieren. Das Grid ist quasi eine Untermenge des Fediverse und umfasst alle Hubzilla-Server. \ No newline at end of file
diff --git a/doc/de/member/the_stream.md b/doc/de/member/the_stream.md
new file mode 100644
index 000000000..08a6abbe8
--- /dev/null
+++ b/doc/de/member/the_stream.md
@@ -0,0 +1,7 @@
+### Der Stream
+
+Der Stream ist die Liste der Beiträge, Kommentare und Boosts von Nutzern im Fediverse. Er ist umgekehrt chronologisch sortiert (die neuesten Beiträge erscheinen zuoberst). Welche Beiträge Ihnen hier genau gezeigt werden, hängt weitgehend von Ihren Berechtigungseinstellungen ab.
+
+Der Stream (bei anderen Fediversedienste wird er auch "Timeline" genannt) kann durch verschiedene Filter gefiltert werden: nach Direktnachrichten, Ereignissen, Umfragen, Privacy Gruppen, Foren, Markierten Beiträgen, den eigenen Beiträgen, gespeicherte Ordner, Namen.
+
+Der Stream im Fediverse wird nicht durch Algorithmen, die vermeintlich interessante Inhalte für den Nutzer auswählt, erstellt, sondern wird ausschließlich durch den Nutzer selbst bestimmt. Im Fediverse sind Sie, der Nutzer, der Algorithmus für den Stream. \ No newline at end of file
diff --git a/doc/de/member/toggle_star_status.md b/doc/de/member/toggle_star_status.md
new file mode 100644
index 000000000..4c901d536
--- /dev/null
+++ b/doc/de/member/toggle_star_status.md
@@ -0,0 +1,9 @@
+#### Markierungsstatus (Stern) umschalten
+
+Das Umschalten des Markierungssterns darf nicht mit dem "Sternen" (= Favorisieren) anderer Fediversedienste (z.B. Mastodon) verwechselt werden. Während das "Sternen" bei diesen Diensten eine positive Wertung des Beitrags darstellt, die am ehesten einem "Link" entspricht, ist das Markieren mit einem Stern bei Hubzilla mit dem Setzen eines Lesezeichens vergleichbar. Mit dem Umschalten (ein Klick setzt den Stern, ein weiterer entfernt ihn wieder) wird der Beitrag für den Nutzer selbst als eine Art Lesezeichen gesetzt.
+
+Einen "gesternten" Beitrag erkennt man am Sternsymbol unten links.
+
+![Stern](/help/de/member/pic/stern.png)
+
+In der Stream-Ansicht findet man in der linken Seitenleiste den Eintrag "Markierte Beiträge". Klickt man darauf werden sämtliche markierte Postings in (umgekehrter) chronologischer Reihenfolge im Stream angezeigt.
diff --git a/doc/de/member/websites.md b/doc/de/member/websites.md
new file mode 100644
index 000000000..f56b69f0e
--- /dev/null
+++ b/doc/de/member/websites.md
@@ -0,0 +1,143 @@
+### Webseiten
+
+Mit der App "Webseiten" ist es Ihnen möglich, statische Webseiten in Ihrem Kanal zu erstellen. Webseiten verbleiben auf Ihren Hub und werden nicht föderiert. Sie können aber den Link zur Webseite teilen und es allen Nutzern im Fediverse ermöglichen, Ihre Webseite zu besuchen.
+
+Wenn Sie die App aufrufen, gelangen Sie zur Webseiten-Übersicht. Die Seiten werden unter `<URL-Ihrer-Instanz>/page/<Ihr-Kanalname>/<Seiten-Link-Titel>` zugänglich sein.
+
+![Webseiten 01](/help/de/member/pic/webseiten01.png)
+
+In der linken Seitenleiste befindet sich ein Widget mit den Gestaltungswerkzeugen für "Blöcke", "Menüs", "Layouts" und "Seiten".
+
+Darunter ist ein weiteres Widget vorhanden, mit der Möglichkeit Webseiten zu ex- und importieren.
+
+In der Mitte werden die vorhandenen Webseiten aufgeführt. Sie können diese bearbeiten, teilen und löschen. Außerdem ist dort ein Button vorhanden, um eine neue Webseite zu erstellen: "Erstelle".
+
+Wenn Sie auf diesen Button klicken, öffnet sich der Webseiten-Editor.
+
+![Webseiten 02](/help/de/member/pic/webseiten02.png)
+
+Sie haben nun die Wahl, auf welche Art Sie die Webseite gestalten wollen: mit bbCode, mit HTML, mit Markdown, mit reinem Text oder mit der Comanche-Layout-Sprache.
+
+Sie können außerdem festlegen, welches Layout (sofern Sie eines oder Mehrere mit dem Layout-Gestaltungswerkzeug erstellt haben) für die Darstellung der Webseite verwendet werden soll.
+
+Es folgt das Eingabefeld für den optionalen Seitentitel, sowie (ebenfalls optional) eine Zusammenfassung, sowie (Pflichtfeld) die Seiten-URL.
+
+Darunter befindet sich der Texteditor für den Inhalt der Webseite.
+
+Möchte man lediglich eine ganz einfache Webseite mit Formatierungen und anderen Auszeichnungselementen erstellen, genügt es, diese im Website-Editor mittel reinem Text, HTML, bbCode oder Markdown zu erstellen. Sie erhalten damit eine Webseite ohne besonderes Layout (ohne Seitenleisten, ohne Menüs etc.).
+
+Für anspruchsvollere Webseiten, empfiehlt es sich, dass Sie mit Blöcken, Layouts und Menüs arbeiten.
+
+#### Blöcke
+
+Blöcke können Teile von Webseiten sein. Der grundlegende HTML-Code eines Blocks sieht wie folgt aus
+
+```
+ <div>
+ Block-Inhalt
+ </div>
+```
+
+Wenn ein Block den Inhaltstyp text/html hat, kann er auch Menüelemente enthalten. Der Beispielinhalt von
+
+```
+ <p>HTML Block-Inhalt</p>
+ [menu]menuname[/menu]
+```
+
+wird HTML wie dieses erzeugen
+
+```
+ <div>
+ <p>HTML Block-Inhalt</p>
+ <div>
+ <ul>
+ <li><a href="#">Link 1</a></li>
+ <li><a href="#">Link 2</a></li>
+ <li><a href="#">Link 3</a></li>
+ </ul>
+ </div>
+ </div>
+```
+
+Über das Makro `$content` kann ein Block auch den eigentlichen Inhalt der Webseite enthalten.
+
+Erstellen Sie dazu einen Block nur mit
+
+```
+ $content
+```
+
+als Inhalt.
+
+Damit ein Block auf der Webseite erscheint, muss er im Seitenlayout innerhalb einer Region definiert werden.
+
+```
+ [region=aside]
+ [block]blockname[/block]
+ [/region]
+```
+
+Das Aussehen des Blocks kann im Seitenlayout manipuliert werden.
+
+Es können eigene Klassen zugewiesen werden
+
+```
+ [region=aside]
+ [block=myclass]blockname[/block]
+ [/region]
+```
+
+wird folgendes HTML erzeugen
+
+```
+ <div class="myclass">
+ Block Content
+ </div>
+```
+
+Über die wrap-Variable kann ein Block von seinem umschließenden `<div></div>`-Tag befreit werden
+
+```
+ [region=aside]
+ [block][var=wrap]none[/var]blockname[/block]
+ [/region]
+```
+
+wird dieses HTML erzeugt
+
+```
+Blockinhalt
+```
+
+Mit dem Block-Editor können Blöcke ebenso einfach erzeugt werden, wie Webseiten.
+
+![Webseiten 03](/help/de/member/pic/webseiten03.png)
+
+#### Menüs
+
+Der Menüeditor dient zum einfachen Erstellen von Navigations-Menüs.
+
+![Webseiten 04](/help/de/member/pic/webseiten04.png)
+
+Dem Menü muss ein eindeutiger Name gegeben werden (mit diesem kann es später in der Webseite und in Blöcken referenziert werden). Die Vergabe des Titels ist optional. Außerdem kann man wählen, ob das Menü für das Hinzufügen von Bookmarks zur Verfügung steht. Mit diesem Feature ist es möglich, als Bookmarks markierte Links aus dem Stream mit einem Klick zum Menü hinzuzufügen.
+
+Klicken Sie auf "Absenden und fortfahren", wird das Menü erstellt.
+
+![Webseiten 05](/help/de/member/pic/webseiten05.png)
+
+Nun öffnet sich der Dialog für das Hinzufügen eines Menüeintrags. Es muss eine Bezeichnung für den Menüeintrag eingegeben werden ("Name des Links") und das Ziel des Links. Dies kann eine URL oder der Name eines anderen Menüs sein (welches dann als Untermenü eingebunden wird).
+
+Über die Eingabe einer Zahl bei "Reihenfolge in der Liste" kann man die Sortierung der Menüeinträge beeinflussen.
+
+Handelt es sich bei der URL um einen externen Link zu einer Quelle auf einem anderen Hub, so kann durch setzen des Schalters "Magic-Auth verwenden, falls verfügbar" dafür gesorgt werden, dass man beim Ziel authentifiziert wird und ggf. eingeschränkte Inhalte verfügbar sind.
+
+Außerdem können Sie festlegen, ob Links in einem neuen Fenster/Tab geöffnet werden sollen.
+
+Mit Klick auf "Absenden und fortfahren" kann man weitere Einträge erstellen. "Absenden und fertigstellen" beendet die Eingabe von Menüeinträgen. Menüs können aber jederzeit nachträglich bearbeitet und ergänzt werden.
+
+#### Layouts
+
+Mit den Layouts kann man den generellen Aufbau von Webseiten festlegen. Die Gestaltung erfolgt mit der Comanche Seitenbeschriebungssprache, einer Variante von bbCode. Man muss einen Namen für das Layout vergeben. Im Textfeld erfolgt dann die Definition des Layouts. Hier können auch die Inhalte der verschiedenen Regionen festgelegt werden.
+
+![Webseiten 06](/help/de/member/pic/webseiten06.png)
diff --git a/doc/de/member/wikis.md b/doc/de/member/wikis.md
new file mode 100644
index 000000000..da3a7a829
--- /dev/null
+++ b/doc/de/member/wikis.md
@@ -0,0 +1,39 @@
+### Wikis
+
+Die App "Wiki" ermöglicht es, im eigenen Kanal Wikis anzulegen.
+
+Wiki-Seiten werden nicht föderiert und verbleiben auf dem eigenen Hub.
+
+Die Wiki-App bietet eine einfache, klassische Wiki-Funktionalität. Wiki-Beiträge können als reiner Text, als Markdown-Text oder als BBcode-Text erstellt werden.
+
+![Wikis 01](/help/de/member/pic/wiki01.png)
+
+Um ein Wiki anzuschauen (oder zu bearbeiten), wählt man auf dieser Seite das entsprechende Wiki aus der Liste.
+
+Möchte man ein neues Wiki erstellen, klickt man auf den Button "+ Neu anlegen".
+
+Es öffnet sich ein Eingabe-Formular, in welchem man den Namen des Wiki eingibt und den Inhaltstyp (als Standard) festlegt. Man kann über einen Schalter bestimmen, dass nur der gewählte Inhaltstyp (Text, Markdown, BBcode) für alle Wiki-Einträge verwendet werden muss. Außerdem kann man per Schalter die Erzeugung eines Statusbeitrags über die Wikierstellung ein- oder ausschalten.
+
+![Wikis 02](/help/de/member/pic/wiki02.png)
+
+Auch für ein Wiki kann man detaillierte [Berechtigungen](/help/de/member/permissions_content.md) festlegen.
+
+Klickt man auf "Absenden", wird das Wiki erstellt und die Startseite (Home) geöffnet.
+
+Die Standard-Ansicht einer Wiki-Seite ist immer "Ansicht", bei welcher der Text gemäß dem Quelltext gerendert wird. Über Reiter (Tabs) am oberen Rand kann man in die Ansicht "Bearbeiten" wechseln, um in den Editor-Modus zu gelangen.
+
+Haben Sie die Seite bearbeitet und schalten Sie dann wieder auf Ansicht, werden die Änderungen sofort angezeigt.
+
+Wenn Sie die Seite speichern möchten, geben Sie das Eingabefeld unter dem Text eine passende Bemerkung ein und klicken auf "Save". Die Wiki-Seite ist erstellt.
+
+Über den dritten Reite mit der Bezeichnung "Historie" können Sie sich den Änderungsverlauf der Wiki-Seite anschauen und, sofern gewünscht, Änderungen rückgängig machen. Es handelt sich also um eine wiki-typische Form der Versionskontrolle.
+
+![Wikis 03](/help/de/member/pic/wiki03.png)
+
+Wenn Sie weitere Wiki-Seiten erstellen, werden diese in der linken Seitenleiste aufgeführt, von wo aus sie auch aufrufbar sind.
+
+![Wikis 04](/help/de/member/pic/wiki04.png)
+
+Für eine kollaborative Bearbeitung eines Wikis ist es erforderlich, den Nutzern, welche an dem Wiki mitarbeiten dürfen, entsprechende Rechte einzuräumen. Dies tut man in den Fällen eines öffentlichen, persönlichen oder Foren-Kanals mittels einer entsprechenden Kontaktrolle, in welcher man die Bearbeitung der Wiki-Seiten erlaubt. Diese Berechtigung ist nämlich bei den genannten Kanalrollen als Standard nicht erteilt (bei einer benutzerdefinierten Kanalrolle kann man die Genehmigung erteilen, sie gilt dann aber generell und kann mittels einer Kontaktrolle nicht wieder entzogen werden).
+
+Will man einzelne Wikis davon ausnehmen, muss man deren Sichtbarkeit über die Berechtigungs-Einstellungen des Wikis (Vorhangschloss) einschränken.
diff --git a/doc/de/member_faq.md b/doc/de/member_faq.md
new file mode 100644
index 000000000..029d45eba
--- /dev/null
+++ b/doc/de/member_faq.md
@@ -0,0 +1,17 @@
+### Hubzilla FAQ
+
+#### Ich kann den Text eines Beitrags nach dem Speichern bearbeiten, aber gibt es eine Möglichkeit, die Berechtigungen zu ändern?
+
+Kurze Antwort: Nein, das geht nicht. Dafür gibt es Gründe. Sie können die Berechtigungen für Ihre Dateien, Fotos und Ähnliches ändern, aber nicht für Beiträge, nachdem Sie diese gespeichert haben. Der Hauptgrund dafür ist: Sobald Sie einen Beitrag gespeichert haben, wird er entweder an den öffentlichen Kanal und von dort an andere Hubzilla-Server oder an diejenigen verteilt, für die er bestimmt war. Genauso wie Sie etwas, das Sie einer anderen Person gegeben haben, nicht zurückfordern können, können Sie die Berechtigungen für Hubzilla-Beiträge nicht ändern. Wir müssten überall nachverfolgen, wohin Ihre Beiträge gelangen, alle Personen erfassen, denen Sie die Anzeige erlaubt haben, und dann nachverfolgen, von wem wir sie löschen müssen. Bei öffentlichen Beiträgen ist dies noch schwieriger, da Hubzilla ein globales Netzwerk ist und es keine Möglichkeit gibt, einen Beitrag zu verfolgen, geschweige denn zuverlässig zurückzuholen. Andere Netzwerke, die Ihren Beitrag erhalten haben, haben keine zuverlässige Möglichkeit, ihn zu löschen oder zurückzuholen.
+
+#### Ich habe meinen Kanal heruntergeladen und auf eine andere Website importiert (meine Identität geklont), aber es gibt keine Inhalte, keine Beiträge, keine Fotos. Was ist los???
+
+Beiträge und Fotos/Dateien werden getrennt von den grundlegenden Kanalinformationen bereitgestellt. Dies ist aufgrund von Speicherbeschränkungen im Zusammenhang mit jahrelangen Unterhaltungen und Fotoarchiven erforderlich. Beiträge und Unterhaltungen können separat von den grundlegenden Kanalinformationen synchronisiert werden. Fotos und Dateiarchive können mit einem Plugin-Tool wie „redfiles“ übertragen werden, das derzeit als „experimentell“ gekennzeichnet ist. Bei der Entwicklung dieser Funktion war es uns wichtig, dass alle Ihre Kontakte erhalten bleiben. Ihre Freunde haben Ihre alten Inhalte bereits gesehen. Beiträge/Konversationen hatten die nächste Priorität und können nun synchronisiert werden. Dateien und Fotos sind der letzte Punkt, der noch vollständig funktioniert. Sobald wir jemanden finden, der die Implementierung fertigstellen möchte, wird dies umgesetzt. :)
+
+#### Ich kann private Ressourcen nicht sehen
+
+Sie haben wahrscheinlich Cookies von Drittanbietern deaktiviert. Sie müssen diese aktivieren, damit die Remote-Authentifizierung funktioniert.
+
+#### Es gibt viele Beiträge in Fremdsprachen. Lassen Sie uns diese automatisch übersetzen.
+
+Es gibt auch viele **private** Beiträge in Fremdsprachen, und automatische Übersetzungsdienste würden erfordern, dass wir diese privaten Nachrichten an den Übersetzungsdienst übermitteln; und wir wissen nicht, was dieser mit ihnen auf seinen Servern macht. Dank Edward Snowden wissen wir das eigentlich. Unsere beste Option ist ein Projekt namens ***Apertium\***, ein Open-Source-Übersetzungsprogramm, das wir lokal installieren können. Derzeit fehlen noch deutsche Übersetzungen – die am häufigsten nachgefragten Übersetzungen in der Matrix. Auch dies wird implementiert, sobald wir jemanden finden, der sich wirklich dafür engagieren möchte. \ No newline at end of file
diff --git a/doc/de/members.bb b/doc/de/members.bb
deleted file mode 100644
index c85855f62..000000000
--- a/doc/de/members.bb
+++ /dev/null
@@ -1,25 +0,0 @@
-[h2]Dokumentation für Hub-Mitglieder[/h2]
-
-[h3]Erste Schritte[/h3]
-[zrl=[baseurl]/help/registration]Ein Konto registrieren[/zrl]
-[zrl=[baseurl]/help/accounts_profiles_channels_basics]Du im Hubzilla-Netzwerk: Konten, Profile und Kanäle kurz erklärt[/zrl]
-[zrl=[baseurl]/help/profiles]Profile[/zrl]
-[zrl=[baseurl]/help/channels]Kanäle[/zrl]
-[zrl=[baseurl]/help/roles]Zugriffsrechte-Kategorien und Kanaltypen[/zrl]
-[zrl=[baseurl]/help/first-post]Dein erster Beitrag[/zrl]
-[zrl=[baseurl]/help/connecting_to_channels]Sich mit anderen Kanälen verbinden[/zrl]
-[zrl=[baseurl]/help/permissions]Zugriffsrechte und Verschlüsselung: Du hast alles unter Kontrolle[/zrl]
-[zrl=[baseurl]/help/cloud]Cloud-Speicher[/zrl]
-[zrl=[baseurl]/help/remove_account]Einen Kanal oder das ganze Konto löschen[/zrl]
-
-[h3]Hilfe für $Projectname-Mitglieder[/h3]
-[zrl=[baseurl]/help/tags_and_mentions]Tags und Erwähnungen[/zrl]
-[zrl=[baseurl]/help/webpages]Webseiten[/zrl]
-[zrl=[baseurl]/help/bbcode]BBcode-Referenz für Beiträge und Kommentare[/zrl]
-[zrl=[baseurl]/help/checking_account_quota_usage]Überprüfung der Kontenlimits[/zrl]
-[zrl=[baseurl]/help/cloud_desktop_clients]Desktop-Anwendungen und die Cloud[/zrl]
-[zrl=[baseurl]/help/AdvancedSearch]Fortgeschrittene Suche im Kanalverzeichnis[/zrl]
-[zrl=[baseurl]/help/addons]Hilfe zu Addons[/zrl]
-[zrl=[baseurl]/help/diaspora_compat]Kompatibilität zum Diaspora-Protokoll (zur Kommunikation mit Kontakten aus Diaspora und Friendica)[/zrl]
-[zrl=[baseurl]/help/faq_members]FAQ für Mitglieder[/zrl]
-[zrl=[baseurl]/help/bugs]Bugs, Probleme und Sachen, die einem um die Ohren fliegen können[/zrl]
diff --git a/doc/de/pic/ui01.png b/doc/de/pic/ui01.png
new file mode 100644
index 000000000..1bae614be
--- /dev/null
+++ b/doc/de/pic/ui01.png
Binary files differ
diff --git a/doc/de/pic/ui02.png b/doc/de/pic/ui02.png
new file mode 100644
index 000000000..59db0c314
--- /dev/null
+++ b/doc/de/pic/ui02.png
Binary files differ
diff --git a/doc/de/platzhalter.md b/doc/de/platzhalter.md
new file mode 100644
index 000000000..7abe6f7ce
--- /dev/null
+++ b/doc/de/platzhalter.md
@@ -0,0 +1 @@
+# Platzhalter \ No newline at end of file
diff --git a/doc/de/profiles.bb b/doc/de/profiles.bb
deleted file mode 100644
index 7860ad47c..000000000
--- a/doc/de/profiles.bb
+++ /dev/null
@@ -1,40 +0,0 @@
-[h3]Profile[/h3]
-
-In $Projectname kannst Du beliebig viele Profile anlegen. Du kannst mehrere Profile nutzen, um verschiedenen Kontakten und Profilbesuchern unterschiedliche Seiten Deiner Persönlichkeit zu zeigen. Das ist nicht das gleiche wie das Anlegen mehrerer [i]Kanäle.[/i]
-
-Mehrere Kanäle erlauben es, komplett voneinander getrennte Informationen zu verwalten. Du könntest zum Beispiel einen Kanal für Dich selbst anlegen, einen für Deinen Schwimmverein, einen für Dein Blog und so weiter und so fort.
-
-Ein Profil erlaubt es, unterschiedlichen Leuten unterschiedliche Informationen über einen Kanal zu präsentieren. Beispiel: In Deinem Standard-Profil, das alle Profilbesucher zu sehen bekommen, könnte nur Dein Vorname stehen, sonst keine weiteren Informationen. Freunde und Bekannte könnten Deinen kompletten Realnamen und Deine Postadresse zu sehen bekommen, und im Profil für Deinen engsten Freundeskreis könnten peinliche Hobbies und Trinkrekorde ihren Platz finden.
-
-Jeder Kanal hat ein Standard-Profil (öffentliches Profil), das vor niemandem versteckt werden kann (außer evtl. auf privaten Servern, die nicht mit der Matrix verbunden sind). Du kannst und solltest Dich in der Auswahl dessen, was Du im Standard-Profil von Dir preisgibst, stark einschränken.
-
-Trotzdem: Wenn Du willst, dass Freunde Dich finden können, macht es Sinn, folgende Informationen ins öffentliche Profil einzutragen:
-
-[ul][*] Deinen Realnamen oder zumindest einen Spitznamen, unter dem Dich jeder kennt
-[*] Ein Foto, das auch wirklich Dich zeigt
-[*] Deinen Wohnort, oder zumindest Land oder Bundesland[/ul]
-
-Wenn Du Leute kennenlernen möchtest, die ähnliche Interessen haben wie Du, nimm Dir einen Moment Zeit und füge einige Schlüsselwörter hinzu. Zum Beispiel „Musik, Linux, Fotografie“ oder was auch immer. Danach können andere dann im Verzeichnis suchen. Du kannst so viele Schlüsselwörter eingeben wie Du möchtest.
-
-Um alternative Profile zu erstellen, besuche zunächst die Seite [zrl=[baseurl]/settings/features]Einstellungen > Zusätzliche Funktionen[/zrl] und aktiviere dort „Mehrfachprofile“. Ohne diese Aktivierung hast Du nur ein Profil, nämlich Dein Standard-Profil.
-
-Klicke dann auf „Profile bearbeiten“ im Menü Deines $Projectname-Servers. Dort kannst Du existierende Profile bearbeiten, Dein Profilfoto verändern, Dinge zu einem Profil hinzufügen oder ein neues Profil erstellen. Du kannst auch ein Profil „klonen“, wenn Du nur einige wenige Einträge ändern willst, ohne die ganzen Informationen noch einmal einzugeben. Klicke dazu auf das Profil, das Du klonen willst, und wähle dann „Dieses Profil klonen“.
-
-In der Liste Deiner Profile kannst Du auch bestimmen, wer ein bestimmtes Profil zu sehen bekommt. Klicke dazu auf „Sichtbarkeit bearbeiten“ neben dem Profil, um das es geht (gibt es nur bei Profilen, die nicht Dein Standard-Profil sind). Klicke dann auf die Bilder derjenigen Kontakte, die dieses Profil sehen sollen – sie sind dann oben zu sehen. Wenn Du oben auf ein Bild klickst, wird dieser Kontakt wieder aus der Gruppe derjenigen herausgenommen, die dieses Profil zu sehen bekommen.
-
-Hast Du einem Kontakt ein Profil zugeordnet, wird er immer dieses Profil sehen, wenn er sich Dein Profil ansieht. Besucht er Deinen $Projectname-Server, ohne sich anzumelden, sieht er aber weiterhin Dein Standard-Profil.
-
-Auf der allgemeinen „Einstellungen“-Seite gibt es eine Einstellung, mit der Du festlegen kannst, ob Dein Standard-Profil in den $Projectname-Verzeichnissen veröffentlicht werden soll.
-
-Wenn Du nicht möchtest, dass andere Dich finden können, ohne dass Du ihnen Deine Kanal-Adresse gibst, kannst Du so verhindern, dass Dein Profil veröffentlicht wird.
-
-[b]Schlüsselwörter und Verzeichnissuche[/b]
-
-Im Verzeichnis (Kanal-Verzeichnis) kannst Du nach Leuten suchen, die ihre Profile veröffentlichen. Zum Beispiel, indem Du Namen oder Spitznamen eingibst. Aktuell werden nur das Namensfeld und die Schlüsselwörter durchsucht. Wenn Du Schlüsselwörter in Dein Standard-Profil einträgst, können Dich Leute mit ähnlichen Interessen finden. Sie werden außerdem bei den Kanal-Vorschlägen benutzt. Sie sind im Verzeichnis nicht direkt sichtbar, wohl aber auf Deiner Profil-Seite.
-
-Auf Deiner „Verbindungen“-Seite und im Verzeichnis gibt es einen Link „Vorschläge“ bzw. „Kanal-Vorschläge“. Dort findest Du Kanäle, die gleiche oder ähnliche Schlüsselwörter im Profil haben wie Du. Je mehr Schlüsselwörter Du in Dein Standard-Profil einträgst, desto besser werden die Suchergebnisse. Sie sind nach Relevanz sortiert.
-
-Siehe auch:
-
-[zrl=[baseurl]/help/AdvancedSearch]Fortgeschrittene Suche[/zrl]
-#include doc/macros/main_footer.bb;
diff --git a/doc/de/registration.bb b/doc/de/registration.bb
deleted file mode 100644
index ac24782a6..000000000
--- a/doc/de/registration.bb
+++ /dev/null
@@ -1,36 +0,0 @@
-[h3]Registrieren[/h3]
-
-Nicht alle $Projectname-Hubs erlauben jedem, sich zu registrieren. Wenn eine Registrierung möglich ist, erscheint unter dem Anmelde-Formular ein Link mit dem Titel „Registrieren“, der Dich zur Registrierungs-Seite des Hubs führt. Auf manchen Hubs wirst Du auf einen anderen Hub weitergeleitet, der Registrierungen erlaubt. Da alle $Projectname-Hubs miteinander verbunden sind, ist es egal, auf welchem Du Dich registrierst.
-
-[b]Deine E-Mail-Adresse[/b]
-
-Bitte gib eine funktionierende E-Mail-Adresse an. Sie wird [b]nie[/b] veröffentlicht. Sie wird benutzt, um Dein Konto zu aktivieren, und wenn Du willst, kannst Du Dir an diese Adresse Benachrichtigungen schicken lassen, wenn Du neue Nachrichten oder Beiträge erhalten hast. Sie wird außerdem benutzt, um vergessene Passwörter wiederherstellen zu können.
-
-[b]Passwort[/b]
-
-Gib ein Passwort Deiner Wahl ein und wiederhole es in der zweiten Box, um sicherzugehen, dass Du Dich nicht vertippt hast. Da $Projectname dezentralisierten Identitäsnachweis beherrscht, kannst Du Dich mit Deinem Konto auf vielen anderen Webseiten anmelden.
-
-[b]Nutzungsbedingungen[/b]
-
-Klicke auf den Link, um die [zrl=[baseurl]/help/TermsOfService]Nutzungsbedingungen[/zrl] dieses Servers zu lesen. Wenn Du sie gelesen hast, setze den Haken im Registrierungsformular, um sie zu akzeptieren.
-
-[b]Registrieren[/b]
-
-Wenn Du alles ausgefüllt hast, klicke auf den „Registrieren“-Knopf. Bei manchen Servern muss der Administrator Deiner Registrierung erst noch zustimmen, bevor Du Dein Konto nutzen kannst. Falls das der Fall ist, wird Dir das entsprechend angezeigt. Du erhältst dann eine E-Mail, wenn die Registrierung vollendet wurde. (Sicherheitshalber auch Spam-Ordner überprüfen!)
-
-[b]Einen Kanal anlegen[/b]
-
-Als nächstes kommst Du direkt auf den „Kanal hinzufügen“-Bildschirm. Dein erster Kanal wird normalerweise der sein, der Dich selbst repräsentiert. Es ist also sinnvoll, Deinen Namen oder Dein Pseudonym als Kanal-Namen zu verwenden.
-
-Der Kanal-Name ist der Titel oder eine kurze Beschreibung des Kanals. Der „Spitzname“ ist so etwas wie ein Benutzername. Was Du hier eingibst wird Teil Deiner Kanal-Adresse, die Andere benutzen, um sich mit Dir zu verbinden, und die Du selbst benutzt, um Dich auf anderen Servern anzumelden. Die Kanal-Adresse sieht aus wie eine E-Mail-Adresse, zum Beispiel so: spitzname@deinserver.xyz
-
-Wenn Dein Kanal angelegt ist, geht es direkt weiter zu den Einstellungen. Dort kannst Du Zugriffsrechte setzen, Funktionen zu- oder abschalten und so weiter. Diese Punkte werden auf den entsprechenden Hilfeseiten erklärt.
-
-Siehe auch
-[zrl=[baseurl]/help/accounts_profiles_channels_basics]Grundlagen zu Identitäten in $Projectname[/zrl]
-[zrl=[baseurl]/help/accounts]Konten[/zrl]
-[zrl=[baseurl]/help/profiles]Profile[/zrl]
-[zrl=[baseurl]/help/permissions]Zugriffsrechte[/zrl]
-[zrl=[baseurl]/help/remove_account]Konto löschen[/zrl]
-
-#include doc/macros/main_footer.bb;
diff --git a/doc/de/roles.bb b/doc/de/roles.bb
deleted file mode 100644
index d1591ff17..000000000
--- a/doc/de/roles.bb
+++ /dev/null
@@ -1,60 +0,0 @@
-[h3]Kanal-Berechtigungsrollen[/h3]
-
-Wenn Du einen neuen Kanal erstellst, wirst Du aufgefordert, eine Berechtigungsrolle auszuwählen, je nachdem, wie Du diesen Kanal nutzen möchtest. Die beliebtesten Berechtigungsrollen sind die Rollen für soziale Netzwerke. Sie haben viele weitere Auswahlmöglichkeiten, die mit Facebook-Gruppen und -Seiten, kollaborativen Bereichen, Newsfeeds und mehr vergleichbar sind. Diese Rollen konfigurieren automatisch verschiedene Systemvariablen, von den Berechtigungen, die Freunden gewährt werden, bis hin zu den Standardeinstellungen für Privatsphäre und Sichtbarkeit. Es stehen erweiterte Konfigurationen zur Verfügung, mit denen Du jeden dieser Parameter an Ihre Bedürfnisse anpassen kannst, aber wir haben die Erfahrung gemacht, dass die meisten Nutzer es vorziehen, die Einstellungen vorzunehmen und sie vergessen. Im Folgenden werden einige der verschiedenen Rollen beschrieben, die derzeit verfügbar sind, und wie sie sich auf Ihre Privatsphäre und Ihre Interaktionsmöglichkeiten auswirken.
-
-
-[h4]Soziales Netzwerk[/h4]
-[b]Föderation[/b]
-
-Der Kanal ist ein sehr freizügiges soziales Netzwerkprofil, das mit anderen föderierten sozialen Netzwerken kompatibel ist. Die Genehmigungsrichtlinien sind ähnlich wie bei Twitter und größtenteils kompatibel mit Diaspora und Mastodon. Die Privatsphäre hat eine geringere Priorität als der einfache Zugang und die Verbindung mit anderen. Jeder im Netzwerk kann Deine öffentlichen Beiträge kommentieren und Dir private Nachrichten schicken. Standardmäßig sind Beiträge und veröffentlichte Artikel öffentlich, aber Du Kannst dies bei der Erstellung des Artikels überschreiben und einschränken. Du bist im Verzeichnis aufgeführt. Deine Online-Präsenz und Deine Verbindungen sind für andere sichtbar. Dieser Modus kann dazu führen, dass Du unerwünschten Mitteilungen und Spam ausgesetzt bist. Diese Rolle wird im Allgemeinen nicht empfohlen, es sei denn, Du musst regelmäßig mit Mitgliedern anderer Netzwerke interagieren.
-
-[b]Weitgehend öffentlich[/b]
-
-Der Kanal ist ein typisches Profil eines sozialen Netzwerks. Standardmäßig sind Beiträge und veröffentlichte Elemente öffentlich, aber Du kannst dies bei der Erstellung des Elements/Beitrags außer Kraft setzen und einschränken. Du bist im Verzeichnis aufgeführt. Deine Online-Präsenz und Deine Verbindungen sind für andere sichtbar. Nur Ihre unmittelbaren Verbindungen können Deine öffentlichen Beiträge kommentieren und Dir private Nachrichten schicken. Die Berechtigungsrichtlinien sind ähnlich wie bei Facebook.
-
-[b]Eingeschränkt[/b]
-
-Standardmäßig werden alle Beiträge und veröffentlichten Elemente an Deine Privatsphärengruppe "Freunde" gesendet und nicht öffentlich gemacht. Neue Freunde werden zu dieser privaten Gruppe hinzugefügt. Du kannst dies außer Kraft setzen und einen öffentlichen Beitrag oder ein veröffentlichtes Element erstellen, wenn Du dies wünschst. Du bist im Verzeichnis aufgeführt. Deine Online-Präsenz (für den Chat) und Deine Verbindungen (Freunde) sind für die Betrachter Deines Profils sichtbar.
-
-[b]Privat[/b]
-
-Standardmäßig werden alle Beiträge und veröffentlichten Elemente an Deine private Gruppe "Freunde" gesendet. Neue Freunde werden zu dieser privaten Gruppe hinzugefügt. Du kannst dies außer Kraft setzen und einen öffentlichen Beitrag oder ein öffentliches Element erstellen, wenn Du dies wünschst. Du bist NICHT im Verzeichnis aufgeführt. Nur Deine Verbindungen können Deine anderen Verbindungen sehen. Deine Online-Präsenz ist verborgen.
-
-
-[h4]Forum[/h4]
-[b]Weitgehend öffentlich[/b]
-
-Der Kanal ist ein typisches Forum. Standardmäßig sind Beiträge und veröffentlichte Artikel öffentlich. Mitglieder können durch @!mention oder wall-to-wall posten. Das Einstellen von Fotos und anderen veröffentlichten Beiträgen ist gesperrt. Der Kanal ist im Verzeichnis sichtbar. Mitglieder werden automatisch hinzugefügt.
-
-[b]Eingeschränkt[/b]
-
-Standardmäßig werden alle Beiträge und veröffentlichten Elemente an die Datenschutzgruppe "Freunde" des Channels gesendet. Neue Freunde werden zu dieser privaten Gruppe hinzugefügt. Mitglieder können Beiträge per @!mention oder wall-to-wall posten, aber Beiträge und Antworten können auch von anderen Empfängern des Top-Level-Posts gesehen werden, die keine Mitglieder sind. Der Channel ist im Verzeichnis sichtbar. Mitglieder müssen manuell durch den Forenbesitzer hinzugefügt werden.
-
-[b]Privat[/b]
-
-Standardmäßig werden alle Beiträge und veröffentlichten Nachrichten an Deine Privatsphärengruppe "Freunde" gesendet. Neue Freunde werden zu dieser privaten Gruppe hinzugefügt. Der Eigentümer kann dies außer Kraft setzen und einen öffentlichen Beitrag oder ein öffentliches Element erstellen, falls gewünscht. Mitglieder können das nicht. Sie sind NICHT im Verzeichnis aufgeführt. Nur Deine Verbindungen können Deine anderen Verbindungen sehen. Deine Online-Präsenz ist verborgen. Mitglieder müssen manuell vom Forumsbesitzer hinzugefügt werden. Das Posten per @!mention ist deaktiviert. Beiträge können nur über Wand-zu-Wand-Beiträge erstellt und an Mitglieder der Privatsphärengruppe "Freunde" gesendet werden. Du bist nicht öffentlich sichtbar.
-
-
-[h4]Teilen von Feed[/h4]
-[b]Weitgehend öffentlich[/b]
-
-Ähnlich wie Sozial: Meistens öffentlich, aber für RSS-Feed-Quellen zugeschnitten. Artikel können frei wiederveröffentlicht und weiterverwendet werden. Die Online-Präsenz ist bedeutungslos, daher verborgen. Neue Verbindungen werden automatisch genehmigt.
-
-[b]Eingeschränkt[/b]
-
-Nicht im Verzeichnis aufgeführt. Die Online-Präsenz ist bedeutungslos und daher verborgen. Der Feed wird nur für Mitglieder der Datenschutzgruppe "Freunde" veröffentlicht. Neue Verbindungen werden automatisch zu dieser privaten Gruppe hinzugefügt. Mitglieder müssen manuell vom Kanalbesitzer genehmigt werden.
-
-
-[h4]Für spezielle Zwecke[/h4]
-[b]Speziell - Mitteilungskanal (keine Kommentare)[/b]
-
-Aufgeführt im Verzeichnis. Die Kommunikation ist standardmäßig öffentlich. Die Online-Präsenz ist verborgen. Kommentare oder Rückmeldungen jeglicher Art sind nicht erlaubt, obwohl Verbindungen die Möglichkeit haben, Ihr Profil zu "mögen".
-
-[b]Speziell - Gruppen-Archiv[/b]
-
-Ein öffentliches Forum, das es Mitgliedern erlaubt, Dateien/Fotos/Webseiten zu veröffentlichen.
-
-
-[h4]Benutzerdefinierter/Expertenmodus[/h4]
-Legen Sie alle Datenschutzeinstellungen und Berechtigungen manuell fest, um sie an Ihre speziellen Bedürfnisse anzupassen.
-
diff --git a/doc/de/the_hubzilla_project.md b/doc/de/the_hubzilla_project.md
new file mode 100644
index 000000000..6f7eec745
--- /dev/null
+++ b/doc/de/the_hubzilla_project.md
@@ -0,0 +1,116 @@
+## Das Hubzilla-Projekt
+
+### Hubzilla Verwaltung
+
+Governance bezieht sich auf die Verwaltung eines Projekts und insbesondere darauf, wie diese mit der Konfliktlösung zusammenhängt.
+
+
+
+#### Gemeinschaftliche Verwaltung
+
+Das Projekt wird von der „Gemeinschaft“ verwaltet und Entscheidungen werden von ihr getroffen. Die Governance-Struktur ist noch in der Entwicklung begriffen. Bis zur endgültigen Festlegung der Struktur werden Entscheidungen in der folgenden Reihenfolge getroffen:
+
+
+
+1. Fauler Konsens
+
+ Wenn ein Projektvorschlag in einem der Community-Governance-Foren unterbreitet wird und innerhalb einer „angemessenen“ Zeitspanne ab dem Datum des Vorschlags (wir geben in der Regel allen interessierten Parteien 2 bis 3 Tage Zeit, sich zu äußern) keine ernsthaften Einwände erhoben werden, muss nicht abgestimmt werden und der Vorschlag gilt als angenommen. Zu diesem Zeitpunkt können zwar Bedenken geäußert werden, aber wenn diese in der Diskussion ausgeräumt werden und Lösungsmöglichkeiten angeboten werden, gilt der Vorschlag dennoch als genehmigt.
+
+
+
+2. Veto
+
+ Erfahrene Entwickler, die bereits viele Projekte zugesagt haben, können gegen jede Entscheidung ein Veto einlegen. Die Entscheidung kann nicht weitergeführt werden, bis das Veto aufgehoben oder ein alternativer Vorschlag vorgelegt wird.
+
+
+
+3. Gemeinschaftsabstimmung
+
+ Eine Entscheidung, für die es kein klares Mandat oder keinen klaren Konsens gibt, gegen die aber kein Veto eingelegt wurde, kann der Gemeinschaft zur Abstimmung vorgelegt werden. Zurzeit ist dies eine einfache Volksabstimmung in einem der entsprechenden Gemeinschaftsforen. Zurzeit entscheidet das Volk über das Ergebnis. Dies kann sich in Zukunft ändern, wenn die Gemeinschaft ein „Rats“-Verwaltungsmodell einführt. Dieses Dokument wird zu diesem Zeitpunkt mit den aktualisierten Verwaltungsregeln aktualisiert werden.
+
+
+
+Die Abstimmung in der Gemeinschaft führt nicht immer zu einem angenehmen Ergebnis und kann zu polarisierten Fraktionen in der Gemeinschaft führen (daher werden auch andere Modelle in Betracht gezogen). Wenn der Vorschlag abgelehnt wird, gibt es immer noch verschiedene Möglichkeiten, den Vorschlag mit leicht veränderten Parametern erneut einzureichen (Umwandlung in ein Addon, Umwandlung in eine optionale Funktion, die standardmäßig deaktiviert ist, usw.). Wenn das Interesse an der Funktion groß ist und die Abstimmung „knapp“ ausfällt, kann dies bei den unterlegenen Wählern viele schlechte Gefühle hervorrufen. Bei solchen knappen Abstimmungen wird **dringend empfohlen**, dass der Antragsteller Maßnahmen ergreift, um alle Bedenken auszuräumen, und den Antrag erneut einreicht.
+
+
+
+
+
+#### Datenschutz
+
+F: Wer kann meinen Inhalt sehen?
+
+A: Standardmäßig JEDER im Internet, es sei denn, Sie schränken dies ein. Bei Hubzilla können Sie die von Ihnen gewünschte Datenschutzstufe wählen. Eingeschränkte Inhalte sind für „Spionagenetzwerke“ und Werbetreibende NICHT sichtbar. Sie werden gegen das Abhören durch Außenstehende geschützt - so gut es eben geht. Hub-Administratoren mit ausreichenden Kenntnissen und Geduld können möglicherweise einige private Kommunikationen abhören, aber sie müssen sich dafür anstrengen. Innerhalb von Hubzilla gibt es Privatsphären-Modi, die selbst für erfahrene und entschlossene Hub-Administratoren abhörsicher sind.
+
+F: Kann mein Inhalt zensiert werden?
+
+A: Hubzilla (das Netzwerk) kann Ihre Inhalte NICHT zensieren. Server- und Hub-Administratoren unterliegen den örtlichen Gesetzen und KÖNNEN anstößige Inhalte von ihrer Site/ihrem Hub entfernen. Jeder KANN Hub-Administrator werden, auch Sie, und daher Inhalte veröffentlichen, die andernfalls zensiert werden könnten. Sie KÖNNEN immer noch den örtlichen Gesetzen unterliegen.
+
+
+
+##### Definitionen
+
+**Hubzilla**
+
+Hubzilla, auch als „das Netzwerk“ bezeichnet, ist eine Sammlung von einzelnen Computern/Servern (auch **Hubs** genannt), die miteinander verbunden sind und ein größeres kooperatives Netzwerk bilden.
+
+**Hub**
+
+Ein einzelner Computer oder Server, der mit Hubzilla verbunden ist. Diese werden von einem **Hub-Administrator** bereitgestellt und können öffentlich oder privat, bezahlt oder kostenlos sein.
+
+**Hub-Administrator**
+
+Der Systembetreiber eines einzelnen Hubs.
+
+
+
+##### Richtlinien
+
+**Öffentliche Informationen**
+
+Alle von Ihnen innerhalb von Hubzilla eingestellten Informationen oder Inhalte KÖNNEN öffentlich oder für jeden im Internet sichtbar sein. Soweit dies möglich ist, erlaubt Hubzilla Ihnen, Inhalte zu schützen und einzuschränken, wer sie sehen kann.
+
+Ihr Profilfoto, Ihr Channelname und der Standort (URL oder Netzwerkadresse) Ihres Channels sind für jedermann im Internet sichtbar, und Datenschutzkontrollen haben keinen Einfluss auf die Anzeige dieser Elemente.
+
+Du KANNST zusätzlich andere Profilinformationen zur Verfügung stellen. Alle Informationen, die Sie in Ihrem „Standard“- oder **öffentlichen Profil** angeben, KÖNNEN an andere Hubs in Hubzilla übermittelt werden und zusätzlich im Channel-Verzeichnis angezeigt werden. Sie können die Einsicht in diese Profilinformationen einschränken. Sie können es nur auf Mitglieder Ihres Hubs, nur auf Verbindungen (Freunde) oder auf andere begrenzte Gruppen von Betrachtern beschränken, wie Sie es wünschen. Wenn Sie möchten, dass Ihr Profil eingeschränkt wird, müssen Sie die entsprechenden Datenschutzeinstellungen vornehmen oder einfach KEINE zusätzlichen Informationen angeben.
+
+**Inhalt**
+
+Die von Ihnen bereitgestellten Inhalte (Statusmeldungen, Fotos, Dateien usw.) gehören Ihnen. Die Standardeinstellung von Hubzilla ist, dass Inhalte offen und für jeden im Internet sichtbar veröffentlicht werden (PUBLIC). Sie KÖNNEN dies in Ihren Channel-Einstellungen steuern und die Standardberechtigungen einschränken oder Sie KÖNNEN die Sichtbarkeit jedes einzelnen veröffentlichten Elements separat einschränken (PRIVAT). Die Hubzilla-Entwickler stellen sicher, dass eingeschränkte Inhalte NUR für diejenigen sichtbar sind, die in der Einschränkungsliste aufgeführt sind - so gut sie können.
+
+Inhalte (insbesondere Statusmeldungen), die Sie mit anderen Netzwerken teilen oder die Sie für jeden im Internet sichtbar gemacht haben (PUBLIC), können nicht einfach zurückgenommen werden, nachdem sie veröffentlicht wurden. Sie KÖNNEN mit anderen Netzwerken geteilt und über RSS/Atom-Feeds verfügbar gemacht werden. Sie können auch auf anderen Hubzilla-Sites syndiziert werden. Sie KÖNNEN in anderen Netzwerken und Websites erscheinen und in der Internetsuche sichtbar sein. Wenn Sie dieses Standardverhalten nicht wünschen, passen Sie bitte Ihre Kanaleinstellungen an und schränken Sie ein, wer Ihre Inhalte sehen kann.
+
+**Kommentare und Forenbeiträge**
+
+Kommentare zu Beiträgen, die von anderen erstellt wurden, und Beiträge, die als Forenbeiträge gekennzeichnet sind, gehören Ihnen als Ersteller/Autor, aber die Verbreitung dieser Beiträge unterliegt nicht Ihrer direkten Kontrolle, und Sie geben EINIGE Rechte an diesen Elementen ab. Diese Beiträge/Kommentare KÖNNEN an andere weitergegeben werden und KÖNNEN für jeden im Internet sichtbar sein. Im Falle von Kommentaren kontrolliert der Ersteller der „ersten Nachricht“ in dem Thread (Gespräch), auf den Sie antworten, die Verbreitung aller Kommentare und Antworten auf diese Nachricht. Er ist „Eigentümer“ und hat daher bestimmte Rechte in Bezug auf die gesamte Unterhaltung (einschließlich aller darin enthaltenen Kommentare). Sie können den Kommentar immer noch bearbeiten oder löschen, aber der Eigentümer der Konversation hat auch das Recht, den gesamten Inhalt der Konversation zu bearbeiten, zu löschen, weiterzuverteilen und zu sichern/wiederherzustellen.
+
+**Private Informationen**
+
+Die Hubzilla-Entwickler stellen sicher, dass alle von Ihnen bereitgestellten Inhalte, die als PRIVAT gekennzeichnet sind, nach bestem Wissen und Gewissen gegen Abhören geschützt werden. Private Channel-Inhalte KÖNNEN in der Datenbank jedes beteiligten Hub-Administrators gesehen werden, aber private Nachrichten werden in der Datenbank unkenntlich gemacht. Letzteres bedeutet, dass es sehr schwierig, aber NICHT unmöglich ist, dass diese Inhalte von einem Hub-Administrator gesehen werden können. Private Kanalinhalte und private Nachrichten werden auch aus den E-Mail-Benachrichtigungen entfernt. Die Ende-zu-Ende-Verschlüsselung ist eine optionale Funktion, die selbst von einem entschlossenen Administrator NICHT eingesehen werden kann.
+
+
+
+##### Identitätsschutz
+
+Der Schutz Ihrer Identität ist ein weiterer Aspekt. Da Sie in Hubzilla eine dezentrale Identität haben, geht Ihre Privatsphäre über Ihren Home Hub hinaus. Wenn Sie die vollständige Kontrolle über Ihre Privatsphäre und Sicherheit haben möchten, sollten Sie Ihren eigenen Hub auf einem eigenen Server betreiben. Für viele Menschen ist dies kompliziert und kann ihre technischen Fähigkeiten überfordern. Lassen Sie uns daher einige Vorsichtsmaßnahmen auflisten, die Sie ergreifen können, um Ihre Privatsphäre so gut wie möglich zu schützen.
+
+Eine dezentralisierte Identität hat viele Vorteile und bietet Ihnen viele interessante Funktionen, aber Sie sollten sich der Tatsache bewusst sein, dass Ihre Identität anderen Hubs im Hubzilla-Netzwerk bekannt ist. Einer dieser Vorteile ist, dass andere Kanäle Ihnen maßgeschneiderte Inhalte anbieten und Ihnen erlauben können, private Dinge zu sehen (wie z.B. private Fotos, die andere mit Ihnen teilen möchten). Aus diesem Grund müssen diese Kanäle wissen, wer Sie sind. Aber wir verstehen, dass diese anderen Kanäle manchmal mehr von Ihnen wissen, als Sie vielleicht wünschen. Zum Beispiel das Plug-in Visage, das einem Kanalbesitzer mitteilen kann, wann du das letzte Mal sein Profil besucht hast. Sie können sich ganz einfach von dieser geringen und, wie wir meinen, harmlosen Verfolgung abmelden.
+
+\* Sie können [Do Not Track (DNT)][(http://donottrack.us/)](http://donottrack.us/)?f=&zid=pepecyb@hub.hubzilla.hu) in Ihrem Webbrowser aktivieren. Wir respektieren diesen neuen Vorschlag zum Datenschutz. Alle modernen Browser unterstützen DNT. Sie finden es in den Datenschutzeinstellungen Ihres Browsers oder Sie können das Handbuch Ihres Webbrowsers konsultieren. Die Funktionalität von Hubzilla wird dadurch nicht beeinträchtigt. Diese Einstellung ist wahrscheinlich für die meisten Menschen ausreichend.
+
+*Sie können die Veröffentlichung Ihres Channels in unserem Channel-Verzeichnis [deaktivieren](Einstellungen). Wenn Sie möchten, dass andere Ihren Channel finden, sollten Sie ihnen Ihre Channel-Adresse direkt mitteilen. Wir denken, dass dies ein guter Hinweis darauf ist, dass du zusätzliche Privatsphäre bevorzugst und aktivieren automatisch „Do Not Track“, wenn dies der Fall ist.
+
+\* Sie können einen blockierten Hub haben. Das bedeutet, dass alle Kanäle und Inhalte in diesem Hub nicht öffentlich und für die Außenwelt nicht sichtbar sind. Dies kann nur Ihr Hub-Administrator tun. Wir respektieren dies ebenfalls und aktivieren automatisch „Do Not Track“, wenn es eingestellt ist.
+
+
+
+##### Zensur
+
+Hubzilla ist ein globales Netzwerk, das alle Religionen und Kulturen einschließt. Das bedeutet nicht, dass jedes Mitglied des Netzwerks in Bezug auf strittige Themen genauso denkt wie Sie, und einige Leute könnten sich den von Ihnen geposteten Inhalten GEGENWINDLICH widersetzen. Wenn Sie etwas posten möchten, von dem Sie wissen, dass es möglicherweise nicht von allen akzeptiert wird, ist es am besten, wenn Sie die Öffentlichkeit mit Hilfe der Privatsphäre-Kontrollen auf einen kleinen Freundeskreis beschränken.
+
+Hubzilla als Netzwerkanbieter kann keine Inhalte zensieren. Hub-Administratoren KÖNNEN jedoch Inhalte, die auf ihrem Hub erscheinen, zensieren, um lokalen Gesetzen oder sogar persönlichem Urteilsvermögen zu entsprechen. Ihre Entscheidung ist endgültig. Wenn Sie Probleme mit einem Hub-Administrator haben, können Sie Ihr Konto und Ihre Beiträge auf eine andere Seite verschieben, die Ihren Erwartungen besser entspricht. Bitte überprüfen Sie (regelmäßig) die [Terms of Service](help/TermsOfService) Ihres Hubs, um sich über etwaige Regeln oder Richtlinien zu informieren. Wenn Ihr Inhalt aus illegalem oder problematischem Material besteht, empfehlen wir Ihnen DRINGEND, Ihren eigenen Hub zu betreiben (werden Sie Hub-Administrator). Es kann sein, dass Ihre Inhalte auf einigen Hubs blockiert werden, aber Hubzilla als Netzwerk kann nicht verhindern, dass sie veröffentlicht werden.
+
+Hubzilla EMPFIEHLT, dass Hub-Administratoren eine Frist von 1-2 Tagen zwischen der Warnung eines Account-Inhabers über zu entfernende Inhalte und der physischen Entfernung oder Deaktivierung des Accounts einräumen. Dies gibt dem Inhaltsinhaber die Möglichkeit, seine Channel-Metadaten zu exportieren und auf eine andere Site zu importieren. In seltenen Fällen kann der Inhalt so beschaffen sein, dass die sofortige Kündigung des Kontos gerechtfertigt ist. Dies ist eine Hub-Entscheidung, nicht eine Hubzilla-Entscheidung.
+
+Wenn Sie typischerweise und regelmäßig Inhalte jugendgefährdender oder anstößiger Natur posten, wird Ihnen DRINGEND empfohlen, Ihr Konto als „NSFW“ (Not Safe For Work) zu kennzeichnen. Dadurch wird die Anzeige Ihres Profilfotos im Verzeichnis verhindert, außer für Betrachter, die den „sicheren Modus“ deaktiviert haben. Wenn Ihr Profilfoto von den Verzeichnisadministratoren als nicht jugendfrei oder anstößig eingestuft wird, KANN der Verzeichnisadministrator Ihr Profilfoto als NSFW kennzeichnen. Derzeit gibt es keinen offiziellen Mechanismus, um diese Entscheidung anzufechten oder rückgängig zu machen. Deshalb SOLLTEN Sie Ihr eigenes Konto als NSFW markieren, wenn es für ein allgemeines Publikum unangemessen sein könnte.
+
diff --git a/doc/de/toc.html b/doc/de/toc.html
new file mode 100644
index 000000000..959ee7ffe
--- /dev/null
+++ b/doc/de/toc.html
@@ -0,0 +1,104 @@
+<div class="" id="accordion">
+ <div class="mb-3">
+ <div class="">
+ <h3 class="panel-title">
+ Benutzer
+ </h3>
+ </div>
+ <div id="members" class="doco-section">
+ <div class="vstack">
+ <a href="/help/member/overview">Überblick</a>
+ <a href="/help/member/registration">Login/Registierung</a>
+ <a href="/help/member/accounts_profiles_channels_basics">Konten, Profile und Kanäle</a>
+ <a href="/help/member/posting">Posten</a>
+ <a href="/help/member/the_grid">Das Grid</a>
+ <a href="/help/member/the_stream">Der Stream</a>
+ <a href="/help/member/apps">Apps</a>
+ <a href="/help/member/connections">Verbindungen</a>
+ <a href="/help/member/directory">Verzeichnis</a>
+ <a href="/help/member/blocking_channels">Kanäle blockieren/ignorieren/archivieren/verstecken</a>
+ <a href="/help/member/permissions">Berechtigungen</a>
+ <a href="/help/member/direct_messages">Direktnachrichten</a>
+ <a href="/help/member/mentions">Erwähnungen</a>
+ <a href="/help/member/tags">Tags</a>
+ <a href="/help/member/bookmarks">Bookmarks (Lesezeichen)</a>
+ <a href="/help/member/search">Suche</a>
+ <a href="/help/member/article">Artikel</a>
+ <a href="/help/member/files">Dateien</a>
+ <a href="/help/member/chat_rooms">Chaträume</a>
+ <a href="/help/member/guest_access">Gastzugang</a>
+ <a href="/help/member/privacy_groups">Privacy Gruppen</a>
+ <a href="/help/member/calendar">Kalender</a>
+ <a href="/help/member/addressbook">Adressbuch</a>
+ <a href="/help/member/wikis">Wikis</a>
+ <a href="/help/member/NSFW">Inhaltswarnung/NSFW</a>
+ <a href="/help/member/clone">Klonen</a>
+ <a href="/help/member/websites">Websiten</a>
+ <a href="/help/member/comanche">Comanche Seitenbeschreibungssprache</a>
+ <a href="/help/member/encryption">Verschlüsselung</a>
+ <a href="/help/member/protection_of_privacy">Tipps zum Schutz der Privatsphäre</a>
+ <a href="/help/member/deleting_channel">Kanal löschen</a>
+ <a href="/help/member/delete_account">Account löschen</a>
+ <hr>
+ <a href="/help/bugs">Bugs melden</a>
+ <a href="/help/member/member_faq">FAQ</a>
+ </div>
+ </div>
+ </div>
+ <div class="mb-3">
+ <div class="">
+ <h3 class="panel-title">
+ Tutorials
+ </h3>
+ </div>
+ <div id="tutorials" class="doco-section">
+ <div class="vstack">
+ <a href="/help/tutorials/step_with_hubzilla">Mit Hubzilla Schritt für Schritt ins Fediverse</a>
+ <a href="/help/tutorials/customise_look">Hubzilla optisch anpassen</a>
+ <a href="/help/tutorials/DerivedTheme">Hubzilla optisch anpassenEin abgeleitetes Thema erstellen</a>
+ </div>
+ </div>
+ </div>
+ <div class="mb-3">
+ <div class="">
+ <h3 class="panel-title">
+ Administratoren
+ </h3>
+ </div>
+ <div id="administrators" class="doco-section">
+ <div class="vstack">
+ <a class="" href="/help/adminmanual/manual_for_administrators">Handbuch für Administratoren</a>
+ </div>
+ </div>
+ </div>
+ <div class="mb-3">
+ <div class="">
+ <h3 class="panel-title">
+ Entwickler
+ </h3>
+ </div>
+ <div id="developers" class="doco-section">
+ <div class="vstack">
+ <a class="" href="/help/developer/developers_guide">Entwicklerhandbuch</a>
+ </div>
+ </div>
+ </div>
+ <div class="mb-3">
+ <div class="">
+ <h3 class="panel-title">
+ Über
+ </h3>
+ </div>
+ <div id="about" class="doco-section">
+ <div class="vstack">
+ <a href="/help/about">Über Hubzilla</a>
+ <a href="/help/functions">Funktionen</a>
+ <a href="/help/glossary">Glossar</a>
+ <a href="/help/ui">Benutzeroberfläche</a>
+ <a href="/help/the_hubzilla_project">Das Hubzilla-Projekt</a>
+ <a href="/help/credits">Credits</a>
+ <a href="/help/about_hub.bb">Über diesen Hub</a>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/doc/de/tutorials/DerivedTheme.md b/doc/de/tutorials/DerivedTheme.md
new file mode 100644
index 000000000..1aca621d7
--- /dev/null
+++ b/doc/de/tutorials/DerivedTheme.md
@@ -0,0 +1,96 @@
+### Ein abgeleitetes Thema erstellen
+
+**Lektion 1**
+Ein abgeleitetes Thema übernimmt die meisten Einstellungen des „übergeordneten“ Themas und ermöglicht es Ihnen, einige Dinge nach Ihren Wünschen zu ändern, ohne ein ganzes Themenpaket zu erstellen.
+Um ein abgeleitetes Thema zu erstellen, wählen Sie zunächst einen Namen. In unserem Beispiel nennen wir unser Thema „mytheme“. Hoffentlich werden Sie etwas kreativer sein. Wo immer Sie in diesem Dokument „mytheme“ sehen, ersetzen Sie es durch den von Ihnen gewählten Namen.
+
+**Verzeichnisstruktur**
+
+Zuerst müssen Sie eine Verzeichnisstruktur für das Thema erstellen. Wir werden es einfach halten. Wir brauchen ein php-Verzeichnis und ein css-Verzeichnis. Hier sind die Unix/Linux-Befehle, um dies zu tun. Gehen Sie davon aus, dass 'mywebsite' Ihr oberster Hubzilla-Ordner ist.
+
+```
+cd mywebsite
+mkdir view/theme/mytheme
+mkdir view/theme/mytheme/css
+mkdir view/theme/mytheme/php
+```
+
+Sehr gut. Jetzt brauchen wir noch ein paar Dateien. Die erste ist Ihre Theme-Info-Datei, die das Theme beschreibt.
+Sie heißt view/theme/mytheme/php/theme.php (cleverer Name, oder?)
+Fügen Sie darin die folgenden Informationen ein - bearbeiten Sie sie nach Bedarf
+
+```
+<?php
+
+/**
+ * * Name: Mytheme
+ * * Description: Beispiel Abgeleitetes Thema
+ * * Version: 1.0
+ * * Author: Ihr Name
+ * * Compat: Red [*]
+ *
+ */
+
+Funktion mytheme_init(&$a) {
+
+ App::$theme_info['extends'] = 'redbasic';
+}
+```
+
+Denken Sie daran, die Funktion mytheme_init mit dem Namen Ihres Themas zu benennen. In diesem Fall werden wir das Thema 'redbasic' erweitern.
+
+Erstellen Sie nun eine weitere Datei. Wir nennen dies eine PCSS-Datei, aber in Wirklichkeit ist es eine PHP-Datei.
+
+Die Datei heißt view/theme/mytheme/php/style.php
+Fügen Sie darin Folgendes ein:
+
+```
+<?php
+
+require_once('view/theme/redbasic/php/style.php');
+
+echo @file_get_contents('view/theme/mytheme/css/style.css');
+```
+
+Das war's. Dies weist die Software an, zuerst die PCSS-Informationen für das redbasic-Theme zu lesen und dann unsere CSS-Datei zu lesen, die nur aus den Änderungen besteht, die wir an unserem übergeordneten Thema (redbasic) vornehmen wollen.
+
+Erstellen Sie nun die eigentliche CSS-Datei für Ihr Thema. Legen Sie sie in view/theme/mytheme/css/style.css ab (wo wir der Software gerade gesagt haben, dass sie danach suchen soll). In unserem Beispiel ändern wir nur die Hintergrundfarbe des Körpers, damit Sie sehen können, dass es funktioniert. Sie können jedes CSS verwenden, das Sie möchten.
+
+```
+body {
+ background-color: #DDD;
+}
+```
+
+Sie haben soeben erfolgreich ein abgeleitetes Thema erstellt. Dieses muss im Adminbereich „Themen“ aktiviert werden und kann dann von jedem auf der Website verwendet werden, indem es unter Einstellungen->Anzeigeeinstellungen als Standardthema ausgewählt wird.
+
+**Lektion 2**
+
+Wenn Sie die redbasic-Schemata für Ihr abgeleitetes Thema verwenden möchten, müssen Sie etwas mehr tun.
+
+Machen Sie alles wie oben, aber erstellen Sie nicht view/theme/mytheme/php/style.php, sondern kopieren Sie stattdessen view/theme/redbasic/php/style.php nach view/theme/mytheme/php/style.php. Ändern Sie diese Datei und entfernen Sie diese beiden Zeilen (oder kommentieren Sie sie aus):
+
+```
+if(local_channel() && App::$channel && App::$channel['channel_theme'] != 'redbasic')
+ set_pconfig(local_channel(), 'redbasic', 'schema', '---');
+```
+
+Fügen Sie außerdem diese Zeile am Ende ein:
+
+```
+echo @file_get_contents('view/theme/mytheme/css/style.css');
+```
+
+Um den Schemaselektor anzuzeigen, müssen Sie view/theme/redbasic/tpl/theme_settings.tpl nach view/theme/mytheme/tpl/theme_settings.tpl kopieren. Ändern Sie diese Datei und ersetzen Sie die Zeilen:
+
+```
+{{if $theme == redbasic}}
+{{include file=„field_select.tpl“ field=$schema}}
+{{/if}}
+```
+
+mit:
+
+```
+{{include file=„field_select.tpl“ field=$schema}}
+``` \ No newline at end of file
diff --git a/doc/de/tutorials/customise_look.md b/doc/de/tutorials/customise_look.md
new file mode 100644
index 000000000..0b24fe832
--- /dev/null
+++ b/doc/de/tutorials/customise_look.md
@@ -0,0 +1,221 @@
+### Hubzilla optisch anpassen
+
+Nach dem Anlegen eines Kanals, ist dessen Optik nicht besonders ansprechend.
+
+![Opt 01](/help/de/tutorials/pic/tut_opt01.png)
+
+#### Apps anpinnen
+
+Als erstes pinnen Sie die wichtigsten Apps an die obere Navigationsleiste: „Beitrag schreiben“, „Kanal“, „Stream“, „Verbindungen“ und „Öffentlicher Beitrags-Stream“.
+
+Dazu wählen Sie im App-Menü (⋮ oben rechts) den untersten Menüeintrag „+ Apps“. Damit gelangen Sie in die App-Einstellung und sofort bei den bereits installierten Apps. Auf dieser Seite klicken Sie nun bei jeder gewünschten App auf das kleine Pin-Nadel-Symbol und sehen dann auch sofort, wie das jeweilige Icon oben in der Navigationsleiste erscheint.
+
+![Opt 02](/help/de/tutorials/pic/tut_opt02.png)
+
+![Opt 03](/help/de/tutorials/pic/tut_opt03.png)
+
+![Opt 04](/help/de/tutorials/pic/tut_opt04.png)
+
+#### Anzeige-Einstellungen
+
+Jetzt geht es an die Einstellungen für die Anzeige. Dafür klicken Sie im Hauptmenü (oben links, wo das eigene Avatarbild zu sehen ist) auf den Eintrag „Einstellungen“. In der linken Seitenleiste sind nun die verschiedenen Einstellungskategorien zu sehen. Hier wählen Sie „Anzeige-Einstellungen“.
+
+![Opt 05](/help/de/tutorials/pic/tut_opt05.png)
+
+![Opt 05a](/help/de/tutorials/pic/tut_opt05a.png)
+
+Die Seite für die Anzeige-Einstellungen wird angezeigt. Da Sie das Design anpassen wollen, ist der Tab „Benutzerdefinierte Design-Einstellungen“ die richtige Wahl.
+
+![Opt 06](/help/de/tutorials/pic/tut_opt06.png)
+
+![Opt 07](/help/de/tutorials/pic/tut_opt07.png)
+
+Die Eingabemaske weist zunächst aber nur wenige Möglichkeiten auf (fünf Einstellungen). Die letzte Einstellung ist diejenige, bei der Sie den Schalter als erstes einschalten müssen: „Erweiterte Einstellungen anzeigen“. Auf „Absenden“ klicken und erneut den Tab „Benutzerdefinierte Design-Einstellungen“ aufrufen. Nun sind viel mehr Einstellungen zu sehen.
+
+![Opt 08](/help/de/tutorials/pic/tut_opt08.png)
+
+![Opt 09](/help/de/tutorials/pic/tut_opt09.png)
+
+Als Beispiel werden hier einmal die Farben abgeändert, die Größe der Avatare im Stream angepasst und ein Hintergrundbild festgelegt.
+
+Unter den Haupteinstellungen finden Sie die Einstellungen für die Farben des Farbschemas. Unter jedem Eingabefeld ist als kleiner Kreis die Standard-Farbe angezeigt. Bei der Wahl einer eigenen Farbe, ist es sinnvoll sich zumindest bezüglich der Helligkeit ungefähr an der Standard-Farbe zu orientieren. Klickt man in eines der Eingabefelder, öffnet sich ein Farbwahl-Dialog, über welchen man nun die Farbe festlegen kann. Als Beispiel hier ein grünes Farbschema bei welchem die grundlegenden Farben geändert werden: „Primary theme color“, also die Grundfarbe des Themes, „Success theme color“, das ist die Farbe z.B. für anklickbare Links, „Info theme color“, die z.B. als Farbe für markierte Menüeinträge als Hintergrundfarbe angezeigt wird, sowie die „Hintergrundfarbe der Navigationsleiste“. Alle anderen Farben werden im Beispiel nicht verändert. Die Farbwahl ist natürlich jedem selbst überlassen.
+
+![Opt 10](/help/de/tutorials/pic/tut_opt10.png)
+
+![Opt 11](/help/de/tutorials/pic/tut_opt11.png)
+
+![Opt 12](/help/de/tutorials/pic/tut_opt12.png)
+
+![Opt 13](/help/de/tutorials/pic/tut_opt13.png)
+
+Schließlich die „Größe der Avatare von Themenstartern“ auf 48 Pixel festlegen.
+
+![Opt 14](/help/de/tutorials/pic/tut_opt14.png)
+
+Fehlt noch das Hintergrundbild. Dieses sollte relativ groß sein, ungefähr der Größe des hauptsächlich genutzten Bildschirms entsprechend. Außerdem ist es empfehlenswert, ein eher helles bzw. blasses Bild zu verwenden (ggf. mit dem Grafikprogramm aufhellen und den Kontrast verringern), damit es die eigentlichen Inhalte nicht „erschlägt“.
+
+Das Hintergrundbild muss irgendwo über eine URL erreichbar sein. Da bietet es sich an, das Bild in die Dateien (Cloud) des eigenen Kanals hochzuladen und von dort aus zu verwenden.
+
+Dafür öffnet man im App-Menü die App „Dateien“. Hier kann man sich, wenn man mag, einen extra Ordner anlegen (bitte beachten, dass der Ordner und auch das dort hochgeladene Bild öffentlich zugreifbar sind… dafür die Zugriffsrechte mit dem kleinen Vorhangschloss, dem Privacy Tool, evtl. anpassen) und anschließend das Bild hochladen.
+
+![Opt 15](/help/de/tutorials/pic/tut_opt15.png)
+
+![Opt 16](/help/de/tutorials/pic/tut_opt16.png)
+
+![Opt 17](/help/de/tutorials/pic/tut_opt17.png)
+
+![Opt 18](/help/de/tutorials/pic/tut_opt18.png)
+
+![Opt 19](/help/de/tutorials/pic/tut_opt19.png)
+
+![Opt 20](/help/de/tutorials/pic/tut_opt20.png)
+
+![Opt 21](/help/de/tutorials/pic/tut_opt21.png)
+
+![Opt 22](/help/de/tutorials/pic/tut_opt22.png)
+
+![Opt 23](/help/de/tutorials/pic/tut_opt23.png)
+
+![Opt 24](/help/de/tutorials/pic/tut_opt24.png)
+
+Nach dem Hochladen wird das Bild in der Dateiliste angezeigt. Ein Rechtsklick auf den Eintrag und die Auswahl, die URL zu kopieren befördert die URL für das Bild in die Zwischenablage.
+
+![Opt 25](/help/de/tutorials/pic/tut_opt25.png)
+
+Zurück bei „Benutzerdefinierte Design-Einstellungen“ kann man nun die URL in das Eingabefeld „Hintergrundbild“ einfügen.
+
+![Opt 26](/help/de/tutorials/pic/tut_opt26.png)
+
+![Opt 27](/help/de/tutorials/pic/tut_opt27.png)
+
+Ein letzter Klick und Hubzilla erstrahlt in der neuen Optik.
+
+![Opt 28](/help/de/tutorials/pic/tut_opt28.png)
+
+![Opt 29](/help/de/tutorials/pic/tut_opt29.png)
+
+#### PDL-Editor
+
+##### Grundlagen
+
+Verschiedene Apps und Grundfunktionen von Hubzilla basieren auf eigens dafür gestalteten Webseiten. Der Nutzer kommt mit den dahinter liegenden Mechanismen nicht in Berührung, er nutzt diese Seiten einfach.
+
+Ruft man beispielsweise die App „Kanal“ auf, so wird einem der eigene Kanal angezeigt.
+
+![PDL 01](/help/de/tutorials/pic/pdle01.png)
+
+Ganz oben kann man das Banner des Kanals sehen. In dieses Banner sind der Kanalname und die Kanaladresse (Handle) eingebettet.
+
+Darunter befindet sich die Navigationsleiste mit dem Hauptmenü, dem Titel des Hub, ggf. angepinnten Apps und dem App-Menü.
+
+Interessant wird es dann aber im Bereich unter der Navigationsleiste. Hier sind bei den verschiedenen Apps die größten Unterschiede festzustellen. Bei der Kanal-Seite findet sich in der linken Seitenleiste (im unveränderten Standard-Zustand) zuoberst eine Karte mit den Kanalinformationen (Banner, Profilbild, Kurzbeschreibung, Profilinfos).
+
+In der Mitte, im Inhaltsbereich, werden die von diesem Kanal erstellten Inhalte angezeigt.
+
+Unter der Profilinfo-Karte befindet sich in der linken Seitenleiste eine Karte mit einem Teil der Verbindungen (beim Aufruf fremder Kanäle werden hier die gemeinsamen Kontakte angezeigt).
+
+Darunter wiederum befindet sich eine Karte mit den Archiven der Inhalte (oberste Ebene sind die Jahre, eine Gliederungsebene darunter die Monate). Wählt man ein Archiv aus, so werden im Inhaltsbereich nur die Inhalte angezeigt, die im gewählten Archivzeitraum veröffentlicht wurden.
+
+Unter der Archivkarte befindet sich dann die Karte der Kategorien. Hier werden sämtliche Kategorien, unter welchen Inhalte veröffentlicht wurden, aufgeführt. Ein Klick auf eine solche Kategorie führt dazu, dass im Inhaltsbereich sämtliche Inhalte angezeigt werden, die von dem Kanal unter der entsprechenden Kategorie veröffentlicht wurden.
+
+Unter der Archiv-Karte befindet sich die Karte mit der Schlagwörter-Wolke, welche die genutzten Hashtags anzeigt und mit denen man den Inhalt des Inhaltsbereichs (Artikel des Kanals) filtern kann.
+
+In der rechten Seitenleiste werden, sofern ungesehene Benachrichtigungen vorhanden sind, diese in einer weiteren Karte gezeigt.
+
+So ist der "Normalzustand".
+
+##### Module mit dem PDL-Editor bearbeiten
+
+Die verschiedenen Seiten, die man über Apps erreichen kann, werden auch als "Module" bezeichnet.
+
+Das Aussehen dieser Seiten kann man nun als Nutzer in großen Teilen anpassen und gestalten. Intern wird der Aufbau einer solchen Seite durch eine PDL-Datei bestimmt. Diese Dateien sind Layout-Dateien, welche die Comanche Seitenbeschreibungssprache nutzen.
+
+Damit sich der Nutzer nicht mit einer solchen Sprache auseinandersetzen muss, gibt es die App "PDL Editor", mit welchem man den Seitenaufbau mit einem GUI verändern / erstellen kann.
+
+Die App muss man zunächst installieren und aktivieren. Dann kann man sie aus dem App-Menü heraus aufrufen.
+
+Ruft man den PDL-Editor auf, so wird als Standard die Seitenstruktur des HQ angezeigt.
+
+Zentral am unteren Bildschirmrand findet man das Hauptmenü des PDL-Editors. Hier gibt es die Einträge
+
+"MODULES", "TEMPLATES", "ITEMS", "SOURCE" und "APPLY".
+
+Aus dem Menü "MODULES" kann man das zu bearbeitende Modul (das entspricht der zu bearbeitenden Seite) auswählen.
+
+Angenommen, Sie möchten die Kanal-Seite (wie sie einem selbst, aber auch Besuchern angezeigt wird) anpassen, so wählt man hier das Modul "channel" aus.
+
+![PDL 02](/help/de/tutorials/pic/pdle02.png)
+
+Die PDL-Datei für die Kanalseite wird geladen und man sieht die entsprechenden, gerade beschriebenen Bestandteile ("ITEMS") dieser Seite.
+
+![PDL 03](/help/de/tutorials/pic/pdle03.png)
+
+Angenommen, Sie möchten nun unsere Kanalseite mit einer Anzeige der Uhrzeit in der rechten Seitenleiste "verfeinern", so wählt man unter "ITEMS" das Item "CLOCK" aus, "ergreift" es mit dem Mauszeiger am Kreuzpfeil-Symbol und zieht es nach rechts in die Seitenleiste.
+
+![PDL 04](/help/de/tutorials/pic/pdle04.png)
+
+![PDL 05](/help/de/tutorials/pic/pdle05.png)
+
+Damit die Änderungen auch übernommen werden, klickt man anschließend auf "APPLY" im Hauptmenü.
+
+![PDL 06](/help/de/tutorials/pic/pdle06.png)
+
+Ruft man nun die Kanalseite auf, so erscheint eine Karte mit der aktuellen Uhrzeit in der rechten Seitenleiste.
+
+![PDL 07](/help/de/tutorials/pic/pdle07.png)
+
+Auf diese Weise kann man alle Seiten, die man unter "MODULES" findet, nach dem eigenen Geschmack anpassen.
+
+Hat man seine Seite angepasst und sie ist irgendwie "so völlig zerhackstückt": Kein Grund zur Panik! Im Hauptmenü findet man bei geänderten Layoutseiten den zusätzlichen Eintrag "RESET". Ein Klick darauf setzt das Seitenlayout auf den Hubzilla-Standard zurück.
+
+Hier werden jetzt aber nicht sämtliche Items vorgestellt... jeder darf ein wenig experimentieren. Die meisten haben einen erklärenden Titel.
+
+Klickt man im PDL-Hauptmenü auf "SOURCE", so wird einem der Quelltext des aktuellen Layouts angezeigt. Ein Blick hier hinein hilft, mit PDL vertraut zu werden. Außerdem sind hier auch Änderungen direkt im Quelltext möglich... falls irgendwas nicht über die "ITEMS" erreichbar ist. Hierfür sollte man sich aber mit der Seitenauszeichnungssprache, den Blöcken und Modulen vorher vertraut machen.
+
+![PDL 08](/help/de/tutorials/pic/pdle08.png)
+
+##### PDL-Editor für Fortgeschrittene
+
+Angenommen, man hat ein paar Artikel erstellt und möchte einige davon über ein Menü in der rechten Seitenleiste der Kanalseite zugänglich machen.
+
+Das ist durchaus machbar.
+
+Doch dafür benötigt man zunächst einmal ein Menü. Um Menüs zu erstellen, muss man allerdings die App "Webseiten" installieren und aktivieren, denn das Erstellen von Menüs ist Teil der Webseiten-Funktionalität. Also selbst wenn man keine Webseiten in seinem Kanal erstellen möchte, braucht man zum Erstellen von Menüs die App "Webseiten". Wobei... so ganz stimmt das nicht. Man kann den Menüeditor auch anders erreichen, als über die App "Webseiten". Dafür gibt man `<url-des-hub>/menu/<kanalname>` ein. Nun landet man auch in der Menü-"App". Einfacher ist es allerdings mit der Webseiten-App.
+
+![PDL 09](/help/de/tutorials/pic/pdle09.png)
+
+Ein Klick auf "Erstelle" öffnet den Menü-Editor.
+
+![PDL 10](/help/de/tutorials/pic/pdle10.png)
+
+Hier muss man nun einen passenden Namen (über den man das Menü später ansprechen kann) und (optional) einen Titel für das Menü eingeben (dieser ist später auf der Webseite zu sehen).
+
+Danach klickt man auf "Absenden und fortfahren".
+
+Nun landet man im Link-Editor des gerade erstellten Menüs. Hier gibt man den Titel des Menüeintrags und die dazugehörige URL ein. Man kann auch die Reihenfolge der Sortierung der Menüeinträge über das Feld "Reihenfolge in der Liste" festlegen. Hat man die Eingabe erledigt und klickt auf "Absenden und fortfahren", kann man anschließend einen weiteren Menüeintrag eingeben. Ein Klick auf "Absenden und fertigstellen", fügt den Eintrag hinzu und beendet den Menüeditor (man kann Menüs selbstverständlich auch im nachhinein noch bearbeiten).
+
+Jetzt erscheint das neue Menü in der Liste der Menüs.
+
+![PDL 11](/help/de/tutorials/pic/pdle11.png)
+
+Jetzt zurück zum PDL-Editor und dort das Kanal-Modul aufrufen.
+
+Nun gibt es wieder zwei Möglichkeiten. Entweder, man öffnet den Quelltexteditor "SOURCE" und gibt den Eintrag für die Menükarte an der passenden Stelle per Hand ein...
+
+Hier wählt man, wenn das Menü in der rechten Seitenleiste erscheinen soll, die Region "right_aside" und gibt dort als neue Zeile `[menu]artikelmenu[/menu]` ein.
+
+![PDL 12](/help/de/tutorials/pic/pdle12.png)
+
+Nun noch auf "Submit" klicken und schon erscheint die neue Karte im visuellen PDL-Editor.
+
+![PDL 13](/help/de/tutorials/pic/pdle13.png)
+
+Mit "APPLY" übernehmen... und dann ist das Menü auf der Kanal-Webseite.
+
+![PDL 14](/help/de/tutorials/pic/pdle14.png)
+
+Die zweite Methode (mit der man nicht die richtige Stelle im Quelltext suchen muss) ist es, im PDL-Editor einfach ein beliebiges Item an die Stelle zu ziehen, wo das Menü erscheinen soll. Dann klickt man auf den "Edit"-Button bei diesem Item, ändert den vorhandenen Eintrag auf `[menu]artikelmenu[/menu]` und klickt auf "Submit". Dann noch ein "APPLY" und man hat das selbe Ergebnis.
+
+![PDL 15](/help/de/tutorials/pic/pdle15.png)
+
+Viel Spaß beim Experimentieren!
diff --git a/doc/de/tutorials/pic/02.png b/doc/de/tutorials/pic/02.png
new file mode 100644
index 000000000..38cf70c49
--- /dev/null
+++ b/doc/de/tutorials/pic/02.png
Binary files differ
diff --git a/doc/de/tutorials/pic/03.png b/doc/de/tutorials/pic/03.png
new file mode 100644
index 000000000..3164d0d17
--- /dev/null
+++ b/doc/de/tutorials/pic/03.png
Binary files differ
diff --git a/doc/de/tutorials/pic/04.png b/doc/de/tutorials/pic/04.png
new file mode 100644
index 000000000..1229c0a2e
--- /dev/null
+++ b/doc/de/tutorials/pic/04.png
Binary files differ
diff --git a/doc/de/tutorials/pic/05.png b/doc/de/tutorials/pic/05.png
new file mode 100644
index 000000000..434a4abbd
--- /dev/null
+++ b/doc/de/tutorials/pic/05.png
Binary files differ
diff --git a/doc/de/tutorials/pic/06.png b/doc/de/tutorials/pic/06.png
new file mode 100644
index 000000000..544b08694
--- /dev/null
+++ b/doc/de/tutorials/pic/06.png
Binary files differ
diff --git a/doc/de/tutorials/pic/07.png b/doc/de/tutorials/pic/07.png
new file mode 100644
index 000000000..25e7063fb
--- /dev/null
+++ b/doc/de/tutorials/pic/07.png
Binary files differ
diff --git a/doc/de/tutorials/pic/08.png b/doc/de/tutorials/pic/08.png
new file mode 100644
index 000000000..164aad0a0
--- /dev/null
+++ b/doc/de/tutorials/pic/08.png
Binary files differ
diff --git a/doc/de/tutorials/pic/09.png b/doc/de/tutorials/pic/09.png
new file mode 100644
index 000000000..767d45244
--- /dev/null
+++ b/doc/de/tutorials/pic/09.png
Binary files differ
diff --git a/doc/de/tutorials/pic/10.png b/doc/de/tutorials/pic/10.png
new file mode 100644
index 000000000..280d5d22c
--- /dev/null
+++ b/doc/de/tutorials/pic/10.png
Binary files differ
diff --git a/doc/de/tutorials/pic/11.png b/doc/de/tutorials/pic/11.png
new file mode 100644
index 000000000..c738c1a80
--- /dev/null
+++ b/doc/de/tutorials/pic/11.png
Binary files differ
diff --git a/doc/de/tutorials/pic/12.png b/doc/de/tutorials/pic/12.png
new file mode 100644
index 000000000..486e3e81a
--- /dev/null
+++ b/doc/de/tutorials/pic/12.png
Binary files differ
diff --git a/doc/de/tutorials/pic/13.png b/doc/de/tutorials/pic/13.png
new file mode 100644
index 000000000..47aa44c0e
--- /dev/null
+++ b/doc/de/tutorials/pic/13.png
Binary files differ
diff --git a/doc/de/tutorials/pic/14.png b/doc/de/tutorials/pic/14.png
new file mode 100644
index 000000000..fcc6684cc
--- /dev/null
+++ b/doc/de/tutorials/pic/14.png
Binary files differ
diff --git a/doc/de/tutorials/pic/15.png b/doc/de/tutorials/pic/15.png
new file mode 100644
index 000000000..69fa838ed
--- /dev/null
+++ b/doc/de/tutorials/pic/15.png
Binary files differ
diff --git a/doc/de/tutorials/pic/16.png b/doc/de/tutorials/pic/16.png
new file mode 100644
index 000000000..b7f0bd1ec
--- /dev/null
+++ b/doc/de/tutorials/pic/16.png
Binary files differ
diff --git a/doc/de/tutorials/pic/17.png b/doc/de/tutorials/pic/17.png
new file mode 100644
index 000000000..9501e6ab9
--- /dev/null
+++ b/doc/de/tutorials/pic/17.png
Binary files differ
diff --git a/doc/de/tutorials/pic/18.png b/doc/de/tutorials/pic/18.png
new file mode 100644
index 000000000..deb18f7de
--- /dev/null
+++ b/doc/de/tutorials/pic/18.png
Binary files differ
diff --git a/doc/de/tutorials/pic/19.png b/doc/de/tutorials/pic/19.png
new file mode 100644
index 000000000..833304712
--- /dev/null
+++ b/doc/de/tutorials/pic/19.png
Binary files differ
diff --git a/doc/de/tutorials/pic/20.png b/doc/de/tutorials/pic/20.png
new file mode 100644
index 000000000..5ca43f3cd
--- /dev/null
+++ b/doc/de/tutorials/pic/20.png
Binary files differ
diff --git a/doc/de/tutorials/pic/fedidb.png b/doc/de/tutorials/pic/fedidb.png
new file mode 100644
index 000000000..988658aff
--- /dev/null
+++ b/doc/de/tutorials/pic/fedidb.png
Binary files differ
diff --git a/doc/de/tutorials/pic/fedieverse-observer.png b/doc/de/tutorials/pic/fedieverse-observer.png
new file mode 100644
index 000000000..a80cbded9
--- /dev/null
+++ b/doc/de/tutorials/pic/fedieverse-observer.png
Binary files differ
diff --git a/doc/de/tutorials/pic/hzreg01.png b/doc/de/tutorials/pic/hzreg01.png
new file mode 100644
index 000000000..e11879307
--- /dev/null
+++ b/doc/de/tutorials/pic/hzreg01.png
Binary files differ
diff --git a/doc/de/tutorials/pic/hzreg01a.png b/doc/de/tutorials/pic/hzreg01a.png
new file mode 100644
index 000000000..714f3d9b4
--- /dev/null
+++ b/doc/de/tutorials/pic/hzreg01a.png
Binary files differ
diff --git a/doc/de/tutorials/pic/hzreg02.png b/doc/de/tutorials/pic/hzreg02.png
new file mode 100644
index 000000000..8a493147e
--- /dev/null
+++ b/doc/de/tutorials/pic/hzreg02.png
Binary files differ
diff --git a/doc/de/tutorials/pic/hzreg03.png b/doc/de/tutorials/pic/hzreg03.png
new file mode 100644
index 000000000..bbe4c4451
--- /dev/null
+++ b/doc/de/tutorials/pic/hzreg03.png
Binary files differ
diff --git a/doc/de/tutorials/pic/hzreg04.png b/doc/de/tutorials/pic/hzreg04.png
new file mode 100644
index 000000000..bdc00f637
--- /dev/null
+++ b/doc/de/tutorials/pic/hzreg04.png
Binary files differ
diff --git a/doc/de/tutorials/pic/nomadapp.png b/doc/de/tutorials/pic/nomadapp.png
new file mode 100644
index 000000000..f62e3d96d
--- /dev/null
+++ b/doc/de/tutorials/pic/nomadapp.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle01.png b/doc/de/tutorials/pic/pdle01.png
new file mode 100644
index 000000000..3bb604397
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle01.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle02.png b/doc/de/tutorials/pic/pdle02.png
new file mode 100644
index 000000000..9efdf5bc8
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle02.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle03.png b/doc/de/tutorials/pic/pdle03.png
new file mode 100644
index 000000000..e82d0c859
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle03.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle04.png b/doc/de/tutorials/pic/pdle04.png
new file mode 100644
index 000000000..b5b409d69
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle04.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle05.png b/doc/de/tutorials/pic/pdle05.png
new file mode 100644
index 000000000..c78680649
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle05.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle06.png b/doc/de/tutorials/pic/pdle06.png
new file mode 100644
index 000000000..aa51dd345
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle06.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle07.png b/doc/de/tutorials/pic/pdle07.png
new file mode 100644
index 000000000..1a314ce73
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle07.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle08.png b/doc/de/tutorials/pic/pdle08.png
new file mode 100644
index 000000000..c5b62842f
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle08.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle09.png b/doc/de/tutorials/pic/pdle09.png
new file mode 100644
index 000000000..5a328cbbe
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle09.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle10.png b/doc/de/tutorials/pic/pdle10.png
new file mode 100644
index 000000000..812f28230
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle10.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle11.png b/doc/de/tutorials/pic/pdle11.png
new file mode 100644
index 000000000..21cbdf6c4
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle11.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle12.png b/doc/de/tutorials/pic/pdle12.png
new file mode 100644
index 000000000..308222766
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle12.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle13.png b/doc/de/tutorials/pic/pdle13.png
new file mode 100644
index 000000000..b857ae8d7
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle13.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle14.png b/doc/de/tutorials/pic/pdle14.png
new file mode 100644
index 000000000..0d479dea8
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle14.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pdle15.png b/doc/de/tutorials/pic/pdle15.png
new file mode 100644
index 000000000..e075df077
--- /dev/null
+++ b/doc/de/tutorials/pic/pdle15.png
Binary files differ
diff --git a/doc/de/tutorials/pic/pubsites.png b/doc/de/tutorials/pic/pubsites.png
new file mode 100644
index 000000000..6fe2e1e71
--- /dev/null
+++ b/doc/de/tutorials/pic/pubsites.png
Binary files differ
diff --git a/doc/de/tutorials/pic/rega.png b/doc/de/tutorials/pic/rega.png
new file mode 100644
index 000000000..9212a4f4b
--- /dev/null
+++ b/doc/de/tutorials/pic/rega.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt01.png b/doc/de/tutorials/pic/tut_opt01.png
new file mode 100644
index 000000000..a9ff138f2
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt01.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt02.png b/doc/de/tutorials/pic/tut_opt02.png
new file mode 100644
index 000000000..63ac59f82
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt02.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt03.png b/doc/de/tutorials/pic/tut_opt03.png
new file mode 100644
index 000000000..6b4cf07ee
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt03.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt04.png b/doc/de/tutorials/pic/tut_opt04.png
new file mode 100644
index 000000000..6d7a74e73
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt04.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt05.png b/doc/de/tutorials/pic/tut_opt05.png
new file mode 100644
index 000000000..391325332
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt05.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt05a.png b/doc/de/tutorials/pic/tut_opt05a.png
new file mode 100644
index 000000000..9b5f2771a
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt05a.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt06.png b/doc/de/tutorials/pic/tut_opt06.png
new file mode 100644
index 000000000..ab458fea0
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt06.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt07.png b/doc/de/tutorials/pic/tut_opt07.png
new file mode 100644
index 000000000..bb50b8abd
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt07.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt08.png b/doc/de/tutorials/pic/tut_opt08.png
new file mode 100644
index 000000000..3f6af6793
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt08.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt09.png b/doc/de/tutorials/pic/tut_opt09.png
new file mode 100644
index 000000000..2f99755ee
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt09.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt10.png b/doc/de/tutorials/pic/tut_opt10.png
new file mode 100644
index 000000000..959daa2fa
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt10.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt11.png b/doc/de/tutorials/pic/tut_opt11.png
new file mode 100644
index 000000000..367a48ab3
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt11.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt12.png b/doc/de/tutorials/pic/tut_opt12.png
new file mode 100644
index 000000000..42e52a6c0
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt12.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt13.png b/doc/de/tutorials/pic/tut_opt13.png
new file mode 100644
index 000000000..4bf7b012c
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt13.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt14.png b/doc/de/tutorials/pic/tut_opt14.png
new file mode 100644
index 000000000..a2b8e1e72
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt14.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt15.png b/doc/de/tutorials/pic/tut_opt15.png
new file mode 100644
index 000000000..690203699
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt15.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt16.png b/doc/de/tutorials/pic/tut_opt16.png
new file mode 100644
index 000000000..6c749c425
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt16.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt17.png b/doc/de/tutorials/pic/tut_opt17.png
new file mode 100644
index 000000000..a6794ac2d
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt17.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt18.png b/doc/de/tutorials/pic/tut_opt18.png
new file mode 100644
index 000000000..36b1c2401
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt18.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt19.png b/doc/de/tutorials/pic/tut_opt19.png
new file mode 100644
index 000000000..f232da9bb
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt19.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt20.png b/doc/de/tutorials/pic/tut_opt20.png
new file mode 100644
index 000000000..04565f65c
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt20.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt21.png b/doc/de/tutorials/pic/tut_opt21.png
new file mode 100644
index 000000000..76430b695
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt21.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt22.png b/doc/de/tutorials/pic/tut_opt22.png
new file mode 100644
index 000000000..8e26bacf9
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt22.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt23.png b/doc/de/tutorials/pic/tut_opt23.png
new file mode 100644
index 000000000..389ba7df0
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt23.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt24.png b/doc/de/tutorials/pic/tut_opt24.png
new file mode 100644
index 000000000..7a7ad0e30
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt24.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt25.png b/doc/de/tutorials/pic/tut_opt25.png
new file mode 100644
index 000000000..c8639a679
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt25.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt26.png b/doc/de/tutorials/pic/tut_opt26.png
new file mode 100644
index 000000000..20e64f61a
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt26.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt27.png b/doc/de/tutorials/pic/tut_opt27.png
new file mode 100644
index 000000000..3e161e20c
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt27.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt28.png b/doc/de/tutorials/pic/tut_opt28.png
new file mode 100644
index 000000000..2f76472c2
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt28.png
Binary files differ
diff --git a/doc/de/tutorials/pic/tut_opt29.png b/doc/de/tutorials/pic/tut_opt29.png
new file mode 100644
index 000000000..037cfdfa6
--- /dev/null
+++ b/doc/de/tutorials/pic/tut_opt29.png
Binary files differ
diff --git a/doc/de/tutorials/step_with_hubzilla.md b/doc/de/tutorials/step_with_hubzilla.md
new file mode 100644
index 000000000..b297082cb
--- /dev/null
+++ b/doc/de/tutorials/step_with_hubzilla.md
@@ -0,0 +1,160 @@
+## Mit Hubzilla Schritt für Schritt ins Fediverse
+
+### Der Einstieg
+
+Wie bei jedem anderen Fediverse-Dienst steht am Anfang die Wahl des Servers (Hub). Das ist im Fediverse so und essenzieller Teil der dortigen Freiheit.
+
+Um Hubs zu finden können Sie die üblichen Wege beschreiben: dieNutzung entsprechende Datenbanken oder Listen.
+
+Z.B.
+
+[FediDB](https://fedidb.org/software/hubzilla)
+
+![FediDB](/help/de/tutorials/pic/fedidb.png)
+
+[Fediverse Observer](https://hubzilla.fediverse.observer/list)
+
+![Fedieverse Observer](/help/de/tutorials/pic/fedieverse-observer.png)
+
+[Liste öffentlicher Hubs bei einem Hubzilla-Server](https://zotsite.net/pubsites)
+
+![Pubsites](/help/de/tutorials/pic/pubsites.png)
+
+Haben Sie sich für einen Hub entschieden, dann rufen Sie die URL auf. Damit gelangen Sie auf eine Standard-Seite. Diese kann von Hub zu Hub etwas variieren, aber in der Regel finden Sie am oberen Rand des Bildschirms zwei Menüeinträge: „Anmelden“ und „Registrieren“.
+
+![Registrierung 1](/help/de/tutorials/pic/hzreg01a.png)
+
+![Registrierung 2](/help/de/tutorials/pic/hzreg01.png)
+
+Ein Klick auf den Link führt zu einem Registrierungsformular. Hier gibt es mehrere mögliche Szenarien. Manche Hubs sind so eingestellt, dass man schon bei der Registrierung gleich einen Kanal mit erstellt (man kann – noch so eine Besonderheit von Hubzilla – mit einem Account mehrere Kanäle betreiben). Bei anderen Hubs legt man mit dem Formular zunächst nur einen Account an. Wenn man diesen erstellt hat und sich erstmalig einloggt, dann wird man auf das Formular zur Kanalerstellung geleitet.
+
+![Registrierung 3](/help/de/tutorials/pic/hzreg02.png)
+
+![Registrierung 4](/help/de/tutorials/pic/rega.png)
+
+Um einen Account anzulegen, brauchen Sie eine (funktionierende) E-Mail-Adresse und Sie müssen sich ein Passwort ausdenken. Außerdem müssen Sie noch Ihr Alter bestätigen und können dann die eingegebenen Informationen abschicken.
+
+Nun landen Sie (meist, es soll auch Hubs geben, die diesen Schritt aussparen… halte ich für riskant) bei einer Eingabemaske, bei welcher man einen Verifizierungscode eingeben muss. Den bekommen Sie nach dem Absenden des Registrierungs-Formulars per E-Mail zugeschickt.
+
+![Registrierung 5](/help/de/tutorials/pic/hzreg03.png)
+
+Mussten Sie bei der Registrierung noch keinen Kanal anlegen, so werden Sie zu diesem Zeitpunkt zur Seite „Erstelle einen Kanal“ weitergeleitet. Hier müssen Sie sich nun einen Namen für die eigene Identität ausdenken. Und zusätzlich eine Kurzbezeichnung („Spitzname“) für den Kanal (ein Vorschlag wird automatisch aus dem Kanalnamen erzeugt). Diese Kurzbezeichnung wird der wesentliche Bestandteil des Fediverse-Handles (also der eigenen „Fediverse-Adresse“).
+
+![Registrierung 6](/help/de/tutorials/pic/hzreg04.png)
+
+| **Hinweis zum Handle bei Hubzilla:** |
+| ------------------------------------------------------------ |
+| Hubzilla macht beim Handle im Vergleich zu anderen Fediverse-Diensten eine Ausnahme. Während beinahe überall vor das Handle ein „@“ gesetzt wird, entfällt dies bei Hubzilla. Das verinnerlicht man aber schnell. Sucht man mit Hubzilla z.B. einen Nutzer, der ein Mastodon-Konto hat, über dessen Handle, dann lässt man das führende „@“ einfach weg. Will man hingegen von einem anderen Fediverse-Dienst aus einem Hubzilla-Nutzer folgen oder diesen suchen, stellt man dem Hubzilla-Handle einfach ein „@“ voran. |
+
+Nach dem Anlegen das Kanals sind Sie dann auch endlich „drin“. Als Standard landet man bei Hubzilla im „Headquarter“ („HQ“), einer Übersichtsseite über den eigenen Kanal.
+
+![HQ](/help/de/tutorials/pic/10.png)
+
+Und es ist, wie immer beim Einstieg im Fediverse, ziemlich leer dort. In der linken Seitenleiste finden Sie Tabs mit verschiedenen Informationen:
+
+- Öffentliche (oder eingeschränkte) Nachrichten:
+ Das ist eine kompakte Ansicht der persönlichen Timeline (bei Hubzilla „Stream“ genannt)
+- Direktnachrichten:
+ Postings, die nur unter ausgewählten Teilnehmern ausgetauscht werden.
+- Favorisierte Postings:
+ Man kann ein Posting mit einem „Sternchen“ versehen, um sie zu markieren. Solchermaßen markierte Beiträge landen hier.
+- Benachrichtigungen
+
+In der Mitte wird der persönliche Stream (Timeline) angezeigt. In der rechten Seitenleiste werden wichtige Informationen (Mitteilung über neue Kontakte, neue Beiträge etc.) angezeigt. Bei einem neu erzeugten Kanal erscheint hier auch eine Auflistung für die ersten Schritte bei Hubzilla und Links, die zu den entsprechenden Funktionen führen.
+
+Benötigt man diese Einsteiger-Hilfe nicht mehr, lässt sie sich unter Einstellungen → Anzeige-Einstellungen → Inhaltseinstellungen per Schalter abschalten.
+
+![erste Schritte 1](/help/de/tutorials/pic/11.png)
+
+![erste Schritte 2](/help/de/tutorials/pic/12.png)
+
+![erste Schritte 3](/help/de/tutorials/pic/13.png)
+
+![erste Schritte 4](/help/de/tutorials/pic/14.png)
+
+Zunächst sollten Sie Ihr Profil sinnvoll befüllen… wie bei jedem Fediverse-Dienst. Keine schwarze Magie!
+
+Am oberen Bildschirmrand befindet sich die Navigationsleiste. Links ist das Menü zur Kanalauswahl, für das eigene Profil und für die Einstellungen zu sehen. Rechts befinden sich Icons für einige Funktionen und das sogenannte „App-Menü“ (⋮), über welches man zu den installierten Apps gelangt. Als Standard werden dort die wichtigsten Anwendungen bereits zur Verfügung gestellt:
+
+- Dateien:
+ Zugang zur eigenen Cloud
+- Fotos:
+ Zugang zum eigenen Fotoalbum
+- Hilfe
+- Kalender
+- Kanal:
+ Die Seite des eigenen Kanals mit den Kanalinformationen. Hier werden im Stream nur die eigenen Beiträge angezeigt.
+- Stream:
+ Es wird zur föderierten Streamansicht gewechselt.
+- Verbindungen:
+ Hier werden die vorhandenen Verbindungen aufgeführt („Follower“ und „Gefolgte“). Außerdem kann man im Verbindungsverzeichnis neue Verbindungen hinzufügen.
+- Verzeichnis:
+ Das Benutzerverzeichnis wird angezeigt. Beachte: Man kann das globale Verzeichnis anschauen oder auch nur ein Verzeichnis mit den Nutzern der eigenen Instanz. Auch hier kann man neue Verbindungen herstellen.
+
+**Ich empfehle dringend, noch einige weitere Apps zu installieren und zu aktivieren: ActivityPub, Superblock und Öffentlicher Beitragsstream.**
+
+**Die App ActivityPub ist essenziell, wenn man am Fediverse teilnehmen möchte.** Diese App ist ein **MUSS** und erfordert lediglich die Installation und Aktivierung. Weitere Einstellungen sind nicht erforderlich.
+
+Superblock ist ebenfalls sehr wichtig, denn damit ist es möglich, Postings von (auch nicht gefolgten) Nutzern aus dem Stream auszuschließen.
+
+Bei vielen (leider, aber auch verständlicher Weise, nicht bei allen) Hubs steht außerdem die App „Öffentlicher Beitragsstream“ zur Verfügung. Diese stellt die öffentliche Zeitleiste aller Fediverse-Instanzen, die mit dem eigenen Hub föderieren, zur Verfügung. Ein guter Ort, sich zu orientieren und neue Kontakte zu finden.
+
+Apps zu installieren und zu aktivieren ist kein Problem. Man wählt im App-Menü den letzten Eintrag „+ Apps“ um zur App-Verwaltung zu gelangen.
+
+![Apps installieren 1](/help/de/tutorials/pic/02.png)
+
+Hier können Sie sich die installierten Apps (also die voreingestellten Apps) und die generell verfügbaren Apps (alle, auch die nicht installierten) anzeigen lassen.
+
+![Apps installieren 2](/help/de/tutorials/pic/03.png)
+
+ActivityPub, Superblock und Öffentlicher Beitragsstream befinden sich noch ausschließlich unter „Verfügbare Apps“. Ein Klick auf „Installieren“ installiert diese und sie stehen dann auch bei „Installierte Apps“ zur Verfügung.
+
+![Apps installieren 3](/help/de/tutorials/pic/04.png)
+
+![Apps installieren 4](/help/de/tutorials/pic/05.png)
+
+Die neu installierten Apps müssen noch „aktiviert“, also auch durch das Menü nutzbar gemacht werden. Man findet in der Box der App ein „Sternchen-Symbol“. Klickt man darauf, färbt sich der Stern gelb und die App ist aktiv und erscheint nun auch im App-Menü.
+
+![Apps installieren 5](/help/de/tutorials/pic/06.png)
+
+![Apps installieren 6](/help/de/tutorials/pic/07.png)
+
+Es gibt auch noch ein Pinnadel-Symbol. Klicken Sie dies an, so erscheint die App dauerhaft auch in der Navigationsleiste oben rechts.![Apps installieren 7](/help/de/tutorials/pic/08.png)
+
+![Apps installieren 8](/help/de/tutorials/pic/09.png)
+
+Es ist außerdem empfehlenswert, bei dieser Gelegenheit die Apps“Kanal“ und „Stream“ an die Navigationsleiste anzupinnen, weil diese oft benötigt werden.
+
+### Wichtig noch, wie man Kontakte hinzufügt…
+
+Haben Sie z.B. im öffentlichen Stream einen interessanten Nutzer gefunden, können Sie einfach auf das Profilbild des Nutzers klicken und im aufklappenden Menü „Verbinden“ auswählen. Sie können aber auch auf das Handle des Nutzer klicken worauf Sie auf eine Seite mit einem Button „Verbinden“ geleitet werden..
+
+Kennt man das Handle eines Fediverse-Nutzers, dann kann man auch einfach auf die App „Verbindungen“ klicken. Es öffnet sich das Verbindungs-Verzeichnis. Ganz oben befindet sich ein Button „+ Add“. Klicken Sie auf diesen, öffnet sich ein Eingabefeld, in welches Sie das Handle eingeben können (dran denken: ohne führendes „@“). Ein Klick auf das daneben befindliche „+“ und der Kontakt wird hinzugefügt.
+
+![Verbindungen 01](/help/de/tutorials/pic/15.png)
+
+![Verbindungen 02](/help/de/tutorials/pic/16.png)
+
+![Verbindungen 03](/help/de/tutorials/pic/17.png)
+
+![Verbindungen 04](/help/de/tutorials/pic/18.png)
+
+Schließlich können Sie auch das Verzeichnis aufrufen (am besten „nur dieser Hub“ in der linken Seitenleiste deaktivieren, um das globale Verzeichnis zu nutzen). Hier können Sie einfach durchscrollen oder auch gezielt nach Namen oder Interessen suchen, oder auch nach Tags (Schnellzugriff auch über eine Schlagwörterwolke in der linken Seitenleiste). Das Verbinden erfolgt hier durch Klick auf den Button „Verbinden“.
+
+![Verbindungen 05](/help/de/tutorials/pic/20.png)
+
+![Verbindungen 06](/help/de/tutorials/pic/19.png)
+
+Haben Sie Kontakte, die Apps installiert und Ihr Profil vervollständigt, können Sie Hubzilla nun ganz einfach wie jeden anderen Fediversedienst nutzen.
+
+### Gibt es eine App?
+
+Jein…
+
+Grundsätzlich benötigt man keine. Man kann auf dem mobilen Endgerät einfach im Webbrowser den Hub aufrufen. Das responsive Design erlaubt eine ordentliche Bedienung.
+
+Allerdings gibt es für Android eine ältere App, die aber auch heute noch sehr gut funktioniert. Man findet sie z.B. bei [F-Droid](https://f-droid.org/de/) unter dem Namen [Nomad](https://f-droid.org/de/packages/com.dfa.hubzilla_android/). Ich verwende sie noch immer, wenn ich mal (was selten ist) auf dem Smartphone mit Hubzilla arbeiten möchte.
+
+![Nomad App](/help/de/tutorials/pic/nomadapp.png)
+
+Als Alternative ist es empfehlenswert, wenn man mobil unterwegs ist, Hubzilla als PWA auf dem Gerät zu installieren: [Eine Hubzilla-App](https://info.hubzilla.hu/de/Hubzilla-App.html)
diff --git a/doc/de/tutorials/tutorials.md b/doc/de/tutorials/tutorials.md
new file mode 100644
index 000000000..006a24bed
--- /dev/null
+++ b/doc/de/tutorials/tutorials.md
@@ -0,0 +1,7 @@
+## Tutorials
+
+#### [Mit Hubzilla Schritt für Schritt ins Fediverse](./step_with_hubzilla.md)
+
+#### [Hubzilla optisch anpassen](./customise_look.md)
+
+#### [Ein abgeleitetes Thema erstellen](./DerivedTheme1.md) \ No newline at end of file
diff --git a/doc/de/ui.md b/doc/de/ui.md
new file mode 100644
index 000000000..1399770b3
--- /dev/null
+++ b/doc/de/ui.md
@@ -0,0 +1,5 @@
+# Oberfläche / Bezeichnungen
+
+![ui01](/help/de/pic/ui01.png)
+
+![ui02](/help/de/pic/ui02.png)
diff --git a/include/account.php b/include/account.php
index 615c802f4..0c07bd85f 100644
--- a/include/account.php
+++ b/include/account.php
@@ -17,10 +17,38 @@ require_once('include/crypto.php');
require_once('include/channel.php');
-function get_account_by_id($account_id) {
- $r = q("select * from account where account_id = %d",
- intval($account_id)
- );
+/**
+ * Returns the id of a locally logged in account or false.
+ *
+ * Returns the numeric account id of the current session if authenticated, or
+ * false otherwise.
+ *
+ * @note It is possible to be authenticated, and not connected to a channel.
+ *
+ * @return int|false Numeric account id or false.
+ */
+function get_account_id(): int|false {
+ if (isset($_SESSION['account_id'])) {
+ return intval($_SESSION['account_id']);
+ }
+
+ if (App::$account) {
+ return intval(App::$account['account_id']);
+ }
+
+ return false;
+}
+
+/**
+ * Get the account with the given id from the database.
+ *
+ * @param int $account_id The numeric id of the account to fetch.
+ *
+ * @return array|false An array containing the attributes of the requested
+ * account, or false if it could not be retreived.
+ */
+function get_account_by_id(int $account_id): array|false {
+ $r = q("select * from account where account_id = %d", $account_id);
return (($r) ? $r[0] : false);
}
@@ -117,11 +145,16 @@ function check_account_invite($invite_code) {
}
function check_account_admin($arr) {
- if(is_site_admin())
+ if (is_site_admin()) {
return true;
+ }
+
$admin_email = trim(Config::Get('system','admin_email'));
- if(strlen($admin_email) && $admin_email === trim($arr['email']))
+
+ if (strlen($admin_email) && $admin_email === trim($arr['reg_email'])) {
return true;
+ }
+
return false;
}
@@ -132,167 +165,6 @@ function account_total() {
return false;
}
-// legacy
-function account_store_lowlevel_IS_OBSOLETE($arr) {
-
- $store = [
- 'account_parent' => ((array_key_exists('account_parent',$arr)) ? $arr['account_parent'] : '0'),
- 'account_default_channel' => ((array_key_exists('account_default_channel',$arr)) ? $arr['account_default_channel'] : '0'),
- 'account_salt' => ((array_key_exists('account_salt',$arr)) ? $arr['account_salt'] : ''),
- 'account_password' => ((array_key_exists('account_password',$arr)) ? $arr['account_password'] : ''),
- 'account_email' => ((array_key_exists('account_email',$arr)) ? $arr['account_email'] : ''),
- 'account_external' => ((array_key_exists('account_external',$arr)) ? $arr['account_external'] : ''),
- 'account_language' => ((array_key_exists('account_language',$arr)) ? $arr['account_language'] : 'en'),
- 'account_created' => ((array_key_exists('account_created',$arr)) ? $arr['account_created'] : '0001-01-01 00:00:00'),
- 'account_lastlog' => ((array_key_exists('account_lastlog',$arr)) ? $arr['account_lastlog'] : '0001-01-01 00:00:00'),
- 'account_flags' => ((array_key_exists('account_flags',$arr)) ? $arr['account_flags'] : '0'),
- 'account_roles' => ((array_key_exists('account_roles',$arr)) ? $arr['account_roles'] : '0'),
- 'account_reset' => ((array_key_exists('account_reset',$arr)) ? $arr['account_reset'] : ''),
- 'account_expires' => ((array_key_exists('account_expires',$arr)) ? $arr['account_expires'] : '0001-01-01 00:00:00'),
- 'account_expire_notified' => ((array_key_exists('account_expire_notified',$arr)) ? $arr['account_expire_notified'] : '0001-01-01 00:00:00'),
- 'account_service_class' => ((array_key_exists('account_service_class',$arr)) ? $arr['account_service_class'] : ''),
- 'account_level' => '5',
- 'account_password_changed' => ((array_key_exists('account_password_changed',$arr)) ? $arr['account_password_changed'] : '0001-01-01 00:00:00')
- ];
-
- // never ever is this a create table but a pdo insert into account
- // strange function placement in text.php (obscure by design :-)
- return create_table_from_array('account',$store);
- // the TODO may be to adjust others using create_table_from_array():
- // channel.php
- // connections.php
- // event.php
- // hubloc.php
- // import.php
-}
-
-
-
-// legacy
-function create_account_IS_OBSOLETE($arr) {
-
- // Required: { email, password }
-
- $result = array('success' => false, 'email' => '', 'password' => '', 'message' => '');
-
- $invite_code = ((x($arr,'invite_code')) ? notags(trim($arr['invite_code'])) : '');
- $email = ((x($arr,'email')) ? notags(punify(trim($arr['email']))) : '');
- $password = ((x($arr,'password')) ? trim($arr['password']) : '');
- $parent = ((x($arr,'parent')) ? intval($arr['parent']) : 0 );
- $flags = ((x($arr,'account_flags')) ? intval($arr['account_flags']) : ACCOUNT_OK);
- $roles = ((x($arr,'account_roles')) ? intval($arr['account_roles']) : 0 );
- $expires = ((x($arr,'expires')) ? intval($arr['expires']) : NULL_DATE);
-
- $default_service_class = Config::Get('system','default_service_class');
-
- if($default_service_class === false)
- $default_service_class = '';
-
- if((! x($email)) || (! x($password))) {
- $result['message'] = t('Please enter the required information.');
- return $result;
- }
-
- // prevent form hackery
-
- if($roles & ACCOUNT_ROLE_ADMIN) {
- $admin_result = check_account_admin($arr);
- if(! $admin_result) {
- $roles = 0;
- }
- }
-
- // allow the admin_email account to be admin, but only if it's the first account.
-
- $c = account_total();
- if (($c === 0) && (check_account_admin($arr)))
- $roles |= ACCOUNT_ROLE_ADMIN;
-
- // Ensure that there is a host keypair.
-
- if ((! Config::Get('system', 'pubkey')) && (! Config::Get('system', 'prvkey'))) {
- $hostkey = Crypto::new_keypair(4096);
- Config::Set('system', 'pubkey', $hostkey['pubkey']);
- Config::Set('system', 'prvkey', $hostkey['prvkey']);
- }
-
- $invite_result = check_account_invite($invite_code);
- if($invite_result['error']) {
- $result['message'] = $invite_result['message'];
- return $result;
- }
-
- $email_result = check_account_email($email);
-
- if($email_result['error']) {
- $result['message'] = $email_result['message'];
- return $result;
- }
-
- $password_result = check_account_password($password);
-
- if($password_result['error']) {
- $result['message'] = $password_result['message'];
- return $result;
- }
-
- $salt = random_string(32);
- $password_encoded = hash('whirlpool', $salt . $password);
-
- $r = account_store_lowlevel(
- [
- 'account_parent' => intval($parent),
- 'account_salt' => $salt,
- 'account_password' => $password_encoded,
- 'account_email' => $email,
- 'account_language' => get_best_language(),
- 'account_created' => datetime_convert(),
- 'account_flags' => intval($flags),
- 'account_roles' => intval($roles),
- 'account_level' => 5,
- 'account_expires' => $expires,
- 'account_service_class' => $default_service_class
- ]
- );
- if(! $r) {
- logger('create_account: DB INSERT failed.');
- $result['message'] = t('Failed to store account information.');
- return($result);
- }
-
- $r = q("select * from account where account_email = '%s' and account_password = '%s' limit 1",
- dbesc($email),
- dbesc($password_encoded)
- );
- if($r && count($r)) {
- $result['account'] = $r[0];
- }
- else {
- logger('create_account: could not retrieve newly created account');
- }
-
- // Set the parent record to the current record_id if no parent was provided
-
- if(! $parent) {
- $r = q("update account set account_parent = %d where account_id = %d",
- intval($result['account']['account_id']),
- intval($result['account']['account_id'])
- );
- if(! $r) {
- logger('create_account: failed to set parent');
- }
- $result['account']['parent'] = $result['account']['account_id'];
- }
-
- $result['success'] = true;
- $result['email'] = $email;
- $result['password'] = $password;
-
- call_hooks('register_account',$result);
-
- return $result;
-}
-
/**
* create_account_from_register
* @author hilmar runge
@@ -324,18 +196,18 @@ function create_account_from_register($arr) {
if($default_service_class === false)
$default_service_class = '';
- $roles = 0;
- // prevent form hackery
- if($roles & ACCOUNT_ROLE_ADMIN) {
- $admin_result = check_account_admin($arr);
- if(! $admin_result) {
- $roles = 0;
- }
+ // any accounts available ?
+ $total = q("SELECT COUNT(*) AS total FROM account");
+
+ if ($total && intval($total[0]['total']) === 0 && !check_account_admin($register[0])) {
+ logger('create_account: first account is not admin');
+ $result['message'] = t('First account is not admin.');
+ return $result;
}
- // any accounts available ?
- $isa = q("SELECT COUNT(*) AS isa FROM account");
- if ($isa && $isa[0]['isa'] == 0) {
+ $roles = 0;
+
+ if (check_account_admin($register[0])) {
$roles = ACCOUNT_ROLE_ADMIN;
}
@@ -446,76 +318,6 @@ function verify_email_address($arr) {
return $res;
}
-function verify_email_addressNOP($arr) {
-
- if(array_key_exists('resend',$arr)) {
- $a = q("select * from account where account_email = '%s' limit 1",
- dbesc($arr['email'])
- );
- if(! ($a && ($a[0]['account_flags'] & ACCOUNT_UNVERIFIED))) {
- return false;
- }
- $account = $a[0];
- // [hilmar ->
- $v = q("SELECT * FROM register WHERE reg_uid = %d AND reg_vital = 1 "
- . " AND reg_pass = 'verify' LIMIT 1",
- intval($account['account_id'])
- );
- // <- hilmar]
- if($v) {
- $hash = $v[0]['reg_hash'];
- }
- else {
- return false;
- }
- }
- else {
- $hash = random_string(24);
-
- // [hilmar ->
- q("INSERT INTO register ( reg_hash, reg_created, reg_uid, reg_pass, reg_lang, reg_stuff ) "
- ." VALUES ( '%s', '%s', %d, '%s', '%s', '' ) ",
- dbesc($hash),
- dbesc(datetime_convert()),
- intval($arr['account']['account_id']),
- dbesc('verify'),
- dbesc($arr['account']['account_language'])
- );
- // <- hilmar]
- $account = $arr['account'];
- }
-
- push_lang(($account['account_language']) ? $account['account_language'] : 'en');
-
- $email_msg = replace_macros(get_intltext_template('register_verify_member.tpl'),
- [
- '$sitename' => Config::Get('system','sitename'),
- '$siteurl' => z_root(),
- '$email' => $arr['email'],
- '$uid' => $account['account_id'],
- '$hash' => $hash,
- '$details' => ''
- ]
- );
-
- $res = z_mail(
- [
- 'toEmail' => $arr['email'],
- 'messageSubject' => sprintf( t('Registration confirmation for %s'), Config::Get('system','sitename')),
- 'textVersion' => $email_msg,
- ]
- );
-
- pop_lang();
-
- if(! $res)
- logger('send_reg_approval_email: failed to account_id: ' . $arr['account']['account_id']);
-
- return $res;
-}
-
-
-
function send_reg_approval_email($arr) {
diff --git a/include/acl_selectors.php b/include/acl_selectors.php
index f0e0140dc..139a913b2 100644
--- a/include/acl_selectors.php
+++ b/include/acl_selectors.php
@@ -181,7 +181,7 @@ function get_post_aclDialogDescription() {
$description = t('Post permissions %s cannot be changed %s after a post is shared.</br />These permissions set who is allowed to view the post.');
// Lets keep the emphasis styling seperate from the translation. It may change.
- $emphasisOpen = '<b><a href="' . z_root() . '/help/acl_dialog_post" target="hubzilla-help">';
+ $emphasisOpen = '<b><a href="' . z_root() . '/help/member/permissions" target="hubzilla-help">';
$emphasisClose = '</a></b>';
return sprintf($description, $emphasisOpen, $emphasisClose);
diff --git a/include/auth.php b/include/auth.php
index 439f064e4..36a9043ce 100644
--- a/include/auth.php
+++ b/include/auth.php
@@ -353,9 +353,6 @@ else {
elseif($atoken) {
atoken_login($atoken);
}
- else {
- notice( t('Failed authentication') . EOL);
- }
if(! ($account || $atoken)) {
$error = 'authenticate: failed login attempt: ' . notags(trim($username)) . ' from IP ' . $_SERVER['REMOTE_ADDR'];
@@ -364,8 +361,8 @@ else {
$authlog = Config::Get('system', 'authlog');
if ($authlog)
@file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND);
- notice( t('Login failed.') . EOL );
- goaway(z_root() . '/login');
+
+ goaway(z_root() . '/login?retry=1');
}
// If the user specified to remember the authentication, then change the cookie
diff --git a/include/bbcode.php b/include/bbcode.php
index 2c8ef3f20..d5e1bafd9 100644
--- a/include/bbcode.php
+++ b/include/bbcode.php
@@ -1190,7 +1190,7 @@ function bbcode($text, $options = []) {
$cache = ((array_key_exists('cache',$options)) ? $options['cache'] : false);
$newwin = ((array_key_exists('newwin',$options)) ? $options['newwin'] : true);
- $target = (($newwin) ? ' target="_blank" ' : '');
+ $target = (($newwin) ? 'target="_blank"' : '');
/**
* @hooks bbcode_filter
@@ -1332,21 +1332,31 @@ function bbcode($text, $options = []) {
$text = str_replace('[observer.photo]','', $text);
}
-
-
// Perform URL Search
-
$urlchars = '[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,\@\(\)]';
if (strpos($text,'http') !== false) {
if($tryoembed) {
$text = preg_replace_callback("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", 'tryoembed', $text);
}
- // Is this still desired?
- // We already turn naked URLs into links during creation time cleanup_bbcode()
+
$text = preg_replace("/([^\]\='".'"'."\;\/]|^|\#\^)(https?\:\/\/$urlchars+)/ismu", '$1<a href="$2" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
}
+ // Turn naked geo URIs into clickable links
+ if (str_contains($text, 'geo:')) {
+ $text = preg_replace_callback(
+ '/([^\]\=\'";\/]|^|\#\^)(geo:([A-Za-z0-9:.,;_+\-?&=%]+)(?:\(([^)]+)\))?)/ismu',
+ function ($matches) {
+ $before = $matches[1];
+ $geo_uri = $matches[2];
+ $label = ((!empty($matches[4])) ? '📍' . urldecode($matches[4]) : $geo_uri);
+ return $before . '<a href="' . htmlspecialchars($geo_uri) . '" target="_blank" rel="nofollow noopener">' . htmlspecialchars($label) . '</a>';
+ },
+ $text
+ );
+ }
+
$count = 0;
while (strpos($text,'[/share]') !== false && $count < 10) {
$text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism", 'bb_ShareAttributes', $text);
@@ -1360,23 +1370,23 @@ function bbcode($text, $options = []) {
}
if (strpos($text,'[/url]') !== false) {
- $text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
- $text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text);
- $text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
- $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text);
+ $text = preg_replace("/\#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
+ $text = preg_replace("/\#\^\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<span class="bookmark-identifier">#^</span><a class="bookmark" href="$1" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
+ $text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
+ $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
}
if (strpos($text,'[/zrl]') !== false) {
- $text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
- $text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text);
- $text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
- $text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text);
+ $text = preg_replace("/\#\^\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
+ $text = preg_replace("/\#\^\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<span class="bookmark-identifier">#^</span><a class="zrl bookmark" href="$1" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
+ $text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
+ $text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
}
// Perform MAIL Search
if (strpos($text,'[/mail]') !== false) {
- $text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
- $text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener" >$2</a>', $text);
+ $text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
+ $text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1" ' . $target . ' rel="nofollow noopener">$2</a>', $text);
}
@@ -1737,17 +1747,17 @@ function bbcode($text, $options = []) {
// if video couldn't be embedded, link to it instead.
if (strpos($text,'[/video]') !== false) {
- $text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
+ $text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
}
if (strpos($text,'[/audio]') !== false) {
- $text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
+ $text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
}
if (strpos($text,'[/zvideo]') !== false) {
- $text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
+ $text = preg_replace("/\[zvideo\](.*?)\[\/zvideo\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
}
if (strpos($text,'[/zaudio]') !== false) {
- $text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener" >$1</a>', $text);
+ $text = preg_replace("/\[zaudio\](.*?)\[\/zaudio\]/", '<a class="zid" href="$1" ' . $target . ' rel="nofollow noopener">$1</a>', $text);
}
// oembed tag
@@ -1813,9 +1823,13 @@ function bbcode($text, $options = []) {
$text = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism", '<$1$2=$3&$4>', $text);
// This is subtle - it's an XSS filter. It only accepts links with a protocol scheme and where
- // the scheme begins with z (zhttp), h (http(s)), f (ftp(s)), m (mailto), t (tel) and named anchors.
+ // the scheme begins with http:, https:, mailto:, tel:, geo: and named anchors.
- $text = preg_replace("/\<(.*?)(src|href)=\"[^zhfmt#](.*?)\>/ism", '<$1$2="">', $text);
+ $text = preg_replace(
+ '/(<[^>]*?\b(?:src|href)\s*=\s*([\'"])\s*)(?!https?:|geo:|mailto:|tel:|#)[^\'"]*?\2/iu',
+ '$1$2$2',
+ $text
+ );
$text = bb_replace_images($text, $saved_images);
diff --git a/include/conversation.php b/include/conversation.php
index 97a65c27d..07e4df088 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -1426,7 +1426,7 @@ function get_responses($response_verbs, $item) {
}
$ret[$v]['count'] = $item[$v . '_count'] ?? 0;
- $ret[$v]['button'] = get_response_button_text($v, $ret[$v]['count']);
+ $ret[$v]['button'] = get_response_button_text($v, $ret[$v]['count'], $item['item_thread_top']);
}
//logger('ret: ' . print_r($ret,true));
@@ -1434,7 +1434,7 @@ function get_responses($response_verbs, $item) {
return $ret;
}
-function get_response_button_text($v, $count = 0) {
+function get_response_button_text($v, $count = 0, $top_level = 0) {
switch($v) {
case 'like':
return ['label' => tt('Like','Likes',$count,'noun'), 'icon' => 'hand-thumbs-up', 'class' => 'like', 'action' => 'dolike'];
@@ -1446,16 +1446,16 @@ function get_response_button_text($v, $count = 0) {
return ['label' => tt('Dislike','Dislikes',$count,'noun'), 'icon' => 'hand-thumbs-down', 'class' => 'dislike', 'action' => 'dolike'];
break;
case 'comment':
- return ['label' => tt('Reply','Replies',$count,'noun'), 'icon' => 'chat', 'class' => 'comment', 'action' => ''];
+ return ['label' => (($top_level) ? tt('Comment', 'Comments' ,$count, 'noun') : tt('Reply', 'Replies', $count, 'noun')), 'icon' => 'chat', 'class' => 'comment', 'action' => ''];
break;
- case 'attendyes':
- return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check', 'class' => 'attendyes', 'action' => 'dolike'];
+ case 'accept':
+ return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check', 'class' => 'accept', 'action' => 'dolike'];
break;
- case 'attendno':
- return ['label' => tt('Not attending','Not attending',$count,'noun'), 'icon' => 'calendar-x', 'class' => 'attendno', 'action' => 'dolike'];
+ case 'reject':
+ return ['label' => tt('Not attending','Not attending',$count,'noun'), 'icon' => 'calendar-x', 'class' => 'reject', 'action' => 'dolike'];
break;
- case 'attendmaybe':
- return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar', 'class' => 'attendmaybe', 'action' => 'dolike'];
+ case 'tentativeaccept':
+ return ['label' => tt('Undecided','Undecided',$count,'noun'), 'icon' => 'calendar', 'class' => 'tentativeaccept', 'action' => 'dolike'];
break;
default:
return [];
diff --git a/include/crypto.php b/include/crypto.php
index 4d50310fb..4292bdb4d 100644
--- a/include/crypto.php
+++ b/include/crypto.php
@@ -2,9 +2,6 @@
use Zotlabs\Lib\Config;
-require_once('library/ASNValue.class.php');
-require_once('library/asn1.php');
-
function rsa_sign($data,$key,$alg = 'sha256') {
if(! $key)
return 'no key';
@@ -309,158 +306,6 @@ function new_keypair($bits) {
}
-function DerToPem($Der, $Private=false)
-{
- //Encode:
- $Der = base64_encode($Der);
- //Split lines:
- $lines = str_split($Der, 65);
- $body = implode("\n", $lines);
- //Get title:
- $title = $Private? 'RSA PRIVATE KEY' : 'PUBLIC KEY';
- //Add wrapping:
- $result = "-----BEGIN {$title}-----\n";
- $result .= $body . "\n";
- $result .= "-----END {$title}-----\n";
-
- return $result;
-}
-
-function DerToRsa($Der)
-{
- //Encode:
- $Der = base64_encode($Der);
- //Split lines:
- $lines = str_split($Der, 64);
- $body = implode("\n", $lines);
- //Get title:
- $title = 'RSA PUBLIC KEY';
- //Add wrapping:
- $result = "-----BEGIN {$title}-----\n";
- $result .= $body . "\n";
- $result .= "-----END {$title}-----\n";
-
- return $result;
-}
-
-
-function pkcs8_encode($Modulus,$PublicExponent) {
- //Encode key sequence
- $modulus = new ASNValue(ASNValue::TAG_INTEGER);
- $modulus->SetIntBuffer($Modulus);
- $publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
- $publicExponent->SetIntBuffer($PublicExponent);
- $keySequenceItems = array($modulus, $publicExponent);
- $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
- $keySequence->SetSequence($keySequenceItems);
- //Encode bit string
- $bitStringValue = $keySequence->Encode();
- $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte
- $bitString = new ASNValue(ASNValue::TAG_BITSTRING);
- $bitString->Value = $bitStringValue;
- //Encode body
- $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode();
- $body = new ASNValue(ASNValue::TAG_SEQUENCE);
- $body->Value = $bodyValue;
- //Get DER encoded public key:
- $PublicDER = $body->Encode();
- return $PublicDER;
-}
-
-
-function pkcs1_encode($Modulus,$PublicExponent) {
- //Encode key sequence
- $modulus = new ASNValue(ASNValue::TAG_INTEGER);
- $modulus->SetIntBuffer($Modulus);
- $publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
- $publicExponent->SetIntBuffer($PublicExponent);
- $keySequenceItems = array($modulus, $publicExponent);
- $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
- $keySequence->SetSequence($keySequenceItems);
- //Encode bit string
- $bitStringValue = $keySequence->Encode();
- return $bitStringValue;
-}
-
-
-// http://stackoverflow.com/questions/27568570/how-to-convert-raw-modulus-exponent-to-rsa-public-key-pem-format
-function metopem($m,$e) {
- $der = pkcs8_encode($m,$e);
- $key = DerToPem($der,false);
- return $key;
-}
-
-
-function pubrsatome($key,&$m,&$e) {
- require_once('library/asn1.php');
-
- $lines = explode("\n",$key);
- unset($lines[0]);
- unset($lines[count($lines)]);
- $x = base64_decode(implode('',$lines));
-
- $r = ASN_BASE::parseASNString($x);
-
- $m = base64url_decode($r[0]->asnData[0]->asnData);
- $e = base64url_decode($r[0]->asnData[1]->asnData);
-}
-
-
-function rsatopem($key) {
- pubrsatome($key,$m,$e);
- return(metopem($m,$e));
-}
-
-function pemtorsa($key) {
- pemtome($key,$m,$e);
- return(metorsa($m,$e));
-}
-
-function pemtome($key,&$m,&$e) {
- $lines = explode("\n",$key);
- unset($lines[0]);
- unset($lines[count($lines)]);
- $x = base64_decode(implode('',$lines));
-
- $r = ASN_BASE::parseASNString($x);
-
- $m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData);
- $e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData);
-}
-
-function metorsa($m,$e) {
- $der = pkcs1_encode($m,$e);
- $key = DerToRsa($der);
- return $key;
-}
-
-
-
-function salmon_key($pubkey) {
- pemtome($pubkey,$m,$e);
- return 'RSA' . '.' . base64url_encode($m,true) . '.' . base64url_encode($e,true) ;
-}
-
-
-function convert_salmon_key($key) {
-
- if(strstr($key,','))
- $rawkey = substr($key,strpos($key,',')+1);
- else
- $rawkey = substr($key,5);
-
- $key_info = explode('.',$rawkey);
-
- $m = base64url_decode($key_info[1]);
- $e = base64url_decode($key_info[2]);
-
- logger('key details: ' . print_r($key_info,true), LOGGER_DATA);
- $salmon_key = metopem($m,$e);
- return $salmon_key;
-
-}
-
-
function z_obscure($s) {
return json_encode(crypto_encapsulate($s,Config::Get('system','pubkey')));
}
diff --git a/include/event.php b/include/event.php
index b83a733b8..39d0c49c2 100644
--- a/include/event.php
+++ b/include/event.php
@@ -106,7 +106,7 @@ function format_event_obj($jobject) {
$title = $object['name'] ?? '';
$content = html2bbcode($object['content']);
- if (strpos($object['source']['content'], '[/event-description]') !== false) {
+ if (isset($object['source']['content']) && strpos($object['source']['content'], '[/event-description]') !== false) {
$bbdescription = [];
preg_match("/\[event\-description\](.*?)\[\/event\-description\]/ism", $object['source']['content'], $bbdescription);
$content = $bbdescription[1];
diff --git a/include/items.php b/include/items.php
index 55d768e28..339a61753 100644
--- a/include/items.php
+++ b/include/items.php
@@ -4802,19 +4802,19 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
return $items;
}
-function webpage_to_namespace($webpage) {
+function item_type_to_namespace($item_type) {
- if($webpage == ITEM_TYPE_WEBPAGE)
+ if($item_type == ITEM_TYPE_WEBPAGE)
$page_type = 'WEBPAGE';
- elseif($webpage == ITEM_TYPE_BLOCK)
+ elseif($item_type == ITEM_TYPE_BLOCK)
$page_type = 'BUILDBLOCK';
- elseif($webpage == ITEM_TYPE_PDL)
+ elseif($item_type == ITEM_TYPE_PDL)
$page_type = 'PDL';
- elseif($webpage == ITEM_TYPE_CARD)
+ elseif($item_type == ITEM_TYPE_CARD)
$page_type = 'CARD';
- elseif($webpage == ITEM_TYPE_ARTICLE)
+ elseif($item_type == ITEM_TYPE_ARTICLE)
$page_type = 'ARTICLE';
- elseif($webpage == ITEM_TYPE_DOC)
+ elseif($item_type == ITEM_TYPE_DOC)
$page_type = 'docfile';
else
$page_type = 'unknown';
@@ -4823,12 +4823,12 @@ function webpage_to_namespace($webpage) {
}
-function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remote_id,$mid) {
+function update_remote_id($channel,$post_id,$item_type,$pagetitle,$namespace,$remote_id,$mid) {
if(! intval($post_id))
return;
- $page_type = webpage_to_namespace($webpage);
+ $page_type = item_type_to_namespace($item_type);
if($page_type == 'unknown' && $namespace && $remote_id) {
$page_type = $namespace;
@@ -5361,250 +5361,238 @@ function set_activity_mid($string) {
}
/**
- * @brief returns SQL which counts activities for an item and
- * if there is an observer also count activities authored by observer.
- * @param string $prefix (optional)
- */
-
-function item_activity_sql($prefix = 'c') {
- $sql = '';
- $observer = get_observer_hash();
-
- $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
-
- if ($observer) {
- $sql = <<<SQL
- COUNT(CASE WHEN $prefix.verb = 'Like' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_liked,
- COUNT(CASE WHEN $prefix.verb = 'Dislike' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_disliked,
- COUNT(CASE WHEN $prefix.verb = 'Announce' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_announced,
- COUNT(CASE WHEN $prefix.verb = 'Accept' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_accepted,
- COUNT(CASE WHEN $prefix.verb = 'Reject' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_rejected,
- COUNT(CASE WHEN $prefix.verb = 'TentativeAccept' AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_tentativelyaccepted,
- SQL;
-
- if ($thread_allow) {
- $sql .= " COUNT(CASE WHEN $prefix.verb IN ('Create','Update') AND $prefix.author_xchan = '$observer' THEN 1 END) AS observer_commented, ";
- }
- }
-
-
- if ($thread_allow) {
- $sql .= "COUNT(CASE WHEN $prefix.verb IN ('Create','Update') THEN 1 END) AS comment_count,";
- }
-
- $sql .= <<<SQL
- COUNT(CASE WHEN $prefix.verb = 'Like' THEN 1 END) AS like_count,
- COUNT(CASE WHEN $prefix.verb = 'Dislike' THEN 1 END) AS dislike_count,
- COUNT(CASE WHEN $prefix.verb = 'Announce' THEN 1 END) AS announce_count,
- COUNT(CASE WHEN $prefix.verb = 'Accept' THEN 1 END) AS attendyes_count,
- COUNT(CASE WHEN $prefix.verb = 'Reject' THEN 1 END) AS attendno_count,
- COUNT(CASE WHEN $prefix.verb = 'TentativeAccept' THEN 1 END) AS attendmaybe_count
- SQL;
-
- return $sql;
-
-}
-
-/**
- * @brief returns an item by id belonging to local_channel()
+ * @brief returns an item by id and parent belonging to local_channel()
* including activity counts.
* @param int $id
+ * @param int $parent
*/
-function item_by_item_id(int $id): array
+function item_by_item_id(int $id, int $parent): array
{
- if (!$id) {
+ if (!$id && !$parent && !local_channel()) {
return [];
}
- $item_normal = item_normal();
- $item_normal_c = item_normal(prefix: 'c');
- $activity_sql = item_activity_sql('c');
+ $item_normal_sql = item_normal();
- $ret = q("SELECT item.*,
- $activity_sql
+ $reaction = item_reaction_sql($parent);
+ $reaction_cte_sql = $reaction['cte'];
+ $reaction_select_sql = $reaction['select'];
+ $reaction_join_sql = $reaction['join'];
+
+ return q("WITH
+ $reaction_cte_sql
+ SELECT
+ *,
+ $reaction_select_sql
FROM item
- LEFT JOIN item c
- ON c.parent = item.parent
- AND c.item_thread_top = 0
- AND c.thr_parent = item.mid
- $item_normal_c
- WHERE item.id = $id
+ $reaction_join_sql
+ WHERE
+ item.id = %d
AND item.uid = %d
- $item_normal
- GROUP BY item.id",
+ AND item.verb IN ('Create', 'Update', 'EmojiReact')
+ $item_normal_sql",
+ intval($id),
intval(local_channel())
);
-
- return $ret;
}
+
/**
* @brief returns an array of items by ids
- * ATTENTION: no permissions for the pa are checked here!!!
- * Permissions MUST be checked by the function which returns the ids.
- * @param string $ids - a string with ids separated by comma
- * @param array $thr_parents (optional) - a string with thr_parent mids separated by comma
- * which will be included
- * @param string $permission_sql (optional) - SQL provided by item_permission_sql() from the calling module
+ * ATTENTION: no permissions for the parents are checked here!!!
+ * Permissions MUST be checked by the module which calls this function.
+ * @param array $parents
+ * @param null|array $thr_parents (optional) - thr_parent mids which will be included
+ * @param string $permission_sql (optional) - SQL as provided by item_permission_sql() from the calling module
* @param bool $blog_mode (optional) - if set to yes only the parent items will be returned
*/
-function items_by_parent_ids(string $ids, array $thr_parents = [], string $permission_sql = '', bool $blog_mode = false): array
+function items_by_parent_ids(array $parents, null|array $thr_parents = null, string $permission_sql = '', bool $blog_mode = false): array
{
- if (!$ids) {
+ if (!$parents) {
return [];
}
-
+ $ids = ids_to_querystr($parents, 'item_id');
$thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
-
$item_normal_sql = item_normal();
- $activity_sql_cte = item_activity_sql_cte();
- $activity_sql_cte_sub = item_activity_sql_cte('sub');
+ $limit = $thread_allow ? 3 : 1000;
$thr_parent_sql = (($thread_allow) ? " AND item.thr_parent = item.parent_mid " : '');
-
if ($thr_parents && $thread_allow) {
+ $limit = 300;
$thr_parent_str = stringify_array($thr_parents, true);
$thr_parent_sql = " AND item.thr_parent IN (" . protect_sprintf($thr_parent_str) . ") ";
}
+ $reaction = item_reaction_sql($ids, $permission_sql, 'final_selection', $blog_mode);
+ $reaction_cte_sql = $reaction['cte'];
+ $reaction_select_sql = $reaction['select'];
+ $reaction_join_sql = $reaction['join'];
+
if ($blog_mode) {
- $ret = q("SELECT item.*,
- $activity_sql_cte
- FROM item
- WHERE item.id IN (%s)
- $item_normal_sql
- $permission_sql",
- dbesc($ids)
- );
+ $q = <<<SQL
+ WITH
+ final_selection AS (
+ SELECT
+ item.*
+ FROM
+ item
+ WHERE
+ item.id IN ($ids)
+ ),
+
+ $reaction_cte_sql
+
+ SELECT
+ final_selection.*,
+ $reaction_select_sql
+ FROM final_selection
+ $reaction_join_sql
+ SQL;
+
+ return dbq(trim($q));
}
- else {
- $ret = q("WITH parents AS (
- SELECT item.*,
- 0 AS rn, -- this is required for union (equal amount of coulumns)
- $activity_sql_cte
+
+ $q = <<<SQL
+ WITH
+ parent_items AS (
+ SELECT
+ item.*,
+ 0 AS rn
FROM item
- WHERE item.id IN (%s)
- $item_normal_sql
+ WHERE
+ item.id IN ($ids)
+ ),
+
+ $reaction_cte_sql,
+
+ all_comments AS (
+ SELECT
+ item.*,
+ ROW_NUMBER() OVER (PARTITION BY item.parent ORDER BY item.created DESC) AS rn
+ FROM item
+ WHERE item.parent IN ($ids)
+ AND item.verb IN ('Create', 'Update', 'EmojiReact')
+ AND item.item_thread_top = 0
+ $thr_parent_sql
$permission_sql
- ),
- comments AS (
- SELECT sub.*,
- $activity_sql_cte_sub
- FROM (
- SELECT item.*,
- ROW_NUMBER() OVER (PARTITION BY item.parent ORDER BY item.created DESC) AS rn
- FROM item
- WHERE item.parent IN (%s)
- AND item.id != item.parent
- AND (
- item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept')
- OR (item.verb = 'Announce' AND item.item_thread_top = 1)
- )
- $thr_parent_sql
- $item_normal_sql
- $permission_sql
- ) sub
- WHERE rn <= 100 -- number of comments we want to load
- )
- SELECT * FROM parents
+ $item_normal_sql
+ ),
+
+ final_selection AS (
+ SELECT * FROM parent_items
UNION ALL
- SELECT * FROM comments",
- dbesc($ids),
- dbesc($ids)
- );
- }
+ SELECT * FROM all_comments WHERE all_comments.rn <= $limit
+ )
+
+ SELECT
+ final_selection.*,
+ $reaction_select_sql
+ FROM final_selection
+ $reaction_join_sql
+ SQL;
- return $ret;
+ return dbq(trim($q));
}
/**
- * @brief returns SQL which counts activities for an item and
- * if there is an observer also count activities authored by observer.
- * @param string $prefix (optional)
+ * @brief prepare reaction sql for items_by_parent_ids()
+ * ATTENTION: no permissions for the pa are checked here!!!
+ * Permissions MUST be checked by the function which returns the ids.
+ * @param string $ids
+ * @param string $permission_sql (optional) - SQL provided by item_permission_sql()
+ * @param string $join_prefix (optional) - prefix for the join part defaults to 'item'
*/
-function item_activity_sql_cte($prefix = 'item'): string
+function item_reaction_sql(string $ids, string $permission_sql = '', string $join_prefix = 'item', bool $blog_mode = false): array
{
- $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
+ $item_normal_sql = item_normal();
$observer = get_observer_hash();
- $sql = '';
-
- if ($observer) {
- $observer_verbs = [
- 'Like' => 'observer_liked',
- 'Dislike' => 'observer_disliked',
- 'Announce' => 'observer_announced',
- 'Accept' => 'observer_accepted',
- 'Reject' => 'observer_rejected',
- 'TentativeAccept' => 'observer_tentativelyaccepted'
- ];
- foreach($observer_verbs as $k => $v) {
- if ($sql) {
- $sql .= ",\n";
- }
+ $verbs = [
+ 'like' => ['Like'],
+ 'dislike' => ['Dislike'],
+ 'announce' => ['Announce'],
+ 'accept' => ['Accept'],
+ 'reject' => ['Reject'],
+ 'tentativeaccept' => ['TentativeAccept']
+ ];
- $sql .= <<<SQL
- (SELECT COUNT(*) FROM item AS reaction
- WHERE reaction.parent = $prefix.parent AND reaction.verb = '$k' AND reaction.author_xchan = '$observer' AND reaction.item_thread_top = 0 AND reaction.thr_parent = $prefix.mid
- ) AS $v
- SQL;
- }
+ $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true));
- if ($thread_allow) {
- $sql .= ",\n";
- $sql .= <<<SQL
- (SELECT COUNT(*) FROM item AS reaction
- WHERE reaction.parent = $prefix.parent AND reaction.verb IN ('Create', 'Update') AND reaction.author_xchan = '$observer' AND reaction.item_thread_top = 0 AND reaction.thr_parent = $prefix.mid
- ) AS observer_commented
- SQL;
- }
+ if ($thread_allow || $blog_mode) {
+ $verbs['comment'] = ['Create', 'Update', 'EmojiReact'];
}
- $verbs = [
- 'Like' => 'like_count',
- 'Dislike' => 'dislike_count',
- 'Announce' => 'announce_count',
- 'Accept' => 'attendyes_count',
- 'Reject' => 'attendno_count',
- 'TentativeAccept' => 'attendmaybe_count'
- ];
+ $cte = '';
+ $select = '';
+ $join = '';
foreach($verbs as $k => $v) {
- if ($sql) {
- $sql .= ",\n";
+
+ $observer_sql = "0 AS observer_{$k}_count";
+ if ($observer) {
+ $observer_sql = "COUNT(CASE WHEN item.author_xchan = '$observer' THEN 1 END) AS observer_{$k}_count";
+ }
+
+ $verbs_str = stringify_array($v);
+
+ if ($cte) {
+ $cte .= ",\n";
}
- $sql .= <<<SQL
- (SELECT COUNT(*) FROM item AS reaction
- WHERE reaction.parent = $prefix.parent AND reaction.verb = '$k' AND reaction.item_thread_top = 0 AND reaction.thr_parent = $prefix.mid
- ) AS $v
+ $cte .= <<<SQL
+ reaction_{$k} AS (
+ SELECT
+ item.thr_parent,
+ -- COUNT(DISTINCT item.author_xchan) AS {$k}_count, (should we prevent multiple reactions by the same author?)
+ COUNT(*) AS {$k}_count,
+ $observer_sql
+ FROM item
+ WHERE item.verb IN ($verbs_str)
+ AND item.item_thread_top = 0
+ AND item.parent IN ($ids)
+ $item_normal_sql
+ $permission_sql
+ GROUP BY item.thr_parent
+ )
+ SQL;
+
+ if ($select) {
+ $select .= ",\n";
+ }
+
+ $select .= <<<SQL
+ COALESCE(reaction_{$k}.{$k}_count, 0) AS {$k}_count,
+ COALESCE(reaction_{$k}.observer_{$k}_count, 0) AS observer_{$k}_count
SQL;
- }
- if ($thread_allow) {
- $sql .= ",\n";
- $sql .= <<<SQL
- (SELECT COUNT(*) FROM item AS reaction
- WHERE reaction.parent = $prefix.parent AND reaction.verb IN ('Create', 'Update') AND reaction.item_thread_top = 0 AND reaction.thr_parent = $prefix.mid
- ) AS comment_count
+ $join .= <<<SQL
+ LEFT JOIN reaction_{$k} ON reaction_{$k}.thr_parent = $join_prefix.mid
SQL;
+
}
- return $sql;
+ $ret['cte'] = $cte;
+ $ret['select'] = $select;
+ $ret['join'] = $join;
+
+ return $ret;
}
+
+
/**
* @brief returns an array of items by thr_parent mid of a parent
* @param string $mid
* @param int $parent
+ * @param int|null $offset
*/
-function items_by_thr_parent(string $mid, int $parent): array
+function items_by_thr_parent(string $mid, int $parent, int|null $offset = null): array
{
if (!$mid && !$parent) {
return [];
@@ -5614,63 +5602,75 @@ function items_by_thr_parent(string $mid, int $parent): array
intval($parent)
);
- $owner_uid = intval($parent_item[0]['uid']);
+ $order_sql = "ORDER BY item.created";
+ if (isset($offset)) {
+ $order_sql = "ORDER BY item.created DESC, item.received DESC LIMIT 3 OFFSET $offset";
+ }
- $item_normal = item_normal($owner_uid);
- $item_normal_c = item_normal($owner_uid, 'c');
- $activity_sql = item_activity_sql('c');
+ $owner_uid = intval($parent_item[0]['uid']);
+ $item_normal_sql = item_normal($owner_uid);
if (local_channel() === $owner_uid) {
- $ret = q(
- "SELECT item.*,
- $activity_sql
+ $reaction = item_reaction_sql($parent);
+ $reaction_cte_sql = $reaction['cte'];
+ $reaction_select_sql = $reaction['select'];
+ $reaction_join_sql = $reaction['join'];
+
+ $ret = q("WITH
+ $reaction_cte_sql
+ SELECT
+ item.*,
+ $reaction_select_sql
FROM item
- LEFT JOIN item c ON c.parent = item.parent
- AND c.item_thread_top = 0
- AND c.thr_parent = item.mid
- $item_normal_c
- WHERE item.thr_parent = '%s'
+ $reaction_join_sql
+ WHERE
+ item.thr_parent = '%s'
AND item.uid = %d
- AND item.parent = %d
- AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept')
+ AND item.verb IN ('Create', 'Update', 'EmojiReact')
AND item.item_thread_top = 0
- $item_normal
- GROUP BY item.id
- ORDER BY item.created",
+ $item_normal_sql
+ $order_sql",
dbesc($mid),
- intval(local_channel()),
- intval($parent)
+ intval($owner_uid)
);
}
-
- if (!$ret) {
+ else {
$observer_hash = get_observer_hash();
- $sql_extra = item_permissions_sql($owner_uid, $observer_hash);
-
- $ret = q(
- "SELECT item.*,
- $activity_sql
+ $permission_sql = item_permissions_sql($owner_uid, $observer_hash);
+
+ $reaction = item_reaction_sql($parent, $permission_sql);
+ $reaction_cte_sql = $reaction['cte'];
+ $reaction_select_sql = $reaction['select'];
+ $reaction_join_sql = $reaction['join'];
+
+ $ret = q("WITH
+ $reaction_cte_sql
+ SELECT
+ item.*,
+ $reaction_select_sql
FROM item
- LEFT JOIN item c ON c.parent = item.parent
- AND c.item_thread_top = 0
- AND c.thr_parent = item.mid
- $item_normal_c
- WHERE item.thr_parent = '%s'
+ $reaction_join_sql
+ WHERE
+ item.thr_parent = '%s'
AND item.uid = %d
- AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept')
+ AND item.verb IN ('Create', 'Update', 'EmojiReact')
AND item.item_thread_top = 0
- $sql_extra
- $item_normal
- GROUP BY item.id
- ORDER BY item.created",
+ $permission_sql
+ $item_normal_sql
+ $order_sql",
dbesc($mid),
intval($owner_uid)
);
}
+ if (isset($offset)) {
+ $ret = array_reverse($ret);
+ }
+
return $ret;
}
+
/**
* @brief returns an array of xchan entries (partly) for activities of an item by mid of a parent.
* Also checks if observer is allowed to add activities to the item.
@@ -5702,6 +5702,7 @@ function item_activity_xchans(string $mid, int $parent, string $verb): array
AND item.verb = '%s'
AND item.item_thread_top = 0
$item_normal
+ -- GROUP BY item.author_xchan (should we prevent multiple reactions by the same author?)
ORDER BY item.created",
intval(local_channel()),
intval($parent),
@@ -5709,8 +5710,7 @@ function item_activity_xchans(string $mid, int $parent, string $verb): array
dbesc($verb)
);
}
-
- if (!$ret) {
+ else {
$sql_extra = item_permissions_sql($owner_uid, $observer_hash);
$ret = q("SELECT item.id, item.item_blocked, xchan.xchan_hash, xchan.xchan_name as name, xchan.xchan_url as url, xchan.xchan_photo_s as photo FROM item
@@ -5721,6 +5721,7 @@ function item_activity_xchans(string $mid, int $parent, string $verb): array
AND item.item_thread_top = 0
$sql_extra
$item_normal
+ -- GROUP BY item.author_xchan (should we prevent multiple reactions by the same author?)
ORDER BY item.created",
intval($owner_uid),
dbesc($mid),
@@ -5740,8 +5741,13 @@ function item_activity_xchans(string $mid, int $parent, string $verb): array
* @param array $item
*/
-function get_recursive_thr_parents(array $item): array
+function get_recursive_thr_parents(array $item): array|null
{
+ if ($item['id'] === $item['parent']) {
+ // This is a toplevel post, return null.
+ return null;
+ }
+
$thr_parents[] = $item['thr_parent'];
$mid = $item['thr_parent'];
@@ -5755,8 +5761,13 @@ function get_recursive_thr_parents(array $item): array
dbesc($mid)
);
+ if (!$x) {
+ break;
+ }
+
$mid = $x[0]['thr_parent'];
$thr_parents[] = $x[0]['thr_parent'];
+
$i++;
}
diff --git a/include/js_strings.php b/include/js_strings.php
index 1772cb66b..6f2ffd351 100644
--- a/include/js_strings.php
+++ b/include/js_strings.php
@@ -36,6 +36,7 @@ function js_strings() {
'$pinned' => t('Pinned'),
'$pin_item' => t('Pin to the top'),
'$unpin_item' => t('Unpin from the top'),
+ '$dblclick_to_exit_zoom' => t('Double click to exit zoom'),
// translatable prefix and suffix strings for jquery.timeago -
// using the defaults set below if left untranslated, empty strings if
diff --git a/include/markdown.php b/include/markdown.php
index 90d671fe4..d2379e7ed 100644
--- a/include/markdown.php
+++ b/include/markdown.php
@@ -64,6 +64,12 @@ function markdown_to_bb($s, $use_zrl = false, $options = []) {
// Escaping the hash tags
$s = preg_replace('/\#([^\s\#])/','&#35;$1',$s);
+ // Protect mentions from being mangled by the markdown parser
+ $s = preg_replace_callback(
+ '|@\{([^}]+)\}|',
+ fn ($matches) => '@{' . base64_encode($matches[1]) . '}',
+ $s);
+
$s = MarkdownExtra::defaultTransform($s);
@@ -76,6 +82,12 @@ function markdown_to_bb($s, $use_zrl = false, $options = []) {
$s = str_replace("\r","",$s);
}
+ // Restore mentions after markdown conversion
+ $s = preg_replace_callback(
+ '|@\{([^}]+)\}|',
+ fn ($matches) => '@{' . base64_decode($matches[1]) . '}',
+ $s);
+
$s = str_replace('&#35;','#',$s);
$s = html2bbcode($s);
diff --git a/include/network.php b/include/network.php
index cb5027922..83bb281a4 100644
--- a/include/network.php
+++ b/include/network.php
@@ -433,7 +433,6 @@ function as_return_and_die($obj, $channel = []) {
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
$headers['Date'] = datetime_convert('UTC','UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
- $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
if ($channel) {
$h = HTTPSig::create_sig($headers, $channel['channel_prvkey'], channel_url($channel));
diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php
index 3de873638..88b9d1d62 100644
--- a/include/photo/photo_driver.php
+++ b/include/photo/photo_driver.php
@@ -76,7 +76,7 @@ function guess_image_type($filename, $data = []) {
logger('filename: ' . print_r($filename, true), LOGGER_DEBUG);
// Try Fileinfo from raw data
- if (!empty($data['body'])) {
+ if (class_exists('finfo') && !empty($data['body'])) {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($data['body']);
if ($mime && array_key_exists($mime, $types)) {
diff --git a/install/INSTALL.txt b/install/INSTALL.txt
index 0d1f7d0ee..464f54a51 100644
--- a/install/INSTALL.txt
+++ b/install/INSTALL.txt
@@ -192,11 +192,11 @@ website.
6. *If* the automated installation fails for any reason, check the following:
- ".htconfig.php" exists
- If not, edit htconfig.php and change system settings. Rename
- to .htconfig.php
- - Database is populated.
+ If not, edit htconfig.php and change system settings. Rename to .htconfig.php
+ - Database is populated.
If not, import the contents of "install/schema_xxxxx.sql" with phpmyadmin
or mysql command line (replace 'xxxxx' with your DB type).
+ - Manualy create the sys channel by running `util/init_sys_channel`
7. At this point visit your website again, and register your personal account.
Registration errors should all be recoverable automatically.
diff --git a/library/ASNValue.class.php b/library/ASNValue.class.php
deleted file mode 100644
index 7c17d10b4..000000000
--- a/library/ASNValue.class.php
+++ /dev/null
@@ -1,169 +0,0 @@
-<?php
-//-----------------------------------------------------------------------------
-// ASNValue class by A.Oliinyk
-// contact@pumka.net
-//-----------------------------------------------------------------------------
-class ASNValue
-{
- const TAG_INTEGER = 0x02;
- const TAG_BITSTRING = 0x03;
- const TAG_SEQUENCE = 0x30;
-
- public $Tag;
- public $Value;
-
- function __construct($Tag=0x00, $Value='')
- {
- $this->Tag = $Tag;
- $this->Value = $Value;
- }
-
- function Encode()
- {
- //Write type
- $result = chr($this->Tag);
-
- //Write size
- $size = strlen($this->Value);
- if ($size < 127) {
- //Write size as is
- $result .= chr($size);
- }
- else {
- //Prepare length sequence
- $sizeBuf = self::IntToBin($size);
-
- //Write length sequence
- $firstByte = 0x80 + strlen($sizeBuf);
- $result .= chr($firstByte) . $sizeBuf;
- }
-
- //Write value
- $result .= $this->Value;
-
- return $result;
- }
-
- function Decode(&$Buffer)
- {
- //Read type
- $this->Tag = self::ReadByte($Buffer);
-
- //Read first byte
- $firstByte = self::ReadByte($Buffer);
-
- if ($firstByte < 127) {
- $size = $firstByte;
- }
- else if ($firstByte > 127) {
- $sizeLen = $firstByte - 0x80;
- //Read length sequence
- $size = self::BinToInt(self::ReadBytes($Buffer, $sizeLen));
- }
- else {
- throw new Exception("Invalid ASN length value");
- }
-
- $this->Value = self::ReadBytes($Buffer, $size);
- }
-
- protected static function ReadBytes(&$Buffer, $Length)
- {
- $result = substr($Buffer, 0, $Length);
- $Buffer = substr($Buffer, $Length);
-
- return $result;
- }
-
- protected static function ReadByte(&$Buffer)
- {
- return ord(self::ReadBytes($Buffer, 1));
- }
-
- protected static function BinToInt($Bin)
- {
- $len = strlen($Bin);
- $result = 0;
- for ($i=0; $i<$len; $i++) {
- $curByte = self::ReadByte($Bin);
- $result += $curByte << (($len-$i-1)*8);
- }
-
- return $result;
- }
-
- protected static function IntToBin($Int)
- {
- $result = '';
- do {
- $curByte = $Int % 256;
- $result .= chr($curByte);
-
- $Int = ($Int - $curByte) / 256;
- } while ($Int > 0);
-
- $result = strrev($result);
-
- return $result;
- }
-
- function SetIntBuffer($Value)
- {
- if (strlen($Value) > 1) {
- $firstByte = ord($Value[0]);
- if ($firstByte & 0x80) { //first bit set
- $Value = chr(0x00) . $Value;
- }
- }
-
- $this->Value = $Value;
- }
-
- function GetIntBuffer()
- {
- $result = $this->Value;
- if (ord($result[0]) == 0x00) {
- $result = substr($result, 1);
- }
-
- return $result;
- }
-
- function SetInt($Value)
- {
- $Value = self::IntToBin($Value);
-
- $this->SetIntBuffer($Value);
- }
-
- function GetInt()
- {
- $result = $this->GetIntBuffer();
- $result = self::BinToInt($result);
-
- return $result;
- }
-
- function SetSequence($Values)
- {
- $result = '';
- foreach ($Values as $item) {
- $result .= $item->Encode();
- }
-
- $this->Value = $result;
- }
-
- function GetSequence()
- {
- $result = array();
- $seq = $this->Value;
- while (strlen($seq)) {
- $val = new ASNValue();
- $val->Decode($seq);
- $result[] = $val;
- }
-
- return $result;
- }
-}
diff --git a/library/asn1.php b/library/asn1.php
deleted file mode 100644
index 6ab8c6210..000000000
--- a/library/asn1.php
+++ /dev/null
@@ -1,291 +0,0 @@
-<?php
-
-// ASN.1 parsing library
-// Attribution: http://www.krisbailey.com
-// license: unknown
-// modified: Mike Macgrivin mike@macgirvin.com 6-oct-2010 to support Salmon auto-discovery
-// from openssl public keys
-
-
-class ASN_BASE {
- public $asnData = null;
- private $cursor = 0;
- private $parent = null;
-
- public static $ASN_MARKERS = array(
- 'ASN_UNIVERSAL' => 0x00,
- 'ASN_APPLICATION' => 0x40,
- 'ASN_CONTEXT' => 0x80,
- 'ASN_PRIVATE' => 0xC0,
-
- 'ASN_PRIMITIVE' => 0x00,
- 'ASN_CONSTRUCTOR' => 0x20,
-
- 'ASN_LONG_LEN' => 0x80,
- 'ASN_EXTENSION_ID' => 0x1F,
- 'ASN_BIT' => 0x80,
- );
-
- public static $ASN_TYPES = array(
- 1 => 'ASN_BOOLEAN',
- 2 => 'ASN_INTEGER',
- 3 => 'ASN_BIT_STR',
- 4 => 'ASN_OCTET_STR',
- 5 => 'ASN_NULL',
- 6 => 'ASN_OBJECT_ID',
- 9 => 'ASN_REAL',
- 10 => 'ASN_ENUMERATED',
- 13 => 'ASN_RELATIVE_OID',
- 48 => 'ASN_SEQUENCE',
- 49 => 'ASN_SET',
- 19 => 'ASN_PRINT_STR',
- 22 => 'ASN_IA5_STR',
- 23 => 'ASN_UTC_TIME',
- 24 => 'ASN_GENERAL_TIME',
- );
-
- function __construct($v = false)
- {
- if (false !== $v) {
- $this->asnData = $v;
- if (is_array($this->asnData)) {
- foreach ($this->asnData as $key => $value) {
- if (is_object($value)) {
- $this->asnData[$key]->setParent($this);
- }
- }
- } else {
- if (is_object($this->asnData)) {
- $this->asnData->setParent($this);
- }
- }
- }
- }
-
- public function setParent($parent)
- {
- if (false !== $parent) {
- $this->parent = $parent;
- }
- }
-
- /**
- * This function will take the markers and types arrays and
- * dynamically generate classes that extend this class for each one,
- * and also define constants for them.
- */
- public static function generateSubclasses()
- {
- define('ASN_BASE', 0);
- foreach (self::$ASN_MARKERS as $name => $bit)
- self::makeSubclass($name, $bit);
- foreach (self::$ASN_TYPES as $bit => $name)
- self::makeSubclass($name, $bit);
- }
-
- /**
- * Helper function for generateSubclasses()
- */
- public static function makeSubclass($name, $bit)
- {
- define($name, $bit);
- eval("class ".$name." extends ASN_BASE {}");
- }
-
- /**
- * This function reset's the internal cursor used for value iteration.
- */
- public function reset()
- {
- $this->cursor = 0;
- }
-
- /**
- * This function catches calls to get the value for the type, typeName, value, values, and data
- * from the object. For type calls we just return the class name or the value of the constant that
- * is named the same as the class.
- */
- public function __get($name)
- {
- if ('type' == $name) {
- // int flag of the data type
- return constant(get_class($this));
- } elseif ('typeName' == $name) {
- // name of the data type
- return get_class($this);
- } elseif ('value' == $name) {
- // will always return one value and can be iterated over with:
- // while ($v = $obj->value) { ...
- // because $this->asnData["invalid key"] will return false
- return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData;
- } elseif ('values' == $name) {
- // will always return an array
- return is_array($this->asnData) ? $this->asnData : array($this->asnData);
- } elseif ('data' == $name) {
- // will always return the raw data
- return $this->asnData;
- }
- }
-
- /**
- * Parse an ASN.1 binary string.
- *
- * This function takes a binary ASN.1 string and parses it into it's respective
- * pieces and returns it. It can optionally stop at any depth.
- *
- * @param string $string The binary ASN.1 String
- * @param int $level The current parsing depth level
- * @param int $maxLevel The max parsing depth level
- * @return ASN_BASE The array representation of the ASN.1 data contained in $string
- */
- public static function parseASNString($string=false, $level=1, $maxLevels=false){
- if (!class_exists('ASN_UNIVERSAL'))
- self::generateSubclasses();
- if ($level>$maxLevels && $maxLevels)
- return array(new ASN_BASE($string));
- $parsed = array();
- $endLength = strlen($string);
- $bigLength = $length = $type = $dtype = $p = 0;
- while ($p<$endLength){
- $type = ord($string[$p++]);
- $dtype = ($type & 192) >> 6;
- if ($type==0){ // if we are type 0, just continue
- } else {
- $length = ord($string[$p++]);
- if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){
- $tempLength = 0;
- for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){
- $tempLength = ord($string[$p++]) + ($tempLength * 256);
- }
- $length = $tempLength;
- }
- $data = substr($string, $p, intval($length));
- $parsed[] = self::parseASNData($type, $data, $level, $maxLevels);
- $p = $p + $length;
- }
- }
- return $parsed;
- }
-
- /**
- * Parse an ASN.1 field value.
- *
- * This function takes a binary ASN.1 value and parses it according to it's specified type
- *
- * @param int $type The type of data being provided
- * @param string $data The raw binary data string
- * @param int $level The current parsing depth
- * @param int $maxLevels The max parsing depth
- * @return mixed The data that was parsed from the raw binary data string
- */
- public static function parseASNData($type, $data, $level, $maxLevels){
- $type = $type%50; // strip out context
- switch ($type){
- default:
- return new ASN_BASE($data);
- case ASN_BOOLEAN:
- return new ASN_BOOLEAN((bool)$data);
- case ASN_INTEGER:
- return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_'));
- case ASN_BIT_STR:
- return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels));
- case ASN_OCTET_STR:
- return new ASN_OCTET_STR($data);
- case ASN_NULL:
- return new ASN_NULL(null);
- case ASN_REAL:
- return new ASN_REAL($data);
- case ASN_ENUMERATED:
- return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels));
- case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-)
- // so, lets just return it ...
- return new ASN_RELATIVE_OID($data);
- case ASN_SEQUENCE:
- return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels));
- case ASN_SET:
- return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels));
- case ASN_PRINT_STR:
- return new ASN_PRINT_STR($data);
- case ASN_IA5_STR:
- return new ASN_IA5_STR($data);
- case ASN_UTC_TIME:
- return new ASN_UTC_TIME($data);
- case ASN_GENERAL_TIME:
- return new ASN_GENERAL_TIME($data);
- case ASN_OBJECT_ID:
- return new ASN_OBJECT_ID(self::parseOID($data));
- }
- }
-
- /**
- * Parse an ASN.1 OID value.
- *
- * This takes the raw binary string that represents an OID value and parses it into its
- * dot notation form. example - 1.2.840.113549.1.1.5
- * look up OID's here: http://www.oid-info.com/
- * (the multi-byte OID section can be done in a more efficient way, I will fix it later)
- *
- * @param string $data The raw binary data string
- * @return string The OID contained in $data
- */
- public static function parseOID($string){
- $ret = floor(ord($string[0])/40).".";
- $ret .= (ord($string[0]) % 40);
- $build = array();
- $cs = 0;
-
- for ($i=1; $i<strlen($string); $i++){
- $v = ord($string[$i]);
- if ($v>127){
- $build[] = ord($string[$i])-ASN_BIT;
- } elseif ($build){
- // do the build here for multibyte values
- $build[] = ord($string[$i])-ASN_BIT;
- // you know, it seems there should be a better way to do this...
- $build = array_reverse($build);
- $num = 0;
- for ($x=0; $x<count($build); $x++){
- $mult = $x==0?1:pow(256, $x);
- if ($x+1==count($build)){
- $value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult;
- } else {
- $value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult;
- }
- $num += $value;
- }
- $ret .= ".".$num;
- $build = array(); // start over
- } else {
- $ret .= ".".$v;
- $build = array();
- }
- }
- return $ret;
- }
-
- public static function printASN($x, $indent=''){
- if (is_object($x)) {
- echo $indent.$x->typeName."\n";
- if (ASN_NULL == $x->type) return;
- if (is_array($x->data)) {
- while ($d = $x->value) {
- echo self::printASN($d, $indent.'. ');
- }
- $x->reset();
- } else {
- echo self::printASN($x->data, $indent.'. ');
- }
- } elseif (is_array($x)) {
- foreach ($x as $d) {
- echo self::printASN($d, $indent);
- }
- } else {
- if (preg_match('/[^[:print:]]/', $x)) // if we have non-printable characters that would
- $x = base64_encode($x); // mess up the console, then print the base64 of them...
- echo $indent.$x."\n";
- }
- }
-
-
-}
-
diff --git a/tests/unit/Lib/ActivityTest.php b/tests/unit/Lib/ActivityTest.php
index 1857487c8..456a7535e 100644
--- a/tests/unit/Lib/ActivityTest.php
+++ b/tests/unit/Lib/ActivityTest.php
@@ -274,4 +274,50 @@ class ActivityTest extends UnitTestCase {
];
}
+ public function testBuildPacketWithEmptyChannel(): void {
+ $data = [ 'aKey' => 'aValue' ];
+ $packet = json_decode(Activity::build_packet($data, []), true);
+
+ $this->assertArrayHasKey('aKey', $packet);
+ $this->assertEquals('aValue', $packet['aKey']);
+ }
+
+ /**
+ * Test get protocols from an activitystreams actor object
+ *
+ * @dataProvider get_actor_protocols_provider
+ */
+ public function test_get_actor_protocols(array $actor, array $expected): void {
+ $this->assertEquals($expected, Activity::get_actor_protocols($actor));
+ }
+
+ /**
+ * Dataprovider for test_get_actor_protocols.
+ */
+ public static function get_actor_protocols_provider(): array {
+ return [
+ 'none' => [
+ ['tag' => [['type' => 'Note', 'name' => 'Website', 'content' => 'https://example.com']]],
+ []
+ ],
+ 'legacy' => [
+ ['tag' => [
+ ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'zot6'],
+ ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'activitypub'],
+ ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'diaspora']
+ ]],
+ ['zot6', 'activitypub', 'diaspora']
+ ],
+ 'fep-fb2a' => [
+ ['tag' => [['type' => 'Note', 'name' => 'Protocols', 'content' => 'zot6,activitypub,diaspora']]],
+ ['zot6', 'activitypub', 'diaspora']
+ ],
+ 'fep-fb2a with spaces' => [
+ ['tag' => [['type' => 'Note', 'name' => 'Protocols', 'content' => 'zot6, activitypub, diaspora']]],
+ ['zot6', 'activitypub', 'diaspora']
+ ],
+ ];
+
+
+ }
}
diff --git a/tests/unit/Lib/JcsEddsa2022Test.php b/tests/unit/Lib/JcsEddsa2022Test.php
index d18ad01ce..7cdc655f8 100644
--- a/tests/unit/Lib/JcsEddsa2022Test.php
+++ b/tests/unit/Lib/JcsEddsa2022Test.php
@@ -3,6 +3,7 @@
namespace Zotlabs\Tests\Unit\Lib;
use Zotlabs\Lib\JcsEddsa2022;
+use Zotlabs\Lib\JcsEddsa2022SignException;
use Zotlabs\Tests\Unit\UnitTestCase;
class JcsEddsa2022Test extends UnitTestCase {
@@ -171,4 +172,11 @@ class JcsEddsa2022Test extends UnitTestCase {
$this->assertTrue($verified, 'Verify encode and decode eddsa-jcs-2022');
}
+
+ public function testSignWithInvalidChannelShouldBeRejected(): void {
+ $this->expectException(JcsEddsa2022SignException::class);
+
+ $alg = new JcsEddsa2022();
+ $res = $alg->sign([], []);
+ }
}
diff --git a/tests/unit/Lib/KeyutilsTest.php b/tests/unit/Lib/KeyutilsTest.php
index d1b0b5ab8..1f3fa2295 100644
--- a/tests/unit/Lib/KeyutilsTest.php
+++ b/tests/unit/Lib/KeyutilsTest.php
@@ -23,8 +23,9 @@
namespace Zotlabs\Tests\Unit\Lib;
-use phpseclib\Crypt\RSA;
-use phpseclib\Math\BigInteger;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Math\BigInteger;
+
use Zotlabs\Tests\Unit\UnitTestCase;
use Zotlabs\Lib\Keyutils;
@@ -38,56 +39,54 @@ class KeyutilsTest extends UnitTestCase {
protected function getPubPKCS1() {
$key = '-----BEGIN RSA PUBLIC KEY-----
-MIIBCgKCAQEArXcEXQSkk25bwDxq5Ym85/OwernfOz0hgve46Jm1KXCF0+yeje8J
-BDbQTsMgkF+G8eP1er3oz3E0qlIFpYrza5o6kaaLETSroTyZR5QW5S21r/QJHE+4
-F08bw1zp9hrlvoOCE/g/W0mr3asO/x7LrQRKOETlZ/U6HGexTdYLyKlXJtB+VKjI
-XKAHxfVLRW2AvnFj+deowS1OhTN8ECpz88xG9wnh5agoq7Uol0WZNNm0p4oR6+cd
-zTPx/mBwcOoSqHLlO7ZACbx/VyD5G7mQKWfGP4b96D8FcUO74531my+aKIpLF4Io
-1JN4R4a4P8tZ8BkCnMvpuq9TF1s6vEthYQIDAQAB
+MIIBCgKCAQEAsSBBV5khOWvMAOIV2AhxQukBgWc1pfYqUM/9OIFfL+KaRDOFAT0y
+jMbtTCbHEjxZbasS3CNnHtAgJ+4BWjtFnZl7zY4pXb2RDe4IWNnK8BjqsWDVuPks
+sc+yRBTepR/50FG/xB4HfI4GRc/8EDmynyAdLDD/r6oPxmz1gMICqSGDX8yXYG4o
+DliNT3bWDH+uf1+6pWsN6IFQYmUoRLly1xsfc8AV4H1GmSFrbAvQpHA95GOlxnPY
+IwVth1m8O+D9SH2+0e8ourdXH3/9ccJnh6FVvFvkyeC1e2vY+J+uC0uwu+6IvHFV
+pSnS8lTmwG/BBh2dA5eqV3s+REdLdS/tsQIDAQAB
-----END RSA PUBLIC KEY-----';
return str_replace(["\r", "\n"], "\r\n", $key);
}
protected function getPubPKCS8() {
$key = '-----BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUKfOIkFX/Zcv6bmaTIYO6OO2g
-XQOne+iPfXo6YDdrtvvQNZwW5P/fptrgBzmUBkpuc/sEEKpMV2bGhBLsWSlPBYHe
-2ewwLwyzbnuHvGhc1PzwMNQ7R60ubVDQT6sBVigYGZIDBgUPjAXeqmg5qgWWh04H
-8Zf/YxyoGEovWDMxGQIDAQAB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSBBV5khOWvMAOIV2Ahx
+QukBgWc1pfYqUM/9OIFfL+KaRDOFAT0yjMbtTCbHEjxZbasS3CNnHtAgJ+4BWjtF
+nZl7zY4pXb2RDe4IWNnK8BjqsWDVuPkssc+yRBTepR/50FG/xB4HfI4GRc/8EDmy
+nyAdLDD/r6oPxmz1gMICqSGDX8yXYG4oDliNT3bWDH+uf1+6pWsN6IFQYmUoRLly
+1xsfc8AV4H1GmSFrbAvQpHA95GOlxnPYIwVth1m8O+D9SH2+0e8ourdXH3/9ccJn
+h6FVvFvkyeC1e2vY+J+uC0uwu+6IvHFVpSnS8lTmwG/BBh2dA5eqV3s+REdLdS/t
+sQIDAQAB
-----END PUBLIC KEY-----';
return str_replace(["\r", "\n"], "\r\n", $key);
}
public function testMeToPem() {
- Keyutils::pemToMe($this->getPubPKCS8(), $m, $e);
+ [$m, $e] = Keyutils::pemToMe($this->getPubPKCS8());
$gen_key = Keyutils::meToPem($m, $e);
self::assertEquals($this->getPubPKCS8(), $gen_key);
}
public function testRsaToPem() {
- $rsa = new RSA();
- $rsa->setPublicKey($this->getPubPKCS8());
- $key = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
- $gen_key = Keyutils::rsaToPem($key);
+ $gen_key = Keyutils::rsaToPem($this->getPubPKCS1());
self::assertEquals($gen_key, $this->getPubPKCS8());
}
public function testPemToRsa() {
- $rsa = new RSA();
- $rsa->setPublicKey($this->getPubPKCS1());
- $key = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8);
- $gen_key = Keyutils::pemToRsa($key);
+ $gen_key = Keyutils::pemToRsa($this->getPubPKCS8());
self::assertEquals($gen_key, $this->getPubPKCS1());
}
public function testPemToMe() {
- Keyutils::pemToMe($this->getPubPKCS8(), $m, $e);
- $gen_key = new RSA();
- $gen_key->loadKey([
+ [$m, $e] = Keyutils::pemToMe($this->getPubPKCS8());
+
+ $parsedKey = PublicKeyLoader::load([
'e' => new BigInteger($e, 256),
'n' => new BigInteger($m, 256)
]);
- self::assertEquals($gen_key->getPublicKey(), $this->getPubPKCS8());
+
+ self::assertEquals($parsedKey->toString('PKCS8'), $this->getPubPKCS8());
}
}
diff --git a/tests/unit/includes/AccountTest.php b/tests/unit/includes/AccountTest.php
index 3978f9d04..66c761ef5 100644
--- a/tests/unit/includes/AccountTest.php
+++ b/tests/unit/includes/AccountTest.php
@@ -1,9 +1,28 @@
<?php
+
+use Zotlabs\Tests\Unit\UnitTestCase;
+
/**
* Tests for account handling helper functions.
*/
+class AccountTest extends UnitTestCase {
+
+ /**
+ * Test the `get_account_id()` function.
+ */
+ public function test_get_account_id() {
+ App::set_account(null);
+ unset($_SESSION['account_id']);
+
+ $this->assertEquals(false, get_account_id(), 'get_account_id() should return false if not authenticated');
+
+ App::set_account(['account_id' => 36]);
+ $this->assertEquals(36, get_account_id(), 'get_account_id() should return account from global App object');
+
+ $_SESSION['account_id'] = 42;
+ $this->assertEquals(42, get_account_id(), 'get_account_id() should return the account from the session');
+ }
-class AccountTest extends Zotlabs\Tests\Unit\UnitTestCase {
public function test_get_account_by_id_returns_existing_account() {
$account = get_account_by_id(42);
$this->assertNotFalse($account);
diff --git a/tests/unit/includes/BBCodeTest.php b/tests/unit/includes/BBCodeTest.php
index 50475efea..982ef4eb9 100644
--- a/tests/unit/includes/BBCodeTest.php
+++ b/tests/unit/includes/BBCodeTest.php
@@ -142,20 +142,36 @@ class BBCodeTest extends UnitTestCase {
],
'naked url is converted to link' => [
'example url: https://example.com',
- 'example url: <a href="https://example.com" target="_blank" rel="nofollow noopener">https://example.com</a>'
+ 'example url: <a href="https://example.com" target="_blank" rel="nofollow noopener">https://example.com</a>'
],
'naked url followed by newline' => [
"https://www.example.com\nhave a great day.",
- '<a href="https://www.example.com" target="_blank" rel="nofollow noopener">https://www.example.com</a><br />have a great day.',
+ '<a href="https://www.example.com" target="_blank" rel="nofollow noopener">https://www.example.com</a><br />have a great day.',
],
'inline naked url' => [
"This is a link https://example.com/some/path more info.",
- 'This is a link <a href="https://example.com/some/path" target="_blank" rel="nofollow noopener">https://example.com/some/path</a> more info.',
+ 'This is a link <a href="https://example.com/some/path" target="_blank" rel="nofollow noopener">https://example.com/some/path</a> more info.',
],
'naked url within code block is not converted to link' => [
"[code]\nhttp://example.com\n[/code]",
"<pre><code>http://example.com</code></pre>"
],
+ 'geo uri is converted to link' => [
+ 'example url: [url]geo:37.786971,-122.399677;u=35[/url]',
+ 'example url: <a href="geo:37.786971,-122.399677;u=35" target="_blank" rel="nofollow noopener">geo:37.786971,-122.399677;u=35</a>'
+ ],
+ 'geo uri with label is converted to link' => [
+ 'example url: [url=geo:37.786971,-122.399677;u=35(Wikimedia+Foundation)]Wikimedia Foundation[/url]',
+ 'example url: <a href="geo:37.786971,-122.399677;u=35(Wikimedia+Foundation)" target="_blank" rel="nofollow noopener">Wikimedia Foundation</a>'
+ ],
+ 'naked geo uri is converted to link' => [
+ 'example url: geo:37.786971,-122.399677;u=35',
+ 'example url: <a href="geo:37.786971,-122.399677;u=35" target="_blank" rel="nofollow noopener">geo:37.786971,-122.399677;u=35</a>'
+ ],
+ 'naked geo uri with label is converted to link' => [
+ 'example url: geo:37.78918,-122.40335(Wikimedia+Foundation)',
+ 'example url: <a href="geo:37.78918,-122.40335(Wikimedia+Foundation)" target="_blank" rel="nofollow noopener">📍Wikimedia Foundation</a>'
+ ],
];
}
@@ -206,7 +222,7 @@ class BBCodeTest extends UnitTestCase {
'[rpost=a title]This is the body[/rpost]',
true,
'en',
- '<a href="https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body" target="_blank" rel="nofollow noopener">https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body</a>',
+ '<a href="https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body" target="_blank" rel="nofollow noopener">https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body</a>',
],
'unauthenticated observer rpost' => [
'[rpost=a title]This is the body[/rpost]',
diff --git a/tests/unit/includes/MarkdownTest.php b/tests/unit/includes/MarkdownTest.php
index 55dbb4445..eec809174 100644
--- a/tests/unit/includes/MarkdownTest.php
+++ b/tests/unit/includes/MarkdownTest.php
@@ -114,6 +114,10 @@ class MarkdownTest extends UnitTestCase {
'This is a link https://example.com/some/path more info.',
'This is a link https://example.com/some/path more info.',
],
+ 'mention with underscores is untouched' => [
+ '@{_test_@somesite.example} @{test_2_@othersite.example}',
+ '@{_test_@somesite.example} @{test_2_@othersite.example}',
+ ],
];
}
diff --git a/util/hmessages.po b/util/hmessages.po
index 8ef51a278..df91a5b63 100644
--- a/util/hmessages.po
+++ b/util/hmessages.po
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: 10.2RC\n"
+"Project-Id-Version: 10.4RC1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-03-05 10:11+0000\n"
+"POT-Creation-Date: 2025-06-24 07:52+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,7 +21,7 @@ msgstr ""
msgid "Source channel not found."
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:18 ../../include/text.php:3518
+#: ../../view/theme/redbasic/php/config.php:19 ../../include/text.php:3544
#: ../../addon/cart/submodules/orderoptions.php:335
#: ../../addon/cart/submodules/orderoptions.php:359
#: ../../addon/cart/submodules/orderoptions.php:435
@@ -30,13 +30,14 @@ msgstr ""
msgid "Default"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:19
-#: ../../view/theme/redbasic/php/config.php:22
+#: ../../view/theme/redbasic/php/config.php:20
+#: ../../view/theme/redbasic/php/config.php:23
msgid "Focus (Hubzilla default)"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:188 ../../include/js_strings.php:23
-#: ../../addon/irc/irc.php:45 ../../addon/socialauth/Mod_SocialAuth.php:341
+#: ../../view/theme/redbasic/php/config.php:193 ../../include/js_strings.php:21
+#: ../../include/conversation.php:1127 ../../addon/irc/irc.php:45
+#: ../../addon/socialauth/Mod_SocialAuth.php:341
#: ../../addon/hubwall/hubwall.php:96
#: ../../addon/openclipatar/openclipatar.php:54
#: ../../addon/hzfiles/hzfiles.php:86 ../../addon/piwik/piwik.php:95
@@ -45,7 +46,6 @@ msgstr ""
#: ../../addon/ijpost/Mod_Ijpost.php:72 ../../addon/redred/Mod_Redred.php:88
#: ../../addon/startpage/Mod_Startpage.php:75
#: ../../addon/libertree/Mod_Libertree.php:68 ../../addon/logrot/logrot.php:35
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:61
#: ../../addon/dwpost/Mod_Dwpost.php:78 ../../addon/diaspora/diaspora.php:90
#: ../../addon/diaspora/Mod_Diaspora.php:101
#: ../../addon/fuzzloc/Mod_Fuzzloc.php:54 ../../addon/mailtest/mailtest.php:100
@@ -56,7 +56,6 @@ msgstr ""
#: ../../addon/statusnet/Mod_Statusnet.php:191
#: ../../addon/statusnet/Mod_Statusnet.php:249
#: ../../addon/statusnet/Mod_Statusnet.php:304
-#: ../../addon/flashcards/Mod_Flashcards.php:269
#: ../../addon/ljpost/Mod_Ljpost.php:80 ../../addon/faces/Mod_Faces.php:141
#: ../../addon/cart/submodules/hzservices.php:645
#: ../../addon/cart/submodules/subscriptions.php:410
@@ -78,7 +77,7 @@ msgstr ""
#: ../../addon/rtof/Mod_Rtof.php:70 ../../addon/nofed/Mod_Nofed.php:51
#: ../../addon/photocache/Mod_Photocache.php:63
#: ../../addon/likebanner/likebanner.php:57
-#: ../../Zotlabs/Lib/ThreadItem.php:799 ../../Zotlabs/Storage/Browser.php:386
+#: ../../Zotlabs/Lib/ThreadItem.php:809 ../../Zotlabs/Storage/Browser.php:390
#: ../../Zotlabs/Module/Oauth.php:110 ../../Zotlabs/Module/Import_items.php:125
#: ../../Zotlabs/Module/Thing.php:364 ../../Zotlabs/Module/Thing.php:414
#: ../../Zotlabs/Module/Tokens.php:294 ../../Zotlabs/Module/Pdledit.php:137
@@ -101,15 +100,15 @@ msgstr ""
#: ../../Zotlabs/Module/Admin/Security.php:130
#: ../../Zotlabs/Module/Affinity.php:84 ../../Zotlabs/Module/Permcats.php:257
#: ../../Zotlabs/Module/Xchan.php:15 ../../Zotlabs/Module/Group.php:151
-#: ../../Zotlabs/Module/Group.php:160 ../../Zotlabs/Module/Invite.php:564
-#: ../../Zotlabs/Module/Mitem.php:257 ../../Zotlabs/Module/Photos.php:1056
-#: ../../Zotlabs/Module/Photos.php:1095 ../../Zotlabs/Module/Photos.php:1208
+#: ../../Zotlabs/Module/Group.php:160 ../../Zotlabs/Module/Invite.php:547
+#: ../../Zotlabs/Module/Mitem.php:257 ../../Zotlabs/Module/Photos.php:1057
+#: ../../Zotlabs/Module/Photos.php:1096 ../../Zotlabs/Module/Photos.php:1197
#: ../../Zotlabs/Module/Sources.php:123 ../../Zotlabs/Module/Sources.php:160
#: ../../Zotlabs/Module/Profiles.php:738 ../../Zotlabs/Module/Chat.php:208
#: ../../Zotlabs/Module/Chat.php:247 ../../Zotlabs/Module/Regate.php:408
#: ../../Zotlabs/Module/Setup.php:322 ../../Zotlabs/Module/Setup.php:362
#: ../../Zotlabs/Module/Editpost.php:88 ../../Zotlabs/Module/Oauth2.php:115
-#: ../../Zotlabs/Module/Settings/Display.php:188
+#: ../../Zotlabs/Module/Settings/Display.php:187
#: ../../Zotlabs/Module/Settings/Network.php:62
#: ../../Zotlabs/Module/Settings/Channel_home.php:91
#: ../../Zotlabs/Module/Settings/Account.php:109
@@ -131,63 +130,62 @@ msgstr ""
msgid "Submit"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:192
+#: ../../view/theme/redbasic/php/config.php:197
msgid "Theme settings"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:193
+#: ../../view/theme/redbasic/php/config.php:198
msgid "Dark style"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:194
+#: ../../view/theme/redbasic/php/config.php:199
msgid "Light style"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:195
+#: ../../view/theme/redbasic/php/config.php:200
msgid "Common settings"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:196
+#: ../../view/theme/redbasic/php/config.php:201
msgid "Primary theme color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:196
-#: ../../view/theme/redbasic/php/config.php:197
-#: ../../view/theme/redbasic/php/config.php:198
-#: ../../view/theme/redbasic/php/config.php:199
-#: ../../view/theme/redbasic/php/config.php:200
+#: ../../view/theme/redbasic/php/config.php:201
+#: ../../view/theme/redbasic/php/config.php:202
+#: ../../view/theme/redbasic/php/config.php:203
+#: ../../view/theme/redbasic/php/config.php:204
+#: ../../view/theme/redbasic/php/config.php:205
msgid "Current color, leave empty for default"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:197
+#: ../../view/theme/redbasic/php/config.php:202
msgid "Success theme color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:198
+#: ../../view/theme/redbasic/php/config.php:203
msgid "Info theme color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:199
+#: ../../view/theme/redbasic/php/config.php:204
msgid "Warning theme color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:200
+#: ../../view/theme/redbasic/php/config.php:205
msgid "Danger theme color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:201
+#: ../../view/theme/redbasic/php/config.php:206
msgid "Default to dark mode"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:201
-#: ../../view/theme/redbasic/php/config.php:202
-#: ../../view/theme/redbasic/php/config.php:203
-#: ../../view/theme/redbasic/php/config.php:215
-#: ../../include/conversation.php:1274
+#: ../../view/theme/redbasic/php/config.php:206
+#: ../../view/theme/redbasic/php/config.php:207
+#: ../../view/theme/redbasic/php/config.php:208
+#: ../../view/theme/redbasic/php/config.php:220
+#: ../../include/conversation.php:1159
#: ../../addon/socialauth/Mod_SocialAuth.php:218
#: ../../addon/ijpost/Mod_Ijpost.php:61 ../../addon/redred/Mod_Redred.php:61
#: ../../addon/libertree/Mod_Libertree.php:57
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:45
#: ../../addon/dwpost/Mod_Dwpost.php:59 ../../addon/dwpost/Mod_Dwpost.php:63
#: ../../addon/diaspora/Mod_Diaspora.php:70
#: ../../addon/pumpio/Mod_Pumpio.php:92 ../../addon/pumpio/Mod_Pumpio.php:96
@@ -223,11 +221,11 @@ msgstr ""
#: ../../addon/content_import/Mod_content_import.php:135
#: ../../addon/content_import/Mod_content_import.php:136
#: ../../addon/rtof/Mod_Rtof.php:47 ../../addon/nofed/Mod_Nofed.php:40
-#: ../../boot.php:1759 ../../Zotlabs/Lib/Libzotdir.php:166
+#: ../../boot.php:1766 ../../Zotlabs/Lib/Libzotdir.php:166
#: ../../Zotlabs/Lib/Libzotdir.php:167 ../../Zotlabs/Lib/Libzotdir.php:169
-#: ../../Zotlabs/Storage/Browser.php:310 ../../Zotlabs/Storage/Browser.php:311
-#: ../../Zotlabs/Storage/Browser.php:312 ../../Zotlabs/Storage/Browser.php:393
-#: ../../Zotlabs/Storage/Browser.php:395 ../../Zotlabs/Storage/Browser.php:558
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:315
+#: ../../Zotlabs/Storage/Browser.php:316 ../../Zotlabs/Storage/Browser.php:397
+#: ../../Zotlabs/Storage/Browser.php:399 ../../Zotlabs/Storage/Browser.php:562
#: ../../Zotlabs/Module/Filestorage.php:203
#: ../../Zotlabs/Module/Filestorage.php:211
#: ../../Zotlabs/Module/Contactedit.php:270
@@ -241,7 +239,7 @@ msgstr ""
#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Mitem.php:176
#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:254
-#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:666
+#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:668
#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
#: ../../Zotlabs/Module/Profiles.php:674 ../../Zotlabs/Module/Profiles.php:684
#: ../../Zotlabs/Module/Profiles.php:692 ../../Zotlabs/Module/Profiles.php:696
@@ -259,15 +257,14 @@ msgstr ""
msgid "No"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:201
-#: ../../view/theme/redbasic/php/config.php:202
-#: ../../view/theme/redbasic/php/config.php:203
-#: ../../view/theme/redbasic/php/config.php:215
-#: ../../include/conversation.php:1274
+#: ../../view/theme/redbasic/php/config.php:206
+#: ../../view/theme/redbasic/php/config.php:207
+#: ../../view/theme/redbasic/php/config.php:208
+#: ../../view/theme/redbasic/php/config.php:220
+#: ../../include/conversation.php:1159
#: ../../addon/socialauth/Mod_SocialAuth.php:218
#: ../../addon/ijpost/Mod_Ijpost.php:61 ../../addon/redred/Mod_Redred.php:61
#: ../../addon/libertree/Mod_Libertree.php:57
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:45
#: ../../addon/dwpost/Mod_Dwpost.php:59 ../../addon/dwpost/Mod_Dwpost.php:63
#: ../../addon/diaspora/Mod_Diaspora.php:70
#: ../../addon/pumpio/Mod_Pumpio.php:92 ../../addon/pumpio/Mod_Pumpio.php:96
@@ -303,11 +300,11 @@ msgstr ""
#: ../../addon/content_import/Mod_content_import.php:135
#: ../../addon/content_import/Mod_content_import.php:136
#: ../../addon/rtof/Mod_Rtof.php:47 ../../addon/nofed/Mod_Nofed.php:40
-#: ../../boot.php:1759 ../../Zotlabs/Lib/Libzotdir.php:166
+#: ../../boot.php:1766 ../../Zotlabs/Lib/Libzotdir.php:166
#: ../../Zotlabs/Lib/Libzotdir.php:167 ../../Zotlabs/Lib/Libzotdir.php:169
-#: ../../Zotlabs/Storage/Browser.php:310 ../../Zotlabs/Storage/Browser.php:311
-#: ../../Zotlabs/Storage/Browser.php:312 ../../Zotlabs/Storage/Browser.php:393
-#: ../../Zotlabs/Storage/Browser.php:395 ../../Zotlabs/Storage/Browser.php:558
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:315
+#: ../../Zotlabs/Storage/Browser.php:316 ../../Zotlabs/Storage/Browser.php:397
+#: ../../Zotlabs/Storage/Browser.php:399 ../../Zotlabs/Storage/Browser.php:562
#: ../../Zotlabs/Module/Filestorage.php:203
#: ../../Zotlabs/Module/Filestorage.php:211
#: ../../Zotlabs/Module/Contactedit.php:270
@@ -319,7 +316,7 @@ msgstr ""
#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Mitem.php:176
#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:254
-#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:666
+#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:668
#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
#: ../../Zotlabs/Module/Profiles.php:674 ../../Zotlabs/Module/Profiles.php:684
#: ../../Zotlabs/Module/Profiles.php:692 ../../Zotlabs/Module/Profiles.php:696
@@ -337,80 +334,80 @@ msgstr ""
msgid "Yes"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:202
+#: ../../view/theme/redbasic/php/config.php:207
msgid "Always use light icons for navbar"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:202
+#: ../../view/theme/redbasic/php/config.php:207
msgid "Enable this option if you use a dark navbar color in light mode"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:203
+#: ../../view/theme/redbasic/php/config.php:208
msgid "Narrow navbar"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:204
+#: ../../view/theme/redbasic/php/config.php:209
msgid "Navigation bar background color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:205
+#: ../../view/theme/redbasic/php/config.php:210
msgid "Dark navigation bar background color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:206
+#: ../../view/theme/redbasic/php/config.php:211
msgid "Set the background color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:207
+#: ../../view/theme/redbasic/php/config.php:212
msgid "Set the dark background color"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:208
+#: ../../view/theme/redbasic/php/config.php:213
msgid "Set the background image"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:209
+#: ../../view/theme/redbasic/php/config.php:214
msgid "Set the dark background image"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:210
+#: ../../view/theme/redbasic/php/config.php:215
msgid "Set font-size for the entire application"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:210
+#: ../../view/theme/redbasic/php/config.php:215
msgid "Examples: 1rem, 100%, 16px"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:211
+#: ../../view/theme/redbasic/php/config.php:216
msgid "Set radius of corners in rem"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:211
+#: ../../view/theme/redbasic/php/config.php:216
msgid "Leave empty for default radius"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:212
+#: ../../view/theme/redbasic/php/config.php:217
msgid "Set maximum width of content region in rem"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:212
+#: ../../view/theme/redbasic/php/config.php:217
msgid "Leave empty for default width"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:213
+#: ../../view/theme/redbasic/php/config.php:218
msgid "Set size of conversation author photo"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:213
-#: ../../view/theme/redbasic/php/config.php:214
+#: ../../view/theme/redbasic/php/config.php:218
+#: ../../view/theme/redbasic/php/config.php:219
msgid "Leave empty for default size"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:214
+#: ../../view/theme/redbasic/php/config.php:219
msgid "Set size of followup author photos"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:215
+#: ../../view/theme/redbasic/php/config.php:220
msgid "Show advanced settings"
msgstr ""
@@ -424,23 +421,23 @@ msgstr ""
msgid "This is the home page of %s."
msgstr ""
-#: ../../include/auth.php:232
+#: ../../include/auth.php:231
msgid "Delegation session ended."
msgstr ""
-#: ../../include/auth.php:236
+#: ../../include/auth.php:235
msgid "Logged out."
msgstr ""
-#: ../../include/auth.php:342
+#: ../../include/auth.php:341
msgid "Email validation is incomplete. Please check your email."
msgstr ""
-#: ../../include/auth.php:358
+#: ../../include/auth.php:357
msgid "Failed authentication"
msgstr ""
-#: ../../include/auth.php:368 ../../addon/openid/Mod_Openid.php:189
+#: ../../include/auth.php:367 ../../addon/openid/Mod_Openid.php:189
msgid "Login failed."
msgstr ""
@@ -502,8 +499,8 @@ msgid "This event has been added to your calendar."
msgstr ""
#: ../../include/event.php:1366 ../../include/conversation.php:153
-#: ../../include/text.php:2359 ../../Zotlabs/Module/Tagger.php:77
-#: ../../Zotlabs/Module/Like.php:453
+#: ../../include/text.php:2380 ../../Zotlabs/Module/Tagger.php:77
+#: ../../Zotlabs/Module/Like.php:441
#: ../../Zotlabs/Module/Channel_calendar.php:209
msgid "event"
msgstr ""
@@ -534,7 +531,6 @@ msgid "Mobile"
msgstr ""
#: ../../include/event.php:1536 ../../include/connections.php:791
-#: ../../Zotlabs/Widget/Notifications.php:43
#: ../../Zotlabs/Module/Connedit.php:742 ../../Zotlabs/Module/Cdav.php:1378
msgid "Home"
msgstr ""
@@ -569,80 +565,79 @@ msgstr ""
msgid "Other"
msgstr ""
-#: ../../include/feedutils.php:863 ../../include/text.php:1580
+#: ../../include/feedutils.php:863 ../../include/text.php:1602
msgid "unknown"
msgstr ""
-#: ../../include/items.php:481 ../../addon/hzfiles/hzfiles.php:75
+#: ../../include/items.php:484 ../../addon/hzfiles/hzfiles.php:75
#: ../../addon/redphotos/redphotos.php:119
#: ../../addon/redfiles/redfiles.php:109
#: ../../Zotlabs/Module/Import_items.php:116
#: ../../Zotlabs/Module/Dreport.php:10 ../../Zotlabs/Module/Dreport.php:60
#: ../../Zotlabs/Module/Subthread.php:89 ../../Zotlabs/Module/Group.php:109
-#: ../../Zotlabs/Module/Like.php:344 ../../Zotlabs/Module/Profperm.php:29
-#: ../../Zotlabs/Web/WebServer.php:120
+#: ../../Zotlabs/Module/Like.php:332 ../../Zotlabs/Module/Profperm.php:29
msgid "Permission denied"
msgstr ""
-#: ../../include/items.php:1275
+#: ../../include/items.php:1278
msgid "Visible to anybody on the internet."
msgstr ""
-#: ../../include/items.php:1277
+#: ../../include/items.php:1280
msgid "Visible to you only."
msgstr ""
-#: ../../include/items.php:1279
+#: ../../include/items.php:1282
msgid "Visible to anybody in this network."
msgstr ""
-#: ../../include/items.php:1281
+#: ../../include/items.php:1284
msgid "Visible to anybody authenticated."
msgstr ""
-#: ../../include/items.php:1283
+#: ../../include/items.php:1286
#, php-format
msgid "Visible to anybody on %s."
msgstr ""
-#: ../../include/items.php:1285
+#: ../../include/items.php:1288
msgid "Visible to all connections."
msgstr ""
-#: ../../include/items.php:1287
+#: ../../include/items.php:1290
msgid "Visible to approved connections."
msgstr ""
-#: ../../include/items.php:1289
+#: ../../include/items.php:1292
msgid "Visible to specific connections."
msgstr ""
-#: ../../include/items.php:3491 ../../Zotlabs/Lib/Activity.php:2282
+#: ../../include/items.php:3502 ../../Zotlabs/Lib/Activity.php:2225
#: ../../Zotlabs/Module/Share.php:124
#, php-format
msgid "&#x1f501; Repeated %1$s's %2$s"
msgstr ""
-#: ../../include/items.php:4568 ../../Zotlabs/Module/Group.php:63
+#: ../../include/items.php:4579 ../../Zotlabs/Module/Group.php:63
#: ../../Zotlabs/Module/Group.php:207
msgid "Privacy group not found."
msgstr ""
-#: ../../include/items.php:4584
+#: ../../include/items.php:4595
msgid "Privacy group is empty."
msgstr ""
-#: ../../include/items.php:4591
+#: ../../include/items.php:4602
#, php-format
msgid "Privacy group: %s"
msgstr ""
-#: ../../include/items.php:4601
+#: ../../include/items.php:4612
#, php-format
msgid "Connection: %s"
msgstr ""
-#: ../../include/items.php:4603
+#: ../../include/items.php:4614
msgid "Connection not found."
msgstr ""
@@ -863,7 +858,8 @@ msgstr ""
msgid "Change channels directly from within the navigation dropdown menu"
msgstr ""
-#: ../../include/features.php:296 ../../Zotlabs/Widget/Notifications.php:23
+#: ../../include/features.php:296 ../../Zotlabs/Lib/Apps.php:344
+#: ../../Zotlabs/Widget/Notifications.php:23
#: ../../Zotlabs/Module/Connections.php:347
msgid "Network"
msgstr ""
@@ -990,12 +986,12 @@ msgstr ""
msgid "Ability to create multiple profiles"
msgstr ""
-#: ../../include/attach.php:156 ../../include/attach.php:205
-#: ../../include/attach.php:278 ../../include/attach.php:329
-#: ../../include/attach.php:431 ../../include/attach.php:445
-#: ../../include/attach.php:452 ../../include/attach.php:534
-#: ../../include/attach.php:1106 ../../include/attach.php:1179
-#: ../../include/attach.php:1344 ../../include/photos.php:32
+#: ../../include/attach.php:157 ../../include/attach.php:206
+#: ../../include/attach.php:279 ../../include/attach.php:330
+#: ../../include/attach.php:432 ../../include/attach.php:446
+#: ../../include/attach.php:453 ../../include/attach.php:535
+#: ../../include/attach.php:1115 ../../include/attach.php:1188
+#: ../../include/attach.php:1359 ../../include/photos.php:32
#: ../../addon/openid/Mod_Id.php:53 ../../addon/keepout/keepout.php:36
#: ../../addon/cards/Mod_Cards.php:89 ../../addon/cards/Mod_Card_edit.php:51
#: ../../addon/pumpio/pumpio.php:44
@@ -1003,7 +999,7 @@ msgstr ""
#: ../../addon/articles/Mod_Articles.php:94 ../../addon/wiki/Mod_Wiki.php:63
#: ../../addon/wiki/Mod_Wiki.php:288 ../../addon/wiki/Mod_Wiki.php:425
#: ../../Zotlabs/Lib/Chatroom.php:135 ../../Zotlabs/Module/Page.php:34
-#: ../../Zotlabs/Module/Page.php:133 ../../Zotlabs/Module/Display.php:392
+#: ../../Zotlabs/Module/Page.php:133 ../../Zotlabs/Module/Display.php:387
#: ../../Zotlabs/Module/Cover_photo.php:299
#: ../../Zotlabs/Module/Cover_photo.php:312
#: ../../Zotlabs/Module/Editwebpage.php:68
@@ -1020,9 +1016,9 @@ msgstr ""
#: ../../Zotlabs/Module/Filestorage.php:119
#: ../../Zotlabs/Module/Filestorage.php:165
#: ../../Zotlabs/Module/Register.php:201 ../../Zotlabs/Module/Appman.php:163
-#: ../../Zotlabs/Module/Viewsrc.php:19 ../../Zotlabs/Module/Item.php:288
-#: ../../Zotlabs/Module/Item.php:307 ../../Zotlabs/Module/Item.php:317
-#: ../../Zotlabs/Module/Item.php:1276 ../../Zotlabs/Module/Achievements.php:34
+#: ../../Zotlabs/Module/Viewsrc.php:19 ../../Zotlabs/Module/Item.php:289
+#: ../../Zotlabs/Module/Item.php:308 ../../Zotlabs/Module/Item.php:318
+#: ../../Zotlabs/Module/Item.php:1272 ../../Zotlabs/Module/Achievements.php:34
#: ../../Zotlabs/Module/Connedit.php:299 ../../Zotlabs/Module/Defperms.php:181
#: ../../Zotlabs/Module/Suggest.php:32 ../../Zotlabs/Module/Regmod.php:20
#: ../../Zotlabs/Module/Profile_photo.php:390
@@ -1039,12 +1035,12 @@ msgstr ""
#: ../../Zotlabs/Module/Attach_edit.php:99
#: ../../Zotlabs/Module/Attach_edit.php:106 ../../Zotlabs/Module/Group.php:15
#: ../../Zotlabs/Module/Group.php:31 ../../Zotlabs/Module/Invite.php:65
-#: ../../Zotlabs/Module/Invite.php:316 ../../Zotlabs/Module/Like.php:242
+#: ../../Zotlabs/Module/Invite.php:316 ../../Zotlabs/Module/Like.php:230
#: ../../Zotlabs/Module/Blocks.php:73 ../../Zotlabs/Module/Blocks.php:80
#: ../../Zotlabs/Module/Mitem.php:129 ../../Zotlabs/Module/New_channel.php:106
#: ../../Zotlabs/Module/New_channel.php:131 ../../Zotlabs/Module/Photos.php:71
-#: ../../Zotlabs/Module/Channel.php:234 ../../Zotlabs/Module/Channel.php:391
-#: ../../Zotlabs/Module/Channel.php:429 ../../Zotlabs/Module/Profile.php:99
+#: ../../Zotlabs/Module/Channel.php:234 ../../Zotlabs/Module/Channel.php:395
+#: ../../Zotlabs/Module/Channel.php:434 ../../Zotlabs/Module/Profile.php:99
#: ../../Zotlabs/Module/Profile.php:114 ../../Zotlabs/Module/Moderate.php:15
#: ../../Zotlabs/Module/Sources.php:80 ../../Zotlabs/Module/Profiles.php:168
#: ../../Zotlabs/Module/Profiles.php:611
@@ -1057,92 +1053,99 @@ msgstr ""
#: ../../Zotlabs/Module/Setup.php:220 ../../Zotlabs/Module/Editpost.php:17
#: ../../Zotlabs/Module/Connections.php:32
#: ../../Zotlabs/Module/Editblock.php:67 ../../Zotlabs/Module/Vote.php:19
-#: ../../Zotlabs/Web/WebServer.php:121
msgid "Permission denied."
msgstr ""
-#: ../../include/attach.php:273 ../../include/attach.php:324
-#: ../../include/attach.php:426
+#: ../../include/attach.php:274 ../../include/attach.php:325
+#: ../../include/attach.php:427
msgid "Item was not found."
msgstr ""
-#: ../../include/attach.php:290
+#: ../../include/attach.php:291
msgid "Unknown error."
msgstr ""
-#: ../../include/attach.php:621
+#: ../../include/attach.php:622
msgid "No source file."
msgstr ""
-#: ../../include/attach.php:643
+#: ../../include/attach.php:644
msgid "Cannot locate file to replace"
msgstr ""
-#: ../../include/attach.php:662
+#: ../../include/attach.php:663
msgid "Cannot locate file to revise/update"
msgstr ""
-#: ../../include/attach.php:808
+#: ../../include/attach.php:804 ../../include/attach.php:2609
+msgid "Filename too long"
+msgstr ""
+
+#: ../../include/attach.php:817
#, php-format
msgid "File exceeds size limit of %d"
msgstr ""
-#: ../../include/attach.php:829
+#: ../../include/attach.php:838
#, php-format
msgid "You have reached your limit of %1$.0f Mbytes attachment storage."
msgstr ""
-#: ../../include/attach.php:1019
+#: ../../include/attach.php:1028
msgid "File upload failed. Possible system limit or action terminated."
msgstr ""
-#: ../../include/attach.php:1048
+#: ../../include/attach.php:1057
msgid "Stored file could not be verified. Upload failed."
msgstr ""
-#: ../../include/attach.php:1120 ../../include/attach.php:1136
+#: ../../include/attach.php:1129 ../../include/attach.php:1145
msgid "Path not available."
msgstr ""
-#: ../../include/attach.php:1184 ../../include/attach.php:1349
+#: ../../include/attach.php:1193 ../../include/attach.php:1364
msgid "Empty pathname"
msgstr ""
-#: ../../include/attach.php:1210
+#: ../../include/attach.php:1199
+msgid "Pathname too long"
+msgstr ""
+
+#: ../../include/attach.php:1225
msgid "duplicate filename or path"
msgstr ""
-#: ../../include/attach.php:1238
+#: ../../include/attach.php:1253
msgid "Path not found."
msgstr ""
-#: ../../include/attach.php:1305
+#: ../../include/attach.php:1320
msgid "mkdir failed."
msgstr ""
-#: ../../include/attach.php:1309
+#: ../../include/attach.php:1324
msgid "database storage failed."
msgstr ""
-#: ../../include/attach.php:1355
+#: ../../include/attach.php:1370
msgid "Empty path"
msgstr ""
-#: ../../include/attach.php:1992
+#: ../../include/attach.php:2007
#, php-format
msgid "%s shared an %s with you"
msgstr ""
-#: ../../include/attach.php:1992
+#: ../../include/attach.php:2007
#, php-format
msgid "%s shared a %s with you"
msgstr ""
-#: ../../include/attach.php:1992 ../../Zotlabs/Module/Like.php:450
+#: ../../include/attach.php:2007 ../../Zotlabs/Module/Like.php:438
msgid "image"
msgstr ""
-#: ../../include/attach.php:1992 ../../addon/redfiles/redfilehelper.php:64
+#: ../../include/attach.php:2007 ../../addon/redfiles/redfilehelper.php:64
msgid "file"
msgstr ""
@@ -1505,8 +1508,8 @@ msgstr ""
#: ../../include/taxonomy.php:527 ../../include/taxonomy.php:548
#: ../../addon/cards/Widget/Cards_categories.php:80
#: ../../addon/articles/Widget/Articles_categories.php:80
-#: ../../Zotlabs/Storage/Browser.php:293 ../../Zotlabs/Storage/Browser.php:392
-#: ../../Zotlabs/Storage/Browser.php:406 ../../Zotlabs/Module/Cdav.php:1062
+#: ../../Zotlabs/Storage/Browser.php:297 ../../Zotlabs/Storage/Browser.php:396
+#: ../../Zotlabs/Storage/Browser.php:410 ../../Zotlabs/Module/Cdav.php:1062
msgid "Categories"
msgstr ""
@@ -1528,12 +1531,11 @@ msgid "Summary: "
msgstr ""
#: ../../include/cdav.php:158 ../../include/cdav.php:159
-#: ../../include/cdav.php:167 ../../include/conversation.php:1006
-#: ../../Zotlabs/Lib/Apps.php:1168 ../../Zotlabs/Lib/Apps.php:1252
-#: ../../Zotlabs/Lib/Activity.php:1726 ../../Zotlabs/Widget/Album.php:90
-#: ../../Zotlabs/Widget/Pinned.php:256 ../../Zotlabs/Widget/Portfolio.php:99
-#: ../../Zotlabs/Module/Embedphotos.php:177 ../../Zotlabs/Module/Photos.php:788
-#: ../../Zotlabs/Module/Photos.php:1246
+#: ../../include/cdav.php:167 ../../Zotlabs/Lib/Apps.php:1168
+#: ../../Zotlabs/Lib/Apps.php:1252 ../../Zotlabs/Lib/Activity.php:1696
+#: ../../Zotlabs/Widget/Album.php:93 ../../Zotlabs/Widget/Portfolio.php:103
+#: ../../Zotlabs/Module/Embedphotos.php:179 ../../Zotlabs/Module/Photos.php:790
+#: ../../Zotlabs/Module/Photos.php:1240
msgid "Unknown"
msgstr ""
@@ -1619,7 +1621,7 @@ msgstr ""
msgid "wants"
msgstr ""
-#: ../../include/taxonomy.php:589 ../../Zotlabs/Lib/ThreadItem.php:296
+#: ../../include/taxonomy.php:589
msgid "like"
msgstr ""
@@ -1627,7 +1629,7 @@ msgstr ""
msgid "likes"
msgstr ""
-#: ../../include/taxonomy.php:590 ../../Zotlabs/Lib/ThreadItem.php:297
+#: ../../include/taxonomy.php:590
msgid "dislike"
msgstr ""
@@ -1635,15 +1637,15 @@ msgstr ""
msgid "dislikes"
msgstr ""
-#: ../../include/taxonomy.php:677 ../../include/conversation.php:1561
-#: ../../Zotlabs/Module/Photos.php:1129
+#: ../../include/taxonomy.php:677 ../../include/conversation.php:1440
+#: ../../Zotlabs/Module/Photos.php:1118
msgctxt "noun"
msgid "Like"
msgid_plural "Likes"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/photo/photo_driver.php:458
+#: ../../include/photo/photo_driver.php:439
#: ../../Zotlabs/Module/Profile_photo.php:168
#: ../../Zotlabs/Module/Profile_photo.php:337
msgid "Profile Photos"
@@ -1675,73 +1677,68 @@ msgstr ""
msgid "Invitation could not be verified."
msgstr ""
-#: ../../include/account.php:192
-msgid "Please enter the required information."
-msgstr ""
-
-#: ../../include/account.php:259 ../../include/account.php:367
+#: ../../include/account.php:206
msgid "Failed to store account information."
msgstr ""
-#: ../../include/account.php:436 ../../include/account.php:504
-#: ../../Zotlabs/Module/Register.php:329
+#: ../../include/account.php:275 ../../Zotlabs/Module/Register.php:329
#, php-format
msgid "Registration confirmation for %s"
msgstr ""
-#: ../../include/account.php:579
+#: ../../include/account.php:348
#, php-format
msgid "Registration request at %s"
msgstr ""
-#: ../../include/account.php:601
+#: ../../include/account.php:370
msgid "your registration password"
msgstr ""
-#: ../../include/account.php:607 ../../include/account.php:680
+#: ../../include/account.php:376 ../../include/account.php:449
#, php-format
msgid "Registration details for %s"
msgstr ""
-#: ../../include/account.php:695
+#: ../../include/account.php:464
msgid "Account approved."
msgstr ""
-#: ../../include/account.php:747
+#: ../../include/account.php:516
#, php-format
msgid "Registration revoked for %s"
msgstr ""
-#: ../../include/account.php:754
+#: ../../include/account.php:523
#, php-format
msgid "Could not revoke registration for %s"
msgstr ""
-#: ../../include/account.php:1171 ../../include/account.php:1173
+#: ../../include/account.php:940 ../../include/account.php:942
msgid "Click here to upgrade."
msgstr ""
-#: ../../include/account.php:1179
+#: ../../include/account.php:948
msgid "This action exceeds the limits set by your subscription plan."
msgstr ""
-#: ../../include/account.php:1184
+#: ../../include/account.php:953
msgid "This action is not available under your subscription plan."
msgstr ""
-#: ../../include/account.php:1244
+#: ../../include/account.php:1013
msgid "open"
msgstr ""
-#: ../../include/account.php:1244
+#: ../../include/account.php:1013
msgid "closed"
msgstr ""
-#: ../../include/account.php:1251
+#: ../../include/account.php:1020
msgid "Registration is currently"
msgstr ""
-#: ../../include/account.php:1260
+#: ../../include/account.php:1029
msgid "please come back"
msgstr ""
@@ -1753,367 +1750,363 @@ msgstr ""
msgid "Item deleted"
msgstr ""
-#: ../../include/js_strings.php:7 ../../Zotlabs/Lib/ThreadItem.php:798
-#: ../../Zotlabs/Module/Photos.php:1094 ../../Zotlabs/Module/Photos.php:1207
+#: ../../include/js_strings.php:7 ../../Zotlabs/Lib/ThreadItem.php:808
+#: ../../Zotlabs/Module/Photos.php:1095 ../../Zotlabs/Module/Photos.php:1196
msgid "Comment"
msgstr ""
-#: ../../include/js_strings.php:8 ../../Zotlabs/Lib/ThreadItem.php:507
-msgid "show all"
-msgstr ""
-
-#: ../../include/js_strings.php:9
-msgid "show less"
-msgstr ""
-
-#: ../../include/js_strings.php:10
+#: ../../include/js_strings.php:8
msgid "expand"
msgstr ""
-#: ../../include/js_strings.php:11
+#: ../../include/js_strings.php:9
msgid "collapse"
msgstr ""
-#: ../../include/js_strings.php:12
+#: ../../include/js_strings.php:10
msgid "Password too short"
msgstr ""
-#: ../../include/js_strings.php:13 ../../Zotlabs/Module/Register.php:162
+#: ../../include/js_strings.php:11 ../../Zotlabs/Module/Register.php:162
msgid "Passwords do not match"
msgstr ""
-#: ../../include/js_strings.php:14
+#: ../../include/js_strings.php:12
msgid "everybody"
msgstr ""
-#: ../../include/js_strings.php:15
+#: ../../include/js_strings.php:13
msgid "Secret Passphrase"
msgstr ""
-#: ../../include/js_strings.php:16
+#: ../../include/js_strings.php:14
msgid "Passphrase hint"
msgstr ""
-#: ../../include/js_strings.php:17
+#: ../../include/js_strings.php:15
msgid "Notice: Permissions have changed but have not yet been submitted."
msgstr ""
-#: ../../include/js_strings.php:18
+#: ../../include/js_strings.php:16
msgid "close all"
msgstr ""
-#: ../../include/js_strings.php:19
+#: ../../include/js_strings.php:17
msgid "Nothing new here"
msgstr ""
-#: ../../include/js_strings.php:20
+#: ../../include/js_strings.php:18
msgid "Rate This Channel (this is public)"
msgstr ""
-#: ../../include/js_strings.php:21
+#: ../../include/js_strings.php:19
msgid "Rating"
msgstr ""
-#: ../../include/js_strings.php:22
+#: ../../include/js_strings.php:20
msgid "Describe (optional)"
msgstr ""
-#: ../../include/js_strings.php:24
+#: ../../include/js_strings.php:22
msgid "Please enter a link URL"
msgstr ""
-#: ../../include/js_strings.php:25
+#: ../../include/js_strings.php:23
msgid "Unsaved changes. Are you sure you wish to leave this page?"
msgstr ""
-#: ../../include/js_strings.php:26 ../../Zotlabs/Module/Locs.php:121
+#: ../../include/js_strings.php:24 ../../Zotlabs/Module/Locs.php:121
#: ../../Zotlabs/Module/Pubsites.php:55 ../../Zotlabs/Module/Profiles.php:476
#: ../../Zotlabs/Module/Profiles.php:749 ../../Zotlabs/Module/Cdav.php:1006
msgid "Location"
msgstr ""
-#: ../../include/js_strings.php:27
+#: ../../include/js_strings.php:25
msgid "lovely"
msgstr ""
-#: ../../include/js_strings.php:28
+#: ../../include/js_strings.php:26
msgid "wonderful"
msgstr ""
-#: ../../include/js_strings.php:29
+#: ../../include/js_strings.php:27
msgid "fantastic"
msgstr ""
-#: ../../include/js_strings.php:30
+#: ../../include/js_strings.php:28
msgid "great"
msgstr ""
-#: ../../include/js_strings.php:31
+#: ../../include/js_strings.php:29
msgid ""
"Your chosen nickname was either already taken or not valid. Please use our "
"suggestion ("
msgstr ""
-#: ../../include/js_strings.php:32
+#: ../../include/js_strings.php:30
msgid ") or enter a new one."
msgstr ""
-#: ../../include/js_strings.php:33
+#: ../../include/js_strings.php:31
msgid "Thank you, this nickname is valid."
msgstr ""
-#: ../../include/js_strings.php:34
+#: ../../include/js_strings.php:32
msgid "A channel name is required."
msgstr ""
-#: ../../include/js_strings.php:35
+#: ../../include/js_strings.php:33
msgid "This is a "
msgstr ""
-#: ../../include/js_strings.php:36
+#: ../../include/js_strings.php:34
msgid " channel name"
msgstr ""
-#: ../../include/js_strings.php:37
+#: ../../include/js_strings.php:35
msgid "Back to reply"
msgstr ""
-#: ../../include/js_strings.php:38
+#: ../../include/js_strings.php:36
msgid "Pinned"
msgstr ""
-#: ../../include/js_strings.php:39 ../../Zotlabs/Lib/ThreadItem.php:457
+#: ../../include/js_strings.php:37 ../../Zotlabs/Lib/ThreadItem.php:454
msgid "Pin to the top"
msgstr ""
-#: ../../include/js_strings.php:40 ../../Zotlabs/Lib/ThreadItem.php:457
-#: ../../Zotlabs/Widget/Pinned.php:150
+#: ../../include/js_strings.php:38 ../../Zotlabs/Lib/ThreadItem.php:454
+#: ../../Zotlabs/Widget/Pinned.php:153
msgid "Unpin from the top"
msgstr ""
-#: ../../include/js_strings.php:46
+#: ../../include/js_strings.php:39
+msgid "Double click to exit zoom"
+msgstr ""
+
+#: ../../include/js_strings.php:45
#, php-format
msgid "%d minutes"
msgid_plural "%d minutes"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/js_strings.php:47
+#: ../../include/js_strings.php:46
#, php-format
msgid "about %d hours"
msgid_plural "about %d hours"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/js_strings.php:48
+#: ../../include/js_strings.php:47
#, php-format
msgid "%d days"
msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/js_strings.php:49
+#: ../../include/js_strings.php:48
#, php-format
msgid "%d months"
msgid_plural "%d months"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/js_strings.php:50
+#: ../../include/js_strings.php:49
#, php-format
msgid "%d years"
msgid_plural "%d years"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/js_strings.php:52 ../../include/text.php:1503
+#: ../../include/js_strings.php:51 ../../include/text.php:1525
msgid "January"
msgstr ""
-#: ../../include/js_strings.php:53 ../../include/text.php:1503
+#: ../../include/js_strings.php:52 ../../include/text.php:1525
msgid "February"
msgstr ""
-#: ../../include/js_strings.php:54 ../../include/text.php:1503
+#: ../../include/js_strings.php:53 ../../include/text.php:1525
msgid "March"
msgstr ""
-#: ../../include/js_strings.php:55 ../../include/text.php:1503
+#: ../../include/js_strings.php:54 ../../include/text.php:1525
msgid "April"
msgstr ""
-#: ../../include/js_strings.php:56
+#: ../../include/js_strings.php:55
msgctxt "long"
msgid "May"
msgstr ""
-#: ../../include/js_strings.php:57 ../../include/text.php:1503
+#: ../../include/js_strings.php:56 ../../include/text.php:1525
msgid "June"
msgstr ""
-#: ../../include/js_strings.php:58 ../../include/text.php:1503
+#: ../../include/js_strings.php:57 ../../include/text.php:1525
msgid "July"
msgstr ""
-#: ../../include/js_strings.php:59 ../../include/text.php:1503
+#: ../../include/js_strings.php:58 ../../include/text.php:1525
msgid "August"
msgstr ""
-#: ../../include/js_strings.php:60 ../../include/text.php:1503
+#: ../../include/js_strings.php:59 ../../include/text.php:1525
msgid "September"
msgstr ""
-#: ../../include/js_strings.php:61 ../../include/text.php:1503
+#: ../../include/js_strings.php:60 ../../include/text.php:1525
msgid "October"
msgstr ""
-#: ../../include/js_strings.php:62 ../../include/text.php:1503
+#: ../../include/js_strings.php:61 ../../include/text.php:1525
msgid "November"
msgstr ""
-#: ../../include/js_strings.php:63 ../../include/text.php:1503
+#: ../../include/js_strings.php:62 ../../include/text.php:1525
msgid "December"
msgstr ""
-#: ../../include/js_strings.php:64
+#: ../../include/js_strings.php:63
msgid "Jan"
msgstr ""
-#: ../../include/js_strings.php:65
+#: ../../include/js_strings.php:64
msgid "Feb"
msgstr ""
-#: ../../include/js_strings.php:66
+#: ../../include/js_strings.php:65
msgid "Mar"
msgstr ""
-#: ../../include/js_strings.php:67
+#: ../../include/js_strings.php:66
msgid "Apr"
msgstr ""
-#: ../../include/js_strings.php:68
+#: ../../include/js_strings.php:67
msgctxt "short"
msgid "May"
msgstr ""
-#: ../../include/js_strings.php:69
+#: ../../include/js_strings.php:68
msgid "Jun"
msgstr ""
-#: ../../include/js_strings.php:70
+#: ../../include/js_strings.php:69
msgid "Jul"
msgstr ""
-#: ../../include/js_strings.php:71
+#: ../../include/js_strings.php:70
msgid "Aug"
msgstr ""
-#: ../../include/js_strings.php:72
+#: ../../include/js_strings.php:71
msgid "Sep"
msgstr ""
-#: ../../include/js_strings.php:73
+#: ../../include/js_strings.php:72
msgid "Oct"
msgstr ""
-#: ../../include/js_strings.php:74
+#: ../../include/js_strings.php:73
msgid "Nov"
msgstr ""
-#: ../../include/js_strings.php:75
+#: ../../include/js_strings.php:74
msgid "Dec"
msgstr ""
-#: ../../include/js_strings.php:76 ../../include/text.php:1499
+#: ../../include/js_strings.php:75 ../../include/text.php:1521
msgid "Sunday"
msgstr ""
-#: ../../include/js_strings.php:77 ../../include/text.php:1499
+#: ../../include/js_strings.php:76 ../../include/text.php:1521
msgid "Monday"
msgstr ""
-#: ../../include/js_strings.php:78 ../../include/text.php:1499
+#: ../../include/js_strings.php:77 ../../include/text.php:1521
msgid "Tuesday"
msgstr ""
-#: ../../include/js_strings.php:79 ../../include/text.php:1499
+#: ../../include/js_strings.php:78 ../../include/text.php:1521
msgid "Wednesday"
msgstr ""
-#: ../../include/js_strings.php:80 ../../include/text.php:1499
+#: ../../include/js_strings.php:79 ../../include/text.php:1521
msgid "Thursday"
msgstr ""
-#: ../../include/js_strings.php:81 ../../include/text.php:1499
+#: ../../include/js_strings.php:80 ../../include/text.php:1521
msgid "Friday"
msgstr ""
-#: ../../include/js_strings.php:82 ../../include/text.php:1499
+#: ../../include/js_strings.php:81 ../../include/text.php:1521
msgid "Saturday"
msgstr ""
-#: ../../include/js_strings.php:83
+#: ../../include/js_strings.php:82
msgid "Sun"
msgstr ""
-#: ../../include/js_strings.php:84
+#: ../../include/js_strings.php:83
msgid "Mon"
msgstr ""
-#: ../../include/js_strings.php:85
+#: ../../include/js_strings.php:84
msgid "Tue"
msgstr ""
-#: ../../include/js_strings.php:86
+#: ../../include/js_strings.php:85
msgid "Wed"
msgstr ""
-#: ../../include/js_strings.php:87
+#: ../../include/js_strings.php:86
msgid "Thu"
msgstr ""
-#: ../../include/js_strings.php:88
+#: ../../include/js_strings.php:87
msgid "Fri"
msgstr ""
-#: ../../include/js_strings.php:89
+#: ../../include/js_strings.php:88
msgid "Sat"
msgstr ""
-#: ../../include/js_strings.php:90
+#: ../../include/js_strings.php:89
msgctxt "calendar"
msgid "today"
msgstr ""
-#: ../../include/js_strings.php:91
+#: ../../include/js_strings.php:90
msgctxt "calendar"
msgid "month"
msgstr ""
-#: ../../include/js_strings.php:92
+#: ../../include/js_strings.php:91
msgctxt "calendar"
msgid "week"
msgstr ""
-#: ../../include/js_strings.php:93
+#: ../../include/js_strings.php:92
msgctxt "calendar"
msgid "day"
msgstr ""
-#: ../../include/js_strings.php:94
+#: ../../include/js_strings.php:93
msgctxt "calendar"
msgid "All day"
msgstr ""
-#: ../../include/js_strings.php:97
+#: ../../include/js_strings.php:96
msgid "Please stand by while your download is being prepared."
msgstr ""
-#: ../../include/js_strings.php:100
+#: ../../include/js_strings.php:99
msgid "Email address not valid"
msgstr ""
-#: ../../include/js_strings.php:101 ../../include/datetime.php:211
+#: ../../include/js_strings.php:100 ../../include/datetime.php:211
#: ../../addon/cart/submodules/orderoptions.php:334
#: ../../addon/cart/submodules/orderoptions.php:358
#: ../../addon/cart/submodules/orderoptions.php:434
@@ -2130,105 +2123,65 @@ msgstr ""
msgid "OpenWebAuth: %1$s welcomes %2$s"
msgstr ""
-#: ../../include/conversation.php:149 ../../include/text.php:2356
+#: ../../include/conversation.php:149 ../../include/text.php:2377
#: ../../addon/redphotos/redphotohelper.php:71
-#: ../../addon/diaspora/Receiver.php:1704
+#: ../../addon/diaspora/Receiver.php:1705
#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Tagger.php:73
msgid "photo"
msgstr ""
-#: ../../include/conversation.php:157 ../../Zotlabs/Module/Like.php:183
+#: ../../include/conversation.php:157 ../../Zotlabs/Module/Like.php:171
msgid "channel"
msgstr ""
-#: ../../include/conversation.php:180 ../../include/markdown.php:192
-#: ../../include/text.php:2362 ../../include/bbcode.php:572
-#: ../../Zotlabs/Module/Tagger.php:81
-msgid "post"
-msgstr ""
-
-#: ../../include/conversation.php:182 ../../include/text.php:2364
-#: ../../Zotlabs/Module/Tagger.php:83
-msgid "comment"
+#: ../../include/conversation.php:180 ../../include/text.php:2385
+msgid "message"
msgstr ""
-#: ../../include/conversation.php:196 ../../addon/diaspora/Receiver.php:1639
-#: ../../Zotlabs/Module/Like.php:486
+#: ../../include/conversation.php:194 ../../addon/diaspora/Receiver.php:1640
+#: ../../Zotlabs/Module/Like.php:474
#, php-format
msgid "%1$s likes %2$s's %3$s"
msgstr ""
-#: ../../include/conversation.php:198
+#: ../../include/conversation.php:196
#, php-format
msgid "likes %1$s's %2$s"
msgstr ""
-#: ../../include/conversation.php:201 ../../Zotlabs/Module/Like.php:488
+#: ../../include/conversation.php:199 ../../Zotlabs/Module/Like.php:476
#, php-format
msgid "%1$s doesn't like %2$s's %3$s"
msgstr ""
-#: ../../include/conversation.php:202
+#: ../../include/conversation.php:200
#, php-format
msgid "doesn't like %1$s's %2$s"
msgstr ""
-#: ../../include/conversation.php:205
+#: ../../include/conversation.php:203
#, php-format
msgid "%1$s repeated %2$s's %3$s"
msgstr ""
-#: ../../include/conversation.php:206
+#: ../../include/conversation.php:204
#, php-format
msgid "repeated %1$s's %2$s"
msgstr ""
-#: ../../include/conversation.php:334 ../../Zotlabs/Lib/ThreadItem.php:474
+#: ../../include/conversation.php:332 ../../Zotlabs/Lib/ThreadItem.php:472
msgid "This is an unsaved preview"
msgstr ""
-#: ../../include/conversation.php:471 ../../Zotlabs/Module/Photos.php:1110
-msgctxt "title"
-msgid "Likes"
-msgstr ""
-
-#: ../../include/conversation.php:472 ../../Zotlabs/Module/Photos.php:1110
-msgctxt "title"
-msgid "Dislikes"
-msgstr ""
-
-#: ../../include/conversation.php:473 ../../Zotlabs/Widget/Pinned.php:73
-#: ../../Zotlabs/Module/Photos.php:1111
-msgctxt "title"
-msgid "Attending"
-msgstr ""
-
-#: ../../include/conversation.php:474 ../../Zotlabs/Widget/Pinned.php:74
-#: ../../Zotlabs/Module/Photos.php:1111
-msgctxt "title"
-msgid "Not attending"
-msgstr ""
-
-#: ../../include/conversation.php:475 ../../Zotlabs/Widget/Pinned.php:75
-#: ../../Zotlabs/Module/Photos.php:1111
-msgctxt "title"
-msgid "Might attend"
-msgstr ""
-
-#: ../../include/conversation.php:477
-msgctxt "title"
-msgid "Repeats"
-msgstr ""
-
-#: ../../include/conversation.php:546
+#: ../../include/conversation.php:533
msgid "Select"
msgstr ""
-#: ../../include/conversation.php:547 ../../include/conversation.php:608
+#: ../../include/conversation.php:534 ../../include/conversation.php:595
#: ../../addon/cards/Mod_Card_edit.php:124
#: ../../addon/articles/Mod_Article_edit.php:124 ../../Zotlabs/Lib/Apps.php:618
-#: ../../Zotlabs/Lib/ThreadItem.php:183 ../../Zotlabs/Lib/ThreadItem.php:482
-#: ../../Zotlabs/Storage/Browser.php:388
+#: ../../Zotlabs/Lib/ThreadItem.php:180 ../../Zotlabs/Lib/ThreadItem.php:480
+#: ../../Zotlabs/Storage/Browser.php:392
#: ../../Zotlabs/Module/Editwebpage.php:167
#: ../../Zotlabs/Module/Webpages.php:251 ../../Zotlabs/Module/Oauth.php:172
#: ../../Zotlabs/Module/Thing.php:302 ../../Zotlabs/Module/Tokens.php:295
@@ -2238,116 +2191,116 @@ msgstr ""
#: ../../Zotlabs/Module/Admin/Profs.php:177
#: ../../Zotlabs/Module/Admin/Channels.php:171
#: ../../Zotlabs/Module/Permcats.php:261 ../../Zotlabs/Module/Group.php:252
-#: ../../Zotlabs/Module/Blocks.php:160 ../../Zotlabs/Module/Photos.php:1173
+#: ../../Zotlabs/Module/Blocks.php:160 ../../Zotlabs/Module/Photos.php:1162
#: ../../Zotlabs/Module/Editlayout.php:138 ../../Zotlabs/Module/Cdav.php:1047
#: ../../Zotlabs/Module/Cdav.php:1385 ../../Zotlabs/Module/Oauth2.php:193
#: ../../Zotlabs/Module/Editblock.php:139
msgid "Delete"
msgstr ""
-#: ../../include/conversation.php:553 ../../Zotlabs/Lib/ThreadItem.php:248
+#: ../../include/conversation.php:540 ../../Zotlabs/Lib/ThreadItem.php:242
msgid "Toggle Star Status"
msgstr ""
-#: ../../include/conversation.php:559
+#: ../../include/conversation.php:546
msgid "Private Message"
msgstr ""
-#: ../../include/conversation.php:568 ../../Zotlabs/Lib/ThreadItem.php:258
+#: ../../include/conversation.php:555 ../../Zotlabs/Lib/ThreadItem.php:251
#: ../../Zotlabs/Widget/Pinned.php:84
msgid "Message signature validated"
msgstr ""
-#: ../../include/conversation.php:569 ../../Zotlabs/Lib/ThreadItem.php:259
+#: ../../include/conversation.php:556 ../../Zotlabs/Lib/ThreadItem.php:252
#: ../../Zotlabs/Widget/Pinned.php:85
msgid "Message signature incorrect"
msgstr ""
-#: ../../include/conversation.php:607 ../../Zotlabs/Lib/ThreadItem.php:481
+#: ../../include/conversation.php:594 ../../Zotlabs/Lib/ThreadItem.php:479
#: ../../Zotlabs/Module/Admin/Accounts.php:218
#: ../../Zotlabs/Module/Connections.php:358
#: ../../Zotlabs/Module/Connections.php:409
msgid "Approve"
msgstr ""
-#: ../../include/conversation.php:613
+#: ../../include/conversation.php:600
#, php-format
msgid "View %s's profile @ %s"
msgstr ""
-#: ../../include/conversation.php:635
+#: ../../include/conversation.php:622
msgid "Categories:"
msgstr ""
-#: ../../include/conversation.php:636
+#: ../../include/conversation.php:623
msgid "Filed under:"
msgstr ""
-#: ../../include/conversation.php:642 ../../Zotlabs/Lib/ThreadItem.php:414
-#: ../../Zotlabs/Widget/Pinned.php:127
+#: ../../include/conversation.php:629 ../../Zotlabs/Lib/ThreadItem.php:411
+#: ../../Zotlabs/Widget/Pinned.php:130
#, php-format
msgid "from %s"
msgstr ""
-#: ../../include/conversation.php:645 ../../Zotlabs/Widget/Pinned.php:130
+#: ../../include/conversation.php:632 ../../Zotlabs/Widget/Pinned.php:133
#, php-format
msgid "last edited: %s"
msgstr ""
-#: ../../include/conversation.php:646 ../../Zotlabs/Widget/Pinned.php:131
+#: ../../include/conversation.php:633 ../../Zotlabs/Widget/Pinned.php:134
#, php-format
msgid "Expires: %s"
msgstr ""
-#: ../../include/conversation.php:661 ../../addon/cards/cards.php:82
+#: ../../include/conversation.php:648 ../../addon/cards/cards.php:82
#: ../../addon/articles/articles.php:83
msgid "View in context"
msgstr ""
-#: ../../include/conversation.php:663 ../../Zotlabs/Lib/ThreadItem.php:475
-#: ../../Zotlabs/Module/Photos.php:1077
+#: ../../include/conversation.php:650 ../../Zotlabs/Lib/ThreadItem.php:473
+#: ../../Zotlabs/Module/Photos.php:1078
msgid "Please wait"
msgstr ""
-#: ../../include/conversation.php:764
+#: ../../include/conversation.php:746
msgid "remove"
msgstr ""
-#: ../../include/conversation.php:768
+#: ../../include/conversation.php:750
msgid "Loading..."
msgstr ""
-#: ../../include/conversation.php:769 ../../Zotlabs/Lib/ThreadItem.php:275
+#: ../../include/conversation.php:751 ../../Zotlabs/Lib/ThreadItem.php:268
msgid "Conversation Features"
msgstr ""
-#: ../../include/conversation.php:770
+#: ../../include/conversation.php:752
msgid "Delete Selected Items"
msgstr ""
-#: ../../include/conversation.php:806
+#: ../../include/conversation.php:788
msgid "View Source"
msgstr ""
-#: ../../include/conversation.php:816
+#: ../../include/conversation.php:798
msgid "Follow Thread"
msgstr ""
-#: ../../include/conversation.php:825
+#: ../../include/conversation.php:807
msgid "Unfollow Thread"
msgstr ""
-#: ../../include/conversation.php:902 ../../include/nav.php:127
+#: ../../include/conversation.php:884 ../../include/nav.php:127
#: ../../addon/openclipatar/openclipatar.php:58 ../../Zotlabs/Lib/Apps.php:350
#: ../../Zotlabs/Module/Connedit.php:480
msgid "View Profile"
msgstr ""
-#: ../../include/conversation.php:914 ../../Zotlabs/Module/Connedit.php:501
+#: ../../include/conversation.php:896 ../../Zotlabs/Module/Connedit.php:501
msgid "Recent Activity"
msgstr ""
-#: ../../include/conversation.php:926 ../../include/connections.php:150
+#: ../../include/conversation.php:908 ../../include/connections.php:150
#: ../../include/channel.php:1627 ../../Zotlabs/Widget/Suggestions.php:51
#: ../../Zotlabs/Widget/Follow.php:37 ../../Zotlabs/Module/Suggest.php:69
#: ../../Zotlabs/Module/Directory.php:371
@@ -2355,209 +2308,198 @@ msgstr ""
msgid "Connect"
msgstr ""
-#: ../../include/conversation.php:938
+#: ../../include/conversation.php:920
msgid "Edit Connection"
msgstr ""
-#: ../../include/conversation.php:1008
-msgid "Approve this item"
-msgstr ""
-
-#: ../../include/conversation.php:1008
-msgid "Delete this item"
-msgstr ""
-
-#: ../../include/conversation.php:1062
+#: ../../include/conversation.php:949
#, php-format
msgid "%s likes this."
msgstr ""
-#: ../../include/conversation.php:1062
+#: ../../include/conversation.php:949
#, php-format
msgid "%s doesn't like this."
msgstr ""
-#: ../../include/conversation.php:1066
+#: ../../include/conversation.php:953
#, php-format
msgid "<span %1$s>%2$d people</span> like this."
msgid_plural "<span %1$s>%2$d people</span> like this."
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1068
+#: ../../include/conversation.php:955
#, php-format
msgid "<span %1$s>%2$d people</span> don't like this."
msgid_plural "<span %1$s>%2$d people</span> don't like this."
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1074
+#: ../../include/conversation.php:961
msgid "and"
msgstr ""
-#: ../../include/conversation.php:1077
+#: ../../include/conversation.php:964
#, php-format
msgid ", and %d other people"
msgid_plural ", and %d other people"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1078
+#: ../../include/conversation.php:965
#, php-format
msgid "%s like this."
msgstr ""
-#: ../../include/conversation.php:1078
+#: ../../include/conversation.php:965
#, php-format
msgid "%s don't like this."
msgstr ""
-#: ../../include/conversation.php:1129 ../../addon/hsse/hsse.php:82
+#: ../../include/conversation.php:1014 ../../addon/hsse/hsse.php:82
msgid "Set your location"
msgstr ""
-#: ../../include/conversation.php:1130 ../../addon/hsse/hsse.php:83
+#: ../../include/conversation.php:1015 ../../addon/hsse/hsse.php:83
msgid "Clear browser location"
msgstr ""
-#: ../../include/conversation.php:1142 ../../addon/cards/Mod_Card_edit.php:94
+#: ../../include/conversation.php:1027 ../../addon/cards/Mod_Card_edit.php:94
#: ../../addon/articles/Mod_Article_edit.php:94 ../../addon/hsse/hsse.php:95
#: ../../Zotlabs/Module/Editwebpage.php:143 ../../Zotlabs/Module/Chat.php:219
#: ../../Zotlabs/Module/Editblock.php:116
msgid "Insert web link"
msgstr ""
-#: ../../include/conversation.php:1146 ../../addon/hsse/hsse.php:99
+#: ../../include/conversation.php:1031 ../../addon/hsse/hsse.php:99
+#: ../../Zotlabs/Lib/ThreadItem.php:816
msgid "Embed (existing) photo from your photo albums"
msgstr ""
-#: ../../include/conversation.php:1179 ../../addon/hsse/hsse.php:134
+#: ../../include/conversation.php:1064 ../../addon/hsse/hsse.php:134
#: ../../Zotlabs/Module/Chat.php:217
msgid "Please enter a link URL:"
msgstr ""
-#: ../../include/conversation.php:1180 ../../addon/hsse/hsse.php:135
+#: ../../include/conversation.php:1065 ../../addon/hsse/hsse.php:135
msgid "Tag term:"
msgstr ""
-#: ../../include/conversation.php:1181 ../../addon/hsse/hsse.php:136
+#: ../../include/conversation.php:1066 ../../addon/hsse/hsse.php:136
msgid "Where are you right now?"
msgstr ""
-#: ../../include/conversation.php:1184 ../../addon/wiki/Mod_Wiki.php:400
+#: ../../include/conversation.php:1069 ../../addon/wiki/Mod_Wiki.php:400
#: ../../addon/hsse/hsse.php:139 ../../Zotlabs/Module/Cover_photo.php:388
#: ../../Zotlabs/Module/Profile_photo.php:555
msgid "Choose images to embed"
msgstr ""
-#: ../../include/conversation.php:1185 ../../addon/wiki/Mod_Wiki.php:401
+#: ../../include/conversation.php:1070 ../../addon/wiki/Mod_Wiki.php:401
#: ../../addon/hsse/hsse.php:140 ../../Zotlabs/Module/Cover_photo.php:389
#: ../../Zotlabs/Module/Profile_photo.php:556
msgid "Choose an album"
msgstr ""
-#: ../../include/conversation.php:1186 ../../addon/hsse/hsse.php:141
+#: ../../include/conversation.php:1071 ../../addon/hsse/hsse.php:141
msgid "Choose a different album..."
msgstr ""
-#: ../../include/conversation.php:1187 ../../addon/wiki/Mod_Wiki.php:403
+#: ../../include/conversation.php:1072 ../../addon/wiki/Mod_Wiki.php:403
#: ../../addon/hsse/hsse.php:142 ../../Zotlabs/Module/Cover_photo.php:391
#: ../../Zotlabs/Module/Profile_photo.php:558
msgid "Error getting album list"
msgstr ""
-#: ../../include/conversation.php:1188 ../../addon/wiki/Mod_Wiki.php:404
+#: ../../include/conversation.php:1073 ../../addon/wiki/Mod_Wiki.php:404
#: ../../addon/hsse/hsse.php:143 ../../Zotlabs/Module/Cover_photo.php:392
#: ../../Zotlabs/Module/Profile_photo.php:559
msgid "Error getting photo link"
msgstr ""
-#: ../../include/conversation.php:1189 ../../addon/wiki/Mod_Wiki.php:405
+#: ../../include/conversation.php:1074 ../../addon/wiki/Mod_Wiki.php:405
#: ../../addon/hsse/hsse.php:144 ../../Zotlabs/Module/Cover_photo.php:393
#: ../../Zotlabs/Module/Profile_photo.php:560
msgid "Error getting album"
msgstr ""
-#: ../../include/conversation.php:1190 ../../addon/hsse/hsse.php:145
+#: ../../include/conversation.php:1075 ../../addon/hsse/hsse.php:145
msgid "Comments enabled"
msgstr ""
-#: ../../include/conversation.php:1191 ../../addon/hsse/hsse.php:146
-#: ../../Zotlabs/Lib/ThreadItem.php:472
+#: ../../include/conversation.php:1076 ../../addon/hsse/hsse.php:146
+#: ../../Zotlabs/Lib/ThreadItem.php:470
msgid "Comments disabled"
msgstr ""
-#: ../../include/conversation.php:1192
+#: ../../include/conversation.php:1077
msgid "Confirm delete"
msgstr ""
-#: ../../include/conversation.php:1209 ../../addon/hsse/hsse.php:153
-#: ../../Zotlabs/Lib/ThreadItem.php:810 ../../Zotlabs/Module/Webpages.php:256
-#: ../../Zotlabs/Module/Photos.php:1096
+#: ../../include/conversation.php:1094 ../../addon/hsse/hsse.php:153
+#: ../../Zotlabs/Lib/ThreadItem.php:820 ../../Zotlabs/Module/Webpages.php:256
+#: ../../Zotlabs/Module/Photos.php:1097
msgid "Preview"
msgstr ""
-#: ../../include/conversation.php:1242 ../../addon/wiki/Mod_Wiki.php:304
-#: ../../addon/hsse/hsse.php:186 ../../Zotlabs/Lib/ThreadItem.php:305
-#: ../../Zotlabs/Widget/Cdav.php:142 ../../Zotlabs/Module/Webpages.php:250
-#: ../../Zotlabs/Module/Layouts.php:192 ../../Zotlabs/Module/Blocks.php:159
-#: ../../Zotlabs/Module/Photos.php:1076
-msgid "Share"
+#: ../../include/conversation.php:1128
+msgid "Start a conversation"
msgstr ""
-#: ../../include/conversation.php:1251 ../../addon/hsse/hsse.php:195
+#: ../../include/conversation.php:1136 ../../addon/hsse/hsse.php:195
msgid "Page link name"
msgstr ""
-#: ../../include/conversation.php:1254 ../../addon/hsse/hsse.php:198
+#: ../../include/conversation.php:1139 ../../addon/hsse/hsse.php:198
msgid "Post as"
msgstr ""
-#: ../../include/conversation.php:1256 ../../addon/hsse/hsse.php:200
-#: ../../Zotlabs/Lib/ThreadItem.php:800
+#: ../../include/conversation.php:1141 ../../addon/hsse/hsse.php:200
+#: ../../Zotlabs/Lib/ThreadItem.php:810
msgid "Bold"
msgstr ""
-#: ../../include/conversation.php:1257 ../../addon/hsse/hsse.php:201
-#: ../../Zotlabs/Lib/ThreadItem.php:801
+#: ../../include/conversation.php:1142 ../../addon/hsse/hsse.php:201
+#: ../../Zotlabs/Lib/ThreadItem.php:811
msgid "Italic"
msgstr ""
-#: ../../include/conversation.php:1258 ../../Zotlabs/Lib/ThreadItem.php:802
+#: ../../include/conversation.php:1143 ../../Zotlabs/Lib/ThreadItem.php:812
msgid "Highlight selected text"
msgstr ""
-#: ../../include/conversation.php:1259 ../../addon/hsse/hsse.php:202
-#: ../../Zotlabs/Lib/ThreadItem.php:803
+#: ../../include/conversation.php:1144 ../../addon/hsse/hsse.php:202
+#: ../../Zotlabs/Lib/ThreadItem.php:813
msgid "Underline"
msgstr ""
-#: ../../include/conversation.php:1260 ../../addon/hsse/hsse.php:203
-#: ../../Zotlabs/Lib/ThreadItem.php:804
+#: ../../include/conversation.php:1145 ../../addon/hsse/hsse.php:203
+#: ../../Zotlabs/Lib/ThreadItem.php:814
msgid "Quote"
msgstr ""
-#: ../../include/conversation.php:1261 ../../addon/hsse/hsse.php:204
-#: ../../Zotlabs/Lib/ThreadItem.php:805
+#: ../../include/conversation.php:1146 ../../addon/hsse/hsse.php:204
+#: ../../Zotlabs/Lib/ThreadItem.php:815
msgid "Code"
msgstr ""
-#: ../../include/conversation.php:1262 ../../addon/hsse/hsse.php:205
-#: ../../Zotlabs/Lib/ThreadItem.php:807
+#: ../../include/conversation.php:1147 ../../addon/hsse/hsse.php:205
+#: ../../Zotlabs/Lib/ThreadItem.php:817
msgid "Attach/Upload file"
msgstr ""
-#: ../../include/conversation.php:1265 ../../addon/wiki/Mod_Wiki.php:397
+#: ../../include/conversation.php:1150 ../../addon/wiki/Mod_Wiki.php:397
#: ../../addon/hsse/hsse.php:208
msgid "Embed an image from your albums"
msgstr ""
-#: ../../include/conversation.php:1266 ../../include/conversation.php:1321
+#: ../../include/conversation.php:1151 ../../include/conversation.php:1206
#: ../../addon/cards/Mod_Card_edit.php:126
#: ../../addon/articles/Mod_Article_edit.php:126
#: ../../addon/wiki/Mod_Wiki.php:365 ../../addon/wiki/Mod_Wiki.php:398
#: ../../addon/hsse/hsse.php:209 ../../addon/hsse/hsse.php:258
-#: ../../Zotlabs/Storage/Browser.php:387
+#: ../../Zotlabs/Storage/Browser.php:391
#: ../../Zotlabs/Module/Cover_photo.php:386
#: ../../Zotlabs/Module/Editwebpage.php:169 ../../Zotlabs/Module/Oauth.php:111
#: ../../Zotlabs/Module/Oauth.php:136 ../../Zotlabs/Module/Connedit.php:750
@@ -2571,118 +2513,132 @@ msgstr ""
msgid "Cancel"
msgstr ""
-#: ../../include/conversation.php:1267 ../../include/conversation.php:1320
+#: ../../include/conversation.php:1152 ../../include/conversation.php:1205
#: ../../addon/wiki/Mod_Wiki.php:399 ../../addon/hsse/hsse.php:210
#: ../../addon/hsse/hsse.php:257 ../../Zotlabs/Module/Cover_photo.php:387
#: ../../Zotlabs/Module/Profile_photo.php:554
msgid "OK"
msgstr ""
-#: ../../include/conversation.php:1269 ../../addon/hsse/hsse.php:212
+#: ../../include/conversation.php:1154 ../../addon/hsse/hsse.php:212
msgid "Toggle voting"
msgstr ""
-#: ../../include/conversation.php:1270
+#: ../../include/conversation.php:1155
msgid "Toggle poll"
msgstr ""
-#: ../../include/conversation.php:1271
+#: ../../include/conversation.php:1156
msgid "Option"
msgstr ""
-#: ../../include/conversation.php:1272
+#: ../../include/conversation.php:1157
msgid "Add option"
msgstr ""
-#: ../../include/conversation.php:1273
+#: ../../include/conversation.php:1158
msgid "Minutes"
msgstr ""
-#: ../../include/conversation.php:1273
+#: ../../include/conversation.php:1158
msgid "Hours"
msgstr ""
-#: ../../include/conversation.php:1273
+#: ../../include/conversation.php:1158
msgid "Days"
msgstr ""
-#: ../../include/conversation.php:1274
+#: ../../include/conversation.php:1159
msgid "Allow multiple answers"
msgstr ""
-#: ../../include/conversation.php:1276 ../../addon/hsse/hsse.php:215
+#: ../../include/conversation.php:1161 ../../addon/hsse/hsse.php:215
msgid "Disable comments"
msgstr ""
-#: ../../include/conversation.php:1277 ../../addon/hsse/hsse.php:216
+#: ../../include/conversation.php:1162 ../../addon/hsse/hsse.php:216
msgid "Toggle comments"
msgstr ""
-#: ../../include/conversation.php:1283 ../../addon/cards/Mod_Card_edit.php:111
+#: ../../include/conversation.php:1168 ../../addon/cards/Mod_Card_edit.php:111
#: ../../addon/articles/Mod_Article_edit.php:111 ../../addon/hsse/hsse.php:221
-#: ../../Zotlabs/Module/Photos.php:667 ../../Zotlabs/Module/Photos.php:1042
+#: ../../Zotlabs/Module/Photos.php:669 ../../Zotlabs/Module/Photos.php:1043
#: ../../Zotlabs/Module/Editblock.php:129
msgid "Title (optional)"
msgstr ""
-#: ../../include/conversation.php:1284
+#: ../../include/conversation.php:1169
msgid "Summary (optional)"
msgstr ""
-#: ../../include/conversation.php:1287 ../../addon/hsse/hsse.php:224
+#: ../../include/conversation.php:1172 ../../addon/hsse/hsse.php:224
msgid "Categories (optional, comma-separated list)"
msgstr ""
-#: ../../include/conversation.php:1288 ../../addon/hsse/hsse.php:225
+#: ../../include/conversation.php:1173 ../../addon/hsse/hsse.php:225
msgid "Permission settings"
msgstr ""
-#: ../../include/conversation.php:1310 ../../addon/hsse/hsse.php:247
+#: ../../include/conversation.php:1195 ../../addon/hsse/hsse.php:247
msgid "Other networks and post services"
msgstr ""
-#: ../../include/conversation.php:1313 ../../addon/hsse/hsse.php:250
+#: ../../include/conversation.php:1198 ../../addon/hsse/hsse.php:250
msgid "Set expiration date"
msgstr ""
-#: ../../include/conversation.php:1316 ../../addon/hsse/hsse.php:253
+#: ../../include/conversation.php:1201 ../../addon/hsse/hsse.php:253
msgid "Set publish date"
msgstr ""
-#: ../../include/conversation.php:1318 ../../addon/hsse/hsse.php:255
-#: ../../Zotlabs/Lib/ThreadItem.php:813 ../../Zotlabs/Module/Chat.php:218
+#: ../../include/conversation.php:1203 ../../addon/hsse/hsse.php:255
+#: ../../Zotlabs/Lib/ThreadItem.php:823 ../../Zotlabs/Module/Chat.php:218
msgid "Encrypt text"
msgstr ""
-#: ../../include/conversation.php:1564
+#: ../../include/conversation.php:1443
msgctxt "noun"
msgid "Repeat"
msgid_plural "Repeats"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1567 ../../Zotlabs/Module/Photos.php:1134
+#: ../../include/conversation.php:1446 ../../Zotlabs/Module/Photos.php:1123
msgctxt "noun"
msgid "Dislike"
msgid_plural "Dislikes"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1570
+#: ../../include/conversation.php:1449
+msgctxt "noun"
+msgid "Comment"
+msgid_plural "Comments"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../../include/conversation.php:1449
+msgctxt "noun"
+msgid "Reply"
+msgid_plural "Replies"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../../include/conversation.php:1452
msgctxt "noun"
msgid "Attending"
msgid_plural "Attending"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1573
+#: ../../include/conversation.php:1455
msgctxt "noun"
-msgid "Not Attending"
-msgid_plural "Not Attending"
+msgid "Not attending"
+msgid_plural "Not attending"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/conversation.php:1576
+#: ../../include/conversation.php:1458
msgctxt "noun"
msgid "Undecided"
msgid_plural "Undecided"
@@ -2721,7 +2677,7 @@ msgid "Account/Channel Settings"
msgstr ""
#: ../../include/nav.php:124 ../../include/nav.php:154
-#: ../../include/nav.php:175 ../../boot.php:1753
+#: ../../include/nav.php:175 ../../boot.php:1760
msgid "Logout"
msgstr ""
@@ -2751,8 +2707,8 @@ msgstr ""
msgid "Edit your profile"
msgstr ""
-#: ../../include/nav.php:139 ../../include/nav.php:143 ../../boot.php:1754
-#: ../../Zotlabs/Lib/Apps.php:342
+#: ../../include/nav.php:139 ../../include/nav.php:143 ../../boot.php:1761
+#: ../../Zotlabs/Lib/Apps.php:342 ../../Zotlabs/Module/Login.php:15
msgid "Login"
msgstr ""
@@ -2768,7 +2724,7 @@ msgstr ""
msgid "Log me out of this site"
msgstr ""
-#: ../../include/nav.php:180 ../../boot.php:1731
+#: ../../include/nav.php:180 ../../boot.php:1738
#: ../../Zotlabs/Module/Register.php:543
msgid "Register"
msgstr ""
@@ -2807,7 +2763,7 @@ msgid "Site Setup and Configuration"
msgstr ""
#: ../../include/nav.php:345 ../../Zotlabs/Widget/Notifications.php:175
-#: ../../Zotlabs/Widget/Messages.php:50 ../../Zotlabs/Module/Defperms.php:254
+#: ../../Zotlabs/Widget/Messages.php:52 ../../Zotlabs/Module/Defperms.php:254
#: ../../Zotlabs/Module/New_channel.php:158
#: ../../Zotlabs/Module/New_channel.php:165
msgid "Loading"
@@ -2842,6 +2798,7 @@ msgid "Featured Apps"
msgstr ""
#: ../../include/nav.php:447 ../../Zotlabs/Lib/Apps.php:349
+#: ../../Zotlabs/Widget/Notifications.php:43
#: ../../Zotlabs/Module/Admin/Channels.php:176
msgid "Channel"
msgstr ""
@@ -2865,7 +2822,7 @@ msgstr ""
#: ../../include/nav.php:478 ../../Zotlabs/Lib/Apps.php:346
#: ../../Zotlabs/Widget/Notifications.php:108
#: ../../Zotlabs/Widget/Channel_activities.php:125
-#: ../../Zotlabs/Storage/Browser.php:351 ../../Zotlabs/Module/Fbrowser.php:87
+#: ../../Zotlabs/Storage/Browser.php:355 ../../Zotlabs/Module/Fbrowser.php:87
msgid "Files"
msgstr ""
@@ -2920,7 +2877,7 @@ msgstr ""
msgid "YYYY-MM-DD or MM-DD"
msgstr ""
-#: ../../include/datetime.php:238 ../../boot.php:2768
+#: ../../include/datetime.php:238 ../../boot.php:2680
msgid "never"
msgstr ""
@@ -3033,8 +2990,8 @@ msgctxt "photo_upload"
msgid "%1$s posted %2$s to %3$s"
msgstr ""
-#: ../../include/photos.php:748 ../../Zotlabs/Module/Photos.php:1339
-#: ../../Zotlabs/Module/Photos.php:1352 ../../Zotlabs/Module/Photos.php:1353
+#: ../../include/photos.php:748 ../../Zotlabs/Module/Photos.php:1333
+#: ../../Zotlabs/Module/Photos.php:1346 ../../Zotlabs/Module/Photos.php:1347
msgid "Recent Photos"
msgstr ""
@@ -3066,27 +3023,27 @@ msgstr ""
msgid "content-type: "
msgstr ""
-#: ../../include/network.php:1775 ../../include/network.php:1776
+#: ../../include/network.php:1774 ../../include/network.php:1775
msgid "Friendica"
msgstr ""
-#: ../../include/network.php:1777
+#: ../../include/network.php:1776
msgid "OStatus"
msgstr ""
-#: ../../include/network.php:1778
+#: ../../include/network.php:1777
msgid "GNU-Social"
msgstr ""
-#: ../../include/network.php:1779
+#: ../../include/network.php:1778
msgid "RSS/Atom"
msgstr ""
-#: ../../include/network.php:1780
+#: ../../include/network.php:1779
msgid "ActivityPub"
msgstr ""
-#: ../../include/network.php:1781 ../../addon/openid/MysqlProvider.php:56
+#: ../../include/network.php:1780 ../../addon/openid/MysqlProvider.php:56
#: ../../addon/openid/MysqlProvider.php:57 ../../addon/redred/Mod_Redred.php:69
#: ../../addon/rtof/Mod_Rtof.php:55 ../../Zotlabs/Module/Connedit.php:736
#: ../../Zotlabs/Module/Admin/Accounts.php:216
@@ -3095,27 +3052,27 @@ msgstr ""
msgid "Email"
msgstr ""
-#: ../../include/network.php:1782
+#: ../../include/network.php:1781
msgid "Diaspora"
msgstr ""
-#: ../../include/network.php:1783
+#: ../../include/network.php:1782
msgid "Facebook"
msgstr ""
-#: ../../include/network.php:1784
+#: ../../include/network.php:1783
msgid "Zot"
msgstr ""
-#: ../../include/network.php:1785
+#: ../../include/network.php:1784
msgid "LinkedIn"
msgstr ""
-#: ../../include/network.php:1786
+#: ../../include/network.php:1785
msgid "XMPP/IM"
msgstr ""
-#: ../../include/network.php:1787
+#: ../../include/network.php:1786
msgid "MySpace"
msgstr ""
@@ -3124,23 +3081,20 @@ msgstr ""
msgid "%1$s wrote the following %2$s %3$s"
msgstr ""
-#: ../../include/markdown.php:262 ../../include/bbcode.php:661
-msgid "spoiler"
-msgstr ""
-
-#: ../../include/language.php:423 ../../include/text.php:2197
-msgid "default"
+#: ../../include/markdown.php:192 ../../include/bbcode.php:572
+#: ../../Zotlabs/Module/Tagger.php:81
+msgid "post"
msgstr ""
-#: ../../include/language.php:436
-msgid "Select an alternate language"
+#: ../../include/markdown.php:262 ../../include/bbcode.php:661
+msgid "spoiler"
msgstr ""
#: ../../include/menu.php:120 ../../include/channel.php:1541
#: ../../include/channel.php:1545 ../../addon/cards/cards.php:74
#: ../../addon/articles/articles.php:75 ../../addon/wiki/Mod_Wiki.php:214
#: ../../addon/wiki/Mod_Wiki.php:381 ../../Zotlabs/Lib/Apps.php:617
-#: ../../Zotlabs/Lib/ThreadItem.php:162 ../../Zotlabs/Widget/Cdav.php:144
+#: ../../Zotlabs/Lib/ThreadItem.php:159 ../../Zotlabs/Widget/Cdav.php:144
#: ../../Zotlabs/Widget/Cdav.php:181 ../../Zotlabs/Module/Editwebpage.php:142
#: ../../Zotlabs/Module/Webpages.php:249 ../../Zotlabs/Module/Oauth.php:171
#: ../../Zotlabs/Module/Thing.php:301 ../../Zotlabs/Module/Layouts.php:191
@@ -3169,7 +3123,6 @@ msgstr ""
#: ../../include/acl_selectors.php:125
#: ../../Zotlabs/Widget/Notifications.php:131
-#: ../../Zotlabs/Widget/Notifications.php:132
#: ../../Zotlabs/Widget/Activity_filter.php:130
#: ../../Zotlabs/Widget/Forums.php:77
msgid "Forums"
@@ -3203,17 +3156,16 @@ msgstr ""
msgid "Don't allow"
msgstr ""
-#: ../../include/acl_selectors.php:154
-#: ../../addon/flashcards/Mod_Flashcards.php:261
-#: ../../Zotlabs/Module/Thing.php:357 ../../Zotlabs/Module/Thing.php:407
-#: ../../Zotlabs/Module/Filestorage.php:195 ../../Zotlabs/Module/Photos.php:671
-#: ../../Zotlabs/Module/Photos.php:1045 ../../Zotlabs/Module/Chat.php:240
+#: ../../include/acl_selectors.php:154 ../../Zotlabs/Module/Thing.php:357
+#: ../../Zotlabs/Module/Thing.php:407 ../../Zotlabs/Module/Filestorage.php:195
+#: ../../Zotlabs/Module/Photos.php:673 ../../Zotlabs/Module/Photos.php:1046
+#: ../../Zotlabs/Module/Chat.php:240
msgid "Permissions"
msgstr ""
-#: ../../include/acl_selectors.php:156 ../../Zotlabs/Lib/ThreadItem.php:470
-#: ../../Zotlabs/Widget/Pinned.php:153 ../../Zotlabs/Storage/Browser.php:414
-#: ../../Zotlabs/Module/Photos.php:1266
+#: ../../include/acl_selectors.php:156 ../../Zotlabs/Lib/ThreadItem.php:467
+#: ../../Zotlabs/Widget/Pinned.php:156 ../../Zotlabs/Storage/Browser.php:418
+#: ../../Zotlabs/Module/Photos.php:1260
msgid "Close"
msgstr ""
@@ -3294,33 +3246,33 @@ msgstr ""
msgid "Save"
msgstr ""
-#: ../../include/text.php:1503
+#: ../../include/text.php:1525
msgid "May"
msgstr ""
-#: ../../include/text.php:1577
+#: ../../include/text.php:1599
msgid "Unknown attachment"
msgstr ""
-#: ../../include/text.php:1580 ../../Zotlabs/Storage/Browser.php:383
+#: ../../include/text.php:1602 ../../Zotlabs/Storage/Browser.php:387
#: ../../Zotlabs/Module/Sharedwithme.php:109
msgid "Size"
msgstr ""
-#: ../../include/text.php:1621
+#: ../../include/text.php:1643
msgid "remove category"
msgstr ""
-#: ../../include/text.php:1699
+#: ../../include/text.php:1721
msgid "remove from file"
msgstr ""
-#: ../../include/text.php:1886
+#: ../../include/text.php:1908
msgid "Download binary/encrypted content"
msgstr ""
-#: ../../include/text.php:1944 ../../include/text.php:1953
-#: ../../include/text.php:1980 ../../include/text.php:1989
+#: ../../include/text.php:1966 ../../include/text.php:1975
+#: ../../include/text.php:2002 ../../include/text.php:2011
#, php-format
msgctxt "noun"
msgid "%d Vote"
@@ -3328,7 +3280,7 @@ msgid_plural "%d Votes"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/text.php:1996
+#: ../../include/text.php:2018
#, php-format
msgctxt "noun"
msgid "%d Vote in total"
@@ -3336,157 +3288,165 @@ msgid_plural "%d Votes in total"
msgstr[0] ""
msgstr[1] ""
-#: ../../include/text.php:2002
+#: ../../include/text.php:2024
msgid "Poll has ended"
msgstr ""
-#: ../../include/text.php:2005
+#: ../../include/text.php:2027
#, php-format
-msgid "Poll ends in %s"
+msgid "Poll ends %s"
msgstr ""
-#: ../../include/text.php:2012 ../../Zotlabs/Lib/ThreadItem.php:430
+#: ../../include/text.php:2034 ../../Zotlabs/Lib/ThreadItem.php:427
msgid "Vote"
msgstr ""
-#: ../../include/text.php:2172
+#: ../../include/text.php:2193
msgid "Link to Source"
msgstr ""
-#: ../../include/text.php:2205
+#: ../../include/text.php:2218 ../../Zotlabs/Module/Lang.php:75
+msgid "default"
+msgstr ""
+
+#: ../../include/text.php:2226
msgid "Page layout"
msgstr ""
-#: ../../include/text.php:2205
+#: ../../include/text.php:2226
msgid "You can create your own with the layouts tool"
msgstr ""
-#: ../../include/text.php:2215 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../include/text.php:2236 ../../addon/wiki/Mod_Wiki.php:220
#: ../../addon/wiki/Mod_Wiki.php:368 ../../addon/wiki/Mod_Wiki.php:903
#: ../../addon/wiki/Widget/Wiki_pages.php:68
msgid "BBcode"
msgstr ""
-#: ../../include/text.php:2216
+#: ../../include/text.php:2237
msgid "HTML"
msgstr ""
-#: ../../include/text.php:2217 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../include/text.php:2238 ../../addon/wiki/Mod_Wiki.php:220
#: ../../addon/wiki/Mod_Wiki.php:368 ../../addon/wiki/Mod_Wiki.php:903
#: ../../addon/wiki/Widget/Wiki_pages.php:68 ../../addon/mdpost/mdpost.php:41
msgid "Markdown"
msgstr ""
-#: ../../include/text.php:2218 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../include/text.php:2239 ../../addon/wiki/Mod_Wiki.php:220
#: ../../addon/wiki/Mod_Wiki.php:903 ../../addon/wiki/Widget/Wiki_pages.php:68
msgid "Text"
msgstr ""
-#: ../../include/text.php:2219
+#: ../../include/text.php:2240
msgid "Comanche Layout"
msgstr ""
-#: ../../include/text.php:2224
+#: ../../include/text.php:2245
msgid "PHP"
msgstr ""
-#: ../../include/text.php:2236
+#: ../../include/text.php:2257
msgid "Page content type"
msgstr ""
-#: ../../include/text.php:2369
+#: ../../include/text.php:2383
+msgid "conversation"
+msgstr ""
+
+#: ../../include/text.php:2390
msgid "activity"
msgstr ""
-#: ../../include/text.php:2372
+#: ../../include/text.php:2394
msgid "poll"
msgstr ""
-#: ../../include/text.php:2485
+#: ../../include/text.php:2508
msgid "a-z, 0-9, -, and _ only"
msgstr ""
-#: ../../include/text.php:2793
+#: ../../include/text.php:2818
msgid "Design Tools"
msgstr ""
-#: ../../include/text.php:2796 ../../Zotlabs/Module/Blocks.php:152
+#: ../../include/text.php:2821 ../../Zotlabs/Module/Blocks.php:152
msgid "Blocks"
msgstr ""
-#: ../../include/text.php:2797 ../../Zotlabs/Module/Menu.php:171
+#: ../../include/text.php:2822 ../../Zotlabs/Module/Menu.php:171
msgid "Menus"
msgstr ""
-#: ../../include/text.php:2798 ../../Zotlabs/Module/Layouts.php:182
+#: ../../include/text.php:2823 ../../Zotlabs/Module/Layouts.php:182
msgid "Layouts"
msgstr ""
-#: ../../include/text.php:2799
+#: ../../include/text.php:2824
msgid "Pages"
msgstr ""
-#: ../../include/text.php:2811
+#: ../../include/text.php:2836
msgid "Import"
msgstr ""
-#: ../../include/text.php:2812
+#: ../../include/text.php:2837
msgid "Import website..."
msgstr ""
-#: ../../include/text.php:2813
+#: ../../include/text.php:2838
msgid "Select folder to import"
msgstr ""
-#: ../../include/text.php:2814
+#: ../../include/text.php:2839
msgid "Import from a zipped folder:"
msgstr ""
-#: ../../include/text.php:2815
+#: ../../include/text.php:2840
msgid "Import from cloud files:"
msgstr ""
-#: ../../include/text.php:2816
+#: ../../include/text.php:2841
msgid "/cloud/channel/path/to/folder"
msgstr ""
-#: ../../include/text.php:2817
+#: ../../include/text.php:2842
msgid "Enter path to website files"
msgstr ""
-#: ../../include/text.php:2818
+#: ../../include/text.php:2843
msgid "Select folder"
msgstr ""
-#: ../../include/text.php:2819
+#: ../../include/text.php:2844
msgid "Export website..."
msgstr ""
-#: ../../include/text.php:2820
+#: ../../include/text.php:2845
msgid "Export to a zip file"
msgstr ""
-#: ../../include/text.php:2821
+#: ../../include/text.php:2846
msgid "website.zip"
msgstr ""
-#: ../../include/text.php:2822
+#: ../../include/text.php:2847
msgid "Enter a name for the zip file."
msgstr ""
-#: ../../include/text.php:2823
+#: ../../include/text.php:2848
msgid "Export to cloud files"
msgstr ""
-#: ../../include/text.php:2824
+#: ../../include/text.php:2849
msgid "/path/to/export/folder"
msgstr ""
-#: ../../include/text.php:2825
+#: ../../include/text.php:2850
msgid "Enter a path to a cloud files destination."
msgstr ""
-#: ../../include/text.php:2826
+#: ../../include/text.php:2851
msgid "Specify folder"
msgstr ""
@@ -3539,6 +3499,7 @@ msgstr ""
#: ../../include/channel.php:1438 ../../addon/cards/Mod_Cards.php:42
#: ../../addon/articles/Mod_Articles.php:46
+#: ../../addon/flashcards/Mod_Flashcards.php:74
#: ../../addon/gallery/Mod_Gallery.php:49
#: ../../Zotlabs/Module/Editwebpage.php:32 ../../Zotlabs/Module/Webpages.php:39
#: ../../Zotlabs/Module/Connect.php:17 ../../Zotlabs/Module/Filestorage.php:59
@@ -3713,7 +3674,7 @@ msgstr ""
msgid "cover photo"
msgstr ""
-#: ../../include/channel.php:2628 ../../boot.php:1755
+#: ../../include/channel.php:2628 ../../boot.php:1762
#: ../../Zotlabs/Module/Rmagic.php:96
msgid "Remote Authentication"
msgstr ""
@@ -3732,7 +3693,7 @@ msgid "Account '%s' deleted"
msgstr ""
#: ../../include/bbcode.php:234 ../../include/bbcode.php:994
-#: ../../include/bbcode.php:1663 ../../include/bbcode.php:1671
+#: ../../include/bbcode.php:1676 ../../include/bbcode.php:1684
msgid "Image/photo"
msgstr ""
@@ -3793,7 +3754,7 @@ msgstr ""
msgid "Different viewers will see this text differently"
msgstr ""
-#: ../../include/bbcode.php:1639
+#: ../../include/bbcode.php:1652
msgid "$1 wrote:"
msgstr ""
@@ -4056,7 +4017,7 @@ msgid "Last Name"
msgstr ""
#: ../../addon/openid/MysqlProvider.php:54 ../../addon/redred/Mod_Redred.php:73
-#: ../../boot.php:1748
+#: ../../boot.php:1755
msgid "Nickname"
msgstr ""
@@ -4306,9 +4267,9 @@ msgid "$Projectname"
msgstr ""
#: ../../addon/opensearch/opensearch.php:42 ../../Zotlabs/Lib/Enotify.php:67
-#: ../../Zotlabs/Module/Home.php:88 ../../Zotlabs/Module/Home.php:96
-#: ../../Zotlabs/Module/Invite.php:239 ../../Zotlabs/Module/Invite.php:508
-#: ../../Zotlabs/Module/Invite.php:522
+#: ../../Zotlabs/Module/Home.php:92 ../../Zotlabs/Module/Home.php:100
+#: ../../Zotlabs/Module/Invite.php:239 ../../Zotlabs/Module/Invite.php:491
+#: ../../Zotlabs/Module/Invite.php:505
msgid "$Projectname"
msgstr ""
@@ -4545,7 +4506,7 @@ msgstr ""
msgid "Channel is required."
msgstr ""
-#: ../../addon/redred/Mod_Redred.php:29 ../../Zotlabs/Module/Network.php:333
+#: ../../addon/redred/Mod_Redred.php:29 ../../Zotlabs/Module/Network.php:317
msgid "Invalid channel."
msgstr ""
@@ -4581,7 +4542,7 @@ msgstr ""
msgid "Hubzilla Crosspost Connector"
msgstr ""
-#: ../../addon/cards/cards.php:48 ../../addon/cards/cards.php:160
+#: ../../addon/cards/cards.php:48 ../../addon/cards/cards.php:161
#: ../../addon/cards/Mod_Cards.php:209 ../../Zotlabs/Lib/Apps.php:332
msgid "Cards"
msgstr ""
@@ -4594,7 +4555,7 @@ msgstr ""
#: ../../addon/wiki/Lib/NativeWikiPage.php:549
#: ../../Zotlabs/Module/Page.php:136 ../../Zotlabs/Module/Display.php:155
#: ../../Zotlabs/Module/Block.php:77 ../../Zotlabs/Module/Help.php:173
-#: ../../Zotlabs/Web/Router.php:188
+#: ../../Zotlabs/Web/Router.php:194
msgid "Page not found."
msgstr ""
@@ -4688,41 +4649,29 @@ msgstr ""
msgid "Your test account is about to expire."
msgstr ""
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:28
-msgid "ActivityPub Protocol Settings updated."
-msgstr ""
-
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:44
+#: ../../addon/pubcrawl/Mod_Pubcrawl.php:24
msgid ""
"The activitypub protocol does not support location independence. Connections "
"you make within that network may be unreachable from alternate channel "
"locations."
msgstr ""
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:50
-msgid "Send activities of type note instead of article"
-msgstr ""
-
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:50
-msgid "Microblog services such as Mastodon do not properly support articles"
-msgstr ""
-
-#: ../../addon/pubcrawl/Mod_Pubcrawl.php:58
+#: ../../addon/pubcrawl/Mod_Pubcrawl.php:31
msgid "Activitypub Protocol"
msgstr ""
-#: ../../addon/pubcrawl/pubcrawl.php:1099 ../../addon/diaspora/diaspora.php:415
+#: ../../addon/pubcrawl/pubcrawl.php:1099 ../../addon/diaspora/diaspora.php:417
#: ../../Zotlabs/Module/Contactedit.php:494
msgid "Refresh failed"
msgstr ""
-#: ../../addon/pubcrawl/pubcrawl.php:1106 ../../addon/diaspora/diaspora.php:420
+#: ../../addon/pubcrawl/pubcrawl.php:1106 ../../addon/diaspora/diaspora.php:422
#: ../../Zotlabs/Module/Contactedit.php:491
msgid "Refresh succeeded"
msgstr ""
#: ../../addon/bookmarker/bookmarker.php:38
-#: ../../Zotlabs/Lib/ThreadItem.php:458
+#: ../../Zotlabs/Lib/ThreadItem.php:455
msgid "Save Bookmarks"
msgstr ""
@@ -4791,7 +4740,7 @@ msgstr ""
msgid "Diaspora relay could not be imported"
msgstr ""
-#: ../../addon/diaspora/diaspora.php:1110
+#: ../../addon/diaspora/diaspora.php:1112
msgid "No subject"
msgstr ""
@@ -4816,27 +4765,27 @@ msgstr ""
msgid "$projectname"
msgstr ""
-#: ../../addon/diaspora/Receiver.php:1643
+#: ../../addon/diaspora/Receiver.php:1644
#, php-format
msgid "%1$s dislikes %2$s's %3$s"
msgstr ""
-#: ../../addon/diaspora/Receiver.php:1704
-#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Like.php:459
+#: ../../addon/diaspora/Receiver.php:1705
+#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Like.php:447
msgid "status"
msgstr ""
-#: ../../addon/diaspora/Receiver.php:2265 ../../Zotlabs/Module/Like.php:490
+#: ../../addon/diaspora/Receiver.php:2266 ../../Zotlabs/Module/Like.php:478
#, php-format
msgid "%1$s is attending %2$s's %3$s"
msgstr ""
-#: ../../addon/diaspora/Receiver.php:2267 ../../Zotlabs/Module/Like.php:492
+#: ../../addon/diaspora/Receiver.php:2268 ../../Zotlabs/Module/Like.php:480
#, php-format
msgid "%1$s is not attending %2$s's %3$s"
msgstr ""
-#: ../../addon/diaspora/Receiver.php:2269 ../../Zotlabs/Module/Like.php:494
+#: ../../addon/diaspora/Receiver.php:2270 ../../Zotlabs/Module/Like.php:482
#, php-format
msgid "%1$s may attend %2$s's %3$s"
msgstr ""
@@ -4896,28 +4845,32 @@ msgstr ""
msgid "Enter some text"
msgstr ""
-#: ../../addon/superblock/Mod_Superblock.php:62
+#: ../../addon/superblock/Mod_Superblock.php:117
msgid "superblock settings updated"
msgstr ""
-#: ../../addon/superblock/Mod_Superblock.php:86
+#: ../../addon/superblock/Mod_Superblock.php:141
msgid "Currently blocked"
msgstr ""
-#: ../../addon/superblock/Mod_Superblock.php:88
+#: ../../addon/superblock/Mod_Superblock.php:143
msgid "No channels currently blocked"
msgstr ""
-#: ../../addon/superblock/Mod_Superblock.php:90
+#: ../../addon/superblock/Mod_Superblock.php:145
#: ../../Zotlabs/Module/Cover_photo.php:382 ../../Zotlabs/Module/Tagrm.php:137
-#: ../../Zotlabs/Module/Photos.php:994
+#: ../../Zotlabs/Module/Photos.php:995
msgid "Remove"
msgstr ""
-#: ../../addon/superblock/superblock.php:355
+#: ../../addon/superblock/superblock.php:378
msgid "Block Completely"
msgstr ""
+#: ../../addon/superblock/superblock.php:387
+msgid "Block from site"
+msgstr ""
+
#: ../../addon/randpost/randpost.php:101
msgid "You're welcome."
msgstr ""
@@ -5051,7 +5004,7 @@ msgstr ""
#: ../../addon/rendezvous/rendezvous.php:172 ../../addon/wiki/Mod_Wiki.php:221
#: ../../addon/wiki/Lib/NativeWikiPage.php:592
#: ../../addon/wiki/Widget/Wiki_page_history.php:28
-#: ../../Zotlabs/Storage/Browser.php:381 ../../Zotlabs/Module/Oauth.php:112
+#: ../../Zotlabs/Storage/Browser.php:385 ../../Zotlabs/Module/Oauth.php:112
#: ../../Zotlabs/Module/Oauth.php:137 ../../Zotlabs/Module/Connedit.php:732
#: ../../Zotlabs/Module/Admin/Channels.php:181
#: ../../Zotlabs/Module/Sharedwithme.php:107 ../../Zotlabs/Module/Chat.php:256
@@ -5251,7 +5204,7 @@ msgstr ""
msgid "Edit Article"
msgstr ""
-#: ../../addon/articles/articles.php:48 ../../addon/articles/articles.php:160
+#: ../../addon/articles/articles.php:48 ../../addon/articles/articles.php:161
#: ../../addon/articles/Mod_Articles.php:228 ../../Zotlabs/Lib/Apps.php:331
msgid "Articles"
msgstr ""
@@ -5265,7 +5218,7 @@ msgid "Add Article"
msgstr ""
#: ../../addon/wiki/Mod_Wiki.php:36
-#: ../../addon/flashcards/Mod_Flashcards.php:52
+#: ../../addon/flashcards/Mod_Flashcards.php:58
#: ../../addon/faces/Mod_Faces.php:64 ../../addon/cart/cart.php:1458
msgid "Profile Unavailable."
msgstr ""
@@ -5273,7 +5226,7 @@ msgstr ""
#: ../../addon/wiki/Mod_Wiki.php:81 ../../addon/cart/manual_payments.php:93
#: ../../addon/cart/submodules/paypalbuttonV2.php:486
#: ../../addon/cart/submodules/paypalbutton.php:456
-#: ../../addon/cart/myshop.php:37 ../../addon/cart/cart.php:1609
+#: ../../addon/cart/myshop.php:37 ../../addon/cart/cart.php:1608
msgid "Invalid channel"
msgstr ""
@@ -5290,11 +5243,11 @@ msgid "Error downloading wiki: "
msgstr ""
#: ../../addon/wiki/Mod_Wiki.php:209 ../../addon/wiki/Widget/Wiki_list.php:23
-#: ../../addon/wiki/wiki.php:45 ../../addon/wiki/wiki.php:98
+#: ../../addon/wiki/wiki.php:45 ../../addon/wiki/wiki.php:99
msgid "Wikis"
msgstr ""
-#: ../../addon/wiki/Mod_Wiki.php:215 ../../Zotlabs/Storage/Browser.php:407
+#: ../../addon/wiki/Mod_Wiki.php:215 ../../Zotlabs/Storage/Browser.php:411
msgid "Download"
msgstr ""
@@ -5317,7 +5270,7 @@ msgstr ""
msgid "Content type"
msgstr ""
-#: ../../addon/wiki/Mod_Wiki.php:222 ../../Zotlabs/Storage/Browser.php:382
+#: ../../addon/wiki/Mod_Wiki.php:222 ../../Zotlabs/Storage/Browser.php:386
msgid "Type"
msgstr ""
@@ -5345,6 +5298,13 @@ msgstr ""
msgid "Rename page"
msgstr ""
+#: ../../addon/wiki/Mod_Wiki.php:304 ../../addon/hsse/hsse.php:186
+#: ../../Zotlabs/Lib/ThreadItem.php:294 ../../Zotlabs/Widget/Cdav.php:142
+#: ../../Zotlabs/Module/Webpages.php:250 ../../Zotlabs/Module/Layouts.php:192
+#: ../../Zotlabs/Module/Blocks.php:159 ../../Zotlabs/Module/Photos.php:1077
+msgid "Share"
+msgstr ""
+
#: ../../addon/wiki/Mod_Wiki.php:318
msgid "Error retrieving page content"
msgstr ""
@@ -5803,23 +5763,10 @@ msgstr ""
msgid "GNU-Social Crosspost Connector"
msgstr ""
-#: ../../addon/flashcards/Mod_Flashcards.php:225
-msgid "Not allowed."
-msgstr ""
-
-#: ../../addon/flashcards/Mod_Flashcards.php:268
-#: ../../Zotlabs/Module/Filestorage.php:202
-msgid "Set/edit permissions"
-msgstr ""
-
-#: ../../addon/flashcards/Mod_Flashcards.php:291
-#: ../../addon/flashcards/Mod_Flashcards.php:292
-#: ../../Zotlabs/Module/Display.php:59 ../../Zotlabs/Module/Display.php:120
-#: ../../Zotlabs/Module/Display.php:396 ../../Zotlabs/Module/Thing.php:120
-#: ../../Zotlabs/Module/Filestorage.php:29 ../../Zotlabs/Module/Viewsrc.php:25
-#: ../../Zotlabs/Module/Admin/Themes.php:73
-#: ../../Zotlabs/Module/Admin/Addons.php:42 ../../Zotlabs/Module/Admin.php:63
-msgid "Item not found."
+#: ../../addon/flashcards/Mod_Flashcards.php:161
+#: ../../Zotlabs/Module/Contactedit.php:431
+#: ../../Zotlabs/Module/Connedit.php:572
+msgid "Affinity"
msgstr ""
#: ../../addon/ljpost/ljpost.php:49
@@ -5861,7 +5808,7 @@ msgstr ""
#: ../../addon/cart/manual_payments.php:68
#: ../../addon/cart/submodules/paypalbuttonV2.php:417
#: ../../addon/cart/submodules/paypalbutton.php:392
-#: ../../addon/cart/cart.php:1631
+#: ../../addon/cart/cart.php:1630
msgid "Order not found."
msgstr ""
@@ -6099,35 +6046,35 @@ msgstr ""
msgid "Shop"
msgstr ""
-#: ../../addon/cart/cart.php:1598
+#: ../../addon/cart/cart.php:1597
msgid "You must be logged into the Grid to shop."
msgstr ""
-#: ../../addon/cart/cart.php:1647
+#: ../../addon/cart/cart.php:1646
msgid "Access denied."
msgstr ""
-#: ../../addon/cart/cart.php:1699 ../../addon/cart/cart.php:1842
+#: ../../addon/cart/cart.php:1698 ../../addon/cart/cart.php:1841
msgid "No Order Found"
msgstr ""
-#: ../../addon/cart/cart.php:1708
+#: ../../addon/cart/cart.php:1707
msgid "An unknown error has occurred Please start again."
msgstr ""
-#: ../../addon/cart/cart.php:1851
+#: ../../addon/cart/cart.php:1850
msgid "Requirements not met."
msgstr ""
-#: ../../addon/cart/cart.php:1851
+#: ../../addon/cart/cart.php:1850
msgid "Review your order and complete any needed requirements."
msgstr ""
-#: ../../addon/cart/cart.php:1877
+#: ../../addon/cart/cart.php:1876
msgid "Invalid Payment Type. Please start again."
msgstr ""
-#: ../../addon/cart/cart.php:1884
+#: ../../addon/cart/cart.php:1883
msgid "Order not found"
msgstr ""
@@ -6629,45 +6576,45 @@ msgstr ""
msgid "I won!"
msgstr ""
-#: ../../boot.php:1730
+#: ../../boot.php:1737
msgid "Create an account to access services and applications"
msgstr ""
-#: ../../boot.php:1748
+#: ../../boot.php:1755
msgid "Email or nickname"
msgstr ""
-#: ../../boot.php:1758
+#: ../../boot.php:1765
msgid "Password"
msgstr ""
-#: ../../boot.php:1759
+#: ../../boot.php:1766
msgid "Remember me"
msgstr ""
-#: ../../boot.php:1762
+#: ../../boot.php:1769
msgid "Forgot your password?"
msgstr ""
-#: ../../boot.php:1763 ../../Zotlabs/Module/Lostpass.php:91
+#: ../../boot.php:1770 ../../Zotlabs/Module/Lostpass.php:91
msgid "Password Reset"
msgstr ""
-#: ../../boot.php:2641
+#: ../../boot.php:2551
#, php-format
msgid "[$Projectname] Website SSL error for %s"
msgstr ""
-#: ../../boot.php:2646
+#: ../../boot.php:2556
msgid "Website SSL certificate is not valid. Please correct."
msgstr ""
-#: ../../boot.php:2762
+#: ../../boot.php:2674
#, php-format
msgid "[$Projectname] Cron tasks not running on %s"
msgstr ""
-#: ../../boot.php:2767
+#: ../../boot.php:2679
msgid "Cron/Scheduled tasks not running."
msgstr ""
@@ -6713,7 +6660,7 @@ msgstr ""
#: ../../Zotlabs/Lib/Enotify.php:134
#, php-format
-msgid "%1$s sent you a new direct message at %2$s"
+msgid "%1$s sent you a new private message at %2$s"
msgstr ""
#: ../../Zotlabs/Lib/Enotify.php:135
@@ -6727,34 +6674,38 @@ msgstr ""
#: ../../Zotlabs/Lib/Enotify.php:136
#, php-format
-msgid "Please visit %s to view and/or reply to your direct messages."
+msgid "Please visit %s to view and/or reply to your private messages."
msgstr ""
#: ../../Zotlabs/Lib/Enotify.php:149
-msgid "requested to comment on"
+msgid "requested to post in"
msgstr ""
#: ../../Zotlabs/Lib/Enotify.php:149
-msgid "commented on"
+msgid "posted in"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:318
+#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:325
msgid "requested to like"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:318
+#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:325
msgid "liked"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:321
+#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:328
msgid "requested to dislike"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:321
+#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:328
msgid "disliked"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:168
+#: ../../Zotlabs/Lib/Enotify.php:168 ../../Zotlabs/Lib/Enotify.php:331
+msgid "requested to repeat"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Enotify.php:168 ../../Zotlabs/Lib/Enotify.php:331
msgid "repeated"
msgstr ""
@@ -6762,226 +6713,221 @@ msgstr ""
msgid "voted on"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:216
+#: ../../Zotlabs/Lib/Enotify.php:217
#, php-format
msgid "%1$s %2$s [zrl=%3$s]a %4$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:224
+#: ../../Zotlabs/Lib/Enotify.php:227
#, php-format
msgid "%1$s %2$s [zrl=%3$s]%4$s's %5$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:233 ../../Zotlabs/Lib/Enotify.php:325
+#: ../../Zotlabs/Lib/Enotify.php:239 ../../Zotlabs/Lib/Enotify.php:335
#, php-format
msgid "%1$s %2$s [zrl=%3$s]your %4$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:245
+#: ../../Zotlabs/Lib/Enotify.php:253
#, php-format
msgid "[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:247
+#: ../../Zotlabs/Lib/Enotify.php:255
#, php-format
msgid "[$Projectname:Notify] Comment to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:248
+#: ../../Zotlabs/Lib/Enotify.php:256
#, php-format
msgid "%1$s commented on an item/conversation you have been following"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:251 ../../Zotlabs/Lib/Enotify.php:345
-#: ../../Zotlabs/Lib/Enotify.php:361 ../../Zotlabs/Lib/Enotify.php:385
-#: ../../Zotlabs/Lib/Enotify.php:402 ../../Zotlabs/Lib/Enotify.php:416
+#: ../../Zotlabs/Lib/Enotify.php:259 ../../Zotlabs/Lib/Enotify.php:356
+#: ../../Zotlabs/Lib/Enotify.php:372 ../../Zotlabs/Lib/Enotify.php:396
+#: ../../Zotlabs/Lib/Enotify.php:413 ../../Zotlabs/Lib/Enotify.php:427
#, php-format
msgid "Please visit %s to view and/or reply to the conversation."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:255 ../../Zotlabs/Lib/Enotify.php:256
+#: ../../Zotlabs/Lib/Enotify.php:263 ../../Zotlabs/Lib/Enotify.php:264
#, php-format
msgid "Please visit %s to approve or reject this comment."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:341
+#: ../../Zotlabs/Lib/Enotify.php:352
#, php-format
msgid "[$Projectname:Notify] Like received to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:342
+#: ../../Zotlabs/Lib/Enotify.php:353
#, php-format
msgid "%1$s liked an item/conversation you created"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:353
+#: ../../Zotlabs/Lib/Enotify.php:364
#, php-format
msgid "[$Projectname:Notify] %s posted to your profile wall"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:355
+#: ../../Zotlabs/Lib/Enotify.php:366
#, php-format
msgid "%1$s posted to your profile wall at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:357
+#: ../../Zotlabs/Lib/Enotify.php:368
#, php-format
msgid "%1$s posted to [zrl=%2$s]your wall[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:379
+#: ../../Zotlabs/Lib/Enotify.php:390
#, php-format
msgid "[$Projectname:Notify] %s tagged you"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:380
+#: ../../Zotlabs/Lib/Enotify.php:391
#, php-format
msgid "%1$s tagged you at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:381
+#: ../../Zotlabs/Lib/Enotify.php:392
#, php-format
msgid "%1$s [zrl=%2$s]tagged you[/zrl]."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:392
+#: ../../Zotlabs/Lib/Enotify.php:403
#, php-format
msgid "[$Projectname:Notify] %1$s poked you"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:393
+#: ../../Zotlabs/Lib/Enotify.php:404
#, php-format
msgid "%1$s poked you at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:394
+#: ../../Zotlabs/Lib/Enotify.php:405
#, php-format
msgid "%1$s [zrl=%2$s]poked you[/zrl]."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:410
+#: ../../Zotlabs/Lib/Enotify.php:421
#, php-format
msgid "[$Projectname:Notify] %s tagged your post"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:411
+#: ../../Zotlabs/Lib/Enotify.php:422
#, php-format
msgid "%1$s tagged your post at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:412
+#: ../../Zotlabs/Lib/Enotify.php:423
#, php-format
msgid "%1$s tagged [zrl=%2$s]your post[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:422
+#: ../../Zotlabs/Lib/Enotify.php:433
msgid "[$Projectname:Notify] Introduction received"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:423
+#: ../../Zotlabs/Lib/Enotify.php:434
#, php-format
msgid "You've received an new connection request from '%1$s' at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:424
+#: ../../Zotlabs/Lib/Enotify.php:435
#, php-format
msgid "You've received [zrl=%1$s]a new connection request[/zrl] from %2$s."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:427 ../../Zotlabs/Lib/Enotify.php:446
+#: ../../Zotlabs/Lib/Enotify.php:438 ../../Zotlabs/Lib/Enotify.php:457
#, php-format
msgid "You may visit their profile at %s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:429
+#: ../../Zotlabs/Lib/Enotify.php:440
#, php-format
msgid "Please visit %s to approve or reject the connection request."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:437
+#: ../../Zotlabs/Lib/Enotify.php:448
msgid "[$Projectname:Notify] Friend suggestion received"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:438
+#: ../../Zotlabs/Lib/Enotify.php:449
#, php-format
msgid "You've received a friend suggestion from '%1$s' at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:439
+#: ../../Zotlabs/Lib/Enotify.php:450
#, php-format
msgid "You've received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:444
+#: ../../Zotlabs/Lib/Enotify.php:455
msgid "Name:"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:445
+#: ../../Zotlabs/Lib/Enotify.php:456
msgid "Photo:"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:448
+#: ../../Zotlabs/Lib/Enotify.php:459
#, php-format
msgid "Please visit %s to approve or reject the suggestion."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:677
+#: ../../Zotlabs/Lib/Enotify.php:694
msgid "[$Projectname:Notify]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:843
-msgid "created a new poll"
+#: ../../Zotlabs/Lib/Enotify.php:860
+msgid "started a poll"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:843
-msgid "created a new post"
+#: ../../Zotlabs/Lib/Enotify.php:860
+msgid "started a conversation"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:844
+#: ../../Zotlabs/Lib/Enotify.php:861
#, php-format
msgid "voted on %s's poll"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:844
+#: ../../Zotlabs/Lib/Enotify.php:861
#, php-format
-msgid "commented on %s's post"
+msgid "posted in %s's conversation"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:848 ../../Zotlabs/Lib/Enotify.php:948
+#: ../../Zotlabs/Lib/Enotify.php:865 ../../Zotlabs/Lib/Enotify.php:957
msgid "shared a file with you"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:857
+#: ../../Zotlabs/Lib/Enotify.php:873
#, php-format
-msgid "edited a post dated %s"
+msgid "edited a message dated %s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:860
-#, php-format
-msgid "edited a comment dated %s"
-msgstr ""
-
-#: ../../Zotlabs/Lib/Enotify.php:933
+#: ../../Zotlabs/Lib/Enotify.php:942
msgid "added your channel"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:963
+#: ../../Zotlabs/Lib/Enotify.php:972
msgid "sent you a direct message"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:970
+#: ../../Zotlabs/Lib/Enotify.php:979
msgid "g A l F d"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:973
+#: ../../Zotlabs/Lib/Enotify.php:982
msgid "[today]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:983
+#: ../../Zotlabs/Lib/Enotify.php:992
msgid "created an event"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:998
+#: ../../Zotlabs/Lib/Enotify.php:1007
msgid "status verified"
msgstr ""
@@ -7009,10 +6955,6 @@ msgstr ""
msgid "Channel Manager"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:344
-msgid "Stream"
-msgstr ""
-
#: ../../Zotlabs/Lib/Apps.php:348
msgid "Wiki"
msgstr ""
@@ -7045,11 +6987,12 @@ msgstr ""
msgid "Features"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:364 ../../Zotlabs/Storage/Browser.php:410
+#: ../../Zotlabs/Lib/Apps.php:364 ../../Zotlabs/Storage/Browser.php:414
msgid "Post"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:369
+#: ../../Zotlabs/Lib/Apps.php:369 ../../Zotlabs/Widget/Notifications.php:116
+#: ../../Zotlabs/Widget/Messages.php:51
msgid "Notifications"
msgstr ""
@@ -7206,216 +7149,225 @@ msgstr ""
msgid "Update %s failed. See error logs."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:45 ../../Zotlabs/Lib/Connect.php:146
+#: ../../Zotlabs/Lib/Connect.php:51 ../../Zotlabs/Lib/Connect.php:152
msgid "Channel is blocked on this site."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:50
+#: ../../Zotlabs/Lib/Connect.php:56
msgid "Channel location missing."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:104
+#: ../../Zotlabs/Lib/Connect.php:110
msgid "Remote channel or protocol unavailable."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:140
+#: ../../Zotlabs/Lib/Connect.php:146
msgid "Channel discovery failed."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:158
+#: ../../Zotlabs/Lib/Connect.php:164
msgid "Protocol disabled."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:170
+#: ../../Zotlabs/Lib/Connect.php:176
msgid "Cannot connect to yourself."
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:275
+#: ../../Zotlabs/Lib/Connect.php:281
msgid "error saving data"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:110
+#: ../../Zotlabs/Lib/ThreadItem.php:107
msgid "Restricted message"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:117
-msgid "Direct message"
+#: ../../Zotlabs/Lib/ThreadItem.php:114
+msgid "Private message"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:122
+#: ../../Zotlabs/Lib/ThreadItem.php:119
msgid "Public Policy"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:156
+#: ../../Zotlabs/Lib/ThreadItem.php:153
msgid "Privacy conflict. Discretion advised."
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:187 ../../Zotlabs/Storage/Browser.php:373
+#: ../../Zotlabs/Lib/ThreadItem.php:184 ../../Zotlabs/Storage/Browser.php:377
msgid "Admin Delete"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:190 ../../Zotlabs/Module/Filer.php:66
+#: ../../Zotlabs/Lib/ThreadItem.php:187 ../../Zotlabs/Module/Filer.php:66
msgid "Save to Folder"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
+#: ../../Zotlabs/Lib/ThreadItem.php:214
msgid "I will attend"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
+#: ../../Zotlabs/Lib/ThreadItem.php:214
msgid "I will not attend"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
+#: ../../Zotlabs/Lib/ThreadItem.php:214
msgid "I might attend"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:296 ../../Zotlabs/Module/Photos.php:1074
-msgid "I like this (toggle)"
-msgstr ""
-
-#: ../../Zotlabs/Lib/ThreadItem.php:297 ../../Zotlabs/Module/Photos.php:1075
-msgid "I don't like this (toggle)"
-msgstr ""
-
-#: ../../Zotlabs/Lib/ThreadItem.php:298
-msgid "Reply to this comment"
+#: ../../Zotlabs/Lib/ThreadItem.php:287
+msgid "Reply to this message"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:298
+#: ../../Zotlabs/Lib/ThreadItem.php:287
msgid "reply"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:298
+#: ../../Zotlabs/Lib/ThreadItem.php:287
msgid "Reply to"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:305 ../../Zotlabs/Widget/Pinned.php:95
+#: ../../Zotlabs/Lib/ThreadItem.php:294 ../../Zotlabs/Widget/Pinned.php:95
msgid "share"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:309
+#: ../../Zotlabs/Lib/ThreadItem.php:298
msgid "Repeat"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:309
+#: ../../Zotlabs/Lib/ThreadItem.php:298
msgid "repeat"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:320
+#: ../../Zotlabs/Lib/ThreadItem.php:309
msgid "Delivery Report"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:341
+#: ../../Zotlabs/Lib/ThreadItem.php:330
#, php-format
msgid "%d comment"
msgid_plural "%d comments"
msgstr[0] ""
msgstr[1] ""
-#: ../../Zotlabs/Lib/ThreadItem.php:342
+#: ../../Zotlabs/Lib/ThreadItem.php:332
#, php-format
msgid "%d unseen"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:391
+#: ../../Zotlabs/Lib/ThreadItem.php:363
+#, php-format
+msgid "Load the next few of total %d comments"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:369
+msgid "Expand Replies"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:388
msgid "Forum"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:398
+#: ../../Zotlabs/Lib/ThreadItem.php:395
msgid "to"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:399 ../../Zotlabs/Widget/Messages.php:175
-#: ../../Zotlabs/Widget/Messages.php:178 ../../Zotlabs/Widget/Pinned.php:122
+#: ../../Zotlabs/Lib/ThreadItem.php:396 ../../Zotlabs/Widget/Messages.php:179
+#: ../../Zotlabs/Widget/Messages.php:182 ../../Zotlabs/Widget/Pinned.php:125
msgid "via"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:400
+#: ../../Zotlabs/Lib/ThreadItem.php:397
msgid "Wall-to-Wall"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:401
+#: ../../Zotlabs/Lib/ThreadItem.php:398
msgid "via Wall-To-Wall:"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:417
+#: ../../Zotlabs/Lib/ThreadItem.php:414
#, php-format
msgid "Last edited %s"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:418
+#: ../../Zotlabs/Lib/ThreadItem.php:415
#, php-format
msgid "Expires %s"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:421
+#: ../../Zotlabs/Lib/ThreadItem.php:418
#, php-format
msgid "Published %s"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:428
+#: ../../Zotlabs/Lib/ThreadItem.php:425
msgid "Attend"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:429 ../../Zotlabs/Widget/Pinned.php:136
+#: ../../Zotlabs/Lib/ThreadItem.php:426 ../../Zotlabs/Widget/Pinned.php:139
msgid "Attendance Options"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:431 ../../Zotlabs/Widget/Pinned.php:137
+#: ../../Zotlabs/Lib/ThreadItem.php:428 ../../Zotlabs/Widget/Pinned.php:140
msgid "Voting Options"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:446
+#: ../../Zotlabs/Lib/ThreadItem.php:442
msgid "Go to previous comment"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:455 ../../Zotlabs/Widget/Pinned.php:149
+#: ../../Zotlabs/Lib/ThreadItem.php:452 ../../Zotlabs/Widget/Pinned.php:152
msgid "Pinned post"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:459
+#: ../../Zotlabs/Lib/ThreadItem.php:456
msgid "Add to Calendar"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:467
+#: ../../Zotlabs/Lib/ThreadItem.php:464
msgid "Mark all comments seen"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:485
+#: ../../Zotlabs/Lib/ThreadItem.php:483
msgid "Add yours"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:485
+#: ../../Zotlabs/Lib/ThreadItem.php:483
msgid "Remove yours"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:796 ../../Zotlabs/Module/Photos.php:1092
-#: ../../Zotlabs/Module/Photos.php:1205
-msgid "This is you"
+#: ../../Zotlabs/Lib/ThreadItem.php:496
+msgid "show less"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:497
+msgid "show more"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:497
+msgid "show all"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:806
-msgid "Image"
+#: ../../Zotlabs/Lib/ThreadItem.php:806 ../../Zotlabs/Module/Photos.php:1093
+#: ../../Zotlabs/Module/Photos.php:1194
+msgid "This is you"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:808
+#: ../../Zotlabs/Lib/ThreadItem.php:818
msgid "Insert Link"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:809
+#: ../../Zotlabs/Lib/ThreadItem.php:819
msgid "Video"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:818
+#: ../../Zotlabs/Lib/ThreadItem.php:828
msgid "Your full name (required)"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:819
+#: ../../Zotlabs/Lib/ThreadItem.php:829
msgid "Your email address (required)"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:820
+#: ../../Zotlabs/Lib/ThreadItem.php:830
msgid "Your website URL (optional)"
msgstr ""
@@ -7423,32 +7375,32 @@ msgstr ""
msgid "Unable to verify channel signature"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2259
+#: ../../Zotlabs/Lib/Activity.php:2202
#, php-format
msgid "Likes %1$s's %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2262
+#: ../../Zotlabs/Lib/Activity.php:2205
#, php-format
msgid "Doesn't like %1$s's %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2268
+#: ../../Zotlabs/Lib/Activity.php:2211
#, php-format
msgid "Will attend %s's event"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2271
+#: ../../Zotlabs/Lib/Activity.php:2214
#, php-format
msgid "Will not attend %s's event"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2274
+#: ../../Zotlabs/Lib/Activity.php:2217
#, php-format
msgid "May attend %s's event"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2277
+#: ../../Zotlabs/Lib/Activity.php:2220
#, php-format
msgid "May not attend %s's event"
msgstr ""
@@ -7707,7 +7659,7 @@ msgid "View Ratings"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:24
-msgid "New network activity notifications"
+msgid "Unseen network activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:27
@@ -7716,14 +7668,14 @@ msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:30
#: ../../Zotlabs/Widget/Notifications.php:69
-msgid "Mark all notifications read"
+msgid "Mark all read"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:33
#: ../../Zotlabs/Widget/Notifications.php:53
#: ../../Zotlabs/Widget/Notifications.php:72
#: ../../Zotlabs/Widget/Notifications.php:166
-msgid "Show new posts only"
+msgid "Conversation starters"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:34
@@ -7731,33 +7683,36 @@ msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:73
#: ../../Zotlabs/Widget/Notifications.php:134
#: ../../Zotlabs/Widget/Notifications.php:167
-#: ../../Zotlabs/Widget/Messages.php:53
+#: ../../Zotlabs/Widget/Messages.php:55
msgid "Filter by name or address"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:44
-msgid "New home activity notifications"
+#: ../../Zotlabs/Module/Settings/Channel.php:259
+msgid "Unseen channel activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:47
-msgid "Home stream"
+msgid "Channel stream"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:50
-msgid "Mark all notifications seen"
+#: ../../Zotlabs/Widget/Notifications.php:88
+#: ../../Zotlabs/Widget/Notifications.php:123
+#: ../../Zotlabs/Module/Notifications.php:111
+msgid "Mark all seen"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:62
-#: ../../Zotlabs/Widget/Activity_filter.php:44
-msgid "Direct Messages"
+msgid "Private"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:63
-msgid "New direct messages notifications"
+msgid "Unseen private activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:66
-msgid "Direct messages stream"
+msgid "Private stream"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:81
@@ -7766,46 +7721,39 @@ msgid "Events"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:82
-msgid "New events notifications"
+msgid "Unseen events activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:85
msgid "View events"
msgstr ""
-#: ../../Zotlabs/Widget/Notifications.php:88
-msgid "Mark all events seen"
-msgstr ""
-
#: ../../Zotlabs/Widget/Notifications.php:96
#: ../../Zotlabs/Module/Connections.php:168
msgid "New Connections"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:97
-msgid "New connections notifications"
+#: ../../Zotlabs/Module/Settings/Channel.php:267
+msgid "New connections"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:100
-msgid "View all connections"
+#: ../../Zotlabs/Widget/Notifications.php:120
+#: ../../Zotlabs/Module/Photos.php:1114 ../../Zotlabs/Module/Photos.php:1126
+msgid "View all"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:109
-msgid "New files notifications"
+msgid "Useen files activity"
msgstr ""
-#: ../../Zotlabs/Widget/Notifications.php:116
#: ../../Zotlabs/Widget/Notifications.php:117
-#: ../../Zotlabs/Widget/Messages.php:49
-msgid "Notices"
+msgid "Unseen notifications"
msgstr ""
-#: ../../Zotlabs/Widget/Notifications.php:120
-msgid "View all notices"
-msgstr ""
-
-#: ../../Zotlabs/Widget/Notifications.php:123
-msgid "Mark all notices seen"
+#: ../../Zotlabs/Widget/Notifications.php:132
+msgid "Unseen forums activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:144
@@ -7813,11 +7761,12 @@ msgid "Registrations"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:145
-msgid "New registrations notifications"
+msgid "Unseen registration activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:155
-msgid "New public stream notifications"
+#: ../../Zotlabs/Module/Settings/Channel.php:270
+msgid "Unseen public stream activity"
msgstr ""
#: ../../Zotlabs/Widget/Notifications.php:158
@@ -7905,7 +7854,7 @@ msgid "View public stream"
msgstr ""
#: ../../Zotlabs/Widget/Newmember.php:82
-#: ../../Zotlabs/Module/Settings/Display.php:202
+#: ../../Zotlabs/Module/Settings/Display.php:201
msgid "New Member Links"
msgstr ""
@@ -7921,31 +7870,31 @@ msgstr ""
msgid "See more..."
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:45
-msgid "Public and restricted messages"
+#: ../../Zotlabs/Widget/Messages.php:47
+msgid "Public and restricted conversations"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:46
-msgid "Direct messages"
+#: ../../Zotlabs/Widget/Messages.php:48
+msgid "Private conversations"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:47
-msgid "Starred messages"
+#: ../../Zotlabs/Widget/Messages.php:49
+msgid "Starred conversations"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:48
+#: ../../Zotlabs/Widget/Messages.php:50
msgid "Filed messages"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:51
-msgid "No messages"
+#: ../../Zotlabs/Widget/Messages.php:53
+msgid "No conversations"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:52
-msgid "Unseen"
+#: ../../Zotlabs/Widget/Messages.php:54
+msgid "Unseen reactions"
msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:54
+#: ../../Zotlabs/Widget/Messages.php:56
msgid "Filter by file name"
msgstr ""
@@ -7961,24 +7910,24 @@ msgstr ""
msgid "Channel activities"
msgstr ""
-#: ../../Zotlabs/Widget/Album.php:84 ../../Zotlabs/Widget/Portfolio.php:91
-#: ../../Zotlabs/Module/Embedphotos.php:171 ../../Zotlabs/Module/Photos.php:782
-#: ../../Zotlabs/Module/Photos.php:1324
+#: ../../Zotlabs/Widget/Album.php:87 ../../Zotlabs/Widget/Portfolio.php:95
+#: ../../Zotlabs/Module/Embedphotos.php:173 ../../Zotlabs/Module/Photos.php:784
+#: ../../Zotlabs/Module/Photos.php:1318
msgid "View Photo"
msgstr ""
-#: ../../Zotlabs/Widget/Album.php:101 ../../Zotlabs/Widget/Portfolio.php:112
-#: ../../Zotlabs/Module/Embedphotos.php:187 ../../Zotlabs/Module/Photos.php:813
+#: ../../Zotlabs/Widget/Album.php:104 ../../Zotlabs/Widget/Portfolio.php:116
+#: ../../Zotlabs/Module/Embedphotos.php:189 ../../Zotlabs/Module/Photos.php:815
msgid "Edit Album"
msgstr ""
-#: ../../Zotlabs/Widget/Album.php:103 ../../Zotlabs/Widget/Portfolio.php:114
+#: ../../Zotlabs/Widget/Album.php:106 ../../Zotlabs/Widget/Portfolio.php:118
#: ../../Zotlabs/Widget/Cdav.php:152 ../../Zotlabs/Widget/Cdav.php:188
-#: ../../Zotlabs/Storage/Browser.php:546
+#: ../../Zotlabs/Storage/Browser.php:550
#: ../../Zotlabs/Module/Cover_photo.php:381
-#: ../../Zotlabs/Module/Embedphotos.php:189
+#: ../../Zotlabs/Module/Embedphotos.php:191
#: ../../Zotlabs/Module/Profile_photo.php:547
-#: ../../Zotlabs/Module/Photos.php:681
+#: ../../Zotlabs/Module/Photos.php:683
msgid "Upload"
msgstr ""
@@ -7986,12 +7935,12 @@ msgstr ""
msgid "Share This"
msgstr ""
-#: ../../Zotlabs/Widget/Pinned.php:117 ../../Zotlabs/Widget/Pinned.php:118
+#: ../../Zotlabs/Widget/Pinned.php:120 ../../Zotlabs/Widget/Pinned.php:121
#, php-format
msgid "View %s's profile - %s"
msgstr ""
-#: ../../Zotlabs/Widget/Pinned.php:151
+#: ../../Zotlabs/Widget/Pinned.php:154
msgid "Don't show"
msgstr ""
@@ -8032,20 +7981,20 @@ msgstr ""
msgid "Saved"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_order.php:96
-msgid "Commented Date"
+#: ../../Zotlabs/Widget/Activity_order.php:94
+msgid "Posted Date"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_order.php:100
-msgid "Order by last commented date"
+#: ../../Zotlabs/Widget/Activity_order.php:98
+msgid "Order by last posted date"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_order.php:103
-msgid "Posted Date"
+#: ../../Zotlabs/Widget/Activity_order.php:102
+msgid "Commented Date"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_order.php:107
-msgid "Order by last posted date"
+#: ../../Zotlabs/Widget/Activity_order.php:106
+msgid "Order by last commented date"
msgstr ""
#: ../../Zotlabs/Widget/Activity_order.php:110
@@ -8060,6 +8009,10 @@ msgstr ""
msgid "Stream Order"
msgstr ""
+#: ../../Zotlabs/Widget/Activity_filter.php:44
+msgid "Direct Messages"
+msgstr ""
+
#: ../../Zotlabs/Widget/Activity_filter.php:48
msgid "Show direct (private) messages"
msgstr ""
@@ -8343,7 +8296,7 @@ msgid "Create new CalDAV calendar"
msgstr ""
#: ../../Zotlabs/Widget/Cdav.php:146 ../../Zotlabs/Widget/Cdav.php:184
-#: ../../Zotlabs/Storage/Browser.php:368 ../../Zotlabs/Storage/Browser.php:544
+#: ../../Zotlabs/Storage/Browser.php:372 ../../Zotlabs/Storage/Browser.php:548
#: ../../Zotlabs/Module/Webpages.php:248 ../../Zotlabs/Module/Connedit.php:747
#: ../../Zotlabs/Module/Layouts.php:183 ../../Zotlabs/Module/Menu.php:182
#: ../../Zotlabs/Module/Blocks.php:157 ../../Zotlabs/Module/New_channel.php:190
@@ -8408,129 +8361,129 @@ msgstr ""
msgid "Privacy groups"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:292
+#: ../../Zotlabs/Storage/Browser.php:296
msgid "Change filename to"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:309 ../../Zotlabs/Storage/Browser.php:394
+#: ../../Zotlabs/Storage/Browser.php:313 ../../Zotlabs/Storage/Browser.php:398
msgid "Select a target location"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:310 ../../Zotlabs/Storage/Browser.php:395
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:399
msgid "Copy to target location"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:311 ../../Zotlabs/Storage/Browser.php:393
+#: ../../Zotlabs/Storage/Browser.php:315 ../../Zotlabs/Storage/Browser.php:397
msgid "Set permissions for all files and sub folders"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:312
+#: ../../Zotlabs/Storage/Browser.php:316
msgid "Notify your contacts about this file"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:351
+#: ../../Zotlabs/Storage/Browser.php:355
msgid "File category"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:365
+#: ../../Zotlabs/Storage/Browser.php:369
msgid "Total"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:367
+#: ../../Zotlabs/Storage/Browser.php:371
msgid "Shared"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:369
+#: ../../Zotlabs/Storage/Browser.php:373
msgid "Add Files"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:384
+#: ../../Zotlabs/Storage/Browser.php:388
#: ../../Zotlabs/Module/Sharedwithme.php:110
msgid "Last Modified"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:385
+#: ../../Zotlabs/Storage/Browser.php:389
msgid "parent"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:390
+#: ../../Zotlabs/Storage/Browser.php:394
#: ../../Zotlabs/Module/Filestorage.php:206
msgid "Copy/paste this code to attach file to a post"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:391
+#: ../../Zotlabs/Storage/Browser.php:395
#: ../../Zotlabs/Module/Filestorage.php:207
msgid "Copy/paste this URL to link file from a web page"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:402
+#: ../../Zotlabs/Storage/Browser.php:406
msgid "Select All"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:403
+#: ../../Zotlabs/Storage/Browser.php:407
msgid "Bulk Actions"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:404
+#: ../../Zotlabs/Storage/Browser.php:408
msgid "Adjust Permissions"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:405
+#: ../../Zotlabs/Storage/Browser.php:409
msgid "Move or Copy"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:408
+#: ../../Zotlabs/Storage/Browser.php:412
msgid "Info"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:409
+#: ../../Zotlabs/Storage/Browser.php:413
msgid "Rename"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:411
+#: ../../Zotlabs/Storage/Browser.php:415
msgid "Attachment BBcode"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:412
+#: ../../Zotlabs/Storage/Browser.php:416
msgid "Embed BBcode"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:413
+#: ../../Zotlabs/Storage/Browser.php:417
msgid "Link BBcode"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:486
+#: ../../Zotlabs/Storage/Browser.php:490
#, php-format
msgid "You are using %1$s of your available file storage."
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:491
+#: ../../Zotlabs/Storage/Browser.php:495
#, php-format
msgid "You are using %1$s of %2$s available file storage. (%3$s&#37;)"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:502
+#: ../../Zotlabs/Storage/Browser.php:506
msgid "WARNING:"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:543
+#: ../../Zotlabs/Storage/Browser.php:547
msgid "Create new folder"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:545
+#: ../../Zotlabs/Storage/Browser.php:549
msgid "Upload file"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:557
+#: ../../Zotlabs/Storage/Browser.php:561
msgid "Drop files here to immediately upload"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:558
+#: ../../Zotlabs/Storage/Browser.php:562
#: ../../Zotlabs/Module/Filestorage.php:211
msgid "Show in your contacts shared folder"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:560
+#: ../../Zotlabs/Storage/Browser.php:564
msgid ""
"You can select files via the upload button or drop them right here or into "
"an existing folder."
@@ -8602,27 +8555,35 @@ msgstr ""
msgid "Public access denied."
msgstr ""
-#: ../../Zotlabs/Module/Display.php:53 ../../Zotlabs/Module/Oep.php:82
+#: ../../Zotlabs/Module/Display.php:53 ../../Zotlabs/Module/Oep.php:83
#: ../../Zotlabs/Module/Pubstream.php:55 ../../Zotlabs/Module/Channel.php:195
msgid "Malformed message id."
msgstr ""
-#: ../../Zotlabs/Module/Display.php:95 ../../Zotlabs/Module/Network.php:213
-#: ../../Zotlabs/Module/Hq.php:99 ../../Zotlabs/Module/Pubstream.php:98
+#: ../../Zotlabs/Module/Display.php:59 ../../Zotlabs/Module/Display.php:120
+#: ../../Zotlabs/Module/Display.php:391 ../../Zotlabs/Module/Thing.php:120
+#: ../../Zotlabs/Module/Filestorage.php:29 ../../Zotlabs/Module/Viewsrc.php:25
+#: ../../Zotlabs/Module/Admin/Themes.php:73
+#: ../../Zotlabs/Module/Admin/Addons.php:42 ../../Zotlabs/Module/Admin.php:63
+msgid "Item not found."
+msgstr ""
+
+#: ../../Zotlabs/Module/Display.php:95 ../../Zotlabs/Module/Network.php:215
+#: ../../Zotlabs/Module/Hq.php:101 ../../Zotlabs/Module/Pubstream.php:98
#: ../../Zotlabs/Module/Channel.php:279 ../../Zotlabs/Module/Rpost.php:111
msgid "Reset form"
msgstr ""
-#: ../../Zotlabs/Module/Display.php:320 ../../Zotlabs/Module/Channel.php:501
+#: ../../Zotlabs/Module/Display.php:315 ../../Zotlabs/Module/Channel.php:503
msgid ""
"You must enable javascript for your browser to be able to view this content."
msgstr ""
-#: ../../Zotlabs/Module/Display.php:340
+#: ../../Zotlabs/Module/Display.php:335
msgid "Article"
msgstr ""
-#: ../../Zotlabs/Module/Display.php:389
+#: ../../Zotlabs/Module/Display.php:384
msgid "Item has been removed."
msgstr ""
@@ -8707,11 +8668,11 @@ msgstr ""
msgid "Edit Webpage"
msgstr ""
-#: ../../Zotlabs/Module/Sse_bs.php:633
+#: ../../Zotlabs/Module/Sse_bs.php:657
msgid "Private forum"
msgstr ""
-#: ../../Zotlabs/Module/Sse_bs.php:633
+#: ../../Zotlabs/Module/Sse_bs.php:657
msgid "Public forum"
msgstr ""
@@ -8847,23 +8808,23 @@ msgstr ""
msgid "Remove authorization"
msgstr ""
-#: ../../Zotlabs/Module/Network.php:108
+#: ../../Zotlabs/Module/Network.php:110
msgid "No such group"
msgstr ""
-#: ../../Zotlabs/Module/Network.php:160
+#: ../../Zotlabs/Module/Network.php:162
msgid "No such channel"
msgstr ""
-#: ../../Zotlabs/Module/Network.php:172 ../../Zotlabs/Module/Channel.php:246
+#: ../../Zotlabs/Module/Network.php:174 ../../Zotlabs/Module/Channel.php:246
msgid "Search Results For:"
msgstr ""
-#: ../../Zotlabs/Module/Network.php:248
+#: ../../Zotlabs/Module/Network.php:250
msgid "Privacy group is empty"
msgstr ""
-#: ../../Zotlabs/Module/Network.php:258
+#: ../../Zotlabs/Module/Network.php:260
msgid "Privacy group: "
msgstr ""
@@ -9054,10 +9015,6 @@ msgstr ""
msgid "System Notifications"
msgstr ""
-#: ../../Zotlabs/Module/Notifications.php:111
-msgid "Mark all seen"
-msgstr ""
-
#: ../../Zotlabs/Module/Z6trans.php:19
msgid "Update to Hubzilla 5.0 step 2"
msgstr ""
@@ -9074,6 +9031,18 @@ msgstr ""
msgid "from the terminal."
msgstr ""
+#: ../../Zotlabs/Module/Request.php:80
+msgid "+ Repeat again"
+msgstr ""
+
+#: ../../Zotlabs/Module/Request.php:80
+msgid "- Remove yours"
+msgstr ""
+
+#: ../../Zotlabs/Module/Request.php:80
+msgid "+ Add yours"
+msgstr ""
+
#: ../../Zotlabs/Module/Thing.php:146
msgid "Thing updated"
msgstr ""
@@ -9285,6 +9254,10 @@ msgstr ""
msgid "Edit file permissions"
msgstr ""
+#: ../../Zotlabs/Module/Filestorage.php:202
+msgid "Set/edit permissions"
+msgstr ""
+
#: ../../Zotlabs/Module/Filestorage.php:203
msgid "Include all files and sub folders"
msgstr ""
@@ -9445,11 +9418,6 @@ msgstr ""
msgid "Permission"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:431
-#: ../../Zotlabs/Module/Connedit.php:572
-msgid "Affinity"
-msgstr ""
-
#: ../../Zotlabs/Module/Contactedit.php:432
msgid "Content filter"
msgstr ""
@@ -9815,36 +9783,36 @@ msgstr ""
msgid "item"
msgstr ""
-#: ../../Zotlabs/Module/Item.php:253 ../../Zotlabs/Module/Pin.php:36
+#: ../../Zotlabs/Module/Item.php:254 ../../Zotlabs/Module/Pin.php:37
msgid "Unable to locate original post."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:541
+#: ../../Zotlabs/Module/Item.php:542
msgid "Empty post discarded."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1010
+#: ../../Zotlabs/Module/Item.php:1005
msgid "Duplicate post suppressed."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1160
+#: ../../Zotlabs/Module/Item.php:1155
msgid "System error. Post not saved."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1200
+#: ../../Zotlabs/Module/Item.php:1195
msgid "Your comment is awaiting approval."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1337
+#: ../../Zotlabs/Module/Item.php:1333
msgid "Unable to obtain post information from database."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1344
+#: ../../Zotlabs/Module/Item.php:1340
#, php-format
msgid "You have reached your limit of %1$.0f top level posts."
msgstr ""
-#: ../../Zotlabs/Module/Item.php:1351
+#: ../../Zotlabs/Module/Item.php:1347
#, php-format
msgid "You have reached your limit of %1$.0f webpages."
msgstr ""
@@ -10313,7 +10281,7 @@ msgstr ""
msgid "The main page content can not be edited!"
msgstr ""
-#: ../../Zotlabs/Module/Home.php:105
+#: ../../Zotlabs/Module/Home.php:112
#, php-format
msgid "Welcome to %s"
msgstr ""
@@ -11189,15 +11157,15 @@ msgid ""
"This role will be used for the first channel created after registration."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:344 ../../Zotlabs/Module/Invite.php:411
+#: ../../Zotlabs/Module/Admin/Site.php:344 ../../Zotlabs/Module/Invite.php:394
msgid "Minute(s)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:345 ../../Zotlabs/Module/Invite.php:412
+#: ../../Zotlabs/Module/Admin/Site.php:345 ../../Zotlabs/Module/Invite.php:395
msgid "Hour(s)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:346 ../../Zotlabs/Module/Invite.php:413
+#: ../../Zotlabs/Module/Admin/Site.php:346 ../../Zotlabs/Module/Invite.php:396
msgid "Day(s)"
msgstr ""
@@ -11222,7 +11190,7 @@ msgid "Time to wait before a registration can be verified"
msgstr ""
#: ../../Zotlabs/Module/Admin/Site.php:363
-#: ../../Zotlabs/Module/Admin/Site.php:385 ../../Zotlabs/Module/Invite.php:422
+#: ../../Zotlabs/Module/Admin/Site.php:385 ../../Zotlabs/Module/Invite.php:405
msgid "duration up from now"
msgstr ""
@@ -11915,12 +11883,12 @@ msgstr ""
msgid "Link to source"
msgstr ""
-#: ../../Zotlabs/Module/Cal.php:199 ../../Zotlabs/Module/Photos.php:946
+#: ../../Zotlabs/Module/Cal.php:199 ../../Zotlabs/Module/Photos.php:948
#: ../../Zotlabs/Module/Cdav.php:1026
msgid "Previous"
msgstr ""
-#: ../../Zotlabs/Module/Cal.php:200 ../../Zotlabs/Module/Photos.php:955
+#: ../../Zotlabs/Module/Cal.php:200 ../../Zotlabs/Module/Photos.php:957
#: ../../Zotlabs/Module/Setup.php:278 ../../Zotlabs/Module/Cdav.php:1027
msgid "Next"
msgstr ""
@@ -12196,6 +12164,10 @@ msgstr ""
msgid "Post not found."
msgstr ""
+#: ../../Zotlabs/Module/Tagger.php:83
+msgid "comment"
+msgstr ""
+
#: ../../Zotlabs/Module/Tagger.php:123
#, php-format
msgid "%1$s tagged %2$s's %3$s with %4$s"
@@ -12209,7 +12181,7 @@ msgstr ""
msgid "Register is closed"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:116 ../../Zotlabs/Module/Invite.php:563
+#: ../../Zotlabs/Module/Invite.php:116 ../../Zotlabs/Module/Invite.php:546
msgid "Note, the invitation code is valid up to"
msgstr ""
@@ -12268,55 +12240,51 @@ msgstr ""
msgid "You have no more invitations available"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:366
+#: ../../Zotlabs/Module/Invite.php:360
msgid "Not on xchan"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:399
-msgid "All users invitation limit exceeded."
-msgstr ""
-
-#: ../../Zotlabs/Module/Invite.php:417
+#: ../../Zotlabs/Module/Invite.php:400
msgid "Invitation expires after"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:518 ../../Zotlabs/Module/Invite.php:557
+#: ../../Zotlabs/Module/Invite.php:501 ../../Zotlabs/Module/Invite.php:540
msgid "Invitation"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:548
+#: ../../Zotlabs/Module/Invite.php:531
msgid "Send invitations"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:549
+#: ../../Zotlabs/Module/Invite.php:532
msgid "Invitations I am using"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:550
+#: ../../Zotlabs/Module/Invite.php:533
msgid "Invitations we are using"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:551
+#: ../../Zotlabs/Module/Invite.php:534
msgid "§ Note, the email(s) sent will be recorded in the system logs"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:552
+#: ../../Zotlabs/Module/Invite.php:535
msgid "Enter email addresses, one per line:"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:553
+#: ../../Zotlabs/Module/Invite.php:536
msgid "Your message:"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:554
+#: ../../Zotlabs/Module/Invite.php:537
msgid "Invite template"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:556
+#: ../../Zotlabs/Module/Invite.php:539
msgid "Subject:"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:562
+#: ../../Zotlabs/Module/Invite.php:545
msgid "Here you may enter personal notes to the recipient(s)"
msgstr ""
@@ -12439,46 +12407,46 @@ msgid ""
"a> on any site containing your channel."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:111
+#: ../../Zotlabs/Module/Like.php:99
msgid "Like/Dislike"
msgstr ""
-#: ../../Zotlabs/Module/Like.php:117
+#: ../../Zotlabs/Module/Like.php:105
msgid "This action is restricted to members."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:118
+#: ../../Zotlabs/Module/Like.php:106
msgid ""
"Please <a href=\"rmagic\">login with your $Projectname ID</a> or <a "
"href=\"register\">register as a new $Projectname member</a> to continue."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:171 ../../Zotlabs/Module/Like.php:197
-#: ../../Zotlabs/Module/Like.php:230
+#: ../../Zotlabs/Module/Like.php:159 ../../Zotlabs/Module/Like.php:185
+#: ../../Zotlabs/Module/Like.php:218
msgid "Invalid request."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:212
+#: ../../Zotlabs/Module/Like.php:200
msgid "thing"
msgstr ""
-#: ../../Zotlabs/Module/Like.php:253
+#: ../../Zotlabs/Module/Like.php:241
msgid "Channel unavailable."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:289
+#: ../../Zotlabs/Module/Like.php:277
msgid "Previous action reversed."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:456
+#: ../../Zotlabs/Module/Like.php:444
msgid "profile"
msgstr ""
-#: ../../Zotlabs/Module/Like.php:623
+#: ../../Zotlabs/Module/Like.php:611
msgid "Action completed."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:624
+#: ../../Zotlabs/Module/Like.php:612
msgid "Thank you."
msgstr ""
@@ -12689,7 +12657,7 @@ msgstr ""
msgid "Delete Album"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:165 ../../Zotlabs/Module/Photos.php:1057
+#: ../../Zotlabs/Module/Photos.php:165 ../../Zotlabs/Module/Photos.php:1058
msgid "Delete Photo"
msgstr ""
@@ -12697,134 +12665,138 @@ msgstr ""
msgid "No photos selected"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:571
+#: ../../Zotlabs/Module/Photos.php:573
msgid "Access to this item is restricted."
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:614
+#: ../../Zotlabs/Module/Photos.php:616
#, php-format
msgid "%1$.2f MB photo storage used."
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:618
+#: ../../Zotlabs/Module/Photos.php:620
#, php-format
msgid "%1$.2f MB of %2$.2f MB photo storage used."
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:660
+#: ../../Zotlabs/Module/Photos.php:662
msgid "Upload Photos"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:664
+#: ../../Zotlabs/Module/Photos.php:666
msgid "Enter an album name"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:665
+#: ../../Zotlabs/Module/Photos.php:667
msgid "or select an existing album (doubleclick)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:666
+#: ../../Zotlabs/Module/Photos.php:668
msgid "Create a status post for this upload"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:668
+#: ../../Zotlabs/Module/Photos.php:670
msgid "Description (optional)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:756
+#: ../../Zotlabs/Module/Photos.php:758
msgid "Show Newest First"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:758
+#: ../../Zotlabs/Module/Photos.php:760
msgid "Show Oldest First"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:815 ../../Zotlabs/Module/Photos.php:1355
+#: ../../Zotlabs/Module/Photos.php:817 ../../Zotlabs/Module/Photos.php:1349
msgid "Add Photos"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:867
+#: ../../Zotlabs/Module/Photos.php:869
msgid "Permission denied. Access to this item may be restricted."
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:869
+#: ../../Zotlabs/Module/Photos.php:871
msgid "Photo not available"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:927
+#: ../../Zotlabs/Module/Photos.php:929
msgid "Use as profile photo"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:928
+#: ../../Zotlabs/Module/Photos.php:930
msgid "Use as cover photo"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:935
+#: ../../Zotlabs/Module/Photos.php:937
msgid "Private Photo"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:950
+#: ../../Zotlabs/Module/Photos.php:952
msgid "View Full Size"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1031
+#: ../../Zotlabs/Module/Photos.php:1032
msgid "Edit photo"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1033
+#: ../../Zotlabs/Module/Photos.php:1034
msgid "Rotate CW (right)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1034
+#: ../../Zotlabs/Module/Photos.php:1035
msgid "Rotate CCW (left)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1037
+#: ../../Zotlabs/Module/Photos.php:1038
msgid "Move photo to album"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1038
+#: ../../Zotlabs/Module/Photos.php:1039
msgid "Enter a new album name"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1039
+#: ../../Zotlabs/Module/Photos.php:1040
msgid "or select an existing one (doubleclick)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1044
+#: ../../Zotlabs/Module/Photos.php:1045
msgid "Add a Tag"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1052
+#: ../../Zotlabs/Module/Photos.php:1053
msgid "Example: @bob, @Barbara_Jensen, @jim@example.com"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1055
+#: ../../Zotlabs/Module/Photos.php:1056
msgid "Flag as adult in album view"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1125 ../../Zotlabs/Module/Photos.php:1137
-msgid "View all"
+#: ../../Zotlabs/Module/Photos.php:1075
+msgid "I like this (toggle)"
+msgstr ""
+
+#: ../../Zotlabs/Module/Photos.php:1076
+msgid "I don't like this (toggle)"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1238
+#: ../../Zotlabs/Module/Photos.php:1232
msgid "Photo Tools"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1247
+#: ../../Zotlabs/Module/Photos.php:1241
msgid "In This Photo:"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1252
+#: ../../Zotlabs/Module/Photos.php:1246
msgid "Map"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1260
+#: ../../Zotlabs/Module/Photos.php:1254
msgctxt "noun"
msgid "Likes"
msgstr ""
-#: ../../Zotlabs/Module/Photos.php:1261
+#: ../../Zotlabs/Module/Photos.php:1255
msgctxt "noun"
msgid "Dislikes"
msgstr ""
@@ -14027,6 +13999,10 @@ msgstr ""
msgid "Language App"
msgstr ""
+#: ../../Zotlabs/Module/Lang.php:80
+msgid "Select an alternate language"
+msgstr ""
+
#: ../../Zotlabs/Module/Rpost.php:117 ../../Zotlabs/Module/Editpost.php:114
msgid "Edit post"
msgstr ""
@@ -14278,69 +14254,67 @@ msgstr ""
msgid "%s - (Experimental)"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:183
+#: ../../Zotlabs/Module/Settings/Display.php:182
msgid "Display Settings"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:184
+#: ../../Zotlabs/Module/Settings/Display.php:183
msgid "Theme Settings"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:185
+#: ../../Zotlabs/Module/Settings/Display.php:184
msgid "Custom Theme Settings"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:186
+#: ../../Zotlabs/Module/Settings/Display.php:185
msgid "Content Settings"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:192
+#: ../../Zotlabs/Module/Settings/Display.php:191
msgid "Display Theme:"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:193
+#: ../../Zotlabs/Module/Settings/Display.php:192
msgid "Select scheme"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:195
-msgid "Preload images before rendering the page"
+#: ../../Zotlabs/Module/Settings/Display.php:194
+msgid "Threaded conversation view"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:195
-msgid ""
-"The subjective page load time will be longer but the page will be ready when "
-"displayed"
+#: ../../Zotlabs/Module/Settings/Display.php:194
+msgid "Display replies below their parent message (default yes)"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:196
+#: ../../Zotlabs/Module/Settings/Display.php:195
msgid "Enable user zoom on mobile devices"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:197
+#: ../../Zotlabs/Module/Settings/Display.php:196
msgid "Update browser every xx seconds"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:197
+#: ../../Zotlabs/Module/Settings/Display.php:196
msgid "Minimum of 10 seconds, no maximum"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:198
+#: ../../Zotlabs/Module/Settings/Display.php:197
msgid "Maximum number of conversations to load at any time:"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:198
+#: ../../Zotlabs/Module/Settings/Display.php:197
msgid "Maximum of 30 items"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:199
+#: ../../Zotlabs/Module/Settings/Display.php:198
msgid "Show emoticons (smilies) as images"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:200
+#: ../../Zotlabs/Module/Settings/Display.php:199
msgid "Link post titles to source"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:202
+#: ../../Zotlabs/Module/Settings/Display.php:201
msgid "Display new member quick links menu"
msgstr ""
@@ -14687,10 +14661,6 @@ msgstr ""
msgid "Unseen stream activity"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:259
-msgid "Unseen channel activity"
-msgstr ""
-
#: ../../Zotlabs/Module/Settings/Channel.php:260
msgid "Unseen private messages"
msgstr ""
@@ -14730,10 +14700,6 @@ msgstr ""
msgid "System critical alerts"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:267
-msgid "New connections"
-msgstr ""
-
#: ../../Zotlabs/Module/Settings/Channel.php:268
msgid "System Registrations"
msgstr ""
@@ -14742,10 +14708,6 @@ msgstr ""
msgid "Unseen shared files"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:270
-msgid "Unseen public stream activity"
-msgstr ""
-
#: ../../Zotlabs/Module/Settings/Channel.php:271
msgid "Unseen likes and dislikes"
msgstr ""
diff --git a/util/init_sys_channel b/util/init_sys_channel
new file mode 100755
index 000000000..a97ef295b
--- /dev/null
+++ b/util/init_sys_channel
@@ -0,0 +1,13 @@
+#!/usr/bin/env php
+<?php
+
+if(!file_exists('include/cli_startup.php')) {
+ echo 'Run init_sys_channel from the top level Hubzilla web directory, as util/init_sys_channel' . PHP_EOL;
+ exit(1);
+}
+
+require_once('include/cli_startup.php');
+
+cli_startup();
+
+create_sys_channel();
diff --git a/vendor/bakame/http-structured-fields/LICENSE b/vendor/bakame/http-structured-fields/LICENSE
new file mode 100644
index 000000000..225c502ed
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Nyamagana Butera
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/bakame/http-structured-fields/composer.json b/vendor/bakame/http-structured-fields/composer.json
new file mode 100644
index 000000000..9a8241ee2
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/composer.json
@@ -0,0 +1,106 @@
+{
+ "name": "bakame/http-structured-fields",
+ "description": "A PHP library that parses, validates and serializes HTTP structured fields according to RFC9561 and RFC8941",
+ "type": "library",
+ "keywords": [
+ "http",
+ "http headers",
+ "http trailers",
+ "headers",
+ "trailers",
+ "structured fields",
+ "structured headers",
+ "structured trailers",
+ "structured values",
+ "parser",
+ "serializer",
+ "validation",
+ "rfc8941",
+ "rfc9651"
+ ],
+ "license": "MIT",
+ "authors": [
+ {
+ "name" : "Ignace Nyamagana Butera",
+ "email" : "nyamsprod@gmail.com",
+ "homepage" : "https://github.com/nyamsprod/",
+ "role" : "Developer"
+ }
+ ],
+ "support": {
+ "docs": "https://github.com/bakame-php/http-structured-fields",
+ "issues": "https://github.com/bakame-php/http-structured-fields/issues",
+ "source": "https://github.com/bakame-php/http-structured-fields"
+ },
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nyamsprod"
+ }
+ ],
+ "require": {
+ "php" : "^8.1"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.65.0",
+ "httpwg/structured-field-tests": "*@dev",
+ "phpstan/phpstan": "^2.0.3",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0.1",
+ "phpstan/phpstan-deprecation-rules": "^2.0.1",
+ "phpunit/phpunit": "^10.5.38 || ^11.5.0",
+ "symfony/var-dumper": "^6.4.15 || ^v7.2.0",
+ "bakame/aide-base32": "dev-main",
+ "phpbench/phpbench": "^1.3.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "Bakame\\Http\\StructuredFields\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Bakame\\Http\\StructuredFields\\": "tests/"
+ }
+ },
+ "scripts": {
+ "benchmark": "phpbench run --report=default",
+ "phpcs": "php-cs-fixer fix --dry-run --diff -vvv --allow-risky=yes --ansi",
+ "phpcs:fix": "php-cs-fixer fix -vvv --allow-risky=yes --ansi",
+ "phpstan": "phpstan analyse -c phpstan.neon --ansi --memory-limit 192M",
+ "phpunit": "XDEBUG_MODE=coverage phpunit --coverage-text",
+ "phpunit:min": "phpunit --no-coverage",
+ "test": [
+ "@phpunit",
+ "@phpstan",
+ "@phpcs"
+ ]
+ },
+ "scripts-descriptions": {
+ "benchmark": "Runs parser benchmark",
+ "phpstan": "Runs complete codebase static analysis",
+ "phpunit": "Runs unit and functional testing",
+ "phpcs": "Runs coding style testing",
+ "phpcs:fix": "Fix coding style issues",
+ "test": "Runs all tests"
+ },
+ "repositories": [
+ {
+ "type": "package",
+ "package": {
+ "name": "httpwg/structured-field-tests",
+ "version": "dev-main",
+ "source": {
+ "url": "https://github.com/httpwg/structured-field-tests.git",
+ "type": "git",
+ "reference": "main"
+ }
+ }
+ }
+ ],
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "1.x-dev"
+ }
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Bytes.php b/vendor/bakame/http-structured-fields/src/Bytes.php
new file mode 100644
index 000000000..aa747b76b
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Bytes.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Stringable;
+use Throwable;
+
+use function base64_decode;
+use function base64_encode;
+use function preg_match;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3.5
+ */
+final class Bytes
+{
+ private function __construct(private readonly string $value)
+ {
+ }
+
+ /**
+ * Returns a new instance from a Base64 encoded string.
+ */
+ public static function fromEncoded(Stringable|string $encoded): self
+ {
+ $encoded = (string) $encoded;
+ if (1 !== preg_match('/^[a-z\d+\/=]*$/i', $encoded)) {
+ throw new SyntaxError('The byte sequence '.$encoded.' contains invalid characters.');
+ }
+
+ $decoded = base64_decode($encoded, true);
+ if (false === $decoded) {
+ throw new SyntaxError('Unable to base64 decode the byte sequence '.$encoded);
+ }
+
+ return new self($decoded);
+ }
+
+ public static function tryFromEncoded(Stringable|string $encoded): ?self
+ {
+ try {
+ return self::fromEncoded($encoded);
+ } catch (Throwable) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a new instance from a raw decoded string.
+ */
+ public static function fromDecoded(Stringable|string $decoded): self
+ {
+ return new self((string) $decoded);
+ }
+
+ /**
+ * Returns the decoded string.
+ */
+ public function decoded(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the base64 encoded string.
+ */
+ public function encoded(): string
+ {
+ return base64_encode($this->value);
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->value === $this->value;
+ }
+
+ public function type(): Type
+ {
+ return Type::Bytes;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/DataType.php b/vendor/bakame/http-structured-fields/src/DataType.php
new file mode 100644
index 000000000..8cc5f5395
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/DataType.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Exception;
+use Stringable;
+
+enum DataType: string
+{
+ case List = 'list';
+ case InnerList = 'innerlist';
+ case Parameters = 'parameters';
+ case Dictionary = 'dictionary';
+ case Item = 'item';
+
+ /**
+ * @throws SyntaxError|Exception
+ */
+ public function parse(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): OuterList|InnerList|Parameters|Dictionary|Item
+ {
+ return match ($this) {
+ self::List => OuterList::fromHttpValue($httpValue, $rfc),
+ self::Dictionary => Dictionary::fromHttpValue($httpValue, $rfc),
+ self::Item => Item::fromHttpValue($httpValue, $rfc),
+ self::InnerList => InnerList::fromHttpValue($httpValue, $rfc),
+ self::Parameters => Parameters::fromHttpValue($httpValue, $rfc),
+ };
+ }
+
+ /**
+ * @throws SyntaxError|Exception
+ */
+ public function serialize(iterable $data, Ietf $rfc = Ietf::Rfc9651): string
+ {
+ return (match ($this) {
+ self::List => OuterList::fromPairs($data),
+ self::Dictionary => Dictionary::fromPairs($data),
+ self::Item => Item::fromPair([...$data]),
+ self::InnerList => InnerList::fromPair([...$data]),
+ self::Parameters => Parameters::fromPairs($data),
+ })->toHttpValue($rfc);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Dictionary.php b/vendor/bakame/http-structured-fields/src/Dictionary.php
new file mode 100644
index 000000000..8a318695c
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Dictionary.php
@@ -0,0 +1,788 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\Validation\Violation;
+use CallbackFilterIterator;
+use Countable;
+use DateTimeInterface;
+use Exception;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+use Throwable;
+
+use function array_key_exists;
+use function array_keys;
+use function array_map;
+use function count;
+use function implode;
+use function is_bool;
+use function is_int;
+use function is_iterable;
+use function is_string;
+use function uasort;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.2
+ *
+ * @phpstan-import-type SfMemberInput from StructuredFieldProvider
+ *
+ * @implements ArrayAccess<string, InnerList|Item>
+ * @implements IteratorAggregate<int, array{0:string, 1:InnerList|Item}>
+ */
+final class Dictionary implements ArrayAccess, Countable, IteratorAggregate
+{
+ /** @var array<string, InnerList|Item> */
+ private readonly array $members;
+
+ /**
+ * @param iterable<string, SfMemberInput> $members
+ */
+ private function __construct(iterable $members = [])
+ {
+ $filteredMembers = [];
+ foreach ($members as $key => $member) {
+ $filteredMembers[Key::from($key)->value] = Member::innerListOrItem($member);
+ }
+
+ $this->members = $filteredMembers;
+ }
+
+ /**
+ * Returns a new instance.
+ */
+ public static function new(): self
+ {
+ return new self();
+ }
+
+ /**
+ * Returns a new instance from an associative iterable construct.
+ *
+ * its keys represent the dictionary entry name
+ * its values represent the dictionary entry value
+ *
+ * @param StructuredFieldProvider|iterable<string, SfMemberInput> $members
+ */
+ public static function fromAssociative(StructuredFieldProvider|iterable $members): self
+ {
+ if ($members instanceof StructuredFieldProvider) {
+ $structuredField = $members->toStructuredField();
+
+ return match (true) {
+ $structuredField instanceof Dictionary,
+ $structuredField instanceof Parameters => new self($structuredField->toAssociative()),
+ default => throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a structured field container; '.$structuredField::class.' given.'),
+ };
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * Returns a new instance from a pair iterable construct.
+ *
+ * Each member is composed of an array with two elements
+ * the first member represents the instance entry key
+ * the second member represents the instance entry value
+ *
+ * @param StructuredFieldProvider|Dictionary|Parameters|iterable<array{0:string, 1?:SfMemberInput}> $pairs
+ */
+ public static function fromPairs(StructuredFieldProvider|Dictionary|Parameters|iterable $pairs): self
+ {
+ if ($pairs instanceof StructuredFieldProvider) {
+ $pairs = $pairs->toStructuredField();
+ }
+
+ if (!is_iterable($pairs)) {
+ throw new InvalidArgument('The "'.$pairs::class.'" instance can not be used for creating a .'.self::class.' structured field.');
+ }
+
+ return match (true) {
+ $pairs instanceof Dictionary,
+ $pairs instanceof Parameters => new self($pairs->toAssociative()),
+ default => new self((function (iterable $pairs) {
+ foreach ($pairs as [$key, $member]) {
+ yield $key => Member::innerListOrItemFromPair($member);
+ }
+ })($pairs)),
+ };
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation compliant with RFC9651.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.2
+ *
+ * @throws StructuredFieldError|Throwable
+ */
+ public static function fromRfc9651(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc9651);
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation compliant with RFC8941.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.2
+ *
+ * @throws StructuredFieldError|Throwable
+ */
+ public static function fromRfc8941(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc8941);
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.2
+ *
+ * @throws StructuredFieldError|Exception If the string is not a valid
+ */
+ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self
+ {
+ return self::fromPairs((new Parser($rfc))->parseDictionary($httpValue)); /* @phpstan-ignore-line */
+ }
+
+ public function toRfc9651(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc9651);
+ }
+
+ public function toRfc8941(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc8941);
+ }
+
+ public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ $formatter = static fn (Item|InnerList $member, string $offset): string => match (true) {
+ $member instanceof Item && true === $member->value() => $offset.$member->parameters()->toHttpValue($rfc),
+ default => $offset.'='.$member->toHttpValue($rfc),
+ };
+
+ return implode(', ', array_map($formatter, $this->members, array_keys($this->members)));
+ }
+
+ public function __toString(): string
+ {
+ return $this->toHttpValue();
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->toHttpValue() === $this->toHttpValue();
+ }
+
+ /**
+ * Apply the callback if the given "condition" is (or resolves to) true.
+ *
+ * @param (callable($this): bool)|bool $condition
+ * @param callable($this): (self|null) $onSuccess
+ * @param ?callable($this): (self|null) $onFail
+ */
+ public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): self
+ {
+ if (!is_bool($condition)) {
+ $condition = $condition($this);
+ }
+
+ return match (true) {
+ $condition => $onSuccess($this),
+ null !== $onFail => $onFail($this),
+ default => $this,
+ } ?? $this;
+ }
+
+ public function count(): int
+ {
+ return count($this->members);
+ }
+
+ /**
+ * Tells whether the instance contains no members.
+ */
+ public function isEmpty(): bool
+ {
+ return !$this->isNotEmpty();
+ }
+
+ /**
+ * Tells whether the instance contains any members.
+ */
+ public function isNotEmpty(): bool
+ {
+ return [] !== $this->members;
+ }
+
+ /**
+ * @return Iterator<string, InnerList|Item>
+ */
+ public function toAssociative(): Iterator
+ {
+ yield from $this->members;
+ }
+
+ /**
+ * Returns an iterable construct of dictionary pairs.
+ *
+ * @return Iterator<int, array{0:string, 1:InnerList|Item}>
+ */
+ public function getIterator(): Iterator
+ {
+ foreach ($this->members as $index => $member) {
+ yield [$index, $member];
+ }
+ }
+
+ /**
+ * Returns an ordered list of the instance keys.
+ *
+ * @return array<string>
+ */
+ public function keys(): array
+ {
+ return array_keys($this->members);
+ }
+
+ /**
+ * @return array<int>
+ */
+ public function indices(): array
+ {
+ return array_keys($this->keys());
+ }
+
+ /**
+ * Tells whether the instance contain a members at the specified offsets.
+ */
+ public function hasKeys(string ...$keys): bool
+ {
+ foreach ($keys as $key) {
+ if (!array_key_exists($key, $this->members)) {
+ return false;
+ }
+ }
+
+ return [] !== $keys;
+ }
+
+ /**
+ * Returns true only if the instance only contains the listed keys, false otherwise.
+ *
+ * @param array<string> $keys
+ */
+ public function allowedKeys(array $keys): bool
+ {
+ foreach ($this->members as $key => $member) {
+ if (!in_array($key, $keys, true)) {
+ return false;
+ }
+ }
+
+ return [] !== $keys;
+ }
+
+ /**
+ * @param ?callable(Item|InnerList): (bool|string) $validate
+ *
+ * @throws InvalidOffset|Violation|StructuredFieldError
+ */
+ public function getByKey(string $key, ?callable $validate = null): Item|InnerList
+ {
+ $value = $this->members[$key] ?? throw InvalidOffset::dueToKeyNotFound($key);
+ if (null === $validate || true === ($exceptionMessage = $validate($value))) {
+ return $value;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The parameter '{key}' whose value is '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{key}' => $key, '{value}' => $value->toHttpValue()]));
+ }
+
+ /**
+ * Tells whether a pair is attached to the given index position.
+ */
+ public function hasIndices(int ...$indexes): bool
+ {
+ $max = count($this->members);
+ foreach ($indexes as $index) {
+ if (null === $this->filterIndex($index, $max)) {
+ return false;
+ }
+ }
+
+ return [] !== $indexes;
+ }
+
+ /**
+ * Filters and format instance index.
+ */
+ private function filterIndex(int $index, ?int $max = null): ?int
+ {
+ $max ??= count($this->members);
+
+ return match (true) {
+ [] === $this->members,
+ 0 > $max + $index,
+ 0 > $max - $index - 1 => null,
+ 0 > $index => $max + $index,
+ default => $index,
+ };
+ }
+
+ /**
+ * Returns the item or the inner-list and its key as attached to the given
+ * collection according to their index position otherwise throw.
+ *
+ * @param ?callable(Item|InnerList, string): (bool|string) $validate
+ *
+ * @throws InvalidOffset|Violation|StructuredFieldError
+ *
+ * @return array{0:string, 1:InnerList|Item}
+ */
+ public function getByIndex(int $index, ?callable $validate = null): array
+ {
+ $foundOffset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ foreach ($this as $offset => $pair) {
+ if ($offset === $foundOffset) {
+ break;
+ }
+ }
+
+ if (!isset($pair)) {
+ throw InvalidOffset::dueToIndexNotFound($index);
+ }
+
+ if (null === $validate || true === ($exceptionMessage = $validate($pair[1], $pair[0]))) {
+ return $pair;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The member at position '{index}' whose key is '{key}' with the value '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{index}' => $index, '{key}' => $pair[0], '{value}' => $pair[1]->toHttpValue()]));
+ }
+
+ /**
+ * Returns the key associated with the given index or null otherwise.
+ */
+ public function indexByKey(string $key): ?int
+ {
+ foreach ($this as $index => $member) {
+ if ($key === $member[0]) {
+ return $index;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the index associated with the given key or null otherwise.
+ */
+ public function keyByIndex(int $index): ?string
+ {
+ $index = $this->filterIndex($index);
+ if (null === $index) {
+ return null;
+ }
+
+ foreach ($this as $offset => $member) {
+ if ($offset === $index) {
+ return $member[0];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the first member whether it is an item or an inner-list and its key as attached to the given
+ * collection according to their index position otherwise returns an empty array.
+ *
+ * @return array{0:string, 1:InnerList|Item}|array{}
+ */
+ public function first(): array
+ {
+ try {
+ return $this->getByIndex(0);
+ } catch (StructuredFieldError) {
+ return [];
+ }
+ }
+
+ /**
+ * Returns the first member whether it is an item or an inner-list and its key as attached to the given
+ * collection according to their index position otherwise returns an empty array.
+ *
+ * @return array{0:string, 1:InnerList|Item}|array{}
+ */
+ public function last(): array
+ {
+ try {
+ return $this->getByIndex(-1);
+ } catch (StructuredFieldError) {
+ return [];
+ }
+ }
+
+ /**
+ * Adds a member at the end of the instance otherwise updates the value associated with the key if already present.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param SfMemberInput $member
+ *
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function add(
+ string $key,
+ iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $members = $this->members;
+ $members[Key::from($key)->value] = Member::innerListOrItem($member);
+
+ return $this->newInstance($members);
+ }
+
+ /**
+ * @param array<string, InnerList|Item> $members
+ */
+ private function newInstance(array $members): self
+ {
+ foreach ($members as $offset => $member) {
+ if (!isset($this->members[$offset]) || !$this->members[$offset]->equals($member)) {
+ return new self($members);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Deletes members associated with the list of submitted keys.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ */
+ private function remove(string|int ...$offsets): self
+ {
+ if ([] === $this->members || [] === $offsets) {
+ return $this;
+ }
+
+ $keys = array_keys($this->members);
+ $max = count($keys);
+ $reducer = fn (array $carry, string|int $key): array => match (true) {
+ is_string($key) && (false !== ($position = array_search($key, $keys, true))),
+ is_int($key) && (null !== ($position = $this->filterIndex($key, $max))) => [$position => true] + $carry,
+ default => $carry,
+ };
+
+ $indices = array_reduce($offsets, $reducer, []);
+
+ return match (true) {
+ [] === $indices => $this,
+ $max === count($indices) => self::new(),
+ default => self::fromPairs((function (array $offsets) {
+ foreach ($this->getIterator() as $offset => $pair) {
+ if (!array_key_exists($offset, $offsets)) {
+ yield $pair;
+ }
+ }
+ })($indices)),
+ };
+ }
+
+ /**
+ * Deletes members associated with the list using the member pair offset.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ */
+ public function removeByIndices(int ...$indices): self
+ {
+ return $this->remove(...$indices);
+ }
+
+ /**
+ * Deletes members associated with the list using the member key.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ */
+ public function removeByKeys(string ...$keys): self
+ {
+ return $this->remove(...$keys);
+ }
+
+ /**
+ * Adds a member at the end of the instance and deletes any previous reference to the key if present.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param SfMemberInput $member
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function append(
+ string $key,
+ iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $members = $this->members;
+ unset($members[$key]);
+
+ return $this->newInstance([...$members, Key::from($key)->value => Member::innerListOrItem($member)]);
+ }
+
+ /**
+ * Adds a member at the beginning of the instance and deletes any previous reference to the key if present.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param SfMemberInput $member
+ *
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function prepend(
+ string $key,
+ iterable|StructuredFieldProvider|Dictionary|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $members = $this->members;
+ unset($members[$key]);
+
+ return $this->newInstance([Key::from($key)->value => Member::innerListOrItem($member), ...$members]);
+ }
+
+ /**
+ * Inserts pairs at the end of the container.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param array{0:string, 1:SfMemberInput} ...$pairs
+ */
+ public function push(array ...$pairs): self
+ {
+ return match (true) {
+ [] === $pairs => $this,
+ default => self::fromPairs((function (iterable $pairs) {
+ yield from $this->getIterator();
+ yield from $pairs;
+ })($pairs)),
+ };
+ }
+
+ /**
+ * Inserts pairs at the beginning of the container.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param array{0:string, 1:SfMemberInput} ...$pairs
+ */
+ public function unshift(array ...$pairs): self
+ {
+ return match (true) {
+ [] === $pairs => $this,
+ default => self::fromPairs((function (iterable $pairs) {
+ yield from $pairs;
+ yield from $this->getIterator();
+ })($pairs)),
+ };
+ }
+
+ /**
+ * Insert a member pair using its offset.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param array{0:string, 1:SfMemberInput} ...$members
+ */
+ public function insert(int $index, array ...$members): self
+ {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+
+ return match (true) {
+ [] === $members => $this,
+ 0 === $offset => $this->unshift(...$members),
+ count($this->members) === $offset => $this->push(...$members),
+ default => (function (Iterator $newMembers) use ($offset, $members) {
+ $newMembers = iterator_to_array($newMembers);
+ array_splice($newMembers, $offset, 0, $members);
+
+ return self::fromPairs($newMembers);
+ })($this->getIterator()),
+ };
+ }
+
+ /**
+ * Replace a member pair using its offset.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param array{0:string, 1:SfMemberInput} $pair
+ */
+ public function replace(int $index, array $pair): self
+ {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ $pair[1] = Member::innerListOrItem($pair[1]);
+ $pairs = iterator_to_array($this->getIterator());
+
+ return match (true) {
+ $pairs[$offset][0] === $pair[0] && $pairs[$offset][1]->equals($pair[1]) => $this,
+ default => self::fromPairs(array_replace($pairs, [$offset => $pair])),
+ };
+ }
+
+ /**
+ * Merges multiple instances using iterable associative structures.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param StructuredFieldProvider|Dictionary|Parameters|iterable<string, SfMemberInput> ...$others
+ */
+ public function mergeAssociative(StructuredFieldProvider|iterable ...$others): self
+ {
+ $members = $this->members;
+ foreach ($others as $other) {
+ if ($other instanceof StructuredFieldProvider) {
+ $other = $other->toStructuredField();
+ if (!is_iterable($other)) {
+ throw new InvalidArgument('The "'.$other::class.'" instance can not be used for creating a .'.self::class.' structured field.');
+ }
+ }
+
+ if ($other instanceof self || $other instanceof Parameters) {
+ $other = $other->toAssociative();
+ }
+
+ foreach ($other as $key => $value) {
+ $members[$key] = $value;
+ }
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * Merges multiple instances using iterable pairs.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param StructuredFieldProvider|Dictionary|Parameters|iterable<array{0:string, 1:SfMemberInput}> ...$others
+ */
+ public function mergePairs(StructuredFieldProvider|Dictionary|Parameters|iterable ...$others): self
+ {
+ $members = $this->members;
+ foreach ($others as $other) {
+ if (!$other instanceof self) {
+ $other = self::fromPairs($other);
+ }
+ foreach ($other->toAssociative() as $key => $value) {
+ $members[$key] = $value;
+ }
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * @param string $offset
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->hasKeys($offset);
+ }
+
+ /**
+ * @param string $offset
+ *
+ * @throws StructuredFieldError
+ */
+ public function offsetGet(mixed $offset): InnerList|Item
+ {
+ return $this->getByKey($offset);
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ /**
+ * Run a map over each container members.
+ *
+ * @template TMap
+ *
+ * @param callable(array{0:string, 1:Item|InnerList}, int): TMap $callback
+ *
+ * @return Iterator<TMap>
+ */
+ public function map(callable $callback): Iterator
+ {
+ foreach ($this as $offset => $member) {
+ yield ($callback)($member, $offset);
+ }
+ }
+
+ /**
+ * @param callable(TInitial|null, array{0:string, 1:Item|InnerList}, int): TInitial $callback
+ * @param TInitial|null $initial
+ *
+ * @template TInitial
+ *
+ * @return TInitial|null
+ */
+ public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ foreach ($this as $offset => $pair) {
+ $initial = $callback($initial, $pair, $offset);
+ }
+
+ return $initial;
+ }
+
+ /**
+ * Run a filter over each container members.
+ *
+ * @param callable(array{0:string, 1:InnerList|Item}, int): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ return self::fromPairs(new CallbackFilterIterator($this, $callback));
+ }
+
+ /**
+ * Sort a container by value using a callback.
+ *
+ * @param callable(array{0:string, 1:InnerList|Item}, array{0:string, 1:InnerList|Item}): int $callback
+ */
+ public function sort(callable $callback): self
+ {
+ $members = iterator_to_array($this);
+ uasort($members, $callback);
+
+ return self::fromPairs($members);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/DisplayString.php b/vendor/bakame/http-structured-fields/src/DisplayString.php
new file mode 100644
index 000000000..361f91ec2
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/DisplayString.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Stringable;
+use Throwable;
+
+use function preg_match;
+use function preg_replace_callback;
+use function rawurldecode;
+use function rawurlencode;
+use function str_contains;
+
+/**
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-sfbis#section-4.2.10
+ */
+final class DisplayString
+{
+ private function __construct(private readonly string $value)
+ {
+ }
+
+ public static function tryFromEncoded(Stringable|string $encoded): ?self
+ {
+ try {
+ return self::fromEncoded($encoded);
+ } catch (Throwable) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a new instance from a Base64 encoded string.
+ */
+ public static function fromEncoded(Stringable|string $encoded): self
+ {
+ $encoded = (string) $encoded;
+
+ if (1 === preg_match('/[^\x20-\x7E]/i', $encoded)) {
+ throw new SyntaxError('The display string '.$encoded.' contains invalid characters.');
+ }
+
+ if (!str_contains($encoded, '%')) {
+ return new self($encoded);
+ }
+
+ if (1 === preg_match('/%(?![0-9a-f]{2})/', $encoded)) {
+ throw new SyntaxError('The display string '.$encoded.' contains invalid utf-8 encoded sequence.');
+ }
+
+ $decoded = (string) preg_replace_callback(
+ ',%[a-f0-9]{2},',
+ fn (array $matches): string => rawurldecode($matches[0]),
+ $encoded
+ );
+
+ if (1 !== preg_match('//u', $decoded)) {
+ throw new SyntaxError('The display string '.$encoded.' contains invalid characters.');
+ }
+
+ return new self($decoded);
+ }
+
+ /**
+ * Returns a new instance from a raw decoded string.
+ */
+ public static function fromDecoded(Stringable|string $decoded): self
+ {
+ return new self((string) $decoded);
+ }
+
+ /**
+ * Returns the decoded string.
+ */
+ public function decoded(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the base64 encoded string.
+ */
+ public function encoded(): string
+ {
+ return (string) preg_replace_callback(
+ '/[%"\x00-\x1F\x7F-\xFF]/',
+ static fn (array $matches): string => strtolower(rawurlencode($matches[0])),
+ $this->value
+ );
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->value === $this->value;
+ }
+
+ public function type(): Type
+ {
+ return Type::DisplayString;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/ForbiddenOperation.php b/vendor/bakame/http-structured-fields/src/ForbiddenOperation.php
new file mode 100644
index 000000000..f1edbdef4
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/ForbiddenOperation.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use LogicException;
+
+final class ForbiddenOperation extends LogicException implements StructuredFieldError
+{
+}
diff --git a/vendor/bakame/http-structured-fields/src/Ietf.php b/vendor/bakame/http-structured-fields/src/Ietf.php
new file mode 100644
index 000000000..a98e16e20
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Ietf.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use DateTimeImmutable;
+use DateTimeZone;
+
+enum Ietf
+{
+ case Rfc8941;
+ case Rfc9651;
+
+ public function uri(): string
+ {
+ return match ($this) {
+ self::Rfc9651 => 'https://www.rfc-editor.org/rfc/rfc9651.html',
+ self::Rfc8941 => 'https://www.rfc-editor.org/rfc/rfc8941.html',
+ };
+ }
+
+ public function publishedAt(): DateTimeImmutable
+ {
+ return new DateTimeImmutable(match ($this) {
+ self::Rfc9651 => '2024-09-01',
+ self::Rfc8941 => '2021-02-01',
+ }, new DateTimeZone('UTC'));
+ }
+
+ public function isActive(): bool
+ {
+ return self::Rfc9651 === $this;
+ }
+
+ public function isObsolete(): bool
+ {
+ return !$this->isActive();
+ }
+
+ public function supports(mixed $value): bool
+ {
+ if ($value instanceof StructuredFieldProvider) {
+ $value = $value->toStructuredField();
+ }
+
+ if ($value instanceof OuterList ||
+ $value instanceof InnerList ||
+ $value instanceof Dictionary ||
+ $value instanceof Parameters ||
+ $value instanceof Item
+ ) {
+ try {
+ $value->toHttpValue($this);
+
+ return true;
+ } catch (MissingFeature) {
+ return false;
+ }
+ }
+
+ if (!$value instanceof Type) {
+ $value = Type::tryFromVariable($value);
+ }
+
+ return match ($value) {
+ null => false,
+ Type::DisplayString,
+ Type::Date => self::Rfc8941 !== $this,
+ default => true,
+ };
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/InnerList.php b/vendor/bakame/http-structured-fields/src/InnerList.php
new file mode 100644
index 000000000..8a9a3e734
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/InnerList.php
@@ -0,0 +1,478 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\Validation\Violation;
+use Countable;
+use DateTimeInterface;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+
+use function array_filter;
+use function array_is_list;
+use function array_map;
+use function array_replace;
+use function array_splice;
+use function array_values;
+use function count;
+use function implode;
+use function uasort;
+
+use const ARRAY_FILTER_USE_BOTH;
+use const ARRAY_FILTER_USE_KEY;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.1
+ *
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ * @phpstan-import-type SfTypeInput from StructuredFieldProvider
+ * @phpstan-import-type SfItemInput from StructuredFieldProvider
+ * @phpstan-import-type SfItemPair from StructuredFieldProvider
+ * @phpstan-import-type SfInnerListPair from StructuredFieldProvider
+ * @phpstan-import-type SfParameterInput from StructuredFieldProvider
+ *
+ * @implements ArrayAccess<int, Item>
+ * @implements IteratorAggregate<int, Item>
+ */
+final class InnerList implements ArrayAccess, Countable, IteratorAggregate
+{
+ use ParameterAccess;
+
+ /** @var list<Item> */
+ private readonly array $members;
+ private readonly Parameters $parameters;
+
+ /**
+ * @param iterable<SfItemInput|SfItemPair> $members
+ */
+ private function __construct(iterable $members, ?Parameters $parameters = null)
+ {
+ $this->members = array_map(Member::item(...), array_values([...$members]));
+ $this->parameters = $parameters ?? Parameters::new();
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1
+ */
+ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self
+ {
+ return self::fromPair((new Parser($rfc))->parseInnerList($httpValue));
+ }
+
+ /**
+ * Returns a new instance with an iter.
+ *
+ * @param iterable<SfItemInput> $value
+ * @param Parameters|iterable<string, SfItemInput> $parameters
+ */
+ public static function fromAssociative(
+ iterable $value,
+ StructuredFieldProvider|Parameters|iterable $parameters
+ ): self {
+ if ($parameters instanceof StructuredFieldProvider) {
+ $parameters = $parameters->toStructuredField();
+ if (!$parameters instanceof Parameters) {
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.');
+ }
+ }
+
+ if (!$parameters instanceof Parameters) {
+ return new self($value, Parameters::fromAssociative($parameters));
+ }
+
+ return new self($value, $parameters);
+ }
+
+ /**
+ * @param array{0:iterable<SfItemInput>, 1?:Parameters|SfParameterInput}|array<mixed> $pair
+ */
+ public static function fromPair(array $pair = []): self
+ {
+ if ([] === $pair) {
+ return self::new();
+ }
+
+ if (!array_is_list($pair) || 2 < count($pair)) {
+ throw new SyntaxError('The pair must be represented by an non-empty array as a list containing at most 2 members.');
+ }
+
+ if (1 === count($pair)) {
+ return new self($pair[0]);
+ }
+
+ if ($pair[1] instanceof StructuredFieldProvider) {
+ $pair[1] = $pair[1]->toStructuredField();
+ if (!$pair[1] instanceof Parameters) {
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$pair[1]::class.' given.');
+ }
+ }
+
+ if (!$pair[1] instanceof Parameters) {
+ return new self($pair[0], Parameters::fromPairs($pair[1]));
+ }
+
+ return new self($pair[0], $pair[1]);
+ }
+
+ /**
+ * Returns a new instance.
+ *
+ * @param StructuredFieldProvider|Item|SfTypeInput|SfItemPair ...$members
+ */
+ public static function new(
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|array|string|int|float|bool ...$members
+ ): self {
+ return new self($members);
+ }
+
+ public static function fromRfc9651(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc9651);
+ }
+
+ public static function fromRfc8941(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc8941);
+ }
+
+ public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ return '('.implode(' ', array_map(fn (Item $value): string => $value->toHttpValue($rfc), $this->members)).')'.$this->parameters->toHttpValue($rfc);
+ }
+
+ public function toRfc9651(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc9651);
+ }
+
+ public function toRfc8941(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc8941);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toHttpValue();
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->toHttpValue() === $this->toHttpValue();
+ }
+
+ /**
+ * Apply the callback if the given "condition" is (or resolves to) true.
+ *
+ * @param (callable($this): bool)|bool $condition
+ * @param callable($this): (self|null) $onSuccess
+ * @param ?callable($this): (self|null) $onFail
+ */
+ public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): self
+ {
+ if (!is_bool($condition)) {
+ $condition = $condition($this);
+ }
+
+ return match (true) {
+ $condition => $onSuccess($this),
+ null !== $onFail => $onFail($this),
+ default => $this,
+ } ?? $this;
+ }
+
+ /**
+ * @return array{0:list<Item>, 1:Parameters}
+ */
+ public function toPair(): array
+ {
+ return [$this->members, $this->parameters];
+ }
+
+ public function getIterator(): Iterator
+ {
+ yield from $this->members;
+ }
+
+ public function count(): int
+ {
+ return count($this->members);
+ }
+
+ public function isEmpty(): bool
+ {
+ return !$this->isNotEmpty();
+ }
+
+ public function isNotEmpty(): bool
+ {
+ return [] !== $this->members;
+ }
+
+ /**
+ * @return array<int>
+ */
+ public function indices(): array
+ {
+ return array_keys($this->members);
+ }
+
+ public function hasIndices(int ...$indices): bool
+ {
+ $max = count($this->members);
+ foreach ($indices as $offset) {
+ if (null === $this->filterIndex($offset, $max)) {
+ return false;
+ }
+ }
+
+ return [] !== $indices;
+ }
+
+ private function filterIndex(int $index, ?int $max = null): ?int
+ {
+ $max ??= count($this->members);
+
+ return match (true) {
+ [] === $this->members,
+ 0 > $max + $index,
+ 0 > $max - $index - 1 => null,
+ 0 > $index => $max + $index,
+ default => $index,
+ };
+ }
+
+ /**
+ * @param ?callable(Item): (bool|string) $validate
+ *
+ * @throws SyntaxError|Violation|StructuredFieldError
+ */
+ public function getByIndex(int $index, ?callable $validate = null): Item
+ {
+ $value = $this->members[$this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index)];
+ if (null === $validate) {
+ return $value;
+ }
+
+ if (true === ($exceptionMessage = $validate($value))) {
+ return $value;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The item at '{index}' whose value is '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{index}' => $index, '{value}' => $value->toHttpValue()]));
+ }
+
+ public function first(): ?Item
+ {
+ return $this->members[0] ?? null;
+ }
+
+ public function last(): ?Item
+ {
+ return $this->members[$this->filterIndex(-1)] ?? null;
+ }
+
+ public function withParameters(StructuredFieldProvider|Parameters $parameters): static
+ {
+ if ($parameters instanceof StructuredFieldProvider) {
+ $parameters = $parameters->toStructuredField();
+ if (!$parameters instanceof Parameters) {
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.');
+ }
+ }
+
+ return $this->parameters->equals($parameters) ? $this : new self($this->members, $parameters);
+ }
+
+ /**
+ * Inserts members at the beginning of the list.
+ */
+ public function unshift(
+ StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $membersToAdd = array_reduce(
+ $members,
+ function (array $carry, $member) {
+ if ($member instanceof StructuredFieldProvider) {
+ $member = $member->toStructuredField();
+ }
+
+ return [...$carry, ...$member instanceof InnerList ? [...$member] : [$member]];
+ },
+ []
+ );
+
+ return match (true) {
+ [] === $membersToAdd => $this,
+ default => new self([...array_values($membersToAdd), ...$this->members], $this->parameters),
+ };
+ }
+
+ /**
+ * Inserts members at the end of the list.
+ */
+ public function push(
+ StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $membersToAdd = array_reduce(
+ $members,
+ function (array $carry, $member) {
+ if ($member instanceof StructuredFieldProvider) {
+ $member = $member->toStructuredField();
+ }
+
+ return [...$carry, ...$member instanceof InnerList ? [...$member] : [$member]];
+ },
+ []
+ );
+
+ return match (true) {
+ [] === $membersToAdd => $this,
+ default => new self([...$this->members, ...array_values($membersToAdd)], $this->parameters),
+ };
+ }
+
+ /**
+ * Inserts members starting at the given index.
+ *
+ * @throws InvalidOffset If the index does not exist
+ */
+ public function insert(
+ int $index,
+ StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+
+ return match (true) {
+ 0 === $offset => $this->unshift(...$members),
+ count($this->members) === $offset => $this->push(...$members),
+ [] === $members => $this,
+ default => (function (array $newMembers) use ($offset, $members) {
+ array_splice($newMembers, $offset, 0, $members);
+
+ return new self($newMembers, $this->parameters);
+ })($this->members),
+ };
+ }
+
+ public function replace(
+ int $index,
+ StructuredFieldProvider|OuterList|Dictionary|InnerList|Parameters|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ $member = Member::item($member);
+
+ return match (true) {
+ $member->equals($this->members[$offset]) => $this,
+ default => new self(array_replace($this->members, [$offset => $member]), $this->parameters),
+ };
+ }
+
+ public function removeByIndices(int ...$indices): self
+ {
+ $max = count($this->members);
+ $indices = array_filter(
+ array_map(fn (int $index): ?int => $this->filterIndex($index, $max), $indices),
+ fn (?int $index): bool => null !== $index
+ );
+
+ return match (true) {
+ [] === $indices => $this,
+ count($indices) === $max => self::new(),
+ default => new self(array_filter(
+ $this->members,
+ fn (int $offset): bool => !in_array($offset, $indices, true),
+ ARRAY_FILTER_USE_KEY
+ ), $this->parameters),
+ };
+ }
+
+ /**
+ * @param int $offset
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->hasIndices($offset);
+ }
+
+ /**
+ * @param int $offset
+ */
+ public function offsetGet(mixed $offset): Item
+ {
+ return $this->getByIndex($offset);
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ /**
+ * @param callable(Item, int): TMap $callback
+ *
+ * @template TMap
+ *
+ * @return Iterator<TMap>
+ */
+ public function map(callable $callback): Iterator
+ {
+ foreach ($this->members as $offset => $member) {
+ yield ($callback)($member, $offset);
+ }
+ }
+
+ /**
+ * @param callable(TInitial|null, Item, int=): TInitial $callback
+ * @param TInitial|null $initial
+ *
+ * @template TInitial
+ *
+ * @return TInitial|null
+ */
+ public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ foreach ($this->members as $offset => $member) {
+ $initial = $callback($initial, $member, $offset);
+ }
+
+ return $initial;
+ }
+
+ /**
+ * @param callable(Item, int): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ $members = array_filter($this->members, $callback, ARRAY_FILTER_USE_BOTH);
+ if ($members === $this->members) {
+ return $this;
+ }
+
+ return new self($members, $this->parameters);
+ }
+
+ /**
+ * @param callable(Item, Item): int $callback
+ */
+ public function sort(callable $callback): self
+ {
+ $members = $this->members;
+ uasort($members, $callback);
+
+ return new self($members, $this->parameters);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/InvalidArgument.php b/vendor/bakame/http-structured-fields/src/InvalidArgument.php
new file mode 100644
index 000000000..6c7c3344f
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/InvalidArgument.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use InvalidArgumentException;
+
+final class InvalidArgument extends InvalidArgumentException implements StructuredFieldError
+{
+}
diff --git a/vendor/bakame/http-structured-fields/src/InvalidOffset.php b/vendor/bakame/http-structured-fields/src/InvalidOffset.php
new file mode 100644
index 000000000..ab878d98d
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/InvalidOffset.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use OutOfBoundsException;
+
+final class InvalidOffset extends OutOfBoundsException implements StructuredFieldError
+{
+ private function __construct(string $message)
+ {
+ parent::__construct($message);
+ }
+
+ public static function dueToIndexNotFound(string|int $index): self
+ {
+ if (is_string($index)) {
+ return new self('The member index can not be the string "'.$index.'".');
+ }
+
+ return new self('No member exists with the index "'.$index.'".');
+ }
+
+ public static function dueToKeyNotFound(string|int $key): self
+ {
+ if (is_int($key)) {
+ return new self('The member key can not be the integer "'.$key.'".');
+ }
+
+ return new self('No member exists with the key "'.$key.'".');
+ }
+
+ public static function dueToMemberNotFound(string|int $offset): self
+ {
+ return new self('No member exists with the '.(is_int($offset) ? 'index' : 'key').' "'.$offset.'".');
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Item.php b/vendor/bakame/http-structured-fields/src/Item.php
new file mode 100644
index 000000000..6d0434ee5
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Item.php
@@ -0,0 +1,502 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use BadMethodCallException;
+use Bakame\Http\StructuredFields\Validation\Violation;
+use DateTimeImmutable;
+use DateTimeInterface;
+use DateTimeZone;
+use Exception;
+use ReflectionMethod;
+use Stringable;
+use Throwable;
+use TypeError;
+
+use function array_is_list;
+use function count;
+use function in_array;
+
+use const JSON_PRESERVE_ZERO_FRACTION;
+use const PHP_ROUND_HALF_EVEN;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3
+ *
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ * @phpstan-import-type SfItemInput from StructuredFieldProvider
+ * @phpstan-import-type SfItemPair from StructuredFieldProvider
+ * @phpstan-import-type SfTypeInput from StructuredFieldProvider
+ *
+ * @method static ?Item tryFromPair(array{0: SfItemInput, 1?: Parameters|iterable<array{0:string, 1:SfItemInput}>}|array<mixed> $pair) try to create a new instance from a Pair
+ * @method static ?Item tryFromRfc9651(Stringable|string $httpValue) try to create a new instance from a string using RFC9651
+ * @method static ?Item tryFromRfc8941(Stringable|string $httpValue) try to create a new instance from a string using RFC8941
+ * @method static ?Item tryFromHttpValue(Stringable|string $httpValue) try to create a new instance from a string
+ * @method static ?Item tryFromAssociative(Bytes|Token|DisplayString|DateTimeInterface|string|int|float|bool $value, StructuredFieldProvider|Parameters|iterable<string, SfItemInput> $parameters) try to create a new instance from a value and a parameters as associative construct
+ * @method static ?Item tryNew(mixed $value) try to create a new bare instance from a value
+ * @method static ?Item tryFromEncodedBytes(Stringable|string $value) try to create a new instance from an encoded byte sequence
+ * @method static ?Item tryFromDecodedBytes(Stringable|string $value) try to create a new instance from a decoded byte sequence
+ * @method static ?Item tryFromEncodedDisplayString(Stringable|string $value) try to create a new instance from an encoded display string
+ * @method static ?Item tryFromDecodedDisplayString(Stringable|string $value) try to create a new instance from a decoded display string
+ * @method static ?Item tryFromToken(Stringable|string $value) try to create a new instance from a token string
+ * @method static ?Item tryFromTimestamp(int $timestamp) try to create a new instance from a timestamp
+ * @method static ?Item tryFromDateFormat(string $format, string $datetime) try to create a new instance from a date format
+ * @method static ?Item tryFromDateString(string $datetime, DateTimeZone|string|null $timezone = null) try to create a new instance from a date string
+ * @method static ?Item tryFromDate(DateTimeInterface $datetime) try to create a new instance from a DateTimeInterface object
+ * @method static ?Item tryFromDecimal(int|float $value) try to create a new instance from a float
+ * @method static ?Item tryFromInteger(int|float $value) try to create a new instance from an integer
+ */
+final class Item
+{
+ use ParameterAccess;
+
+ private readonly Token|Bytes|DisplayString|DateTimeImmutable|int|float|string|bool $value;
+ private readonly Parameters $parameters;
+ private readonly Type $type;
+
+ private function __construct(Token|Bytes|DisplayString|DateTimeInterface|int|float|string|bool $value, ?Parameters $parameters = null)
+ {
+ if ($value instanceof DateTimeInterface && !$value instanceof DateTimeImmutable) {
+ $value = DateTimeImmutable::createFromInterface($value);
+ }
+
+ $this->value = $value;
+ $this->parameters = $parameters ?? Parameters::new();
+ $this->type = Type::fromVariable($value);
+ }
+
+ /**
+ * @throws BadMethodCallException
+ */
+ public static function __callStatic(string $name, array $arguments): ?self /* @phpstan-ignore-line */
+ {
+ if (!str_starts_with($name, 'try')) {
+ throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" does not exist.');
+ }
+
+ $namedConstructor = lcfirst(substr($name, strlen('try')));
+ if (!method_exists(self::class, $namedConstructor)) {
+ throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" does not exist.');
+ }
+
+ $method = new ReflectionMethod(self::class, $namedConstructor);
+ if (!$method->isPublic() || !$method->isStatic()) {
+ throw new BadMethodCallException('The method "'.self::class.'::'.$name.'" can not be accessed directly.');
+ }
+
+ try {
+ return self::$namedConstructor(...$arguments); /* @phpstan-ignore-line */
+ } catch (Throwable) { /* @phpstan-ignore-line */
+ return null;
+ }
+ }
+
+ public static function fromRfc9651(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc9651);
+ }
+
+ public static function fromRfc8941(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc8941);
+ }
+
+ /**
+ * Returns a new instance from an HTTP Header or Trailer value string
+ * in compliance with a published RFC.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3
+ *
+ * @throws SyntaxError|Exception If the HTTP value can not be parsed
+ */
+ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self
+ {
+ return self::fromPair((new Parser($rfc))->parseItem($httpValue));
+ }
+
+ /**
+ * Returns a new instance from a value type and an iterable of key-value parameters.
+ *
+ * @param StructuredFieldProvider|Parameters|iterable<string, SfItemInput> $parameters
+ *
+ * @throws SyntaxError If the value or the parameters are not valid
+ */
+ public static function fromAssociative(
+ Bytes|Token|DisplayString|DateTimeInterface|string|int|float|bool $value,
+ StructuredFieldProvider|Parameters|iterable $parameters
+ ): self {
+ if ($parameters instanceof StructuredFieldProvider) {
+ $parameters = $parameters->toStructuredField();
+ if ($parameters instanceof Parameters) {
+ return new self($value, $parameters);
+ }
+
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.');
+ }
+
+ if (!$parameters instanceof Parameters) {
+ return new self($value, Parameters::fromAssociative($parameters));
+ }
+
+ return new self($value, $parameters);
+ }
+
+ /**
+ * @param array{0: SfItemInput, 1?: Parameters|iterable<array{0:string, 1:SfItemInput}>}|array<mixed> $pair
+ *
+ * @throws SyntaxError If the pair or its content is not valid.
+ */
+ public static function fromPair(array $pair): self
+ {
+ $nbElements = count($pair);
+ if (!in_array($nbElements, [1, 2], true) || !array_is_list($pair)) {
+ throw new SyntaxError('The pair must be represented by an non-empty array as a list containing exactly 1 or 2 members.');
+ }
+
+ if (1 === $nbElements) {
+ return new self($pair[0]);
+ }
+
+ if ($pair[1] instanceof StructuredFieldProvider) {
+ $pair[1] = $pair[1]->toStructuredField();
+ if ($pair[1] instanceof Parameters) {
+ return new self($pair[0], Parameters::fromPairs($pair[1]));
+ }
+
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$pair[1]::class.' given.');
+ }
+
+ if (!$pair[1] instanceof Parameters) {
+ return new self($pair[0], Parameters::fromPairs($pair[1]));
+ }
+
+ return new self($pair[0], $pair[1]);
+ }
+
+ /**
+ * Returns a new bare instance from value.
+ *
+ * @param SfItemPair|SfItemInput $value
+ *
+ * @throws SyntaxError|TypeError If the value is not valid.
+ */
+ public static function new(mixed $value): self
+ {
+ if (is_array($value)) {
+ return self::fromPair($value);
+ }
+
+ return new self($value); /* @phpstan-ignore-line */
+ }
+
+ /**
+ * Returns a new instance from a string.
+ *
+ * @throws SyntaxError if the string is invalid
+ */
+ public static function fromString(Stringable|string $value): self
+ {
+ return new self((string)$value);
+ }
+
+ /**
+ * Returns a new instance from an encoded byte sequence and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the sequence is invalid
+ */
+ public static function fromEncodedBytes(Stringable|string $value): self
+ {
+ return new self(Bytes::fromEncoded($value));
+ }
+
+ /**
+ * Returns a new instance from a decoded byte sequence and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the sequence is invalid
+ */
+ public static function fromDecodedBytes(Stringable|string $value): self
+ {
+ return new self(Bytes::fromDecoded($value));
+ }
+
+ /**
+ * Returns a new instance from an encoded byte sequence and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the sequence is invalid
+ */
+ public static function fromEncodedDisplayString(Stringable|string $value): self
+ {
+ return new self(DisplayString::fromEncoded($value));
+ }
+
+ /**
+ * Returns a new instance from a decoded byte sequence and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the sequence is invalid
+ */
+ public static function fromDecodedDisplayString(Stringable|string $value): self
+ {
+ return new self(DisplayString::fromDecoded($value));
+ }
+
+ /**
+ * Returns a new instance from a Token and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the token is invalid
+ */
+ public static function fromToken(Stringable|string $value): self
+ {
+ return new self(Token::fromString($value));
+ }
+
+ /**
+ * Returns a new instance from a timestamp and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the timestamp value is not supported
+ */
+ public static function fromTimestamp(int $timestamp): self
+ {
+ return new self((new DateTimeImmutable())->setTimestamp($timestamp));
+ }
+
+ /**
+ * Returns a new instance from a date format its date string representation and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the format is invalid
+ */
+ public static function fromDateFormat(string $format, string $datetime): self
+ {
+ try {
+ $value = DateTimeImmutable::createFromFormat($format, $datetime);
+ } catch (Exception $exception) {
+ throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.', 0, $exception);
+ }
+
+ if (!$value instanceof DateTimeImmutable) {
+ throw new SyntaxError('The date notation `'.$datetime.'` is incompatible with the date format `'.$format.'`.');
+ }
+
+ return new self($value);
+ }
+
+ /**
+ * Returns a new instance from a string parsable by DateTimeImmutable constructor, an optional timezone and an iterable of key-value parameters.
+ *
+ * @throws SyntaxError if the format is invalid
+ */
+ public static function fromDateString(string $datetime, DateTimeZone|string|null $timezone = null): self
+ {
+ $timezone ??= date_default_timezone_get();
+ if (!$timezone instanceof DateTimeZone) {
+ try {
+ $timezone = new DateTimeZone($timezone);
+ } catch (Throwable $exception) {
+ throw new SyntaxError('The timezone could not be instantiated.', 0, $exception);
+ }
+ }
+
+ try {
+ return new self(new DateTimeImmutable($datetime, $timezone));
+ } catch (Throwable $exception) {
+ throw new SyntaxError('Unable to create a '.DateTimeImmutable::class.' instance with the date notation `'.$datetime.'.`', 0, $exception);
+ }
+ }
+
+ /**
+ * Returns a new instance from a DateTineInterface implementing object.
+ *
+ * @throws SyntaxError if the format is invalid
+ */
+ public static function fromDate(DateTimeInterface $datetime): self
+ {
+ return new self($datetime);
+ }
+
+ /**
+ * Returns a new instance from a float value.
+ *
+ * @throws SyntaxError if the format is invalid
+ */
+ public static function fromDecimal(int|float $value): self
+ {
+ return new self((float)$value);
+ }
+
+ /**
+ * Returns a new instance from an integer value.
+ *
+ * @throws SyntaxError if the format is invalid
+ */
+ public static function fromInteger(int|float $value): self
+ {
+ return new self((int)$value);
+ }
+
+ /**
+ * Returns a new instance for the boolean true type.
+ */
+ public static function true(): self
+ {
+ return new self(true);
+ }
+
+ /**
+ * Returns a new instance for the boolean false type.
+ */
+ public static function false(): self
+ {
+ return new self(false);
+ }
+
+ /**
+ * Returns the underlying value.
+ * If a validation rule is provided, an exception will be thrown
+ * if the validation rules does not return true.
+ *
+ * if the validation returns false then a default validation message will be return; otherwise the submitted message string will be returned as is.
+ *
+ * @param ?callable(SfType): (string|bool) $validate
+ *
+ * @throws Violation
+ */
+ public function value(?callable $validate = null): Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool
+ {
+ if (null === $validate) {
+ return $this->value;
+ }
+
+ $exceptionMessage = $validate($this->value);
+ if (true === $exceptionMessage) {
+ return $this->value;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The item value '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{value}' => $this->serialize()]));
+ }
+
+ public function type(): Type
+ {
+ return $this->type;
+ }
+
+ /**
+ * Serialize the Item value according to RFC8941.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.1
+ */
+ public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ return $this->serialize($rfc).$this->parameters->toHttpValue($rfc);
+ }
+
+ /**
+ * Serialize the Item value according to RFC8941.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.1
+ */
+ private function serialize(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ return match (true) {
+ !$rfc->supports($this->type) => throw MissingFeature::dueToLackOfSupport($this->type, $rfc),
+ $this->value instanceof DateTimeImmutable => '@'.$this->value->getTimestamp(),
+ $this->value instanceof Token => $this->value->toString(),
+ $this->value instanceof Bytes => ':'.$this->value->encoded().':',
+ $this->value instanceof DisplayString => '%"'.$this->value->encoded().'"',
+ is_int($this->value) => (string) $this->value,
+ is_float($this->value) => (string) json_encode(round($this->value, 3, PHP_ROUND_HALF_EVEN), JSON_PRESERVE_ZERO_FRACTION),
+ $this->value,
+ false === $this->value => '?'.($this->value ? '1' : '0'),
+ default => '"'.preg_replace('/(["\\\])/', '\\\$1', $this->value).'"',
+ };
+ }
+
+ public function toRfc9651(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc9651);
+ }
+
+ public function toRfc8941(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc8941);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toHttpValue();
+ }
+
+ /**
+ * @return array{0:SfItemInput, 1:Parameters}
+ */
+ public function toPair(): array
+ {
+ return [$this->value, $this->parameters];
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->toHttpValue() === $this->toHttpValue();
+ }
+
+ /**
+ * Apply the callback if the given "condition" is (or resolves to) true.
+ *
+ * @param (callable($this): bool)|bool $condition
+ * @param callable($this): (self|null) $onSuccess
+ * @param ?callable($this): (self|null) $onFail
+ */
+ public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): self
+ {
+ if (!is_bool($condition)) {
+ $condition = $condition($this);
+ }
+
+ return match (true) {
+ $condition => $onSuccess($this),
+ null !== $onFail => $onFail($this),
+ default => $this,
+ } ?? $this;
+ }
+
+ /**
+ * Returns a new instance with the newly associated value.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified value change.
+ *
+ * @throws SyntaxError If the value is invalid or not supported
+ */
+ public function withValue(DateTimeInterface|Bytes|Token|DisplayString|string|int|float|bool $value): self
+ {
+ $isEqual = match (true) {
+ $this->value instanceof Bytes,
+ $this->value instanceof Token,
+ $this->value instanceof DisplayString => $this->value->equals($value),
+ $this->value instanceof DateTimeInterface && $value instanceof DateTimeInterface => $value->getTimestamp() === $this->value->getTimestamp(),
+ default => $value === $this->value,
+ };
+
+ if ($isEqual) {
+ return $this;
+ }
+
+ return new self($value, $this->parameters);
+ }
+
+ public function withParameters(StructuredFieldProvider|Parameters $parameters): static
+ {
+ if ($parameters instanceof StructuredFieldProvider) {
+ $parameters = $parameters->toStructuredField();
+ if (!$parameters instanceof Parameters) {
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Parameters::class.'; '.$parameters::class.' given.');
+ }
+ }
+
+ return $this->parameters->equals($parameters) ? $this : new self($this->value, $parameters);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Key.php b/vendor/bakame/http-structured-fields/src/Key.php
new file mode 100644
index 000000000..e1971426a
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Key.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Bakame\Http\StructuredFields;
+
+use Stringable;
+
+use function preg_match;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.2
+ * @internal normalize HTTP field key
+ */
+final class Key
+{
+ private function __construct(public readonly string $value)
+ {
+ }
+
+ /**
+ * @throws SyntaxError If the string is not a valid HTTP value field key
+ */
+ public static function from(Stringable|string|int $httpValue): self
+ {
+ $key = (string) $httpValue;
+ $instance = self::fromStringBeginning($key);
+ if ($instance->value !== $key) {
+ throw new SyntaxError('No valid http value key could be extracted from "'.$httpValue.'".');
+ }
+
+ return $instance;
+ }
+
+ public static function tryFrom(Stringable|string|int $httpValue): ?self
+ {
+ try {
+ return self::from($httpValue);
+ } catch (SyntaxError $e) {
+ return null;
+ }
+ }
+
+ /**
+ * @throws SyntaxError If the string does not start with a valid HTTP value field key
+ */
+ public static function fromStringBeginning(string $httpValue): self
+ {
+ if (1 !== preg_match('/^(?<key>[a-z*][a-z\d.*_-]*)/', $httpValue, $found)) {
+ throw new SyntaxError('No valid http value key could be extracted from "'.$httpValue.'".');
+ }
+
+ return new self($found['key']);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Member.php b/vendor/bakame/http-structured-fields/src/Member.php
new file mode 100644
index 000000000..ee315b078
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Member.php
@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use function count;
+use function in_array;
+use function is_array;
+use function is_iterable;
+
+/**
+ * @phpstan-import-type SfMemberInput from StructuredFieldProvider
+ * @phpstan-import-type SfItemInput from StructuredFieldProvider
+ * @phpstan-import-type SfItemPair from StructuredFieldProvider
+ * @phpstan-import-type SfInnerListPair from StructuredFieldProvider
+ * @phpstan-import-type SfTypeInput from StructuredFieldProvider
+ *
+ * @internal Validate containers member
+ */
+final class Member
+{
+ /**
+ * @param SfMemberInput $value
+ */
+ public static function innerListOrItem(mixed $value): InnerList|Item
+ {
+ if ($value instanceof StructuredFieldProvider) {
+ $value = $value->toStructuredField();
+ if ($value instanceof Item || $value instanceof InnerList) {
+ return $value;
+ }
+
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$value::class.' given.');
+ }
+
+ return match (true) {
+ $value instanceof InnerList,
+ $value instanceof Item => $value,
+ is_iterable($value) => InnerList::new(...$value),
+ default => Item::new($value),
+ };
+ }
+
+ public static function innerListOrItemFromPair(mixed $value): InnerList|Item
+ {
+ if ($value instanceof StructuredFieldProvider) {
+ $value = $value->toStructuredField();
+ if ($value instanceof Item || $value instanceof InnerList) {
+ return $value;
+ }
+
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.' or an '.InnerList::class.'; '.$value::class.' given.');
+ }
+
+ if ($value instanceof InnerList || $value instanceof Item) {
+ return $value;
+ }
+
+ if (!is_array($value)) {
+ if (is_iterable($value)) {
+ throw new SyntaxError('The value must be an Item value not an iterable.');
+ }
+
+ return Item::new($value); /* @phpstan-ignore-line */
+ }
+
+ if (!array_is_list($value)) {
+ throw new SyntaxError('The pair must be represented by an array as a list.');
+ }
+
+ if ([] === $value) {
+ return InnerList::new();
+ }
+
+ if (!in_array(count($value), [1, 2], true)) {
+ throw new SyntaxError('The pair first member represents its value; the second member is its associated parameters.');
+ }
+
+ return is_iterable($value[0]) ? InnerList::fromPair($value) : Item::fromPair($value);
+ }
+
+ /**
+ * @param SfItemInput|SfItemPair $value
+ */
+ public static function item(mixed $value): Item
+ {
+ if ($value instanceof StructuredFieldProvider) {
+ $value = $value->toStructuredField();
+ if (!$value instanceof Item) {
+ throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a '.Item::class.'; '.$value::class.' given.');
+ }
+
+ return $value;
+ }
+
+ if ($value instanceof Item) {
+ return $value;
+ }
+
+ return Item::new($value);
+ }
+
+ /**
+ * @param SfItemInput|SfItemPair $value
+ */
+ public static function bareItem(mixed $value): Item
+ {
+ $bareItem = self::item($value);
+ if ($bareItem->parameters()->isNotEmpty()) {
+ throw new InvalidArgument('The "'.$bareItem::class.'" instance is not a Bare Item.');
+ }
+
+ return $bareItem;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/MissingFeature.php b/vendor/bakame/http-structured-fields/src/MissingFeature.php
new file mode 100644
index 000000000..03bd8c05f
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/MissingFeature.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+final class MissingFeature extends SyntaxError
+{
+ public static function dueToLackOfSupport(Type $type, Ietf $rfc): self
+ {
+ return new self('The \''.$type->value.'\' type is not handled by '.strtoupper($rfc->name));
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/OuterList.php b/vendor/bakame/http-structured-fields/src/OuterList.php
new file mode 100644
index 000000000..644f1990e
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/OuterList.php
@@ -0,0 +1,430 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\Validation\Violation;
+use Countable;
+use DateTimeInterface;
+use Exception;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+
+use function array_filter;
+use function array_map;
+use function array_replace;
+use function array_splice;
+use function array_values;
+use function count;
+use function implode;
+use function is_iterable;
+use function uasort;
+
+use const ARRAY_FILTER_USE_BOTH;
+use const ARRAY_FILTER_USE_KEY;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#name-lists
+ *
+ * @phpstan-import-type SfMemberInput from StructuredFieldProvider
+ * @phpstan-import-type SfInnerListPair from StructuredFieldProvider
+ * @phpstan-import-type SfItemPair from StructuredFieldProvider
+ *
+ * @implements ArrayAccess<int, InnerList|Item>
+ * @implements IteratorAggregate<int, InnerList|Item>
+ */
+final class OuterList implements ArrayAccess, Countable, IteratorAggregate
+{
+ /** @var list<InnerList|Item> */
+ private readonly array $members;
+
+ /**
+ * @param SfMemberInput ...$members
+ */
+ private function __construct(
+ iterable|StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ) {
+ $this->members = array_map(Member::innerListOrItem(...), array_values([...$members]));
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1
+ *
+ * @throws SyntaxError|Exception
+ */
+ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self
+ {
+ return self::fromPairs((new Parser($rfc))->parseList($httpValue)); /* @phpstan-ignore-line */
+ }
+
+ /**
+ * @param StructuredFieldProvider|iterable<SfInnerListPair|SfItemPair> $pairs
+ */
+ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self
+ {
+ if ($pairs instanceof StructuredFieldProvider) {
+ $pairs = $pairs->toStructuredField();
+ }
+
+ if (!is_iterable($pairs)) {
+ throw new InvalidArgument('The "'.$pairs::class.'" instance can not be used for creating a .'.self::class.' structured field.');
+ }
+
+ return match (true) {
+ $pairs instanceof OuterList,
+ $pairs instanceof InnerList => new self($pairs),
+ default => new self(...(function (iterable $pairs) {
+ foreach ($pairs as $member) {
+ yield Member::innerListOrItemFromPair($member);
+ }
+ })($pairs)),
+ };
+ }
+
+ /**
+ * @param StructuredFieldProvider|SfInnerListPair|SfItemPair|SfMemberInput ...$members
+ */
+ public static function new(iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members): self
+ {
+ return self::fromPairs($members); /* @phpstan-ignore-line*/
+ }
+
+ public static function fromRfc9651(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc9651);
+ }
+
+ public static function fromRfc8941(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc8941);
+ }
+
+ public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ return implode(', ', array_map(fn (Item|InnerList $member): string => $member->toHttpValue($rfc), $this->members));
+ }
+
+ public function toRfc9651(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc9651);
+ }
+
+ public function toRfc8941(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc8941);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toHttpValue();
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->toHttpValue() === $this->toHttpValue();
+ }
+
+ /**
+ * Apply the callback if the given "condition" is (or resolves to) true.
+ *
+ * @param (callable($this): bool)|bool $condition
+ * @param callable($this): (self|null) $onSuccess
+ * @param ?callable($this): (self|null) $onFail
+ */
+ public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): self
+ {
+ if (!is_bool($condition)) {
+ $condition = $condition($this);
+ }
+
+ return match (true) {
+ $condition => $onSuccess($this),
+ null !== $onFail => $onFail($this),
+ default => $this,
+ } ?? $this;
+ }
+
+ public function getIterator(): Iterator
+ {
+ yield from $this->members;
+ }
+
+ public function count(): int
+ {
+ return count($this->members);
+ }
+
+ public function isEmpty(): bool
+ {
+ return !$this->isNotEmpty();
+ }
+
+ public function isNotEmpty(): bool
+ {
+ return [] !== $this->members;
+ }
+
+ /**
+ * @return array<int>
+ */
+ public function indices(): array
+ {
+ return array_keys($this->members);
+ }
+
+ public function hasIndices(int ...$indices): bool
+ {
+ $max = count($this->members);
+ foreach ($indices as $index) {
+ if (null === $this->filterIndex($index, $max)) {
+ return false;
+ }
+ }
+
+ return [] !== $indices;
+ }
+
+ private function filterIndex(int $index, ?int $max = null): ?int
+ {
+ $max ??= count($this->members);
+
+ return match (true) {
+ [] === $this->members,
+ 0 > $max + $index,
+ 0 > $max - $index - 1 => null,
+ 0 > $index => $max + $index,
+ default => $index,
+ };
+ }
+
+ /**
+ * @param ?callable(InnerList|Item): (bool|string) $validate
+ *
+ * @throws SyntaxError|Violation|StructuredFieldError
+ */
+ public function getByIndex(int $index, ?callable $validate = null): InnerList|Item
+ {
+ $value = $this->members[$this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index)];
+ if (null === $validate) {
+ return $value;
+ }
+
+ if (true === ($exceptionMessage = $validate($value))) {
+ return $value;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The member at position '{index}' whose value is '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{index}' => $index, '{value}' => $value->toHttpValue()]));
+ }
+
+ public function first(): InnerList|Item|null
+ {
+ return $this->members[0] ?? null;
+ }
+
+ public function last(): InnerList|Item|null
+ {
+ return $this->members[$this->filterIndex(-1)] ?? null;
+ }
+
+ /**
+ * Inserts members at the beginning of the list.
+ *
+ * @param SfMemberInput ...$members
+ */
+ public function unshift(
+ StructuredFieldProvider|InnerList|Item|iterable|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $membersToAdd = array_reduce(
+ $members,
+ function (array $carry, $member) {
+ if ($member instanceof StructuredFieldProvider) {
+ $member = $member->toStructuredField();
+ }
+
+ return [...$carry, ...$member instanceof InnerList ? [...$member] : [$member]];
+ },
+ []
+ );
+
+ return match (true) {
+ [] === $membersToAdd => $this,
+ default => new self(...array_values($membersToAdd), ...$this->members),
+ };
+ }
+
+ /**
+ * Inserts members at the end of the list.
+ *
+ * @param SfMemberInput ...$members
+ */
+ public function push(
+ iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $membersToAdd = array_reduce(
+ $members,
+ function (array $carry, $member) {
+ if ($member instanceof StructuredFieldProvider) {
+ $member = $member->toStructuredField();
+ }
+
+ return [...$carry, ...$member instanceof InnerList ? [...$member] : [$member]];
+ },
+ []
+ );
+
+ return match (true) {
+ [] === $membersToAdd => $this,
+ default => new self(...$this->members, ...array_values($membersToAdd)),
+ };
+ }
+
+ /**
+ * Inserts members starting at the given index.
+ *
+ * @param SfMemberInput ...$members
+ *
+ * @throws InvalidOffset If the index does not exist
+ */
+ public function insert(
+ int $index,
+ iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool ...$members
+ ): self {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+
+ return match (true) {
+ 0 === $offset => $this->unshift(...$members),
+ count($this->members) === $offset => $this->push(...$members),
+ [] === $members => $this,
+ default => (function (array $newMembers) use ($offset, $members) {
+ array_splice($newMembers, $offset, 0, $members);
+
+ return new self(...$newMembers);
+ })($this->members),
+ };
+ }
+
+ /**
+ * @param SfMemberInput $member
+ */
+ public function replace(
+ int $index,
+ iterable|StructuredFieldProvider|InnerList|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ $member = Member::innerListOrItem($member);
+
+ return match (true) {
+ $member->equals($this->members[$offset]) => $this,
+ default => new self(...array_replace($this->members, [$offset => $member])),
+ };
+ }
+
+ public function removeByIndices(int ...$indices): self
+ {
+ $max = count($this->members);
+ $offsets = array_filter(
+ array_map(fn (int $index): ?int => $this->filterIndex($index, $max), $indices),
+ fn (?int $index): bool => null !== $index
+ );
+
+ return match (true) {
+ [] === $offsets => $this,
+ $max === count($offsets) => new self(),
+ default => new self(...array_filter(
+ $this->members,
+ fn (int $index): bool => !in_array($index, $offsets, true),
+ ARRAY_FILTER_USE_KEY
+ )),
+ };
+ }
+
+ /**
+ * @param int $offset
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->hasIndices($offset);
+ }
+
+ /**
+ * @param int $offset
+ */
+ public function offsetGet(mixed $offset): InnerList|Item
+ {
+ return $this->getByIndex($offset);
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ /**
+ * @param callable(InnerList|Item, int): TMap $callback
+ *
+ * @template TMap
+ *
+ * @return Iterator<TMap>
+ */
+ public function map(callable $callback): Iterator
+ {
+ foreach ($this->members as $offset => $member) {
+ yield ($callback)($member, $offset);
+ }
+ }
+
+ /**
+ * @param callable(TInitial|null, InnerList|Item, int): TInitial $callback
+ * @param TInitial|null $initial
+ *
+ * @template TInitial
+ *
+ * @return TInitial|null
+ */
+ public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ foreach ($this->members as $offset => $member) {
+ $initial = $callback($initial, $member, $offset);
+ }
+
+ return $initial;
+ }
+
+ /**
+ * @param callable(InnerList|Item, int): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ $members = array_filter($this->members, $callback, ARRAY_FILTER_USE_BOTH);
+ if ($members === $this->members) {
+ return $this;
+ }
+
+ return new self(...$members);
+ }
+
+ /**
+ * @param callable(InnerList|Item, InnerList|Item): int $callback
+ */
+ public function sort(callable $callback): self
+ {
+ $members = $this->members;
+ uasort($members, $callback);
+
+ return new self(...$members);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/ParameterAccess.php b/vendor/bakame/http-structured-fields/src/ParameterAccess.php
new file mode 100644
index 000000000..9c71f0955
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/ParameterAccess.php
@@ -0,0 +1,260 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Bakame\Http\StructuredFields\Validation\Violation;
+use DateTimeImmutable;
+use DateTimeInterface;
+
+/**
+ * Common manipulation methods used when interacting with an object
+ * with a Parameters instance attached to it.
+ *
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ * @phpstan-import-type SfItemInput from StructuredFieldProvider
+ */
+trait ParameterAccess
+{
+ /**
+ * Returns a copy of the associated parameter instance.
+ */
+ public function parameters(): Parameters
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns the member value or null if no members value exists.
+ *
+ * @param ?callable(SfType): (bool|string) $validate
+ *
+ * @throws Violation if the validation fails
+ *
+ * @return SfType|null
+ */
+ public function parameterByKey(
+ string $key,
+ ?callable $validate = null,
+ bool|string $required = false,
+ Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool|null $default = null
+ ): Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool|null {
+ return $this->parameters->valueByKey($key, $validate, $required, $default);
+ }
+
+ /**
+ * Returns the member value and key as pair or an empty array if no members value exists.
+ *
+ * @param ?callable(SfType, string): (bool|string) $validate
+ * @param array{0:string, 1:SfType}|array{} $default
+ *
+ * @throws Violation if the validation fails
+ *
+ * @return array{0:string, 1:SfType}|array{}
+ */
+ public function parameterByIndex(
+ int $index,
+ ?callable $validate = null,
+ bool|string $required = false,
+ array $default = []
+ ): array {
+ return $this->parameters->valueByIndex($index, $validate, $required, $default);
+ }
+
+ /**
+ * Returns a new instance with the newly associated parameter instance.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ */
+ abstract public function withParameters(Parameters $parameters): static;
+
+ /**
+ * Adds a member if its key is not present at the of the associated parameter instance or update the instance at the given key.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param StructuredFieldProvider|Item|SfType $member
+ *
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function addParameter(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): static {
+ return $this->withParameters($this->parameters()->add($key, $member));
+ }
+
+ /**
+ * Adds a member at the start of the associated parameter instance and deletes any previous reference to the key if present.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param StructuredFieldProvider|Item|SfType $member
+ *
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function prependParameter(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): static {
+ return $this->withParameters($this->parameters()->prepend($key, $member));
+ }
+
+ /**
+ * Adds a member at the end of the associated parameter instance and deletes any previous reference to the key if present.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param StructuredFieldProvider|Item|SfType $member
+ *
+ * @throws SyntaxError If the string key is not a valid
+ */
+ public function appendParameter(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): static {
+ return $this->withParameters($this->parameters()->append($key, $member));
+ }
+
+ /**
+ * Removes all parameters members associated with the list of submitted keys in the associated parameter instance.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ */
+ public function withoutAnyParameter(): static
+ {
+ return $this->withParameters(Parameters::new());
+ }
+
+ /**
+ * Inserts pair at the end of the member list.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param array{0:string, 1:SfItemInput} ...$pairs
+ */
+ public function pushParameters(array ...$pairs): static
+ {
+ return $this->withParameters($this->parameters()->push(...$pairs));
+ }
+
+ /**
+ * Inserts pair at the beginning of the member list.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param array{0:string, 1:SfItemInput} ...$pairs
+ */
+ public function unshiftParameters(array ...$pairs): static
+ {
+ return $this->withParameters($this->parameters()->unshift(...$pairs));
+ }
+
+ /**
+ * Delete member based on their key.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ */
+ public function withoutParameterByKeys(string ...$keys): static
+ {
+ return $this->withParameters($this->parameters()->removeByKeys(...$keys));
+ }
+
+ /**
+ * Delete member based on their offsets.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ */
+ public function withoutParameterByIndices(int ...$indices): static
+ {
+ return $this->withParameters($this->parameters()->removeByIndices(...$indices));
+ }
+
+ /**
+ * Inserts members at the specified index.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param array{0:string, 1:SfType} ...$pairs
+ */
+ public function insertParameters(int $index, array ...$pairs): static
+ {
+ return $this->withParameters($this->parameters()->insert($index, ...$pairs));
+ }
+
+ /**
+ * Replace the member at the specified index.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param array{0:string, 1:SfType} $pair
+ */
+ public function replaceParameter(int $index, array $pair): static
+ {
+ return $this->withParameters($this->parameters()->replace($index, $pair));
+ }
+
+ /**
+ * Sort the object parameters by value using a callback.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param callable(array{0:string, 1:Item}, array{0:string, 1:Item}): int $callback
+ */
+ public function sortParameters(callable $callback): static
+ {
+ return $this->withParameters($this->parameters()->sort($callback));
+ }
+
+ /**
+ * Filter the object parameters using a callback.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified parameter change.
+ *
+ * @param callable(array{0:string, 1:Item}, int): bool $callback
+ */
+ public function filterParameters(callable $callback): static
+ {
+ return $this->withParameters($this->parameters()->filter($callback));
+ }
+
+ /**
+ * Merges multiple instances using iterable pairs.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param StructuredFieldProvider|Parameters|Dictionary|iterable<array{0:string, 1:SfItemInput}> ...$others
+ */
+ public function mergeParametersByPairs(...$others): static
+ {
+ return $this->withParameters($this->parameters()->mergePairs(...$others));
+ }
+
+ /**
+ * Merges multiple instances using iterable associative.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified changes.
+ *
+ * @param StructuredFieldProvider|Dictionary|Parameters|iterable<string, SfItemInput> ...$others
+ */
+ public function mergeParametersByAssociative(...$others): static
+ {
+ return $this->withParameters($this->parameters()->mergeAssociative(...$others));
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Parameters.php b/vendor/bakame/http-structured-fields/src/Parameters.php
new file mode 100644
index 000000000..9fd9a4e2b
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Parameters.php
@@ -0,0 +1,763 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\Validation\Violation;
+use CallbackFilterIterator;
+use Countable;
+use DateTimeImmutable;
+use DateTimeInterface;
+use Exception;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+
+use function array_key_exists;
+use function array_keys;
+use function array_map;
+use function array_replace;
+use function count;
+use function implode;
+use function is_int;
+use function is_string;
+use function trim;
+use function uasort;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.2
+ *
+ * @phpstan-import-type SfItemInput from StructuredFieldProvider
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ *
+ * @implements ArrayAccess<string, Item>
+ * @implements IteratorAggregate<int, array{0:string, 1:Item}>
+ */
+final class Parameters implements ArrayAccess, Countable, IteratorAggregate
+{
+ /** @var array<string, Item> */
+ private readonly array $members;
+
+ /**
+ * @param iterable<string, SfItemInput> $members
+ */
+ private function __construct(iterable $members = [])
+ {
+ $filteredMembers = [];
+ foreach ($members as $key => $member) {
+ $filteredMembers[Key::from($key)->value] = Member::bareItem($member);
+ }
+
+ $this->members = $filteredMembers;
+ }
+
+ /**
+ * Returns a new instance.
+ */
+ public static function new(): self
+ {
+ return new self();
+ }
+
+ /**
+ * Returns a new instance from an associative iterable construct.
+ *
+ * its keys represent the dictionary entry key
+ * its values represent the dictionary entry value
+ *
+ * @param StructuredFieldProvider|iterable<string, SfItemInput> $members
+ */
+ public static function fromAssociative(StructuredFieldProvider|iterable $members): self
+ {
+ if ($members instanceof StructuredFieldProvider) {
+ $structuredField = $members->toStructuredField();
+
+ return match (true) {
+ $structuredField instanceof Dictionary,
+ $structuredField instanceof Parameters => new self($structuredField->toAssociative()),
+ default => throw new InvalidArgument('The '.StructuredFieldProvider::class.' must provide a structured field container; '.$structuredField::class.' given.'),
+ };
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * Returns a new instance from a pair iterable construct.
+ *
+ * Each member is composed of an array with two elements
+ * the first member represents the instance entry key
+ * the second member represents the instance entry value
+ *
+ * @param StructuredFieldProvider|iterable<array{0:string, 1:SfItemInput}> $pairs
+ */
+ public static function fromPairs(StructuredFieldProvider|iterable $pairs): self
+ {
+ if ($pairs instanceof StructuredFieldProvider) {
+ $pairs = $pairs->toStructuredField();
+ }
+
+ if (!is_iterable($pairs)) {
+ throw new InvalidArgument('The "'.$pairs::class.'" instance can not be used for creating a .'.self::class.' structured field.');
+ }
+
+ return match (true) {
+ $pairs instanceof Parameters,
+ $pairs instanceof Dictionary => new self($pairs->toAssociative()),
+ default => new self((function (iterable $pairs) {
+ foreach ($pairs as [$key, $member]) {
+ yield $key => $member;
+ }
+ })($pairs)),
+ };
+ }
+
+ /**
+ * Returns an instance from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.2
+ *
+ * @throws SyntaxError|Exception If the string is not a valid
+ */
+ public static function fromHttpValue(Stringable|string $httpValue, Ietf $rfc = Ietf::Rfc9651): self
+ {
+ return self::fromPairs((new Parser($rfc))->parseParameters($httpValue)); /* @phpstan-ignore-line */
+ }
+
+ public static function fromRfc9651(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc9651);
+ }
+
+ public static function fromRfc8941(Stringable|string $httpValue): self
+ {
+ return self::fromHttpValue($httpValue, Ietf::Rfc8941);
+ }
+
+ public function toHttpValue(Ietf $rfc = Ietf::Rfc9651): string
+ {
+ $formatter = static fn (Item $member, string $offset): string => match ($member->value()) {
+ true => ';'.$offset,
+ default => ';'.$offset.'='.$member->toHttpValue($rfc),
+ };
+
+ return implode('', array_map($formatter, $this->members, array_keys($this->members)));
+ }
+
+ public function toRfc9651(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc9651);
+ }
+
+ public function toRfc8941(): string
+ {
+ return $this->toHttpValue(Ietf::Rfc8941);
+ }
+
+ public function __toString(): string
+ {
+ return $this->toHttpValue();
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->toHttpValue() === $this->toHttpValue();
+ }
+
+ /**
+ * Apply the callback if the given "condition" is (or resolves to) true.
+ *
+ * @param (callable($this): bool)|bool $condition
+ * @param callable($this): (self|null) $onSuccess
+ * @param ?callable($this): (self|null) $onFail
+ */
+ public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): self
+ {
+ if (!is_bool($condition)) {
+ $condition = $condition($this);
+ }
+
+ return match (true) {
+ $condition => $onSuccess($this),
+ null !== $onFail => $onFail($this),
+ default => $this,
+ } ?? $this;
+ }
+
+ public function count(): int
+ {
+ return count($this->members);
+ }
+
+ public function isEmpty(): bool
+ {
+ return !$this->isNotEmpty();
+ }
+
+ public function isNotEmpty(): bool
+ {
+ return [] !== $this->members;
+ }
+
+ /**
+ * @return Iterator<string, Item>
+ */
+ public function toAssociative(): Iterator
+ {
+ yield from $this->members;
+ }
+
+ /**
+ * @return Iterator<int, array{0:string, 1:Item}>
+ */
+ public function getIterator(): Iterator
+ {
+ foreach ($this->members as $index => $member) {
+ yield [$index, $member];
+ }
+ }
+
+ /**
+ * @return array<string>
+ */
+ public function keys(): array
+ {
+ return array_keys($this->members);
+ }
+
+ /**
+ * Tells whether the instance contain a members at the specified offsets.
+ */
+ public function hasKeys(string ...$keys): bool
+ {
+ foreach ($keys as $key) {
+ if (!array_key_exists($key, $this->members)) {
+ return false;
+ }
+ }
+
+ return [] !== $keys;
+ }
+
+ /**
+ * @param ?callable(SfType): (bool|string) $validate
+ *
+ * @throws Violation|InvalidOffset
+ */
+ public function getByKey(string $key, ?callable $validate = null): Item
+ {
+ $value = $this->members[$key] ?? throw InvalidOffset::dueToKeyNotFound($key);
+ if (null === $validate || true === ($exceptionMessage = $validate($value->value()))) {
+ return $value;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The parameter '{key}' whose value is '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{key}' => $key, '{value}' => $value->toHttpValue()]));
+ }
+
+ /**
+ * @return array<int>
+ */
+ public function indices(): array
+ {
+ return array_keys($this->keys());
+ }
+
+ public function hasIndices(int ...$indices): bool
+ {
+ $max = count($this->members);
+ foreach ($indices as $index) {
+ if (null === $this->filterIndex($index, $max)) {
+ return false;
+ }
+ }
+
+ return [] !== $indices;
+ }
+
+ /**
+ * Filters and format instance index.
+ */
+ private function filterIndex(int $index, int|null $max = null): int|null
+ {
+ $max ??= count($this->members);
+
+ return match (true) {
+ [] === $this->members,
+ 0 > $max + $index,
+ 0 > $max - $index - 1 => null,
+ 0 > $index => $max + $index,
+ default => $index,
+ };
+ }
+
+ /**
+ * @param ?callable(SfType, string): (bool|string) $validate
+ *
+ * @throws InvalidOffset|Violation
+ *
+ * @return array{0:string, 1:Item}
+ */
+ public function getByIndex(int $index, ?callable $validate = null): array
+ {
+ $foundOffset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ foreach ($this as $offset => $pair) {
+ if ($offset === $foundOffset) {
+ break;
+ }
+ }
+
+ if (!isset($pair)) {
+ throw InvalidOffset::dueToIndexNotFound($index);
+ }
+
+ if (null === $validate || true === ($exceptionMessage = $validate($pair[1]->value(), $pair[0]))) {
+ return $pair;
+ }
+
+ if (!is_string($exceptionMessage) || '' === trim($exceptionMessage)) {
+ $exceptionMessage = "The parameter at position '{index}' whose key is '{key}' with the value '{value}' failed validation.";
+ }
+
+ throw new Violation(strtr($exceptionMessage, ['{index}' => $index, '{key}' => $pair[0], '{value}' => $pair[1]->toHttpValue()]));
+ }
+
+ /**
+ * Returns the key associated with the given index or null otherwise.
+ */
+ public function indexByKey(string $key): ?int
+ {
+ foreach ($this as $index => $member) {
+ if ($key === $member[0]) {
+ return $index;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the index associated with the given key or null otherwise.
+ */
+ public function keyByIndex(int $index): ?string
+ {
+ $index = $this->filterIndex($index);
+ if (null === $index) {
+ return null;
+ }
+
+ foreach ($this as $offset => $member) {
+ if ($offset === $index) {
+ return $member[0];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return array{0:string, 1:Item}|array{}
+ */
+ public function first(): array
+ {
+ try {
+ return $this->getByIndex(0);
+ } catch (InvalidOffset) {
+ return [];
+ }
+ }
+
+ /**
+ * @return array{0:string, 1:Item}|array{}
+ */
+ public function last(): array
+ {
+ try {
+ return $this->getByIndex(-1);
+ } catch (InvalidOffset) {
+ return [];
+ }
+ }
+
+ /**
+ * Returns true only if the instance only contains the listed keys, false otherwise.
+ *
+ * @param array<string> $keys
+ */
+ public function allowedKeys(array $keys): bool
+ {
+ foreach ($this->members as $key => $member) {
+ if (!in_array($key, $keys, true)) {
+ return false;
+ }
+ }
+
+ return [] !== $keys;
+ }
+
+ /**
+ * Returns the member value or null if no members value exists.
+ *
+ * @param ?callable(SfType): (bool|string) $validate
+ *
+ * @throws Violation if the validation fails
+ *
+ * @return SfType|null
+ */
+ public function valueByKey(
+ string $key,
+ ?callable $validate = null,
+ bool|string $required = false,
+ Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool|null $default = null
+ ): Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool|null {
+ if (null !== $default && null === Type::tryFromVariable($default)) {
+ throw new SyntaxError('The default parameter is invalid.');
+ }
+
+ try {
+ return $this->getByKey($key, $validate)->value();
+ } catch (InvalidOffset $exception) {
+ if (false === $required) {
+ return $default;
+ }
+
+ $message = $required;
+ if (!is_string($message) || '' === trim($message)) {
+ $message = "The required parameter '{key}' is missing.";
+ }
+
+ throw new Violation(strtr($message, ['{key}' => $key]), previous: $exception);
+ }
+ }
+
+ /**
+ * Returns the member value and key as pair or an empty array if no members value exists.
+ *
+ * @param ?callable(SfType, string): (bool|string) $validate
+ * @param array{0:string, 1:SfType}|array{} $default
+ *
+ * @throws Violation if the validation fails
+ *
+ * @return array{0:string, 1:SfType}|array{}
+ */
+ public function valueByIndex(int $index, ?callable $validate = null, bool|string $required = false, array $default = []): array
+ {
+ $default = match (true) {
+ [] === $default => [],
+ !array_is_list($default) => throw new SyntaxError('The pair must be represented by an array as a list.'), /* @phpstan-ignore-line */
+ 2 !== count($default) => throw new SyntaxError('The pair first member is the key; its second member is its value.'), /* @phpstan-ignore-line */
+ null === ($key = Key::tryFrom($default[0])?->value) => throw new SyntaxError('The pair first member is invalid.'),
+ null === ($value = Item::tryNew($default[1])?->value()) => throw new SyntaxError('The pair second member is invalid.'),
+ default => [$key, $value],
+ };
+
+ try {
+ $tuple = $this->getByIndex($index, $validate);
+
+ return [$tuple[0], $tuple[1]->value()];
+ } catch (InvalidOffset $exception) {
+ if (false === $required) {
+ return $default;
+ }
+
+ $message = $required;
+ if (!is_string($message) || '' === trim($message)) {
+ $message = "The required parameter at position '{index}' is missing.";
+ }
+
+ throw new Violation(strtr($message, ['{index}' => $index]), previous: $exception);
+ }
+ }
+
+ /**
+ * @param StructuredFieldProvider|Item|SfType $member
+ */
+ public function add(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $key = Key::from($key)->value;
+ $member = Member::bareItem($member);
+ $oldMember = $this->members[$key] ?? null;
+ if (null === $oldMember || !$oldMember->equals($member)) {
+ $members = $this->members;
+ $members[$key] = $member;
+
+ return new self($members);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param array<string, Item> $members
+ */
+ private function newInstance(array $members): self
+ {
+ foreach ($members as $offset => $member) {
+ if (!isset($this->members[$offset]) || !$this->members[$offset]->equals($member)) {
+ return new self($members);
+ }
+ }
+
+ return $this;
+ }
+
+ private function remove(string|int ...$offsets): self
+ {
+ if ([] === $this->members || [] === $offsets) {
+ return $this;
+ }
+
+ $keys = array_keys($this->members);
+ $max = count($keys);
+ $reducer = fn (array $carry, string|int $key): array => match (true) {
+ is_string($key) && (false !== ($position = array_search($key, $keys, true))),
+ is_int($key) && (null !== ($position = $this->filterIndex($key, $max))) => [$position => true] + $carry,
+ default => $carry,
+ };
+
+ $indices = array_reduce($offsets, $reducer, []);
+
+ return match (true) {
+ [] === $indices => $this,
+ $max === count($indices) => self::new(),
+ default => self::fromPairs((function (array $offsets) {
+ foreach ($this as $offset => $pair) {
+ if (!array_key_exists($offset, $offsets)) {
+ yield $pair;
+ }
+ }
+ })($indices)),
+ };
+ }
+
+ public function removeByIndices(int ...$indices): self
+ {
+ return $this->remove(...$indices);
+ }
+
+ public function removeByKeys(string ...$keys): self
+ {
+ return $this->remove(...$keys);
+ }
+
+ /**
+ * @param StructuredFieldProvider|Item|SfType $member
+ */
+ public function append(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $key = Key::from($key)->value;
+ $member = Member::bareItem($member);
+ $members = $this->members;
+ unset($members[$key]);
+ $members[$key] = $member;
+
+ return $this->newInstance($members);
+ }
+
+ /**
+ * @param StructuredFieldProvider|Item|SfType $member
+ */
+ public function prepend(
+ string $key,
+ StructuredFieldProvider|Item|Token|Bytes|DisplayString|DateTimeInterface|string|int|float|bool $member
+ ): self {
+ $key = Key::from($key)->value;
+ $member = Member::bareItem($member);
+ $members = $this->members;
+ unset($members[$key]);
+
+ return $this->newInstance([$key => $member, ...$members]);
+ }
+
+ /**
+ * @param array{0:string, 1:SfItemInput} ...$pairs
+ */
+ public function push(array ...$pairs): self
+ {
+ return match (true) {
+ [] === $pairs => $this,
+ default => self::fromPairs((function (iterable $pairs) {
+ yield from $this->getIterator();
+ yield from $pairs;
+ })($pairs)),
+ };
+ }
+
+ /**
+ * @param array{0:string, 1:SfItemInput} ...$pairs
+ */
+ public function unshift(array ...$pairs): self
+ {
+ return match (true) {
+ [] === $pairs => $this,
+ default => self::fromPairs((function (iterable $pairs) {
+ yield from $pairs;
+ yield from $this->getIterator();
+ })($pairs)),
+ };
+ }
+
+ /**
+ * @param array{0:string, 1:SfItemInput} ...$members
+ */
+ public function insert(int $index, array ...$members): self
+ {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+
+ return match (true) {
+ [] === $members => $this,
+ 0 === $offset => $this->unshift(...$members),
+ count($this->members) === $offset => $this->push(...$members),
+ default => (function (Iterator $newMembers) use ($offset, $members) {
+ $newMembers = iterator_to_array($newMembers);
+ array_splice($newMembers, $offset, 0, $members);
+
+ return self::fromPairs($newMembers);
+ })($this->getIterator()),
+ };
+ }
+
+ /**
+ * @param array{0:string, 1:SfItemInput} $pair
+ */
+ public function replace(int $index, array $pair): self
+ {
+ $offset = $this->filterIndex($index) ?? throw InvalidOffset::dueToIndexNotFound($index);
+ $pair[1] = Member::bareItem($pair[1]);
+ $pairs = iterator_to_array($this);
+
+ return match (true) {
+ $pairs[$offset][0] === $pair[0] && $pairs[$offset][1]->equals($pair[1]) => $this,
+ default => self::fromPairs(array_replace($pairs, [$offset => $pair])),
+ };
+ }
+
+ /**
+ * @param StructuredFieldProvider|Dictionary|Parameters|iterable<string, SfItemInput> ...$others
+ */
+ public function mergeAssociative(StructuredFieldProvider|iterable ...$others): self
+ {
+ $members = $this->members;
+ foreach ($others as $other) {
+ if ($other instanceof StructuredFieldProvider) {
+ $other = $other->toStructuredField();
+ if (!$other instanceof Dictionary && !$other instanceof Parameters) {
+ throw new InvalidArgument('The "'.$other::class.'" instance can not be used for creating a .'.self::class.' structured field.');
+ }
+ }
+
+ if ($other instanceof self || $other instanceof Dictionary) {
+ $other = $other->toAssociative();
+ }
+
+ foreach ($other as $key => $value) {
+ $members[$key] = $value;
+ }
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * @param StructuredFieldProvider|Parameters|Dictionary|iterable<array{0:string, 1:SfItemInput}> ...$others
+ */
+ public function mergePairs(Dictionary|Parameters|StructuredFieldProvider|iterable ...$others): self
+ {
+ $members = $this->members;
+ foreach ($others as $other) {
+ if (!$other instanceof self) {
+ $other = self::fromPairs($other);
+ }
+ foreach ($other->toAssociative() as $key => $value) {
+ $members[$key] = $value;
+ }
+ }
+
+ return new self($members);
+ }
+
+ /**
+ * @param string $offset
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->hasKeys($offset);
+ }
+
+ /**
+ * @param string $offset
+ */
+ public function offsetGet(mixed $offset): Item
+ {
+ return $this->getByKey($offset);
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ /**
+ * @param callable(array{0:string, 1:Item}, int): TMap $callback
+ *
+ * @template TMap
+ *
+ * @return Iterator<TMap>
+ */
+ public function map(callable $callback): Iterator
+ {
+ foreach ($this as $offset => $pair) {
+ yield ($callback)($pair, $offset);
+ }
+ }
+
+ /**
+ * @param callable(TInitial|null, array{0:string, 1:Item}, int): TInitial $callback
+ * @param TInitial|null $initial
+ *
+ * @template TInitial
+ *
+ * @return TInitial|null
+ */
+ public function reduce(callable $callback, mixed $initial = null): mixed
+ {
+ foreach ($this as $offset => $pair) {
+ $initial = $callback($initial, $pair, $offset);
+ }
+
+ return $initial;
+ }
+
+ /**
+ * @param callable(array{0:string, 1:Item}, int): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ return self::fromPairs(new CallbackFilterIterator($this->getIterator(), $callback));
+ }
+
+ /**
+ * @param callable(array{0:string, 1:Item}, array{0:string, 1:Item}): int $callback
+ */
+ public function sort(callable $callback): self
+ {
+ $members = iterator_to_array($this);
+ uasort($members, $callback);
+
+ return self::fromPairs($members);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Parser.php b/vendor/bakame/http-structured-fields/src/Parser.php
new file mode 100644
index 000000000..4c7448c15
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Parser.php
@@ -0,0 +1,523 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use DateTimeImmutable;
+use Exception;
+use Stringable;
+
+use function in_array;
+use function ltrim;
+use function preg_match;
+use function str_contains;
+use function strlen;
+use function substr;
+use function trim;
+
+/**
+ * A class to parse HTTP Structured Fields from their HTTP textual representation according to RFC8941.
+ *
+ * Based on gapple\StructuredFields\Parser class in Structured Field Values for PHP v1.0.0.
+ *
+ * @link https://github.com/gapple/structured-fields/blob/v1.0.0/src/Parser.php
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2
+ *
+ * @see Dictionary::fromHttpValue()
+ * @see Parameters::fromHttpValue()
+ * @see OuterList::fromHttpValue()
+ * @see InnerList::fromHttpValue()
+ * @see Item::fromHttpValue()
+ *
+ * @internal Do not use directly this class as it's behaviour and return type
+ * MAY change significantly even during a major release cycle.
+ *
+ * @phpstan-type SfValue Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool
+ * @phpstan-type SfParameter array<array{0:string, 1:SfValue}>
+ * @phpstan-type SfItem array{0:SfValue, 1: SfParameter}
+ * @phpstan-type SfInnerList array{0:array<SfItem>, 1: SfParameter}
+ */
+final class Parser
+{
+ private const REGEXP_BYTES = '/^(?<sequence>:(?<byte>[a-z\d+\/=]*):)/i';
+ private const REGEXP_BOOLEAN = '/^\?[01]/';
+ private const REGEXP_DATE = '/^@(?<date>-?\d{1,15})(?:[^\d.]|$)/';
+ private const REGEXP_DECIMAL = '/^-?\d{1,12}\.\d{1,3}$/';
+ private const REGEXP_INTEGER = '/^-?\d{1,15}$/';
+ private const REGEXP_TOKEN = "/^(?<token>[a-z*][a-z\d:\/!#\$%&'*+\-.^_`|~]*)/i";
+ private const REGEXP_INVALID_CHARACTERS = "/[\r\t\n]|[^\x20-\x7E]/";
+ private const REGEXP_VALID_NUMBER = '/^(?<number>-?\d+(?:\.\d+)?)(?:[^\d.]|$)/';
+ private const REGEXP_VALID_SPACE = '/^(?<space>,[ \t]*)/';
+ private const FIRST_CHARACTER_RANGE_NUMBER = '-1234567890';
+ private const FIRST_CHARACTER_RANGE_TOKEN = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*';
+
+ public function __construct(private readonly Ietf $rfc)
+ {
+ }
+
+ /**
+ * Returns an Item as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-item
+ *
+ *
+ * @throws Exception|SyntaxError
+ *
+ * @return SfItem
+ */
+ public function parseItem(Stringable|string $httpValue): array
+ {
+ $remainder = trim((string) $httpValue, ' ');
+ if ('' === $remainder || 1 === preg_match(self::REGEXP_INVALID_CHARACTERS, $remainder)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for an item contains invalid characters.");
+ }
+
+ [$value, $offset] = $this->extractValue($remainder);
+ $remainder = substr($remainder, $offset);
+ if ('' !== $remainder && !str_contains($remainder, ';')) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for an item contains invalid characters.");
+ }
+
+ return [$value, $this->parseParameters($remainder)]; /* @phpstan-ignore-line */
+ }
+
+ /**
+ * Returns a Parameters ordered map container as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.1.2
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array<SfParameter>
+ */
+ public function parseParameters(Stringable|string $httpValue): array
+ {
+ $remainder = trim((string) $httpValue);
+ [$parameters, $offset] = $this->extractParametersValues($remainder);
+ if (strlen($remainder) !== $offset) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for Parameters contains invalid characters.");
+ }
+
+ return $parameters; /* @phpstan-ignore-line */
+ }
+
+ /**
+ * Returns an ordered list represented as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.1
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array<SfInnerList|SfItem>
+ */
+ public function parseList(Stringable|string $httpValue): array
+ {
+ $list = [];
+ $remainder = ltrim((string) $httpValue, ' ');
+ while ('' !== $remainder) {
+ [$list[], $offset] = $this->extractItemOrInnerList($remainder);
+ $remainder = self::removeCommaSeparatedWhiteSpaces($remainder, $offset);
+ }
+
+ return $list;
+ }
+
+ /**
+ * Returns a Dictionary represented as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.2
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array<array{0:string, 1:SfInnerList|SfItem}>
+ */
+ public function parseDictionary(Stringable|string $httpValue): array
+ {
+ $map = [];
+ $remainder = ltrim((string) $httpValue, ' ');
+ while ('' !== $remainder) {
+ $key = Key::fromStringBeginning($remainder)->value;
+ $remainder = substr($remainder, strlen($key));
+ if ('' === $remainder || '=' !== $remainder[0]) {
+ $remainder = '=?1'.$remainder;
+ }
+ $member = [$key];
+
+ [$member[1], $offset] = $this->extractItemOrInnerList(substr($remainder, 1));
+ $remainder = self::removeCommaSeparatedWhiteSpaces($remainder, ++$offset);
+ $map[] = $member;
+ }
+
+ return $map;
+ }
+
+ /**
+ * Returns an inner list represented as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.1.2
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return SfInnerList
+ */
+ public function parseInnerList(Stringable|string $httpValue): array
+ {
+ $remainder = ltrim((string) $httpValue, ' ');
+ if ('(' !== $remainder[0]) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a inner list is missing a parenthesis.");
+ }
+
+ [$list, $offset] = $this->extractInnerList($remainder);
+ $remainder = self::removeOptionalWhiteSpaces(substr($remainder, $offset));
+ if ('' !== $remainder) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a inner list contains invalid data.");
+ }
+
+ return $list;
+ }
+
+ /**
+ * Filter optional white spaces before and after comma.
+ *
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.3
+ */
+ private static function removeCommaSeparatedWhiteSpaces(string $remainder, int $offset): string
+ {
+ $remainder = self::removeOptionalWhiteSpaces(substr($remainder, $offset));
+ if ('' === $remainder) {
+ return '';
+ }
+
+ if (1 !== preg_match(self::REGEXP_VALID_SPACE, $remainder, $found)) {
+ throw new SyntaxError('The HTTP textual representation is missing an excepted comma.');
+ }
+
+ $remainder = substr($remainder, strlen($found['space']));
+
+ if ('' === $remainder) {
+ throw new SyntaxError('The HTTP textual representation has an unexpected end of line.');
+ }
+
+ return $remainder;
+ }
+
+ /**
+ * Remove optional white spaces before field value.
+ *
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.3
+ */
+ private static function removeOptionalWhiteSpaces(string $httpValue): string
+ {
+ return ltrim($httpValue, " \t");
+ }
+
+ /**
+ * Returns an item or an inner list as a PHP list array from an HTTP textual representation.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.1.1
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array{0: SfInnerList|SfItem, 1:int}
+ */
+ private function extractItemOrInnerList(string $httpValue): array
+ {
+ if ('(' === $httpValue[0]) {
+ return $this->extractInnerList($httpValue);
+ }
+
+ [$item, $remainder] = $this->extractItem($httpValue);
+
+ return [$item, strlen($httpValue) - strlen($remainder)];
+ }
+
+ /**
+ * Returns an inner list represented as a PHP list array from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.1.2
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array{0: SfInnerList, 1 :int}
+ */
+ private function extractInnerList(string $httpValue): array
+ {
+ $list = [];
+ $remainder = substr($httpValue, 1);
+ while ('' !== $remainder) {
+ $remainder = ltrim($remainder, ' ');
+
+ if (')' === $remainder[0]) {
+ $remainder = substr($remainder, 1);
+ [$parameters, $offset] = $this->extractParametersValues($remainder);
+ $remainder = substr($remainder, $offset);
+
+ return [[$list, $parameters], strlen($httpValue) - strlen($remainder)];
+ }
+
+ [$list[], $remainder] = $this->extractItem($remainder);
+
+ if ('' !== $remainder && !in_array($remainder[0], [' ', ')'], true)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a inner list is using invalid characters.");
+ }
+ }
+
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a inner list has an unexpected end of line.");
+ }
+
+ /**
+ * Returns an item represented as a PHP array from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array{0:SfItem, 1:string}
+ */
+ private function extractItem(string $remainder): array
+ {
+ [$value, $offset] = $this->extractValue($remainder);
+ $remainder = substr($remainder, $offset);
+ [$parameters, $offset] = $this->extractParametersValues($remainder);
+
+ return [[$value, $parameters], substr($remainder, $offset)];
+ }
+
+ /**
+ * Returns an item value from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.3.1
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array{0:SfValue, 1:int}
+ */
+ private function extractValue(string $httpValue): array
+ {
+ return match (true) {
+ '"' === $httpValue[0] => self::extractString($httpValue),
+ ':' === $httpValue[0] => self::extractBytes($httpValue),
+ '?' === $httpValue[0] => self::extractBoolean($httpValue),
+ '@' === $httpValue[0] => self::extractDate($httpValue, $this->rfc),
+ str_starts_with($httpValue, '%"') => self::extractDisplayString($httpValue, $this->rfc),
+ str_contains(self::FIRST_CHARACTER_RANGE_NUMBER, $httpValue[0]) => self::extractNumber($httpValue),
+ str_contains(self::FIRST_CHARACTER_RANGE_TOKEN, $httpValue[0]) => self::extractToken($httpValue),
+ default => throw new SyntaxError("The HTTP textual representation \"$httpValue\" for an item value is unknown or unsupported."),
+ };
+ }
+
+ /**
+ * Returns a parameters container represented as a PHP associative array from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.3.2
+ *
+ * @throws SyntaxError|Exception
+ *
+ * @return array{0:SfParameter, 1:int}
+ */
+ private function extractParametersValues(Stringable|string $httpValue): array
+ {
+ $map = [];
+ $httpValue = (string) $httpValue;
+ $remainder = $httpValue;
+ while ('' !== $remainder && ';' === $remainder[0]) {
+ $remainder = ltrim(substr($remainder, 1), ' ');
+ $key = Key::fromStringBeginning($remainder)->value;
+ $member = [$key, true];
+ $remainder = substr($remainder, strlen($key));
+ if ('' !== $remainder && '=' === $remainder[0]) {
+ $remainder = substr($remainder, 1);
+ [$member[1], $offset] = $this->extractValue($remainder);
+ $remainder = substr($remainder, $offset);
+ }
+
+ $map[] = $member;
+ }
+
+ return [$map, strlen($httpValue) - strlen($remainder)];
+ }
+
+ /**
+ * Returns a boolean from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.8
+ *
+ * @return array{0:bool, 1:int}
+ */
+ private static function extractBoolean(string $httpValue): array
+ {
+ return match (1) {
+ preg_match(self::REGEXP_BOOLEAN, $httpValue) => ['1' === $httpValue[1], 2],
+ default => throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Boolean contains invalid characters."),
+ };
+ }
+
+ /**
+ * Returns an int or a float from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.4
+ *
+ * @return array{0:int|float, 1:int}
+ */
+ private static function extractNumber(string $httpValue): array
+ {
+ if (1 !== preg_match(self::REGEXP_VALID_NUMBER, $httpValue, $found)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Number contains invalid characters.");
+ }
+
+ return match (1) {
+ preg_match(self::REGEXP_DECIMAL, $found['number']) => [(float) $found['number'], strlen($found['number'])],
+ preg_match(self::REGEXP_INTEGER, $found['number']) => [(int) $found['number'], strlen($found['number'])],
+ default => throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Number contains too much digit."),
+ };
+ }
+
+ /**
+ * Returns DateTimeImmutable instance from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://httpwg.org/http-extensions/draft-ietf-httpbis-sfbis.html#name-dates
+ *
+ * @throws SyntaxError
+ * @throws Exception
+ *
+ * @return array{0:DateTimeImmutable, 1:int}
+ */
+ private static function extractDate(string $httpValue, Ietf $rfc): array
+ {
+ if (!$rfc->supports(Type::Date)) {
+ throw MissingFeature::dueToLackOfSupport(Type::Date, $rfc);
+ }
+
+ if (1 !== preg_match(self::REGEXP_DATE, $httpValue, $found)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Date contains invalid characters.");
+ }
+
+ return [new DateTimeImmutable('@'.$found['date']), strlen($found['date']) + 1];
+ }
+
+ /**
+ * Returns a string from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.5
+ *
+ * @return array{0:string, 1:int}
+ */
+ private static function extractString(string $httpValue): array
+ {
+ $offset = 1;
+ $remainder = substr($httpValue, $offset);
+ $output = '';
+
+ if (1 === preg_match(self::REGEXP_INVALID_CHARACTERS, $remainder)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a String contains an invalid end string.");
+ }
+
+ while ('' !== $remainder) {
+ $char = $remainder[0];
+ $offset += 1;
+
+ if ('"' === $char) {
+ return [$output, $offset];
+ }
+
+ $remainder = substr($remainder, 1);
+
+ if ('\\' !== $char) {
+ $output .= $char;
+ continue;
+ }
+
+ $char = $remainder[0] ?? '';
+ $offset += 1;
+ $remainder = substr($remainder, 1);
+
+ if (!in_array($char, ['"', '\\'], true)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a String contains an invalid end string.");
+ }
+
+ $output .= $char;
+ }
+
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a String contains an invalid end string.");
+ }
+
+ /**
+ * Returns a string from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-sfbis#section-4.2.10
+ *
+ * @return array{0:DisplayString, 1:int}
+ */
+ private static function extractDisplayString(string $httpValue, Ietf $rfc): array
+ {
+ if (!$rfc->supports(Type::DisplayString)) {
+ throw MissingFeature::dueToLackOfSupport(Type::DisplayString, $rfc);
+ }
+
+ $offset = 2;
+ $remainder = substr($httpValue, $offset);
+ $output = '';
+
+ if (1 === preg_match(self::REGEXP_INVALID_CHARACTERS, $remainder)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a DisplayString contains an invalid character string.");
+ }
+
+ while ('' !== $remainder) {
+ $char = $remainder[0];
+ $offset += 1;
+
+ if ('"' === $char) {
+ return [DisplayString::fromEncoded($output), $offset];
+ }
+
+ $remainder = substr($remainder, 1);
+ if ('%' !== $char) {
+ $output .= $char;
+ continue;
+ }
+
+ $octet = substr($remainder, 0, 2);
+ $offset += 2;
+ if (1 === preg_match('/^[0-9a-f]]{2}$/', $octet)) {
+ throw new SyntaxError("The HTTP textual representation '$httpValue' for a DisplayString contains uppercased percent encoding sequence.");
+ }
+
+ $remainder = substr($remainder, 2);
+ $output .= $char.$octet;
+ }
+
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a DisplayString contains an invalid end string.");
+ }
+
+ /**
+ * Returns a Token from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.6
+ *
+ * @return array{0:Token, 1:int}
+ */
+ private static function extractToken(string $httpValue): array
+ {
+ preg_match(self::REGEXP_TOKEN, $httpValue, $found);
+
+ $token = $found['token'] ?? throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Token contains invalid characters.");
+
+ return [Token::fromString($token), strlen($token)];
+ }
+
+ /**
+ * Returns a Byte Sequence from an HTTP textual representation and the consumed offset in a tuple.
+ *
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-4.2.7
+ *
+ * @return array{0:Bytes, 1:int}
+ */
+ private static function extractBytes(string $httpValue): array
+ {
+ if (1 !== preg_match(self::REGEXP_BYTES, $httpValue, $found)) {
+ throw new SyntaxError("The HTTP textual representation \"$httpValue\" for a Byte Sequence contains invalid characters.");
+ }
+
+ return [Bytes::fromEncoded($found['byte']), strlen($found['sequence'])];
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/StructuredFieldError.php b/vendor/bakame/http-structured-fields/src/StructuredFieldError.php
new file mode 100644
index 000000000..fa823f6c8
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/StructuredFieldError.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Throwable;
+
+interface StructuredFieldError extends Throwable
+{
+}
diff --git a/vendor/bakame/http-structured-fields/src/StructuredFieldProvider.php b/vendor/bakame/http-structured-fields/src/StructuredFieldProvider.php
new file mode 100644
index 000000000..fd9e2be99
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/StructuredFieldProvider.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use DateTimeImmutable;
+use DateTimeInterface;
+
+/**
+ * @phpstan-type SfType Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool
+ * @phpstan-type SfTypeInput SfType|DateTimeInterface
+ * @phpstan-type SfList InnerList|OuterList
+ * @phpstan-type SfOrderedMap Dictionary|Parameters
+ * @phpstan-type SfDataType SfList|SfOrderedMap|Item
+ * @phpstan-type SfItemInput SfTypeInput|SfDataType|StructuredFieldProvider
+ * @phpstan-type SfMemberInput iterable<SfItemInput>|SfItemInput
+ * @phpstan-type SfParameterInput iterable<array{0:string, 1?:SfItemInput}>
+ * @phpstan-type SfInnerListPair array{0:iterable<SfItemInput>, 1?:Parameters|SfParameterInput}
+ * @phpstan-type SfItemPair array{0:SfTypeInput, 1?:Parameters|SfParameterInput}
+ */
+interface StructuredFieldProvider
+{
+ /**
+ * Returns one of the StructuredField Data Type class.
+ */
+ public function toStructuredField(): Dictionary|InnerList|Item|OuterList|Parameters;
+}
diff --git a/vendor/bakame/http-structured-fields/src/SyntaxError.php b/vendor/bakame/http-structured-fields/src/SyntaxError.php
new file mode 100644
index 000000000..304671fe9
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/SyntaxError.php
@@ -0,0 +1,11 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use InvalidArgumentException;
+
+class SyntaxError extends InvalidArgumentException implements StructuredFieldError
+{
+}
diff --git a/vendor/bakame/http-structured-fields/src/Token.php b/vendor/bakame/http-structured-fields/src/Token.php
new file mode 100644
index 000000000..88f41dbe8
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Token.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use Stringable;
+use Throwable;
+
+use function preg_match;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#name-tokens
+ */
+final class Token
+{
+ private function __construct(private readonly string $value)
+ {
+ if (1 !== preg_match("/^([a-z*][a-z\d:\/!#\$%&'*+\-.^_`|~]*)$/i", $this->value)) {
+ throw new SyntaxError('The token '.$this->value.' contains invalid characters.');
+ }
+ }
+
+ public function toString(): string
+ {
+ return $this->value;
+ }
+
+ public static function tryFromString(Stringable|string $value): ?self
+ {
+ try {
+ return self::fromString($value);
+ } catch (Throwable) {
+ return null;
+ }
+ }
+
+ public static function fromString(Stringable|string $value): self
+ {
+ return new self((string)$value);
+ }
+
+ public function equals(mixed $other): bool
+ {
+ return $other instanceof self && $other->value === $this->value;
+ }
+
+ public function type(): Type
+ {
+ return Type::Token;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Type.php b/vendor/bakame/http-structured-fields/src/Type.php
new file mode 100644
index 000000000..44eb8764d
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Type.php
@@ -0,0 +1,88 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields;
+
+use DateTimeInterface;
+
+use function abs;
+use function floor;
+use function gettype;
+use function is_float;
+use function is_int;
+use function is_object;
+use function is_string;
+use function preg_match;
+
+/**
+ * @see https://www.rfc-editor.org/rfc/rfc9651.html#section-3.3
+ */
+enum Type: string
+{
+ private const MAXIMUM_INT = 999_999_999_999_999;
+ private const MAXIMUM_FLOAT = 999_999_999_999;
+
+ case Integer = 'integer';
+ case Decimal = 'decimal';
+ case String = 'string';
+ case Token = 'token';
+ case Bytes = 'binary';
+ case DisplayString = 'displaystring';
+ case Boolean = 'boolean';
+ case Date = 'date';
+
+ public function equals(mixed $other): bool
+ {
+ return match (true) {
+ $other instanceof Item => $other->type() === $this,
+ default => $other instanceof self && $other === $this,
+ };
+ }
+
+ public function isOneOf(mixed ...$other): bool
+ {
+ foreach ($other as $item) {
+ if ($this->equals($item)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @throws SyntaxError if the value can not be resolved into a supported HTTP structured field data type
+ */
+ public static function fromVariable(Item|Token|DisplayString|Bytes|DateTimeInterface|int|float|bool|string $value): self
+ {
+ return self::tryFromVariable($value) ?? throw new SyntaxError(match (true) {
+ $value instanceof DateTimeInterface => 'The integer representation of a date is limited to 15 digits for a HTTP structured field date type.',
+ is_int($value) => 'The integer is limited to 15 digits for a HTTP structured field integer type.',
+ is_float($value) => 'The integer portion of decimals is limited to 12 digits for a HTTP structured field decimal type.',
+ is_string($value) => 'The string contains characters that are invalid for a HTTP structured field string type',
+ default => (is_object($value) ? 'An instance of "'.$value::class.'"' : 'A value of type "'.gettype($value).'"').' can not be used as an HTTP structured field value type.',
+ });
+ }
+
+ public static function tryFromVariable(mixed $variable): ?self
+ {
+ return match (true) {
+ $variable instanceof Item,
+ $variable instanceof Token,
+ $variable instanceof DisplayString,
+ $variable instanceof Bytes => $variable->type(),
+ $variable instanceof DateTimeInterface && self::MAXIMUM_INT >= abs($variable->getTimestamp()) => Type::Date,
+ is_int($variable) && self::MAXIMUM_INT >= abs($variable) => Type::Integer,
+ is_float($variable) && self::MAXIMUM_FLOAT >= abs(floor($variable)) => Type::Decimal,
+ is_bool($variable) => Type::Boolean,
+ is_string($variable) && 1 !== preg_match('/[^\x20-\x7f]/', $variable) => Type::String,
+ default => null,
+ };
+ }
+
+ public function supports(mixed $value): bool
+ {
+ return self::tryFromVariable($value)?->equals($this) ?? false;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ErrorCode.php b/vendor/bakame/http-structured-fields/src/Validation/ErrorCode.php
new file mode 100644
index 000000000..3f6ed43ae
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ErrorCode.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+/**
+ * General Error Code-.
+ *
+ * When adding new codes the name MUST be prefixed with
+ * a `@` to avoid conflicting with parameters keys.
+ */
+enum ErrorCode: string
+{
+ case ItemFailedParsing = '@item.failed.parsing';
+ case ItemValueFailedValidation = '@item.value.failed.validation';
+ case ParametersFailedParsing = '@parameters.failed.parsing';
+ case ParametersMissingConstraints = '@parameters.missing.constraints';
+ case ParametersFailedCriteria = '@parameters.failed.criteria';
+
+ /**
+ * @return array<string>
+ */
+ public static function list(): array
+ {
+ return array_map(fn (self $case) => $case->value, self::cases());
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ItemValidator.php b/vendor/bakame/http-structured-fields/src/Validation/ItemValidator.php
new file mode 100644
index 000000000..21d8cdd2f
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ItemValidator.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use Bakame\Http\StructuredFields\Item;
+use Bakame\Http\StructuredFields\StructuredFieldProvider;
+use Bakame\Http\StructuredFields\SyntaxError;
+use Stringable;
+
+/**
+ * Structured field Item validator.
+ *
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ */
+final class ItemValidator
+{
+ /** @var callable(SfType): (string|bool) */
+ private mixed $valueConstraint;
+ private ParametersValidator $parametersConstraint;
+
+ /**
+ * @param callable(SfType): (string|bool) $valueConstraint
+ */
+ private function __construct(
+ callable $valueConstraint,
+ ParametersValidator $parametersConstraint,
+ ) {
+ $this->valueConstraint = $valueConstraint;
+ $this->parametersConstraint = $parametersConstraint;
+ }
+
+ public static function new(): self
+ {
+ return new self(fn (mixed $value) => false, ParametersValidator::new());
+ }
+
+ /**
+ * Validates the Item value.
+ *
+ * On success populate the result item property
+ * On failure populates the result errors property
+ *
+ * @param callable(SfType): (string|bool) $constraint
+ */
+ public function value(callable $constraint): self
+ {
+ return new self($constraint, $this->parametersConstraint);
+ }
+
+ /**
+ * Validates the Item parameters as a whole.
+ *
+ * On failure populates the result errors property
+ */
+ public function parameters(ParametersValidator $constraint): self
+ {
+ return new self($this->valueConstraint, $constraint);
+ }
+
+ public function __invoke(Item|Stringable|string $item): bool|string
+ {
+ $result = $this->validate($item);
+
+ return $result->isSuccess() ? true : (string) $result->errors;
+ }
+
+ /**
+ * Validates the structured field Item.
+ */
+ public function validate(Item|Stringable|string $item): Result
+ {
+ $violations = new ViolationList();
+ if (!$item instanceof Item) {
+ try {
+ $item = Item::fromHttpValue($item);
+ } catch (SyntaxError $exception) {
+ $violations->add(ErrorCode::ItemFailedParsing->value, new Violation('The item string could not be parsed.', previous: $exception));
+
+ return Result::failed($violations);
+ }
+ }
+
+ try {
+ $itemValue = $item->value($this->valueConstraint);
+ } catch (Violation $exception) {
+ $itemValue = null;
+ $violations->add(ErrorCode::ItemValueFailedValidation->value, $exception);
+ }
+
+ $validate = $this->parametersConstraint->validate($item->parameters());
+ $violations->addAll($validate->errors);
+ if ($violations->isNotEmpty()) {
+ return Result::failed($violations);
+ }
+
+ /** @var ValidatedParameters $validatedParameters */
+ $validatedParameters = $validate->data;
+
+ return Result::success(new ValidatedItem($itemValue, $validatedParameters));
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ParametersValidator.php b/vendor/bakame/http-structured-fields/src/Validation/ParametersValidator.php
new file mode 100644
index 000000000..e851b9686
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ParametersValidator.php
@@ -0,0 +1,244 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use Bakame\Http\StructuredFields\Parameters;
+use Bakame\Http\StructuredFields\StructuredFieldProvider;
+use Bakame\Http\StructuredFields\SyntaxError;
+use Stringable;
+
+/**
+ * Structured field Item validator.
+ *
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ *
+ * @phpstan-type SfParameterKeyRule array{validate?:callable(SfType): (bool|string), required?:bool|string, default?:SfType|null}
+ * @phpstan-type SfParameterIndexRule array{validate?:callable(SfType, string): (bool|string), required?:bool|string, default?:array{0:string, 1:SfType}|array{}}
+ */
+final class ParametersValidator
+{
+ public const USE_KEYS = 1;
+ public const USE_INDICES = 2;
+
+ /** @var ?callable(Parameters): (string|bool) */
+ private mixed $criteria;
+ private int $type;
+ /** @var array<string, SfParameterKeyRule>|array<int, SfParameterIndexRule> */
+ private array $filterConstraints;
+
+ /**
+ * @param ?callable(Parameters): (string|bool) $criteria
+ * @param array<string, SfParameterKeyRule>|array<int, SfParameterIndexRule> $filterConstraints
+ */
+ private function __construct(
+ ?callable $criteria = null,
+ int $type = self::USE_KEYS,
+ array $filterConstraints = [],
+ ) {
+ $this->criteria = $criteria;
+ $this->type = $type;
+ $this->filterConstraints = $filterConstraints;
+ }
+
+ public static function new(): self
+ {
+ return new self();
+ }
+
+ /**
+ * Validates the Item parameters as a whole.
+ *
+ * On failure populates the result errors property
+ *
+ * @param ?callable(Parameters): (string|bool) $criteria
+ */
+ public function filterByCriteria(?callable $criteria, int $type = self::USE_KEYS): self
+ {
+ return new self($criteria, [] === $this->filterConstraints ? $type : $this->type, $this->filterConstraints);
+ }
+
+ /**
+ * Validate each parameters value per name.
+ *
+ * On success populate the result item property
+ * On failure populates the result errors property
+ *
+ * @param array<string, SfParameterKeyRule> $constraints
+ */
+ public function filterByKeys(array $constraints): self
+ {
+ return new self($this->criteria, self::USE_KEYS, $constraints);
+ }
+
+ /**
+ * Validate each parameters value per indices.
+ *
+ * On success populate the result item property
+ * On failure populates the result errors property
+ *
+ * @param array<int, SfParameterIndexRule> $constraints
+ */
+ public function filterByIndices(array $constraints): self
+ {
+ return new self($this->criteria, self::USE_INDICES, $constraints);
+ }
+
+ public function __invoke(Parameters|Stringable|string $parameters): bool|string
+ {
+ $result = $this->validate($parameters);
+
+ return $result->isSuccess() ? true : (string) $result->errors;
+ }
+
+ /**
+ * Validates the structured field Item.
+ */
+ public function validate(Parameters|Stringable|string $parameters): Result
+ {
+ $violations = new ViolationList();
+ if (!$parameters instanceof Parameters) {
+ try {
+ $parameters = Parameters::fromHttpValue($parameters);
+ } catch (SyntaxError $exception) {
+ $violations->add(ErrorCode::ParametersFailedParsing->value, new Violation('The parameters string could not be parsed.', previous: $exception));
+
+ return Result::failed($violations);
+ }
+ }
+
+ if ([] === $this->filterConstraints && null === $this->criteria) {
+ $violations->add(ErrorCode::ParametersMissingConstraints->value, new Violation('The parameters constraints are missing.'));
+ }
+
+ $parsedParameters = new ValidatedParameters();
+ if ([] !== $this->filterConstraints) {
+ $parsedParameters = match ($this->type) {
+ self::USE_INDICES => $this->validateByIndices($parameters),
+ default => $this->validateByKeys($parameters),
+ };
+
+ if ($parsedParameters->isFailed()) {
+ $violations->addAll($parsedParameters->errors);
+ } else {
+ $parsedParameters = $parsedParameters->data;
+ }
+ }
+
+ $errorMessage = $this->validateByCriteria($parameters);
+ if (!is_bool($errorMessage)) {
+ $violations->add(ErrorCode::ParametersFailedCriteria->value, new Violation($errorMessage));
+ }
+
+ /** @var ValidatedParameters $parsedParameters */
+ $parsedParameters = $parsedParameters ?? new ValidatedParameters();
+ if ([] === $this->filterConstraints && true === $errorMessage) {
+ $parsedParameters = new ValidatedParameters(match ($this->type) {
+ self::USE_KEYS => $this->toAssociative($parameters),
+ default => $this->toList($parameters),
+ });
+ }
+
+ return match ($violations->isNotEmpty()) {
+ true => Result::failed($violations),
+ default => Result::success($parsedParameters),
+ };
+ }
+
+ private function validateByCriteria(Parameters $parameters): bool|string
+ {
+ if (null === $this->criteria) {
+ return true;
+ }
+
+ $errorMessage = ($this->criteria)($parameters);
+ if (true === $errorMessage) {
+ return true;
+ }
+
+ if (!is_string($errorMessage) || '' === trim($errorMessage)) {
+ $errorMessage = 'The parameters constraints are not met.';
+ }
+
+ return $errorMessage;
+ }
+
+ /**
+ * Validate the current parameter object using its keys and return the parsed values and the errors.
+ *
+ * @return Result<ValidatedParameters>|Result<null>
+ */
+ private function validateByKeys(Parameters $parameters): Result /* @phpstan-ignore-line */
+ {
+ $data = [];
+ $violations = new ViolationList();
+ /**
+ * @var string $key
+ * @var SfParameterKeyRule $rule
+ */
+ foreach ($this->filterConstraints as $key => $rule) {
+ try {
+ $data[$key] = $parameters->valueByKey($key, $rule['validate'] ?? null, $rule['required'] ?? false, $rule['default'] ?? null);
+ } catch (Violation $exception) {
+ $violations[$key] = $exception;
+ }
+ }
+
+ return match ($violations->isNotEmpty()) {
+ true => Result::failed($violations),
+ default => Result::success(new ValidatedParameters($data)),
+ };
+ }
+
+ /**
+ * Validate the current parameter object using its indices and return the parsed values and the errors.
+ */
+ public function validateByIndices(Parameters $parameters): Result
+ {
+ $data = [];
+ $violations = new ViolationList();
+ /**
+ * @var int $index
+ * @var SfParameterIndexRule $rule
+ */
+ foreach ($this->filterConstraints as $index => $rule) {
+ try {
+ $data[$index] = $parameters->valueByIndex($index, $rule['validate'] ?? null, $rule['required'] ?? false, $rule['default'] ?? []);
+ } catch (Violation $exception) {
+ $violations[$index] = $exception;
+ }
+ }
+
+ return match ($violations->isNotEmpty()) {
+ true => Result::failed($violations),
+ default => Result::success(new ValidatedParameters($data)),
+ };
+ }
+
+ /**
+ * @return array<string,SfType>
+ */
+ private function toAssociative(Parameters $parameters): array
+ {
+ $assoc = [];
+ foreach ($parameters as $parameter) {
+ $assoc[$parameter[0]] = $parameter[1]->value();
+ }
+
+ return $assoc;
+ }
+
+ /**
+ * @return array<int, array{0:string, 1:SfType}>
+ */
+ private function toList(Parameters $parameters): array
+ {
+ $list = [];
+ foreach ($parameters as $index => $parameter) {
+ $list[$index] = [$parameter[0], $parameter[1]->value()];
+ }
+
+ return $list;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/Result.php b/vendor/bakame/http-structured-fields/src/Validation/Result.php
new file mode 100644
index 000000000..f765efda0
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/Result.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+final class Result
+{
+ private function __construct(
+ public readonly ValidatedParameters|ValidatedItem|null $data,
+ public readonly ViolationList $errors,
+ ) {
+ }
+
+ public function isSuccess(): bool
+ {
+ return $this->errors->isEmpty();
+ }
+
+ public function isFailed(): bool
+ {
+ return $this->errors->isNotEmpty();
+ }
+
+ public static function success(ValidatedItem|ValidatedParameters $data): self
+ {
+ return new self($data, new ViolationList());
+ }
+
+ public static function failed(ViolationList $errors): self
+ {
+ return new self(null, $errors);
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ValidatedItem.php b/vendor/bakame/http-structured-fields/src/Validation/ValidatedItem.php
new file mode 100644
index 000000000..df61a1aa8
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ValidatedItem.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use Bakame\Http\StructuredFields\Bytes;
+use Bakame\Http\StructuredFields\DisplayString;
+use Bakame\Http\StructuredFields\Token;
+use DateTimeImmutable;
+
+final class ValidatedItem
+{
+ public function __construct(
+ public readonly Bytes|Token|DisplayString|DateTimeImmutable|string|int|float|bool|null $value,
+ public readonly ValidatedParameters $parameters = new ValidatedParameters(),
+ ) {
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ValidatedParameters.php b/vendor/bakame/http-structured-fields/src/Validation/ValidatedParameters.php
new file mode 100644
index 000000000..c7f5b5eb6
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ValidatedParameters.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\ForbiddenOperation;
+use Bakame\Http\StructuredFields\InvalidOffset;
+use Bakame\Http\StructuredFields\StructuredFieldProvider;
+use Countable;
+use Iterator;
+use IteratorAggregate;
+
+/**
+ * @phpstan-import-type SfType from StructuredFieldProvider
+ *
+ * @implements ArrayAccess<array-key, array{0:string, 1:SfType}|array{}|SfType|null>
+ * @implements IteratorAggregate<array-key, array{0:string, 1:SfType}|array{}|SfType|null>
+ */
+final class ValidatedParameters implements ArrayAccess, Countable, IteratorAggregate
+{
+ /**
+ * @param array<array-key, array{0:string, 1:SfType}|array{}|SfType|null> $values
+ */
+ public function __construct(
+ private readonly array $values = [],
+ ) {
+ }
+
+ public function count(): int
+ {
+ return count($this->values);
+ }
+
+ public function getIterator(): Iterator
+ {
+ yield from $this->values;
+ }
+
+ public function offsetExists($offset): bool
+ {
+ return array_key_exists($offset, $this->values);
+ }
+
+ public function offsetGet($offset): mixed
+ {
+ return $this->offsetExists($offset) ? $this->values[$offset] : throw InvalidOffset::dueToMemberNotFound($offset);
+ }
+
+ public function offsetUnset(mixed $offset): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ throw new ForbiddenOperation(self::class.' instance can not be updated using '.ArrayAccess::class.' methods.');
+ }
+
+ /**
+ * @return array<array-key, array{0:string, 1:SfType}|array{}|SfType|null>
+ */
+ public function all(): array
+ {
+ return $this->values;
+ }
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/Violation.php b/vendor/bakame/http-structured-fields/src/Validation/Violation.php
new file mode 100644
index 000000000..ec666de98
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/Violation.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use Bakame\Http\StructuredFields\StructuredFieldError;
+use LogicException;
+
+final class Violation extends LogicException implements StructuredFieldError
+{
+}
diff --git a/vendor/bakame/http-structured-fields/src/Validation/ViolationList.php b/vendor/bakame/http-structured-fields/src/Validation/ViolationList.php
new file mode 100644
index 000000000..8cbf5e741
--- /dev/null
+++ b/vendor/bakame/http-structured-fields/src/Validation/ViolationList.php
@@ -0,0 +1,169 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Bakame\Http\StructuredFields\Validation;
+
+use ArrayAccess;
+use Bakame\Http\StructuredFields\InvalidOffset;
+use Countable;
+use Iterator;
+use IteratorAggregate;
+use Stringable;
+use TypeError;
+
+use function array_filter;
+use function array_map;
+use function count;
+use function implode;
+use function is_int;
+
+use const ARRAY_FILTER_USE_BOTH;
+
+/**
+ * @implements IteratorAggregate<array-key,Violation>
+ * @implements ArrayAccess<array-key,Violation>
+ */
+final class ViolationList implements IteratorAggregate, Countable, ArrayAccess, Stringable
+{
+ /** @var array<Violation> */
+ private array $errors = [];
+
+ /**
+ * @param iterable<array-key, Violation> $errors
+ */
+ public function __construct(iterable $errors = [])
+ {
+ $this->addAll($errors);
+ }
+
+ public function count(): int
+ {
+ return count($this->errors);
+ }
+
+ public function getIterator(): Iterator
+ {
+ yield from $this->errors;
+ }
+
+ public function __toString(): string
+ {
+ return implode(PHP_EOL, array_map(fn (Violation $e): string => $e->getMessage(), $this->errors));
+ }
+
+ /**
+ * @return array<array-key, string>
+ */
+ public function summary(): array
+ {
+ return array_map(fn (Violation $e): string => $e->getMessage(), $this->errors);
+ }
+
+ public function isEmpty(): bool
+ {
+ return [] === $this->errors;
+ }
+
+ public function isNotEmpty(): bool
+ {
+ return ! $this->isEmpty();
+ }
+
+ /**
+ * @param string|int $offset
+ */
+ public function offsetExists(mixed $offset): bool
+ {
+ return $this->has($offset);
+ }
+
+ /**
+ * @param string|int $offset
+ *
+ * @return Violation
+ */
+ public function offsetGet(mixed $offset): mixed
+ {
+ return $this->get($offset);
+ }
+
+ /**
+ * @param string|int $offset
+ */
+ public function offsetUnset(mixed $offset): void
+ {
+ unset($this->errors[$offset]);
+ }
+
+ /**
+ * @param string|int|null $offset
+ * @param Violation $value
+ */
+ public function offsetSet(mixed $offset, mixed $value): void
+ {
+ if (null === $offset) {
+ throw new TypeError('null can not be used as a valid offset value.');
+ }
+ $this->add($offset, $value);
+ }
+
+ public function has(string|int $offset): bool
+ {
+ if (is_int($offset)) {
+ return null !== $this->filterIndex($offset);
+ }
+
+ return array_key_exists($offset, $this->errors);
+ }
+
+ public function get(string|int $offset): Violation
+ {
+ return $this->errors[$this->filterIndex($offset) ?? throw InvalidOffset::dueToIndexNotFound($offset)];
+ }
+
+ public function add(string|int $offset, Violation $error): void
+ {
+ $this->errors[$offset] = $error;
+ }
+
+ /**
+ * @param iterable<array-key, Violation> $errors
+ */
+ public function addAll(iterable $errors): void
+ {
+ foreach ($errors as $offset => $error) {
+ $this->add($offset, $error);
+ }
+ }
+
+ private function filterIndex(string|int $index, int|null $max = null): string|int|null
+ {
+ if (!is_int($index)) {
+ return $index;
+ }
+
+ $max ??= count($this->errors);
+
+ return match (true) {
+ [] === $this->errors,
+ 0 > $max + $index,
+ 0 > $max - $index - 1 => null,
+ 0 > $index => $max + $index,
+ default => $index,
+ };
+ }
+
+ /**
+ * @param callable(Violation, array-key): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ return new self(array_filter($this->errors, $callback, ARRAY_FILTER_USE_BOTH));
+ }
+
+ public function toException(): Violation
+ {
+ return new Violation((string) $this);
+ }
+}
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 3b4744860..ec901b895 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -6,6 +6,36 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
+ 'Bakame\\Http\\StructuredFields\\Bytes' => $vendorDir . '/bakame/http-structured-fields/src/Bytes.php',
+ 'Bakame\\Http\\StructuredFields\\DataType' => $vendorDir . '/bakame/http-structured-fields/src/DataType.php',
+ 'Bakame\\Http\\StructuredFields\\Dictionary' => $vendorDir . '/bakame/http-structured-fields/src/Dictionary.php',
+ 'Bakame\\Http\\StructuredFields\\DisplayString' => $vendorDir . '/bakame/http-structured-fields/src/DisplayString.php',
+ 'Bakame\\Http\\StructuredFields\\ForbiddenOperation' => $vendorDir . '/bakame/http-structured-fields/src/ForbiddenOperation.php',
+ 'Bakame\\Http\\StructuredFields\\Ietf' => $vendorDir . '/bakame/http-structured-fields/src/Ietf.php',
+ 'Bakame\\Http\\StructuredFields\\InnerList' => $vendorDir . '/bakame/http-structured-fields/src/InnerList.php',
+ 'Bakame\\Http\\StructuredFields\\InvalidArgument' => $vendorDir . '/bakame/http-structured-fields/src/InvalidArgument.php',
+ 'Bakame\\Http\\StructuredFields\\InvalidOffset' => $vendorDir . '/bakame/http-structured-fields/src/InvalidOffset.php',
+ 'Bakame\\Http\\StructuredFields\\Item' => $vendorDir . '/bakame/http-structured-fields/src/Item.php',
+ 'Bakame\\Http\\StructuredFields\\Key' => $vendorDir . '/bakame/http-structured-fields/src/Key.php',
+ 'Bakame\\Http\\StructuredFields\\Member' => $vendorDir . '/bakame/http-structured-fields/src/Member.php',
+ 'Bakame\\Http\\StructuredFields\\MissingFeature' => $vendorDir . '/bakame/http-structured-fields/src/MissingFeature.php',
+ 'Bakame\\Http\\StructuredFields\\OuterList' => $vendorDir . '/bakame/http-structured-fields/src/OuterList.php',
+ 'Bakame\\Http\\StructuredFields\\ParameterAccess' => $vendorDir . '/bakame/http-structured-fields/src/ParameterAccess.php',
+ 'Bakame\\Http\\StructuredFields\\Parameters' => $vendorDir . '/bakame/http-structured-fields/src/Parameters.php',
+ 'Bakame\\Http\\StructuredFields\\Parser' => $vendorDir . '/bakame/http-structured-fields/src/Parser.php',
+ 'Bakame\\Http\\StructuredFields\\StructuredFieldError' => $vendorDir . '/bakame/http-structured-fields/src/StructuredFieldError.php',
+ 'Bakame\\Http\\StructuredFields\\StructuredFieldProvider' => $vendorDir . '/bakame/http-structured-fields/src/StructuredFieldProvider.php',
+ 'Bakame\\Http\\StructuredFields\\SyntaxError' => $vendorDir . '/bakame/http-structured-fields/src/SyntaxError.php',
+ 'Bakame\\Http\\StructuredFields\\Token' => $vendorDir . '/bakame/http-structured-fields/src/Token.php',
+ 'Bakame\\Http\\StructuredFields\\Type' => $vendorDir . '/bakame/http-structured-fields/src/Type.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ErrorCode' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ErrorCode.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ItemValidator' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ItemValidator.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ParametersValidator' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ParametersValidator.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\Result' => $vendorDir . '/bakame/http-structured-fields/src/Validation/Result.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ValidatedItem' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ValidatedItem.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ValidatedParameters' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ValidatedParameters.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\Violation' => $vendorDir . '/bakame/http-structured-fields/src/Validation/Violation.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ViolationList' => $vendorDir . '/bakame/http-structured-fields/src/Validation/ViolationList.php',
'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php',
'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php',
'Brick\\Math\\BigNumber' => $vendorDir . '/brick/math/src/BigNumber.php',
@@ -44,6 +74,37 @@ return array(
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepository' => $vendorDir . '/commerceguys/intl/src/NumberFormat/NumberFormatRepository.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepositoryInterface' => $vendorDir . '/commerceguys/intl/src/NumberFormat/NumberFormatRepositoryInterface.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php',
+ 'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php',
+ 'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php',
+ 'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php',
+ 'GuzzleHttp\\Psr7\\Exception\\MalformedUriException' => $vendorDir . '/guzzlehttp/psr7/src/Exception/MalformedUriException.php',
+ 'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php',
+ 'GuzzleHttp\\Psr7\\Header' => $vendorDir . '/guzzlehttp/psr7/src/Header.php',
+ 'GuzzleHttp\\Psr7\\HttpFactory' => $vendorDir . '/guzzlehttp/psr7/src/HttpFactory.php',
+ 'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php',
+ 'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php',
+ 'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php',
+ 'GuzzleHttp\\Psr7\\Message' => $vendorDir . '/guzzlehttp/psr7/src/Message.php',
+ 'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php',
+ 'GuzzleHttp\\Psr7\\MimeType' => $vendorDir . '/guzzlehttp/psr7/src/MimeType.php',
+ 'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php',
+ 'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php',
+ 'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php',
+ 'GuzzleHttp\\Psr7\\Query' => $vendorDir . '/guzzlehttp/psr7/src/Query.php',
+ 'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php',
+ 'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php',
+ 'GuzzleHttp\\Psr7\\Rfc7230' => $vendorDir . '/guzzlehttp/psr7/src/Rfc7230.php',
+ 'GuzzleHttp\\Psr7\\ServerRequest' => $vendorDir . '/guzzlehttp/psr7/src/ServerRequest.php',
+ 'GuzzleHttp\\Psr7\\Stream' => $vendorDir . '/guzzlehttp/psr7/src/Stream.php',
+ 'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
+ 'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php',
+ 'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php',
+ 'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php',
+ 'GuzzleHttp\\Psr7\\UriComparator' => $vendorDir . '/guzzlehttp/psr7/src/UriComparator.php',
+ 'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php',
+ 'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php',
+ 'GuzzleHttp\\Psr7\\Utils' => $vendorDir . '/guzzlehttp/psr7/src/Utils.php',
'HTMLPurifier' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.php',
'HTMLPurifier_Arborize' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php',
'HTMLPurifier_AttrCollections' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php',
@@ -276,6 +337,9 @@ return array(
'HTMLPurifier_VarParser_Flexible' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php',
'HTMLPurifier_VarParser_Native' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php',
'HTMLPurifier_Zipper' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php',
+ 'HttpSignature\\HttpMessageSigner' => $vendorDir . '/macgirvin/http-message-signer/src/HttpMessageSigner.php',
+ 'HttpSignature\\StructuredFieldTypes' => $vendorDir . '/macgirvin/http-message-signer/src/StructuredFieldTypes.php',
+ 'HttpSignature\\UnProcessableSignatureException' => $vendorDir . '/macgirvin/http-message-signer/src/UnProcessableSignatureException.php',
'ID3Parser\\ID3Parser' => $vendorDir . '/lukasreschke/id3parser/src/ID3Parser.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v1' => $vendorDir . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v1.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v2' => $vendorDir . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v2.php',
@@ -1619,6 +1683,7 @@ return array(
'Zotlabs\\Lib\\Img_filesize' => $baseDir . '/Zotlabs/Lib/Img_filesize.php',
'Zotlabs\\Lib\\JSalmon' => $baseDir . '/Zotlabs/Lib/JSalmon.php',
'Zotlabs\\Lib\\JcsEddsa2022' => $baseDir . '/Zotlabs/Lib/JcsEddsa2022.php',
+ 'Zotlabs\\Lib\\JcsEddsa2022SignException' => $baseDir . '/Zotlabs/Lib/JcsEddsa2022SignException.php',
'Zotlabs\\Lib\\Keyutils' => $baseDir . '/Zotlabs/Lib/Keyutils.php',
'Zotlabs\\Lib\\LDSignatures' => $baseDir . '/Zotlabs/Lib/LDSignatures.php',
'Zotlabs\\Lib\\Libsync' => $baseDir . '/Zotlabs/Lib/Libsync.php',
@@ -1682,14 +1747,12 @@ return array(
'Zotlabs\\Module\\Branchtopic' => $baseDir . '/Zotlabs/Module/Branchtopic.php',
'Zotlabs\\Module\\Cal' => $baseDir . '/Zotlabs/Module/Cal.php',
'Zotlabs\\Module\\Cdav' => $baseDir . '/Zotlabs/Module/Cdav.php',
- 'Zotlabs\\Module\\Ceditor' => $baseDir . '/Zotlabs/Module/Ceditor.php',
'Zotlabs\\Module\\Changeaddr' => $baseDir . '/Zotlabs/Module/Changeaddr.php',
'Zotlabs\\Module\\Channel' => $baseDir . '/Zotlabs/Module/Channel.php',
'Zotlabs\\Module\\Channel_calendar' => $baseDir . '/Zotlabs/Module/Channel_calendar.php',
'Zotlabs\\Module\\Chanview' => $baseDir . '/Zotlabs/Module/Chanview.php',
'Zotlabs\\Module\\Chat' => $baseDir . '/Zotlabs/Module/Chat.php',
'Zotlabs\\Module\\Chatsvc' => $baseDir . '/Zotlabs/Module/Chatsvc.php',
- 'Zotlabs\\Module\\Cleditor' => $baseDir . '/Zotlabs/Module/Cleditor.php',
'Zotlabs\\Module\\Cloud' => $baseDir . '/Zotlabs/Module/Cloud.php',
'Zotlabs\\Module\\Cloud_tiles' => $baseDir . '/Zotlabs/Module/Cloud_tiles.php',
'Zotlabs\\Module\\Common' => $baseDir . '/Zotlabs/Module/Common.php',
@@ -1865,7 +1928,6 @@ return array(
'Zotlabs\\Module\\Xchan' => $baseDir . '/Zotlabs/Module/Xchan.php',
'Zotlabs\\Module\\Xpoco' => $baseDir . '/Zotlabs/Module/Xpoco.php',
'Zotlabs\\Module\\Xrd' => $baseDir . '/Zotlabs/Module/Xrd.php',
- 'Zotlabs\\Module\\Xref' => $baseDir . '/Zotlabs/Module/Xref.php',
'Zotlabs\\Module\\Z6trans' => $baseDir . '/Zotlabs/Module/Z6trans.php',
'Zotlabs\\Module\\Zot' => $baseDir . '/Zotlabs/Module/Zot.php',
'Zotlabs\\Module\\Zot_probe' => $baseDir . '/Zotlabs/Module/Zot_probe.php',
@@ -2292,30 +2354,363 @@ return array(
'chillerlan\\QRCode\\QROptionsTrait' => $vendorDir . '/chillerlan/php-qrcode/src/QROptionsTrait.php',
'chillerlan\\Settings\\SettingsContainerAbstract' => $vendorDir . '/chillerlan/php-settings-container/src/SettingsContainerAbstract.php',
'chillerlan\\Settings\\SettingsContainerInterface' => $vendorDir . '/chillerlan/php-settings-container/src/SettingsContainerInterface.php',
- 'phpseclib\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
- 'phpseclib\\Crypt\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php',
- 'phpseclib\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
- 'phpseclib\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
- 'phpseclib\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
- 'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
- 'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
- 'phpseclib\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
- 'phpseclib\\Crypt\\Random' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
- 'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
- 'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
- 'phpseclib\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
- 'phpseclib\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
- 'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
- 'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
- 'phpseclib\\File\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/X509.php',
- 'phpseclib\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
- 'phpseclib\\Net\\SCP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SCP.php',
- 'phpseclib\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
- 'phpseclib\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
- 'phpseclib\\Net\\SSH1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php',
- 'phpseclib\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
- 'phpseclib\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
- 'phpseclib\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
+ 'phpseclib3\\Common\\Functions\\Strings' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php',
+ 'phpseclib3\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
+ 'phpseclib3\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
+ 'phpseclib3\\Crypt\\ChaCha20' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php',
+ 'phpseclib3\\Crypt\\Common\\AsymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php',
+ 'phpseclib3\\Crypt\\Common\\BlockCipher' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\Common\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php',
+ 'phpseclib3\\Crypt\\Common\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php',
+ 'phpseclib3\\Crypt\\Common\\StreamCipher' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php',
+ 'phpseclib3\\Crypt\\Common\\SymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php',
+ 'phpseclib3\\Crypt\\Common\\Traits\\Fingerprint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php',
+ 'phpseclib3\\Crypt\\Common\\Traits\\PasswordProtected' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php',
+ 'phpseclib3\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
+ 'phpseclib3\\Crypt\\DH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH.php',
+ 'phpseclib3\\Crypt\\DH\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\DH\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\DH\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php',
+ 'phpseclib3\\Crypt\\DH\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php',
+ 'phpseclib3\\Crypt\\DH\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php',
+ 'phpseclib3\\Crypt\\DSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php',
+ 'phpseclib3\\Crypt\\DSA\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php',
+ 'phpseclib3\\Crypt\\DSA\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php',
+ 'phpseclib3\\Crypt\\DSA\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php',
+ 'phpseclib3\\Crypt\\EC' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Binary' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\KoblitzPrime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Prime' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\TwistedEdwards' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Curve25519' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Curve448' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Ed25519' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Ed448' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP160r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP160t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP192r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP192t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP224r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP224t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP256r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP256t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP320r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP320t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP384r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP384t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP512r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP512t1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistb233' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistb409' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk163' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk233' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk283' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk409' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp192' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp224' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp256' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp384' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp521' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistt571' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v3' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v3' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime256v1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp112r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp112r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp128r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp128r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp192k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp192r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp224k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp224r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp256k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp256r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp384r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp521r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect113r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect113r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect131r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect131r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect193r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect193r2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect233k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect233r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect239k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect283k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect283r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect409k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect409r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect571k1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect571r1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\Common' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\MontgomeryPrivate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\MontgomeryPublic' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\libsodium' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\IEEE' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php',
+ 'phpseclib3\\Crypt\\EC\\Parameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php',
+ 'phpseclib3\\Crypt\\EC\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php',
+ 'phpseclib3\\Crypt\\EC\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php',
+ 'phpseclib3\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
+ 'phpseclib3\\Crypt\\PublicKeyLoader' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php',
+ 'phpseclib3\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
+ 'phpseclib3\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
+ 'phpseclib3\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\JWK' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\MSBLOB' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PSS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\RSA\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php',
+ 'phpseclib3\\Crypt\\RSA\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php',
+ 'phpseclib3\\Crypt\\Random' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
+ 'phpseclib3\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
+ 'phpseclib3\\Crypt\\Salsa20' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php',
+ 'phpseclib3\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
+ 'phpseclib3\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
+ 'phpseclib3\\Exception\\BadConfigurationException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php',
+ 'phpseclib3\\Exception\\BadDecryptionException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php',
+ 'phpseclib3\\Exception\\BadModeException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php',
+ 'phpseclib3\\Exception\\ConnectionClosedException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php',
+ 'phpseclib3\\Exception\\FileNotFoundException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php',
+ 'phpseclib3\\Exception\\InconsistentSetupException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php',
+ 'phpseclib3\\Exception\\InsufficientSetupException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php',
+ 'phpseclib3\\Exception\\InvalidPacketLengthException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php',
+ 'phpseclib3\\Exception\\NoKeyLoadedException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php',
+ 'phpseclib3\\Exception\\NoSupportedAlgorithmsException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php',
+ 'phpseclib3\\Exception\\TimeoutException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php',
+ 'phpseclib3\\Exception\\UnableToConnectException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php',
+ 'phpseclib3\\Exception\\UnsupportedAlgorithmException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php',
+ 'phpseclib3\\Exception\\UnsupportedCurveException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php',
+ 'phpseclib3\\Exception\\UnsupportedFormatException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php',
+ 'phpseclib3\\Exception\\UnsupportedOperationException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php',
+ 'phpseclib3\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
+ 'phpseclib3\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
+ 'phpseclib3\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AccessDescription' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AdministrationDomainName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AlgorithmIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AnotherName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Attribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeType' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeTypeAndValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Attributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AuthorityInfoAccessSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AuthorityKeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BaseDistance' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BasicConstraints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInStandardAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CPSuri' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLDistributionPoints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLNumber' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLReason' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertPolicyId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Certificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateIssuer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateList' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificatePolicies' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateSerialNumber' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificationRequest' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificationRequestInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Characteristic_two' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CountryName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Curve' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DHParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAParams' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAPublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DigestInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DirectoryString' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DisplayText' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DistributionPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DistributionPointName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DssSigValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECParameters' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EDIPartyName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EcdsaSigValue' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EncryptedData' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EncryptedPrivateKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtKeyUsageSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Extension' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtensionAttribute' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtensionAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Extensions' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\FieldElement' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\FieldID' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralNames' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralSubtree' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralSubtrees' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\HashAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\HoldInstructionCode' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\InvalidityDate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\IssuerAltName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\IssuingDistributionPoint' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyPurposeId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyUsage' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\MaskGenAlgorithm' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Name' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NameConstraints' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NetworkAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NoticeReference' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NumericUserIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ORAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OneAsymmetricKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OrganizationName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OrganizationalUnitNames' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OtherPrimeInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OtherPrimeInfos' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBEParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBES2params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBKDF2params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBMAC1params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PKCS9String' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Pentanomial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PersonalName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyInformation' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyMappings' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyQualifierId' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyQualifierInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PostalAddress' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Prime_p' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateDomainName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKeyUsagePeriod' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKeyAndChallenge' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RC2CBCParameter' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RDNSequence' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSAPrivateKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSAPublicKey' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSASSA_PSS_params' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ReasonFlags' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RelativeDistinguishedName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RevokedCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SignedPublicKeyAndChallenge' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SpecifiedECDomain' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectAltName' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectDirectoryAttributes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectInfoAccessSyntax' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectPublicKeyInfo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TBSCertList' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TBSCertificate' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TerminalIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Time' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Trinomial' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\UniqueIdentifier' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\UserNotice' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Validity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_ca_policy_url' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_cert_type' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_comment' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php',
+ 'phpseclib3\\File\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/X509.php',
+ 'phpseclib3\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\BuiltIn' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\Barrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\EvalBarrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\Engine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\GMP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\GMP\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP32' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP64' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\DefaultEngine' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\OpenSSL' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Barrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Classic' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\EvalBarrett' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Montgomery' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\MontgomeryMult' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\PowerOfTwo' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php',
+ 'phpseclib3\\Math\\BinaryField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BinaryField.php',
+ 'phpseclib3\\Math\\BinaryField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php',
+ 'phpseclib3\\Math\\Common\\FiniteField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php',
+ 'phpseclib3\\Math\\Common\\FiniteField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php',
+ 'phpseclib3\\Math\\PrimeField' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/PrimeField.php',
+ 'phpseclib3\\Math\\PrimeField\\Integer' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php',
+ 'phpseclib3\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
+ 'phpseclib3\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
+ 'phpseclib3\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
+ 'phpseclib3\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
+ 'phpseclib3\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
+ 'phpseclib3\\System\\SSH\\Common\\Traits\\ReadBytes' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php',
+ 'phpseclib\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/AES.php',
+ 'phpseclib\\Crypt\\Base' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Base.php',
+ 'phpseclib\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php',
+ 'phpseclib\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/DES.php',
+ 'phpseclib\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Hash.php',
+ 'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/RC2.php',
+ 'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/RC4.php',
+ 'phpseclib\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/RSA.php',
+ 'phpseclib\\Crypt\\Random' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Random.php',
+ 'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php',
+ 'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php',
+ 'phpseclib\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php',
+ 'phpseclib\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib2_compat/src/File/ANSI.php',
+ 'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib2_compat/src/File/ASN1.php',
+ 'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php',
+ 'phpseclib\\File\\X509' => $vendorDir . '/phpseclib/phpseclib2_compat/src/File/X509.php',
+ 'phpseclib\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Math/BigInteger.php',
+ 'phpseclib\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Net/SFTP.php',
+ 'phpseclib\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php',
+ 'phpseclib\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib2_compat/src/Net/SSH2.php',
+ 'phpseclib\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php',
+ 'phpseclib\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php',
'voku\\helper\\ASCII' => $vendorDir . '/voku/portable-ascii/src/voku/helper/ASCII.php',
'voku\\helper\\StopWords' => $vendorDir . '/voku/stop-words/src/voku/helper/StopWords.php',
'voku\\helper\\StopWordsLanguageNotExists' => $vendorDir . '/voku/stop-words/src/voku/helper/StopWordsLanguageNotExists.php',
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index 070ea14ba..47b457816 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -8,16 +8,17 @@ $baseDir = dirname($vendorDir);
return array(
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+ 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'2b9d0f43f9552984cfa82fee95491826' => $vendorDir . '/sabre/event/lib/coroutine.php',
'd81bab31d3feb45bfe2f283ea3c8fdf7' => $vendorDir . '/sabre/event/lib/Loop/functions.php',
'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php',
'3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php',
'93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php',
+ '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
- 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
'c15d4a1253e33e055d05e547c61dcb71' => $vendorDir . '/smarty/smarty/src/functions.php',
);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index 3f54528b2..3a0f148bf 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -7,7 +7,8 @@ $baseDir = dirname($vendorDir);
return array(
'voku\\' => array($vendorDir . '/voku/stop-words/src/voku', $vendorDir . '/voku/portable-ascii/src/voku'),
- 'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
+ 'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib2_compat/src'),
+ 'phpseclib3\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
'chillerlan\\Settings\\' => array($vendorDir . '/chillerlan/php-settings-container/src'),
'chillerlan\\QRCode\\' => array($vendorDir . '/chillerlan/php-qrcode/src'),
'Zotlabs\\' => array($baseDir . '/Zotlabs'),
@@ -40,6 +41,9 @@ return array(
'LanguageDetection\\' => array($vendorDir . '/patrickschur/language-detection/src/LanguageDetection'),
'ID3Parser\\' => array($vendorDir . '/lukasreschke/id3parser/src'),
'Hubzilla\\' => array($baseDir . '/include'),
+ 'HttpSignature\\' => array($vendorDir . '/macgirvin/http-message-signer/src'),
+ 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'CommerceGuys\\Intl\\' => array($vendorDir . '/commerceguys/intl/src'),
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
+ 'Bakame\\Http\\StructuredFields\\' => array($vendorDir . '/bakame/http-structured-fields/src'),
);
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 615cc3636..7ae0881e4 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -9,16 +9,17 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
public static $files = array (
'383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+ 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'2b9d0f43f9552984cfa82fee95491826' => __DIR__ . '/..' . '/sabre/event/lib/coroutine.php',
'd81bab31d3feb45bfe2f283ea3c8fdf7' => __DIR__ . '/..' . '/sabre/event/lib/Loop/functions.php',
'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php',
'3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php',
'93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php',
+ '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
- 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
'c15d4a1253e33e055d05e547c61dcb71' => __DIR__ . '/..' . '/smarty/smarty/src/functions.php',
);
@@ -31,6 +32,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'p' =>
array (
'phpseclib\\' => 10,
+ 'phpseclib3\\' => 11,
),
'c' =>
array (
@@ -93,6 +95,11 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'H' =>
array (
'Hubzilla\\' => 9,
+ 'HttpSignature\\' => 14,
+ ),
+ 'G' =>
+ array (
+ 'GuzzleHttp\\Psr7\\' => 16,
),
'C' =>
array (
@@ -101,6 +108,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'B' =>
array (
'Brick\\Math\\' => 11,
+ 'Bakame\\Http\\StructuredFields\\' => 29,
),
);
@@ -112,6 +120,10 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
),
'phpseclib\\' =>
array (
+ 0 => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src',
+ ),
+ 'phpseclib3\\' =>
+ array (
0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
),
'chillerlan\\Settings\\' =>
@@ -244,6 +256,14 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
array (
0 => __DIR__ . '/../..' . '/include',
),
+ 'HttpSignature\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/macgirvin/http-message-signer/src',
+ ),
+ 'GuzzleHttp\\Psr7\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+ ),
'CommerceGuys\\Intl\\' =>
array (
0 => __DIR__ . '/..' . '/commerceguys/intl/src',
@@ -252,6 +272,10 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
array (
0 => __DIR__ . '/..' . '/brick/math/src',
),
+ 'Bakame\\Http\\StructuredFields\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/bakame/http-structured-fields/src',
+ ),
);
public static $prefixesPsr0 = array (
@@ -293,6 +317,36 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
);
public static $classMap = array (
+ 'Bakame\\Http\\StructuredFields\\Bytes' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Bytes.php',
+ 'Bakame\\Http\\StructuredFields\\DataType' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/DataType.php',
+ 'Bakame\\Http\\StructuredFields\\Dictionary' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Dictionary.php',
+ 'Bakame\\Http\\StructuredFields\\DisplayString' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/DisplayString.php',
+ 'Bakame\\Http\\StructuredFields\\ForbiddenOperation' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/ForbiddenOperation.php',
+ 'Bakame\\Http\\StructuredFields\\Ietf' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Ietf.php',
+ 'Bakame\\Http\\StructuredFields\\InnerList' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/InnerList.php',
+ 'Bakame\\Http\\StructuredFields\\InvalidArgument' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/InvalidArgument.php',
+ 'Bakame\\Http\\StructuredFields\\InvalidOffset' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/InvalidOffset.php',
+ 'Bakame\\Http\\StructuredFields\\Item' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Item.php',
+ 'Bakame\\Http\\StructuredFields\\Key' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Key.php',
+ 'Bakame\\Http\\StructuredFields\\Member' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Member.php',
+ 'Bakame\\Http\\StructuredFields\\MissingFeature' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/MissingFeature.php',
+ 'Bakame\\Http\\StructuredFields\\OuterList' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/OuterList.php',
+ 'Bakame\\Http\\StructuredFields\\ParameterAccess' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/ParameterAccess.php',
+ 'Bakame\\Http\\StructuredFields\\Parameters' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Parameters.php',
+ 'Bakame\\Http\\StructuredFields\\Parser' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Parser.php',
+ 'Bakame\\Http\\StructuredFields\\StructuredFieldError' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/StructuredFieldError.php',
+ 'Bakame\\Http\\StructuredFields\\StructuredFieldProvider' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/StructuredFieldProvider.php',
+ 'Bakame\\Http\\StructuredFields\\SyntaxError' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/SyntaxError.php',
+ 'Bakame\\Http\\StructuredFields\\Token' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Token.php',
+ 'Bakame\\Http\\StructuredFields\\Type' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Type.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ErrorCode' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ErrorCode.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ItemValidator' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ItemValidator.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ParametersValidator' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ParametersValidator.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\Result' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/Result.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ValidatedItem' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ValidatedItem.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ValidatedParameters' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ValidatedParameters.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\Violation' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/Violation.php',
+ 'Bakame\\Http\\StructuredFields\\Validation\\ViolationList' => __DIR__ . '/..' . '/bakame/http-structured-fields/src/Validation/ViolationList.php',
'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php',
'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php',
'Brick\\Math\\BigNumber' => __DIR__ . '/..' . '/brick/math/src/BigNumber.php',
@@ -331,6 +385,37 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepository' => __DIR__ . '/..' . '/commerceguys/intl/src/NumberFormat/NumberFormatRepository.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepositoryInterface' => __DIR__ . '/..' . '/commerceguys/intl/src/NumberFormat/NumberFormatRepositoryInterface.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'GuzzleHttp\\Psr7\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/AppendStream.php',
+ 'GuzzleHttp\\Psr7\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/BufferStream.php',
+ 'GuzzleHttp\\Psr7\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/CachingStream.php',
+ 'GuzzleHttp\\Psr7\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/DroppingStream.php',
+ 'GuzzleHttp\\Psr7\\Exception\\MalformedUriException' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Exception/MalformedUriException.php',
+ 'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php',
+ 'GuzzleHttp\\Psr7\\Header' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Header.php',
+ 'GuzzleHttp\\Psr7\\HttpFactory' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/HttpFactory.php',
+ 'GuzzleHttp\\Psr7\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/InflateStream.php',
+ 'GuzzleHttp\\Psr7\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LazyOpenStream.php',
+ 'GuzzleHttp\\Psr7\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LimitStream.php',
+ 'GuzzleHttp\\Psr7\\Message' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Message.php',
+ 'GuzzleHttp\\Psr7\\MessageTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MessageTrait.php',
+ 'GuzzleHttp\\Psr7\\MimeType' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MimeType.php',
+ 'GuzzleHttp\\Psr7\\MultipartStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MultipartStream.php',
+ 'GuzzleHttp\\Psr7\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/NoSeekStream.php',
+ 'GuzzleHttp\\Psr7\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/PumpStream.php',
+ 'GuzzleHttp\\Psr7\\Query' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Query.php',
+ 'GuzzleHttp\\Psr7\\Request' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Request.php',
+ 'GuzzleHttp\\Psr7\\Response' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Response.php',
+ 'GuzzleHttp\\Psr7\\Rfc7230' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Rfc7230.php',
+ 'GuzzleHttp\\Psr7\\ServerRequest' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/ServerRequest.php',
+ 'GuzzleHttp\\Psr7\\Stream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Stream.php',
+ 'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
+ 'GuzzleHttp\\Psr7\\StreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamWrapper.php',
+ 'GuzzleHttp\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UploadedFile.php',
+ 'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php',
+ 'GuzzleHttp\\Psr7\\UriComparator' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriComparator.php',
+ 'GuzzleHttp\\Psr7\\UriNormalizer' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriNormalizer.php',
+ 'GuzzleHttp\\Psr7\\UriResolver' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriResolver.php',
+ 'GuzzleHttp\\Psr7\\Utils' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Utils.php',
'HTMLPurifier' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.php',
'HTMLPurifier_Arborize' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php',
'HTMLPurifier_AttrCollections' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php',
@@ -563,6 +648,9 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'HTMLPurifier_VarParser_Flexible' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php',
'HTMLPurifier_VarParser_Native' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php',
'HTMLPurifier_Zipper' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php',
+ 'HttpSignature\\HttpMessageSigner' => __DIR__ . '/..' . '/macgirvin/http-message-signer/src/HttpMessageSigner.php',
+ 'HttpSignature\\StructuredFieldTypes' => __DIR__ . '/..' . '/macgirvin/http-message-signer/src/StructuredFieldTypes.php',
+ 'HttpSignature\\UnProcessableSignatureException' => __DIR__ . '/..' . '/macgirvin/http-message-signer/src/UnProcessableSignatureException.php',
'ID3Parser\\ID3Parser' => __DIR__ . '/..' . '/lukasreschke/id3parser/src/ID3Parser.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v1' => __DIR__ . '/..' . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v1.php',
'ID3Parser\\getID3\\Tags\\getid3_id3v2' => __DIR__ . '/..' . '/lukasreschke/id3parser/src/getID3/Tags/getid3_id3v2.php',
@@ -1906,6 +1994,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Lib\\Img_filesize' => __DIR__ . '/../..' . '/Zotlabs/Lib/Img_filesize.php',
'Zotlabs\\Lib\\JSalmon' => __DIR__ . '/../..' . '/Zotlabs/Lib/JSalmon.php',
'Zotlabs\\Lib\\JcsEddsa2022' => __DIR__ . '/../..' . '/Zotlabs/Lib/JcsEddsa2022.php',
+ 'Zotlabs\\Lib\\JcsEddsa2022SignException' => __DIR__ . '/../..' . '/Zotlabs/Lib/JcsEddsa2022SignException.php',
'Zotlabs\\Lib\\Keyutils' => __DIR__ . '/../..' . '/Zotlabs/Lib/Keyutils.php',
'Zotlabs\\Lib\\LDSignatures' => __DIR__ . '/../..' . '/Zotlabs/Lib/LDSignatures.php',
'Zotlabs\\Lib\\Libsync' => __DIR__ . '/../..' . '/Zotlabs/Lib/Libsync.php',
@@ -1969,14 +2058,12 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Branchtopic' => __DIR__ . '/../..' . '/Zotlabs/Module/Branchtopic.php',
'Zotlabs\\Module\\Cal' => __DIR__ . '/../..' . '/Zotlabs/Module/Cal.php',
'Zotlabs\\Module\\Cdav' => __DIR__ . '/../..' . '/Zotlabs/Module/Cdav.php',
- 'Zotlabs\\Module\\Ceditor' => __DIR__ . '/../..' . '/Zotlabs/Module/Ceditor.php',
'Zotlabs\\Module\\Changeaddr' => __DIR__ . '/../..' . '/Zotlabs/Module/Changeaddr.php',
'Zotlabs\\Module\\Channel' => __DIR__ . '/../..' . '/Zotlabs/Module/Channel.php',
'Zotlabs\\Module\\Channel_calendar' => __DIR__ . '/../..' . '/Zotlabs/Module/Channel_calendar.php',
'Zotlabs\\Module\\Chanview' => __DIR__ . '/../..' . '/Zotlabs/Module/Chanview.php',
'Zotlabs\\Module\\Chat' => __DIR__ . '/../..' . '/Zotlabs/Module/Chat.php',
'Zotlabs\\Module\\Chatsvc' => __DIR__ . '/../..' . '/Zotlabs/Module/Chatsvc.php',
- 'Zotlabs\\Module\\Cleditor' => __DIR__ . '/../..' . '/Zotlabs/Module/Cleditor.php',
'Zotlabs\\Module\\Cloud' => __DIR__ . '/../..' . '/Zotlabs/Module/Cloud.php',
'Zotlabs\\Module\\Cloud_tiles' => __DIR__ . '/../..' . '/Zotlabs/Module/Cloud_tiles.php',
'Zotlabs\\Module\\Common' => __DIR__ . '/../..' . '/Zotlabs/Module/Common.php',
@@ -2152,7 +2239,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Xchan' => __DIR__ . '/../..' . '/Zotlabs/Module/Xchan.php',
'Zotlabs\\Module\\Xpoco' => __DIR__ . '/../..' . '/Zotlabs/Module/Xpoco.php',
'Zotlabs\\Module\\Xrd' => __DIR__ . '/../..' . '/Zotlabs/Module/Xrd.php',
- 'Zotlabs\\Module\\Xref' => __DIR__ . '/../..' . '/Zotlabs/Module/Xref.php',
'Zotlabs\\Module\\Z6trans' => __DIR__ . '/../..' . '/Zotlabs/Module/Z6trans.php',
'Zotlabs\\Module\\Zot' => __DIR__ . '/../..' . '/Zotlabs/Module/Zot.php',
'Zotlabs\\Module\\Zot_probe' => __DIR__ . '/../..' . '/Zotlabs/Module/Zot_probe.php',
@@ -2579,30 +2665,363 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'chillerlan\\QRCode\\QROptionsTrait' => __DIR__ . '/..' . '/chillerlan/php-qrcode/src/QROptionsTrait.php',
'chillerlan\\Settings\\SettingsContainerAbstract' => __DIR__ . '/..' . '/chillerlan/php-settings-container/src/SettingsContainerAbstract.php',
'chillerlan\\Settings\\SettingsContainerInterface' => __DIR__ . '/..' . '/chillerlan/php-settings-container/src/SettingsContainerInterface.php',
- 'phpseclib\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
- 'phpseclib\\Crypt\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php',
- 'phpseclib\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
- 'phpseclib\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
- 'phpseclib\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
- 'phpseclib\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
- 'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
- 'phpseclib\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
- 'phpseclib\\Crypt\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
- 'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
- 'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
- 'phpseclib\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
- 'phpseclib\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
- 'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
- 'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
- 'phpseclib\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/X509.php',
- 'phpseclib\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
- 'phpseclib\\Net\\SCP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SCP.php',
- 'phpseclib\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
- 'phpseclib\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
- 'phpseclib\\Net\\SSH1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php',
- 'phpseclib\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
- 'phpseclib\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
- 'phpseclib\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
+ 'phpseclib3\\Common\\Functions\\Strings' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php',
+ 'phpseclib3\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php',
+ 'phpseclib3\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php',
+ 'phpseclib3\\Crypt\\ChaCha20' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php',
+ 'phpseclib3\\Crypt\\Common\\AsymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php',
+ 'phpseclib3\\Crypt\\Common\\BlockCipher' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\Common\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\Common\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php',
+ 'phpseclib3\\Crypt\\Common\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php',
+ 'phpseclib3\\Crypt\\Common\\StreamCipher' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php',
+ 'phpseclib3\\Crypt\\Common\\SymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php',
+ 'phpseclib3\\Crypt\\Common\\Traits\\Fingerprint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php',
+ 'phpseclib3\\Crypt\\Common\\Traits\\PasswordProtected' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php',
+ 'phpseclib3\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php',
+ 'phpseclib3\\Crypt\\DH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH.php',
+ 'phpseclib3\\Crypt\\DH\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\DH\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\DH\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php',
+ 'phpseclib3\\Crypt\\DH\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php',
+ 'phpseclib3\\Crypt\\DH\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php',
+ 'phpseclib3\\Crypt\\DSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\DSA\\Formats\\Signature\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php',
+ 'phpseclib3\\Crypt\\DSA\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php',
+ 'phpseclib3\\Crypt\\DSA\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php',
+ 'phpseclib3\\Crypt\\DSA\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php',
+ 'phpseclib3\\Crypt\\EC' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Binary' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\KoblitzPrime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\Prime' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php',
+ 'phpseclib3\\Crypt\\EC\\BaseCurves\\TwistedEdwards' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Curve25519' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Curve448' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Ed25519' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\Ed448' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP160r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP160t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP192r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP192t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP224r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP224t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP256r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP256t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP320r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP320t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP384r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP384t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP512r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\brainpoolP512t1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistb233' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistb409' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk163' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk233' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk283' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistk409' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp192' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp224' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp256' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp384' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistp521' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\nistt571' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime192v3' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime239v3' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\prime256v1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp112r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp112r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp128r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp128r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp160r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp192k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp192r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp224k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp224r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp256k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp256r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp384r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\secp521r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect113r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect113r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect131r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect131r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect163r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect193r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect193r2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect233k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect233r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect239k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect283k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect283r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect409k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect409r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect571k1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php',
+ 'phpseclib3\\Crypt\\EC\\Curves\\sect571r1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\Common' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\MontgomeryPrivate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\MontgomeryPublic' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Keys\\libsodium' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\IEEE' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php',
+ 'phpseclib3\\Crypt\\EC\\Formats\\Signature\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php',
+ 'phpseclib3\\Crypt\\EC\\Parameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php',
+ 'phpseclib3\\Crypt\\EC\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php',
+ 'phpseclib3\\Crypt\\EC\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php',
+ 'phpseclib3\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php',
+ 'phpseclib3\\Crypt\\PublicKeyLoader' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php',
+ 'phpseclib3\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
+ 'phpseclib3\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
+ 'phpseclib3\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\JWK' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\MSBLOB' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PSS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php',
+ 'phpseclib3\\Crypt\\RSA\\Formats\\Keys\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php',
+ 'phpseclib3\\Crypt\\RSA\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php',
+ 'phpseclib3\\Crypt\\RSA\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php',
+ 'phpseclib3\\Crypt\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
+ 'phpseclib3\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
+ 'phpseclib3\\Crypt\\Salsa20' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php',
+ 'phpseclib3\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
+ 'phpseclib3\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php',
+ 'phpseclib3\\Exception\\BadConfigurationException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php',
+ 'phpseclib3\\Exception\\BadDecryptionException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php',
+ 'phpseclib3\\Exception\\BadModeException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php',
+ 'phpseclib3\\Exception\\ConnectionClosedException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php',
+ 'phpseclib3\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php',
+ 'phpseclib3\\Exception\\InconsistentSetupException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php',
+ 'phpseclib3\\Exception\\InsufficientSetupException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php',
+ 'phpseclib3\\Exception\\InvalidPacketLengthException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php',
+ 'phpseclib3\\Exception\\NoKeyLoadedException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php',
+ 'phpseclib3\\Exception\\NoSupportedAlgorithmsException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php',
+ 'phpseclib3\\Exception\\TimeoutException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php',
+ 'phpseclib3\\Exception\\UnableToConnectException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php',
+ 'phpseclib3\\Exception\\UnsupportedAlgorithmException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php',
+ 'phpseclib3\\Exception\\UnsupportedCurveException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php',
+ 'phpseclib3\\Exception\\UnsupportedFormatException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php',
+ 'phpseclib3\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php',
+ 'phpseclib3\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
+ 'phpseclib3\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
+ 'phpseclib3\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AccessDescription' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AdministrationDomainName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AlgorithmIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AnotherName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Attribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeType' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeTypeAndValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AttributeValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Attributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AuthorityInfoAccessSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\AuthorityKeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BaseDistance' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BasicConstraints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInDomainDefinedAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\BuiltInStandardAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CPSuri' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLDistributionPoints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLNumber' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CRLReason' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertPolicyId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Certificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateIssuer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateList' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificatePolicies' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificateSerialNumber' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificationRequest' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CertificationRequestInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Characteristic_two' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\CountryName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Curve' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DHParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAParams' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DSAPublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DigestInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DirectoryString' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DisplayText' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DistributionPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DistributionPointName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\DssSigValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECParameters' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ECPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EDIPartyName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EcdsaSigValue' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EncryptedData' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\EncryptedPrivateKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtKeyUsageSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Extension' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtensionAttribute' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ExtensionAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Extensions' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\FieldElement' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\FieldID' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralNames' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralSubtree' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\GeneralSubtrees' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\HashAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\HoldInstructionCode' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\InvalidityDate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\IssuerAltName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\IssuingDistributionPoint' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyPurposeId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\KeyUsage' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\MaskGenAlgorithm' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Name' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NameConstraints' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NetworkAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NoticeReference' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\NumericUserIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ORAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OneAsymmetricKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OrganizationName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OrganizationalUnitNames' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OtherPrimeInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\OtherPrimeInfos' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBEParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBES2params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBKDF2params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PBMAC1params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PKCS9String' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Pentanomial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PersonalName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyInformation' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyMappings' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyQualifierId' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PolicyQualifierInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PostalAddress' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Prime_p' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateDomainName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PrivateKeyUsagePeriod' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKeyAndChallenge' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\PublicKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RC2CBCParameter' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RDNSequence' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSAPrivateKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSAPublicKey' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RSASSA_PSS_params' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\ReasonFlags' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RelativeDistinguishedName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\RevokedCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SignedPublicKeyAndChallenge' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SpecifiedECDomain' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectAltName' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectDirectoryAttributes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectInfoAccessSyntax' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\SubjectPublicKeyInfo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TBSCertList' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TBSCertificate' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\TerminalIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Time' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Trinomial' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\UniqueIdentifier' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\UserNotice' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\Validity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_ca_policy_url' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_cert_type' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php',
+ 'phpseclib3\\File\\ASN1\\Maps\\netscape_comment' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php',
+ 'phpseclib3\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/X509.php',
+ 'phpseclib3\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\BuiltIn' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\Barrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\BCMath\\Reductions\\EvalBarrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\Engine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\GMP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\GMP\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP32' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP64' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\DefaultEngine' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\OpenSSL' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Barrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Classic' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\EvalBarrett' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\Montgomery' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\MontgomeryMult' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php',
+ 'phpseclib3\\Math\\BigInteger\\Engines\\PHP\\Reductions\\PowerOfTwo' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php',
+ 'phpseclib3\\Math\\BinaryField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BinaryField.php',
+ 'phpseclib3\\Math\\BinaryField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php',
+ 'phpseclib3\\Math\\Common\\FiniteField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php',
+ 'phpseclib3\\Math\\Common\\FiniteField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php',
+ 'phpseclib3\\Math\\PrimeField' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/PrimeField.php',
+ 'phpseclib3\\Math\\PrimeField\\Integer' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php',
+ 'phpseclib3\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php',
+ 'phpseclib3\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php',
+ 'phpseclib3\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php',
+ 'phpseclib3\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php',
+ 'phpseclib3\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php',
+ 'phpseclib3\\System\\SSH\\Common\\Traits\\ReadBytes' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php',
+ 'phpseclib\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/AES.php',
+ 'phpseclib\\Crypt\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Base.php',
+ 'phpseclib\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php',
+ 'phpseclib\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/DES.php',
+ 'phpseclib\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Hash.php',
+ 'phpseclib\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/RC2.php',
+ 'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/RC4.php',
+ 'phpseclib\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/RSA.php',
+ 'phpseclib\\Crypt\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Random.php',
+ 'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php',
+ 'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php',
+ 'phpseclib\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php',
+ 'phpseclib\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/File/ANSI.php',
+ 'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/File/ASN1.php',
+ 'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php',
+ 'phpseclib\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/File/X509.php',
+ 'phpseclib\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Math/BigInteger.php',
+ 'phpseclib\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Net/SFTP.php',
+ 'phpseclib\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php',
+ 'phpseclib\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/Net/SSH2.php',
+ 'phpseclib\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php',
+ 'phpseclib\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php',
'voku\\helper\\ASCII' => __DIR__ . '/..' . '/voku/portable-ascii/src/voku/helper/ASCII.php',
'voku\\helper\\StopWords' => __DIR__ . '/..' . '/voku/stop-words/src/voku/helper/StopWords.php',
'voku\\helper\\StopWordsLanguageNotExists' => __DIR__ . '/..' . '/voku/stop-words/src/voku/helper/StopWordsLanguageNotExists.php',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 17c42b0a7..de089f123 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1,6 +1,91 @@
{
"packages": [
{
+ "name": "bakame/http-structured-fields",
+ "version": "2.0.0",
+ "version_normalized": "2.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bakame-php/http-structured-fields.git",
+ "reference": "d0fc193e5b173a4e90f2fa589d5b97b2b653b323"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bakame-php/http-structured-fields/zipball/d0fc193e5b173a4e90f2fa589d5b97b2b653b323",
+ "reference": "d0fc193e5b173a4e90f2fa589d5b97b2b653b323",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "bakame/aide-base32": "dev-main",
+ "friendsofphp/php-cs-fixer": "^3.65.0",
+ "httpwg/structured-field-tests": "*@dev",
+ "phpbench/phpbench": "^1.3.1",
+ "phpstan/phpstan": "^2.0.3",
+ "phpstan/phpstan-deprecation-rules": "^2.0.1",
+ "phpstan/phpstan-phpunit": "^2.0.1",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^10.5.38 || ^11.5.0",
+ "symfony/var-dumper": "^6.4.15 || ^v7.2.0"
+ },
+ "time": "2024-12-12T08:25:42+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Bakame\\Http\\StructuredFields\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://github.com/nyamsprod/",
+ "role": "Developer"
+ }
+ ],
+ "description": "A PHP library that parses, validates and serializes HTTP structured fields according to RFC9561 and RFC8941",
+ "keywords": [
+ "headers",
+ "http",
+ "http headers",
+ "http trailers",
+ "parser",
+ "rfc8941",
+ "rfc9651",
+ "serializer",
+ "structured fields",
+ "structured headers",
+ "structured trailers",
+ "structured values",
+ "trailers",
+ "validation"
+ ],
+ "support": {
+ "docs": "https://github.com/bakame-php/http-structured-fields",
+ "issues": "https://github.com/bakame-php/http-structured-fields/issues",
+ "source": "https://github.com/bakame-php/http-structured-fields"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "install-path": "../bakame/http-structured-fields"
+ },
+ {
"name": "blueimp/jquery-file-upload",
"version": "v10.32.0",
"version_normalized": "10.32.0.0",
@@ -522,6 +607,125 @@
"install-path": "../ezyang/htmlpurifier"
},
{
+ "name": "guzzlehttp/psr7",
+ "version": "2.7.1",
+ "version_normalized": "2.7.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+ "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "time": "2025-03-27T12:30:47+00:00",
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "issues": "https://github.com/guzzle/psr7/issues",
+ "source": "https://github.com/guzzle/psr7/tree/2.7.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/Nyholm",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../guzzlehttp/psr7"
+ },
+ {
"name": "jbroadway/urlify",
"version": "1.2.5-stable",
"version_normalized": "1.2.5.0",
@@ -907,6 +1111,52 @@
"install-path": "../lukasreschke/id3parser"
},
{
+ "name": "macgirvin/http-message-signer",
+ "version": "v0.2.2",
+ "version_normalized": "0.2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/macgirvin/HTTP-Message-Signer.git",
+ "reference": "47604de860b822cd202dcd8b1da910d6c84720ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/macgirvin/HTTP-Message-Signer/zipball/47604de860b822cd202dcd8b1da910d6c84720ab",
+ "reference": "47604de860b822cd202dcd8b1da910d6c84720ab",
+ "shasum": ""
+ },
+ "require": {
+ "bakame/http-structured-fields": "^2.0",
+ "ext-openssl": "*",
+ "guzzlehttp/psr7": "^2.0",
+ "php": "^8.1",
+ "phpseclib/phpseclib": "~3.0",
+ "phpseclib/phpseclib2_compat": "^1.0",
+ "psr/http-message": "^2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "time": "2025-07-10T01:13:05+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "HttpSignature\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "RFC 9421 HTTP Message Signer and Verifier for PSR-7 requests",
+ "support": {
+ "issues": "https://github.com/macgirvin/HTTP-Message-Signer/issues",
+ "source": "https://github.com/macgirvin/HTTP-Message-Signer/tree/v0.2.2"
+ },
+ "install-path": "../macgirvin/http-message-signer"
+ },
+ {
"name": "michelf/php-markdown",
"version": "2.0.0",
"version_normalized": "2.0.0.0",
@@ -1097,6 +1347,59 @@
"install-path": "../paragonie/constant_time_encoding"
},
{
+ "name": "paragonie/random_compat",
+ "version": "v9.99.100",
+ "version_normalized": "9.99.100.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">= 7"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.*|5.*",
+ "vimeo/psalm": "^1"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ },
+ "time": "2020-10-15T08:29:30+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "polyfill",
+ "pseudorandom",
+ "random"
+ ],
+ "support": {
+ "email": "info@paragonie.com",
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "install-path": "../paragonie/random_compat"
+ },
+ {
"name": "patrickschur/language-detection",
"version": "v5.3.1",
"version_normalized": "5.3.1.0",
@@ -1203,35 +1506,35 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "2.0.48",
- "version_normalized": "2.0.48.0",
+ "version": "3.0.46",
+ "version_normalized": "3.0.46.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61"
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/eaa7be704b8b93a6913b69eb7f645a59d7731b61",
- "reference": "eaa7be704b8b93a6913b69eb7f645a59d7731b61",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
+ "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "paragonie/constant_time_encoding": "^1|^2|^3",
+ "paragonie/random_compat": "^1.4|^2.0|^9.99.99",
+ "php": ">=5.6.1"
},
"require-dev": {
- "phing/phing": "~2.7",
- "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
- "squizlabs/php_codesniffer": "~2.0"
+ "phpunit/phpunit": "*"
},
"suggest": {
+ "ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
- "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
- "ext-xml": "Install the XML extension to load XML formatted public keys."
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
- "time": "2024-12-14T21:03:54+00:00",
+ "time": "2025-06-26T16:29:55+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1239,7 +1542,7 @@
"phpseclib/bootstrap.php"
],
"psr-4": {
- "phpseclib\\": "phpseclib/"
+ "phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1296,7 +1599,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/2.0.48"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
},
"funding": [
{
@@ -1315,6 +1618,57 @@
"install-path": "../phpseclib/phpseclib"
},
{
+ "name": "phpseclib/phpseclib2_compat",
+ "version": "1.0.6",
+ "version_normalized": "1.0.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpseclib/phpseclib2_compat.git",
+ "reference": "90976f25d6c2ff936878624b9cfaa322db11dde7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib2_compat/zipball/90976f25d6c2ff936878624b9cfaa322db11dde7",
+ "reference": "90976f25d6c2ff936878624b9cfaa322db11dde7",
+ "shasum": ""
+ },
+ "require": {
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "provide": {
+ "phpseclib/phpseclib": "2.0.47"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7|^6.0|^9.4"
+ },
+ "time": "2024-02-26T14:37:15+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "phpseclib\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "phpseclib 2.0 polyfill built with phpseclib 3.0",
+ "homepage": "https://github.com/phpseclib/phpseclib2_compat",
+ "support": {
+ "issues": "https://github.com/phpseclib/phpseclib2_compat/issues",
+ "source": "https://github.com/phpseclib/phpseclib2_compat"
+ },
+ "install-path": "../phpseclib/phpseclib2_compat"
+ },
+ {
"name": "psr/clock",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
@@ -1533,6 +1887,53 @@
"install-path": "../psr/log"
},
{
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "version_normalized": "3.0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "time": "2019-03-08T08:55:37+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "support": {
+ "issues": "https://github.com/ralouphie/getallheaders/issues",
+ "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+ },
+ "install-path": "../ralouphie/getallheaders"
+ },
+ {
"name": "ramsey/collection",
"version": "2.1.1",
"version_normalized": "2.1.1.0",
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 6fe3de607..fe56b9d2d 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -1,15 +1,24 @@
<?php return array(
'root' => array(
'name' => 'zotlabs/hubzilla',
- 'pretty_version' => 'dev-10.2RC',
- 'version' => 'dev-10.2RC',
- 'reference' => '457cb748833f0f4926668a18974b6248dac462a7',
+ 'pretty_version' => 'dev-10.4RC',
+ 'version' => 'dev-10.4RC',
+ 'reference' => '43ebf69d09a9cd779a9fcc70ea642632bb0aeeae',
'type' => 'application',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => false,
),
'versions' => array(
+ 'bakame/http-structured-fields' => array(
+ 'pretty_version' => '2.0.0',
+ 'version' => '2.0.0.0',
+ 'reference' => 'd0fc193e5b173a4e90f2fa589d5b97b2b653b323',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../bakame/http-structured-fields',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'blueimp/jquery-file-upload' => array(
'pretty_version' => 'v10.32.0',
'version' => '10.32.0.0',
@@ -82,6 +91,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'guzzlehttp/psr7' => array(
+ 'pretty_version' => '2.7.1',
+ 'version' => '2.7.1.0',
+ 'reference' => 'c2270caaabe631b3b44c85f99e5a04bbb8060d16',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../guzzlehttp/psr7',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'jbroadway/urlify' => array(
'pretty_version' => '1.2.5-stable',
'version' => '1.2.5.0',
@@ -127,6 +145,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'macgirvin/http-message-signer' => array(
+ 'pretty_version' => 'v0.2.2',
+ 'version' => '0.2.2.0',
+ 'reference' => '47604de860b822cd202dcd8b1da910d6c84720ab',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../macgirvin/http-message-signer',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'michelf/php-markdown' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
@@ -154,6 +181,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'paragonie/random_compat' => array(
+ 'pretty_version' => 'v9.99.100',
+ 'version' => '9.99.100.0',
+ 'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../paragonie/random_compat',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'patrickschur/language-detection' => array(
'pretty_version' => 'v5.3.1',
'version' => '5.3.1.0',
@@ -173,13 +209,25 @@
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
- 'pretty_version' => '2.0.48',
- 'version' => '2.0.48.0',
- 'reference' => 'eaa7be704b8b93a6913b69eb7f645a59d7731b61',
+ 'pretty_version' => '3.0.46',
+ 'version' => '3.0.46.0',
+ 'reference' => '56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6',
'type' => 'library',
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
'aliases' => array(),
'dev_requirement' => false,
+ 'provided' => array(
+ 0 => '2.0.47',
+ ),
+ ),
+ 'phpseclib/phpseclib2_compat' => array(
+ 'pretty_version' => '1.0.6',
+ 'version' => '1.0.6.0',
+ 'reference' => '90976f25d6c2ff936878624b9cfaa322db11dde7',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../phpseclib/phpseclib2_compat',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
),
'psr/clock' => array(
'pretty_version' => '1.0.0',
@@ -199,6 +247,12 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'psr/http-factory-implementation' => array(
+ 'dev_requirement' => false,
+ 'provided' => array(
+ 0 => '1.0',
+ ),
+ ),
'psr/http-message' => array(
'pretty_version' => '2.0',
'version' => '2.0.0.0',
@@ -208,6 +262,12 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'psr/http-message-implementation' => array(
+ 'dev_requirement' => false,
+ 'provided' => array(
+ 0 => '1.0',
+ ),
+ ),
'psr/log' => array(
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
@@ -217,6 +277,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'ralouphie/getallheaders' => array(
+ 'pretty_version' => '3.0.3',
+ 'version' => '3.0.3.0',
+ 'reference' => '120b605dfeb996808c31b6477290a714d356e822',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../ralouphie/getallheaders',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'ramsey/collection' => array(
'pretty_version' => '2.1.1',
'version' => '2.1.1.0',
@@ -428,9 +497,9 @@
'dev_requirement' => false,
),
'zotlabs/hubzilla' => array(
- 'pretty_version' => 'dev-10.2RC',
- 'version' => 'dev-10.2RC',
- 'reference' => '457cb748833f0f4926668a18974b6248dac462a7',
+ 'pretty_version' => 'dev-10.4RC',
+ 'version' => 'dev-10.4RC',
+ 'reference' => '43ebf69d09a9cd779a9fcc70ea642632bb0aeeae',
'type' => 'application',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md
new file mode 100644
index 000000000..a85929521
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/CHANGELOG.md
@@ -0,0 +1,475 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## 2.7.1 - 2025-03-27
+
+### Fixed
+
+- Fixed uppercase IPv6 addresses in URI
+
+### Changed
+
+- Improve uploaded file error message
+
+## 2.7.0 - 2024-07-18
+
+### Added
+
+- Add `Utils::redactUserInfo()` method
+- Add ability to encode bools as ints in `Query::build`
+
+## 2.6.3 - 2024-07-18
+
+### Fixed
+
+- Make `StreamWrapper::stream_stat()` return `false` if inner stream's size is `null`
+
+### Changed
+
+- PHP 8.4 support
+
+## 2.6.2 - 2023-12-03
+
+### Fixed
+
+- Fixed another issue with the fact that PHP transforms numeric strings in array keys to ints
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+## 2.6.1 - 2023-08-27
+
+### Fixed
+
+- Properly handle the fact that PHP transforms numeric strings in array keys to ints
+
+## 2.6.0 - 2023-08-03
+
+### Changed
+
+- Updated the mime type map to add some new entries, fix a couple of invalid entries, and remove an invalid entry
+- Fallback to `application/octet-stream` if we are unable to guess the content type for a multipart file upload
+
+## 2.5.1 - 2023-08-03
+
+### Fixed
+
+- Corrected mime type for `.acc` files to `audio/aac`
+
+### Changed
+
+- PHP 8.3 support
+
+## 2.5.0 - 2023-04-17
+
+### Changed
+
+- Adjusted `psr/http-message` version constraint to `^1.1 || ^2.0`
+
+## 2.4.5 - 2023-04-17
+
+### Fixed
+
+- Prevent possible warnings on unset variables in `ServerRequest::normalizeNestedFileSpec`
+- Fixed `Message::bodySummary` when `preg_match` fails
+- Fixed header validation issue
+
+## 2.4.4 - 2023-03-09
+
+### Changed
+
+- Removed the need for `AllowDynamicProperties` in `LazyOpenStream`
+
+## 2.4.3 - 2022-10-26
+
+### Changed
+
+- Replaced `sha1(uniqid())` by `bin2hex(random_bytes(20))`
+
+## 2.4.2 - 2022-10-25
+
+### Fixed
+
+- Fixed erroneous behaviour when combining host and relative path
+
+## 2.4.1 - 2022-08-28
+
+### Fixed
+
+- Rewind body before reading in `Message::bodySummary`
+
+## 2.4.0 - 2022-06-20
+
+### Added
+
+- Added provisional PHP 8.2 support
+- Added `UriComparator::isCrossOrigin` method
+
+## 2.3.0 - 2022-06-09
+
+### Fixed
+
+- Added `Header::splitList` method
+- Added `Utils::tryGetContents` method
+- Improved `Stream::getContents` method
+- Updated mimetype mappings
+
+## 2.2.2 - 2022-06-08
+
+### Fixed
+
+- Fix `Message::parseRequestUri` for numeric headers
+- Re-wrap exceptions thrown in `fread` into runtime exceptions
+- Throw an exception when multipart options is misformatted
+
+## 2.2.1 - 2022-03-20
+
+### Fixed
+
+- Correct header value validation
+
+## 2.2.0 - 2022-03-20
+
+### Added
+
+- A more compressive list of mime types
+- Add JsonSerializable to Uri
+- Missing return types
+
+### Fixed
+
+- Bug MultipartStream no `uri` metadata
+- Bug MultipartStream with filename for `data://` streams
+- Fixed new line handling in MultipartStream
+- Reduced RAM usage when copying streams
+- Updated parsing in `Header::normalize()`
+
+## 2.1.1 - 2022-03-20
+
+### Fixed
+
+- Validate header values properly
+
+## 2.1.0 - 2021-10-06
+
+### Changed
+
+- Attempting to create a `Uri` object from a malformed URI will no longer throw a generic
+ `InvalidArgumentException`, but rather a `MalformedUriException`, which inherits from the former
+ for backwards compatibility. Callers relying on the exception being thrown to detect invalid
+ URIs should catch the new exception.
+
+### Fixed
+
+- Return `null` in caching stream size if remote size is `null`
+
+## 2.0.0 - 2021-06-30
+
+Identical to the RC release.
+
+## 2.0.0@RC-1 - 2021-04-29
+
+### Fixed
+
+- Handle possibly unset `url` in `stream_get_meta_data`
+
+## 2.0.0@beta-1 - 2021-03-21
+
+### Added
+
+- PSR-17 factories
+- Made classes final
+- PHP7 type hints
+
+### Changed
+
+- When building a query string, booleans are represented as 1 and 0.
+
+### Removed
+
+- PHP < 7.2 support
+- All functions in the `GuzzleHttp\Psr7` namespace
+
+## 1.8.1 - 2021-03-21
+
+### Fixed
+
+- Issue parsing IPv6 URLs
+- Issue modifying ServerRequest lost all its attributes
+
+## 1.8.0 - 2021-03-21
+
+### Added
+
+- Locale independent URL parsing
+- Most classes got a `@final` annotation to prepare for 2.0
+
+### Fixed
+
+- Issue when creating stream from `php://input` and curl-ext is not installed
+- Broken `Utils::tryFopen()` on PHP 8
+
+## 1.7.0 - 2020-09-30
+
+### Added
+
+- Replaced functions by static methods
+
+### Fixed
+
+- Converting a non-seekable stream to a string
+- Handle multiple Set-Cookie correctly
+- Ignore array keys in header values when merging
+- Allow multibyte characters to be parsed in `Message:bodySummary()`
+
+### Changed
+
+- Restored partial HHVM 3 support
+
+
+## [1.6.1] - 2019-07-02
+
+### Fixed
+
+- Accept null and bool header values again
+
+
+## [1.6.0] - 2019-06-30
+
+### Added
+
+- Allowed version `^3.0` of `ralouphie/getallheaders` dependency (#244)
+- Added MIME type for WEBP image format (#246)
+- Added more validation of values according to PSR-7 and RFC standards, e.g. status code range (#250, #272)
+
+### Changed
+
+- Tests don't pass with HHVM 4.0, so HHVM support got dropped. Other libraries like composer have done the same. (#262)
+- Accept port number 0 to be valid (#270)
+
+### Fixed
+
+- Fixed subsequent reads from `php://input` in ServerRequest (#247)
+- Fixed readable/writable detection for certain stream modes (#248)
+- Fixed encoding of special characters in the `userInfo` component of an URI (#253)
+
+
+## [1.5.2] - 2018-12-04
+
+### Fixed
+
+- Check body size when getting the message summary
+
+
+## [1.5.1] - 2018-12-04
+
+### Fixed
+
+- Get the summary of a body only if it is readable
+
+
+## [1.5.0] - 2018-12-03
+
+### Added
+
+- Response first-line to response string exception (fixes #145)
+- A test for #129 behavior
+- `get_message_body_summary` function in order to get the message summary
+- `3gp` and `mkv` mime types
+
+### Changed
+
+- Clarify exception message when stream is detached
+
+### Deprecated
+
+- Deprecated parsing folded header lines as per RFC 7230
+
+### Fixed
+
+- Fix `AppendStream::detach` to not close streams
+- `InflateStream` preserves `isSeekable` attribute of the underlying stream
+- `ServerRequest::getUriFromGlobals` to support URLs in query parameters
+
+
+Several other fixes and improvements.
+
+
+## [1.4.2] - 2017-03-20
+
+### Fixed
+
+- Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
+ calls to `trigger_error` when deprecated methods are invoked.
+
+
+## [1.4.1] - 2017-02-27
+
+### Added
+
+- Rriggering of silenced deprecation warnings.
+
+### Fixed
+
+- Reverted BC break by reintroducing behavior to automagically fix a URI with a
+ relative path and an authority by adding a leading slash to the path. It's only
+ deprecated now.
+
+
+## [1.4.0] - 2017-02-21
+
+### Added
+
+- Added common URI utility methods based on RFC 3986 (see documentation in the readme):
+ - `Uri::isDefaultPort`
+ - `Uri::isAbsolute`
+ - `Uri::isNetworkPathReference`
+ - `Uri::isAbsolutePathReference`
+ - `Uri::isRelativePathReference`
+ - `Uri::isSameDocumentReference`
+ - `Uri::composeComponents`
+ - `UriNormalizer::normalize`
+ - `UriNormalizer::isEquivalent`
+ - `UriResolver::relativize`
+
+### Changed
+
+- Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
+- Allow `parse_response` to parse a response without delimiting space and reason.
+- Ensure each URI modification results in a valid URI according to PSR-7 discussions.
+ Invalid modifications will throw an exception instead of returning a wrong URI or
+ doing some magic.
+ - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
+ because the path of a URI with an authority must start with a slash "/" or be empty
+ - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+
+### Deprecated
+
+- `Uri::resolve` in favor of `UriResolver::resolve`
+- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+### Fixed
+
+- `Stream::read` when length parameter <= 0.
+- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+- `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+- Compatibility of URIs with `file` scheme and empty host.
+
+
+## [1.3.1] - 2016-06-25
+
+### Fixed
+
+- `Uri::__toString` for network path references, e.g. `//example.org`.
+- Missing lowercase normalization for host.
+- Handling of URI components in case they are `'0'` in a lot of places,
+ e.g. as a user info password.
+- `Uri::withAddedHeader` to correctly merge headers with different case.
+- Trimming of header values in `Uri::withAddedHeader`. Header values may
+ be surrounded by whitespace which should be ignored according to RFC 7230
+ Section 3.2.4. This does not apply to header names.
+- `Uri::withAddedHeader` with an array of header values.
+- `Uri::resolve` when base path has no slash and handling of fragment.
+- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+ key/value both in encoded as well as decoded form to those methods. This is
+ consistent with withPath, withQuery etc.
+- `ServerRequest::withoutAttribute` when attribute value is null.
+
+
+## [1.3.0] - 2016-04-13
+
+### Added
+
+- Remaining interfaces needed for full PSR7 compatibility
+ (ServerRequestInterface, UploadedFileInterface, etc.).
+- Support for stream_for from scalars.
+
+### Changed
+
+- Can now extend Uri.
+
+### Fixed
+- A bug in validating request methods by making it more permissive.
+
+
+## [1.2.3] - 2016-02-18
+
+### Fixed
+
+- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+ streams, which can sometimes return fewer bytes than requested with `fread`.
+- Handling of gzipped responses with FNAME headers.
+
+
+## [1.2.2] - 2016-01-22
+
+### Added
+
+- Support for URIs without any authority.
+- Support for HTTP 451 'Unavailable For Legal Reasons.'
+- Support for using '0' as a filename.
+- Support for including non-standard ports in Host headers.
+
+
+## [1.2.1] - 2015-11-02
+
+### Changes
+
+- Now supporting negative offsets when seeking to SEEK_END.
+
+
+## [1.2.0] - 2015-08-15
+
+### Changed
+
+- Body as `"0"` is now properly added to a response.
+- Now allowing forward seeking in CachingStream.
+- Now properly parsing HTTP requests that contain proxy targets in
+ `parse_request`.
+- functions.php is now conditionally required.
+- user-info is no longer dropped when resolving URIs.
+
+
+## [1.1.0] - 2015-06-24
+
+### Changed
+
+- URIs can now be relative.
+- `multipart/form-data` headers are now overridden case-insensitively.
+- URI paths no longer encode the following characters because they are allowed
+ in URIs: "(", ")", "*", "!", "'"
+- A port is no longer added to a URI when the scheme is missing and no port is
+ present.
+
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`
+
+
+
+[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
+[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
+[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
+[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
+[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
+[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
+[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
+[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
+[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
+[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
+[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
+[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
+[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
diff --git a/vendor/guzzlehttp/psr7/LICENSE b/vendor/guzzlehttp/psr7/LICENSE
new file mode 100644
index 000000000..51c7ec81c
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/LICENSE
@@ -0,0 +1,26 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Michael Dowling <mtdowling@gmail.com>
+Copyright (c) 2015 Márk Sági-Kazár <mark.sagikazar@gmail.com>
+Copyright (c) 2015 Graham Campbell <hello@gjcampbell.co.uk>
+Copyright (c) 2016 Tobias Schultze <webmaster@tubo-world.de>
+Copyright (c) 2016 George Mponos <gmponos@gmail.com>
+Copyright (c) 2018 Tobias Nyholm <tobias.nyholm@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/guzzlehttp/psr7/README.md b/vendor/guzzlehttp/psr7/README.md
new file mode 100644
index 000000000..2e9bb0b9b
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/README.md
@@ -0,0 +1,887 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+![CI](https://github.com/guzzle/psr7/workflows/CI/badge.svg)
+![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
+
+
+## Features
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## Installation
+
+```shell
+composer require guzzlehttp/psr7
+```
+
+## Version Guidance
+
+| Version | Status | PHP Version |
+|---------|---------------------|--------------|
+| 1.x | EOL (2024-06-30) | >=5.4,<8.2 |
+| 2.x | Latest | >=7.2.5,<8.5 |
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\Utils::streamFor('abc, ');
+$b = Psr7\Utils::streamFor('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## CachingStream
+
+The CachingStream is used to allow seeking over previously read bytes on
+non-seekable streams. This can be useful when transferring a non-seekable
+entity body fails due to needing to rewind the stream (for example, resulting
+from a redirect). Data that is read from the remote stream will be buffered in
+a PHP temp stream so that previously read bytes are cached first in memory,
+then on disk.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\Utils::streamFor();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\Utils::streamFor('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+ 'rewind' => function () use ($stream) {
+ echo 'About to rewind - ';
+ $stream->rewind();
+ echo 'rewound!';
+ }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
+
+This stream decorator converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\LimitStream`
+
+LimitStream can be used to read a subset or slice of an existing stream object.
+This can be useful for breaking a large file into smaller pieces to be sent in
+chunks (e.g. Amazon S3's multipart upload API).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
+echo $original->getSize();
+// >>> 1048576
+
+// Limit the size of the body to 1024 bytes and start reading from byte 2048
+$stream = new Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
+stream. Just `use` the `StreamDecoratorTrait` and implement your custom
+methods.
+
+For example, let's say we wanted to call a specific function each time the last
+byte is read from a stream. This could be implemented by overriding the
+`read()` method.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ private $callback;
+
+ private $stream;
+
+ public function __construct(StreamInterface $stream, callable $cb)
+ {
+ $this->stream = $stream;
+ $this->callback = $cb;
+ }
+
+ public function read($length)
+ {
+ $result = $this->stream->read($length);
+
+ // Invoke the callback when EOF is hit.
+ if ($this->eof()) {
+ ($this->callback)();
+ }
+
+ return $result;
+ }
+}
+```
+
+This decorator could be added to any existing stream and used like so:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\Utils::streamFor('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+ echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Static API
+
+There are various static methods available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `GuzzleHttp\Psr7\Message::toString`
+
+`public static function toString(MessageInterface $message): string`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\Message::toString($request);
+```
+
+
+## `GuzzleHttp\Psr7\Message::bodySummary`
+
+`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
+
+Get a short summary of the message body.
+
+Will return `null` if the response is not printable.
+
+
+## `GuzzleHttp\Psr7\Message::rewindBody`
+
+`public static function rewindBody(MessageInterface $message): void`
+
+Attempts to rewind a message body and throws an exception on failure.
+
+The body of the message will only be rewound if a call to `tell()`
+returns a value other than `0`.
+
+
+## `GuzzleHttp\Psr7\Message::parseMessage`
+
+`public static function parseMessage(string $message): array`
+
+Parses an HTTP message into an associative array.
+
+The array contains the "start-line" key containing the start line of
+the message, "headers" key containing an associative array of header
+array values, and a "body" key containing the body of the message.
+
+
+## `GuzzleHttp\Psr7\Message::parseRequestUri`
+
+`public static function parseRequestUri(string $path, array $headers): string`
+
+Constructs a URI for an HTTP request message.
+
+
+## `GuzzleHttp\Psr7\Message::parseRequest`
+
+`public static function parseRequest(string $message): Request`
+
+Parses a request message string into a request object.
+
+
+## `GuzzleHttp\Psr7\Message::parseResponse`
+
+`public static function parseResponse(string $message): Response`
+
+Parses a response message string into a response object.
+
+
+## `GuzzleHttp\Psr7\Header::parse`
+
+`public static function parse(string|array $header): array`
+
+Parse an array of header values containing ";" separated data into an
+array of associative arrays representing the header key value pair data
+of the header. When a parameter does not contain a value, but just
+contains a key, this function will inject a key with a '' string value.
+
+
+## `GuzzleHttp\Psr7\Header::splitList`
+
+`public static function splitList(string|string[] $header): string[]`
+
+Splits a HTTP header defined to contain a comma-separated list into
+each individual value:
+
+```
+$knownEtags = Header::splitList($request->getHeader('if-none-match'));
+```
+
+Example headers include `accept`, `cache-control` and `if-none-match`.
+
+
+## `GuzzleHttp\Psr7\Header::normalize` (deprecated)
+
+`public static function normalize(string|array $header): array`
+
+`Header::normalize()` is deprecated in favor of [`Header::splitList()`](README.md#guzzlehttppsr7headersplitlist)
+which performs the same operation with a cleaned up API and improved
+documentation.
+
+Converts an array of header values that may contain comma separated
+headers into an array of headers with no comma separated values.
+
+
+## `GuzzleHttp\Psr7\Query::parse`
+
+`public static function parse(string $str, int|bool $urlEncoding = true): array`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key
+value pair will become an array. This function does not parse nested
+PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
+will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
+
+
+## `GuzzleHttp\Psr7\Query::build`
+
+`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of `parse()` to build a query
+string. This function does not modify the provided keys when an array is
+encountered (like `http_build_query()` would).
+
+
+## `GuzzleHttp\Psr7\Utils::caselessRemove`
+
+`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
+
+Remove the items given by the keys, case insensitively from the data.
+
+
+## `GuzzleHttp\Psr7\Utils::copyToStream`
+
+`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
+
+Copy the contents of a stream into another stream until the given number
+of bytes have been read.
+
+
+## `GuzzleHttp\Psr7\Utils::copyToString`
+
+`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
+
+Copy the contents of a stream into a string until the given number of
+bytes have been read.
+
+
+## `GuzzleHttp\Psr7\Utils::hash`
+
+`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
+
+Calculate a hash of a stream.
+
+This method reads the entire stream to calculate a rolling hash, based on
+PHP's `hash_init` functions.
+
+
+## `GuzzleHttp\Psr7\Utils::modifyRequest`
+
+`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
+
+Clone and modify a request with the given changes.
+
+This method is useful for reducing the number of clones needed to mutate
+a message.
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `GuzzleHttp\Psr7\Utils::readLine`
+
+`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `GuzzleHttp\Psr7\Utils::redactUserInfo`
+
+`public static function redactUserInfo(UriInterface $uri): UriInterface`
+
+Redact the password in the user info part of a URI.
+
+
+## `GuzzleHttp\Psr7\Utils::streamFor`
+
+`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+- metadata: Array of custom metadata.
+- size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+ stream object will be created that wraps the given iterable. Each time the
+ stream is read from, data from the iterator will fill a buffer and will be
+ continuously called until the buffer is equal to the requested read size.
+ Subsequent read calls will first read from the buffer and then call `next`
+ on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+ the object will be cast to a string and then a stream will be returned that
+ uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+ created that invokes the given callable. The callable is invoked with the
+ number of suggested bytes to read. The callable can return any number of
+ bytes, but MUST return `false` when there is no more data to return. The
+ stream object that wraps the callable will invoke the callable until the
+ number of requested bytes are available. Any additional bytes will be
+ buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
+$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
+
+$generator = function ($bytes) {
+ for ($i = 0; $i < $bytes; $i++) {
+ yield ' ';
+ }
+}
+
+$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
+```
+
+
+## `GuzzleHttp\Psr7\Utils::tryFopen`
+
+`public static function tryFopen(string $filename, string $mode): resource`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an
+error handler that checks for errors and throws an exception instead.
+
+
+## `GuzzleHttp\Psr7\Utils::tryGetContents`
+
+`public static function tryGetContents(resource $stream): string`
+
+Safely gets the contents of a given stream.
+
+When stream_get_contents fails, PHP normally raises a warning. This
+function adds an error handler that checks for errors and throws an
+exception instead.
+
+
+## `GuzzleHttp\Psr7\Utils::uriFor`
+
+`public static function uriFor(string|UriInterface $uri): UriInterface`
+
+Returns a UriInterface for the given value.
+
+This function accepts a string or UriInterface and returns a
+UriInterface for the given value. If the value is already a
+UriInterface, it is returned as-is.
+
+
+## `GuzzleHttp\Psr7\MimeType::fromFilename`
+
+`public static function fromFilename(string $filename): string|null`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `GuzzleHttp\Psr7\MimeType::fromExtension`
+
+`public static function fromExtension(string $extension): string|null`
+
+Maps a file extensions to a mimetype.
+
+
+## Upgrading from Function API
+
+The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:
+
+| Original Function | Replacement Method |
+|----------------|----------------|
+| `str` | `Message::toString` |
+| `uri_for` | `Utils::uriFor` |
+| `stream_for` | `Utils::streamFor` |
+| `parse_header` | `Header::parse` |
+| `normalize_header` | `Header::normalize` |
+| `modify_request` | `Utils::modifyRequest` |
+| `rewind_body` | `Message::rewindBody` |
+| `try_fopen` | `Utils::tryFopen` |
+| `copy_to_string` | `Utils::copyToString` |
+| `copy_to_stream` | `Utils::copyToStream` |
+| `hash` | `Utils::hash` |
+| `readline` | `Utils::readLine` |
+| `parse_request` | `Message::parseRequest` |
+| `parse_response` | `Message::parseResponse` |
+| `parse_query` | `Query::parse` |
+| `build_query` | `Query::build` |
+| `mimetype_from_filename` | `MimeType::fromFilename` |
+| `mimetype_from_extension` | `MimeType::fromExtension` |
+| `_parse_message` | `Message::parseMessage` |
+| `_parse_request_uri` | `Message::parseRequestUri` |
+| `get_message_body_summary` | `Message::bodySummary` |
+| `_caseless_remove` | `Utils::caselessRemove` |
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
+to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+### `GuzzleHttp\Psr7\Uri::withQueryValues`
+
+`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
+
+Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
+associative array of key => value.
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Cross-Origin Detection
+
+`GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.
+
+### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`
+
+`public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`
+
+Determines if a modified URL should be considered cross-origin with respect to an original URL.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
+browsers do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+ Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+ All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+ Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+ Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+ ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+ not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+ characters by URI normalizers.
+
+ Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+ Converts the empty path to "/" for http and https URIs.
+
+ Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+ Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+ "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+ RFC 3986.
+
+ Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+ Removes the default port of the given URI scheme from the URI.
+
+ Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+ Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+ change the semantics of the URI reference.
+
+ Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+ Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+ and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+ may change the semantics. Encoded slashes (%2F) are not removed.
+
+ Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+ Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+ significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+ of the URI.
+
+ Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.
+
+
+## Security
+
+If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.
+
+
+## License
+
+Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
+
+
+## For Enterprise
+
+Available as part of the Tidelift Subscription
+
+The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json
new file mode 100644
index 000000000..28d15f571
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/composer.json
@@ -0,0 +1,93 @@
+{
+ "name": "guzzlehttp/psr7",
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "request",
+ "response",
+ "message",
+ "stream",
+ "http",
+ "uri",
+ "url",
+ "psr-7"
+ ],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "George Mponos",
+ "email": "gmponos@gmail.com",
+ "homepage": "https://github.com/gmponos"
+ },
+ {
+ "name": "Tobias Nyholm",
+ "email": "tobias.nyholm@gmail.com",
+ "homepage": "https://github.com/Nyholm"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://github.com/sagikazarmark"
+ },
+ {
+ "name": "Tobias Schultze",
+ "email": "webmaster@tubo-world.de",
+ "homepage": "https://github.com/Tobion"
+ },
+ {
+ "name": "Márk Sági-Kazár",
+ "email": "mark.sagikazar@gmail.com",
+ "homepage": "https://sagikazarmark.hu"
+ }
+ ],
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "ralouphie/getallheaders": "^3.0"
+ },
+ "provide": {
+ "psr/http-factory-implementation": "1.0",
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "http-interop/http-factory-tests": "0.9.0",
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20"
+ },
+ "suggest": {
+ "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "GuzzleHttp\\Tests\\Psr7\\": "tests/"
+ }
+ },
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "bamarni/composer-bin-plugin": true
+ },
+ "preferred-install": "dist",
+ "sort-packages": true
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/AppendStream.php b/vendor/guzzlehttp/psr7/src/AppendStream.php
new file mode 100644
index 000000000..ee8f37882
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/AppendStream.php
@@ -0,0 +1,248 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Reads from multiple streams, one after the other.
+ *
+ * This is a read-only stream decorator.
+ */
+final class AppendStream implements StreamInterface
+{
+ /** @var StreamInterface[] Streams being decorated */
+ private $streams = [];
+
+ /** @var bool */
+ private $seekable = true;
+
+ /** @var int */
+ private $current = 0;
+
+ /** @var int */
+ private $pos = 0;
+
+ /**
+ * @param StreamInterface[] $streams Streams to decorate. Each stream must
+ * be readable.
+ */
+ public function __construct(array $streams = [])
+ {
+ foreach ($streams as $stream) {
+ $this->addStream($stream);
+ }
+ }
+
+ public function __toString(): string
+ {
+ try {
+ $this->rewind();
+
+ return $this->getContents();
+ } catch (\Throwable $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+ return '';
+ }
+ }
+
+ /**
+ * Add a stream to the AppendStream
+ *
+ * @param StreamInterface $stream Stream to append. Must be readable.
+ *
+ * @throws \InvalidArgumentException if the stream is not readable
+ */
+ public function addStream(StreamInterface $stream): void
+ {
+ if (!$stream->isReadable()) {
+ throw new \InvalidArgumentException('Each stream must be readable');
+ }
+
+ // The stream is only seekable if all streams are seekable
+ if (!$stream->isSeekable()) {
+ $this->seekable = false;
+ }
+
+ $this->streams[] = $stream;
+ }
+
+ public function getContents(): string
+ {
+ return Utils::copyToString($this);
+ }
+
+ /**
+ * Closes each attached stream.
+ */
+ public function close(): void
+ {
+ $this->pos = $this->current = 0;
+ $this->seekable = true;
+
+ foreach ($this->streams as $stream) {
+ $stream->close();
+ }
+
+ $this->streams = [];
+ }
+
+ /**
+ * Detaches each attached stream.
+ *
+ * Returns null as it's not clear which underlying stream resource to return.
+ */
+ public function detach()
+ {
+ $this->pos = $this->current = 0;
+ $this->seekable = true;
+
+ foreach ($this->streams as $stream) {
+ $stream->detach();
+ }
+
+ $this->streams = [];
+
+ return null;
+ }
+
+ public function tell(): int
+ {
+ return $this->pos;
+ }
+
+ /**
+ * Tries to calculate the size by adding the size of each stream.
+ *
+ * If any of the streams do not return a valid number, then the size of the
+ * append stream cannot be determined and null is returned.
+ */
+ public function getSize(): ?int
+ {
+ $size = 0;
+
+ foreach ($this->streams as $stream) {
+ $s = $stream->getSize();
+ if ($s === null) {
+ return null;
+ }
+ $size += $s;
+ }
+
+ return $size;
+ }
+
+ public function eof(): bool
+ {
+ return !$this->streams
+ || ($this->current >= count($this->streams) - 1
+ && $this->streams[$this->current]->eof());
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ /**
+ * Attempts to seek to the given position. Only supports SEEK_SET.
+ */
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ if (!$this->seekable) {
+ throw new \RuntimeException('This AppendStream is not seekable');
+ } elseif ($whence !== SEEK_SET) {
+ throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+ }
+
+ $this->pos = $this->current = 0;
+
+ // Rewind each stream
+ foreach ($this->streams as $i => $stream) {
+ try {
+ $stream->rewind();
+ } catch (\Exception $e) {
+ throw new \RuntimeException('Unable to seek stream '
+ .$i.' of the AppendStream', 0, $e);
+ }
+ }
+
+ // Seek to the actual position by reading from each stream
+ while ($this->pos < $offset && !$this->eof()) {
+ $result = $this->read(min(8096, $offset - $this->pos));
+ if ($result === '') {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads from all of the appended streams until the length is met or EOF.
+ */
+ public function read($length): string
+ {
+ $buffer = '';
+ $total = count($this->streams) - 1;
+ $remaining = $length;
+ $progressToNext = false;
+
+ while ($remaining > 0) {
+ // Progress to the next stream if needed.
+ if ($progressToNext || $this->streams[$this->current]->eof()) {
+ $progressToNext = false;
+ if ($this->current === $total) {
+ break;
+ }
+ ++$this->current;
+ }
+
+ $result = $this->streams[$this->current]->read($remaining);
+
+ if ($result === '') {
+ $progressToNext = true;
+ continue;
+ }
+
+ $buffer .= $result;
+ $remaining = $length - strlen($buffer);
+ }
+
+ $this->pos += strlen($buffer);
+
+ return $buffer;
+ }
+
+ public function isReadable(): bool
+ {
+ return true;
+ }
+
+ public function isWritable(): bool
+ {
+ return false;
+ }
+
+ public function isSeekable(): bool
+ {
+ return $this->seekable;
+ }
+
+ public function write($string): int
+ {
+ throw new \RuntimeException('Cannot write to an AppendStream');
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ return $key ? null : [];
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/BufferStream.php b/vendor/guzzlehttp/psr7/src/BufferStream.php
new file mode 100644
index 000000000..2b0eb77be
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/BufferStream.php
@@ -0,0 +1,147 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a buffer stream that can be written to to fill a buffer, and read
+ * from to remove bytes from the buffer.
+ *
+ * This stream returns a "hwm" metadata value that tells upstream consumers
+ * what the configured high water mark of the stream is, or the maximum
+ * preferred size of the buffer.
+ */
+final class BufferStream implements StreamInterface
+{
+ /** @var int */
+ private $hwm;
+
+ /** @var string */
+ private $buffer = '';
+
+ /**
+ * @param int $hwm High water mark, representing the preferred maximum
+ * buffer size. If the size of the buffer exceeds the high
+ * water mark, then calls to write will continue to succeed
+ * but will return 0 to inform writers to slow down
+ * until the buffer has been drained by reading from it.
+ */
+ public function __construct(int $hwm = 16384)
+ {
+ $this->hwm = $hwm;
+ }
+
+ public function __toString(): string
+ {
+ return $this->getContents();
+ }
+
+ public function getContents(): string
+ {
+ $buffer = $this->buffer;
+ $this->buffer = '';
+
+ return $buffer;
+ }
+
+ public function close(): void
+ {
+ $this->buffer = '';
+ }
+
+ public function detach()
+ {
+ $this->close();
+
+ return null;
+ }
+
+ public function getSize(): ?int
+ {
+ return strlen($this->buffer);
+ }
+
+ public function isReadable(): bool
+ {
+ return true;
+ }
+
+ public function isWritable(): bool
+ {
+ return true;
+ }
+
+ public function isSeekable(): bool
+ {
+ return false;
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ throw new \RuntimeException('Cannot seek a BufferStream');
+ }
+
+ public function eof(): bool
+ {
+ return strlen($this->buffer) === 0;
+ }
+
+ public function tell(): int
+ {
+ throw new \RuntimeException('Cannot determine the position of a BufferStream');
+ }
+
+ /**
+ * Reads data from the buffer.
+ */
+ public function read($length): string
+ {
+ $currentLength = strlen($this->buffer);
+
+ if ($length >= $currentLength) {
+ // No need to slice the buffer because we don't have enough data.
+ $result = $this->buffer;
+ $this->buffer = '';
+ } else {
+ // Slice up the result to provide a subset of the buffer.
+ $result = substr($this->buffer, 0, $length);
+ $this->buffer = substr($this->buffer, $length);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Writes data to the buffer.
+ */
+ public function write($string): int
+ {
+ $this->buffer .= $string;
+
+ if (strlen($this->buffer) >= $this->hwm) {
+ return 0;
+ }
+
+ return strlen($string);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ if ($key === 'hwm') {
+ return $this->hwm;
+ }
+
+ return $key ? null : [];
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/CachingStream.php b/vendor/guzzlehttp/psr7/src/CachingStream.php
new file mode 100644
index 000000000..7e4554d5c
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -0,0 +1,153 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that can cache previously read bytes from a sequentially
+ * read stream.
+ */
+final class CachingStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var StreamInterface Stream being wrapped */
+ private $remoteStream;
+
+ /** @var int Number of bytes to skip reading due to a write on the buffer */
+ private $skipReadBytes = 0;
+
+ /**
+ * @var StreamInterface
+ */
+ private $stream;
+
+ /**
+ * We will treat the buffer object as the body of the stream
+ *
+ * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
+ * @param StreamInterface $target Optionally specify where data is cached
+ */
+ public function __construct(
+ StreamInterface $stream,
+ ?StreamInterface $target = null
+ ) {
+ $this->remoteStream = $stream;
+ $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
+ }
+
+ public function getSize(): ?int
+ {
+ $remoteSize = $this->remoteStream->getSize();
+
+ if (null === $remoteSize) {
+ return null;
+ }
+
+ return max($this->stream->getSize(), $remoteSize);
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ if ($whence === SEEK_SET) {
+ $byte = $offset;
+ } elseif ($whence === SEEK_CUR) {
+ $byte = $offset + $this->tell();
+ } elseif ($whence === SEEK_END) {
+ $size = $this->remoteStream->getSize();
+ if ($size === null) {
+ $size = $this->cacheEntireStream();
+ }
+ $byte = $size + $offset;
+ } else {
+ throw new \InvalidArgumentException('Invalid whence');
+ }
+
+ $diff = $byte - $this->stream->getSize();
+
+ if ($diff > 0) {
+ // Read the remoteStream until we have read in at least the amount
+ // of bytes requested, or we reach the end of the file.
+ while ($diff > 0 && !$this->remoteStream->eof()) {
+ $this->read($diff);
+ $diff = $byte - $this->stream->getSize();
+ }
+ } else {
+ // We can just do a normal seek since we've already seen this byte.
+ $this->stream->seek($byte);
+ }
+ }
+
+ public function read($length): string
+ {
+ // Perform a regular read on any previously read data from the buffer
+ $data = $this->stream->read($length);
+ $remaining = $length - strlen($data);
+
+ // More data was requested so read from the remote stream
+ if ($remaining) {
+ // If data was written to the buffer in a position that would have
+ // been filled from the remote stream, then we must skip bytes on
+ // the remote stream to emulate overwriting bytes from that
+ // position. This mimics the behavior of other PHP stream wrappers.
+ $remoteData = $this->remoteStream->read(
+ $remaining + $this->skipReadBytes
+ );
+
+ if ($this->skipReadBytes) {
+ $len = strlen($remoteData);
+ $remoteData = substr($remoteData, $this->skipReadBytes);
+ $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
+ }
+
+ $data .= $remoteData;
+ $this->stream->write($remoteData);
+ }
+
+ return $data;
+ }
+
+ public function write($string): int
+ {
+ // When appending to the end of the currently read stream, you'll want
+ // to skip bytes from being read from the remote stream to emulate
+ // other stream wrappers. Basically replacing bytes of data of a fixed
+ // length.
+ $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
+ if ($overflow > 0) {
+ $this->skipReadBytes += $overflow;
+ }
+
+ return $this->stream->write($string);
+ }
+
+ public function eof(): bool
+ {
+ return $this->stream->eof() && $this->remoteStream->eof();
+ }
+
+ /**
+ * Close both the remote stream and buffer stream
+ */
+ public function close(): void
+ {
+ $this->remoteStream->close();
+ $this->stream->close();
+ }
+
+ private function cacheEntireStream(): int
+ {
+ $target = new FnStream(['write' => 'strlen']);
+ Utils::copyToStream($this, $target);
+
+ return $this->tell();
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/DroppingStream.php b/vendor/guzzlehttp/psr7/src/DroppingStream.php
new file mode 100644
index 000000000..6e3d209d0
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/DroppingStream.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that begins dropping data once the size of the underlying
+ * stream becomes too full.
+ */
+final class DroppingStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var int */
+ private $maxLength;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ /**
+ * @param StreamInterface $stream Underlying stream to decorate.
+ * @param int $maxLength Maximum size before dropping data.
+ */
+ public function __construct(StreamInterface $stream, int $maxLength)
+ {
+ $this->stream = $stream;
+ $this->maxLength = $maxLength;
+ }
+
+ public function write($string): int
+ {
+ $diff = $this->maxLength - $this->stream->getSize();
+
+ // Begin returning 0 when the underlying stream is too large.
+ if ($diff <= 0) {
+ return 0;
+ }
+
+ // Write the stream or a subset of the stream if needed.
+ if (strlen($string) < $diff) {
+ return $this->stream->write($string);
+ }
+
+ return $this->stream->write(substr($string, 0, $diff));
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php b/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
new file mode 100644
index 000000000..3a084779a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7\Exception;
+
+use InvalidArgumentException;
+
+/**
+ * Exception thrown if a URI cannot be parsed because it's malformed.
+ */
+class MalformedUriException extends InvalidArgumentException
+{
+}
diff --git a/vendor/guzzlehttp/psr7/src/FnStream.php b/vendor/guzzlehttp/psr7/src/FnStream.php
new file mode 100644
index 000000000..9e6a7f31a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/FnStream.php
@@ -0,0 +1,180 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Compose stream implementations based on a hash of functions.
+ *
+ * Allows for easy testing and extension of a provided stream without needing
+ * to create a concrete class for a simple extension point.
+ */
+#[\AllowDynamicProperties]
+final class FnStream implements StreamInterface
+{
+ private const SLOTS = [
+ '__toString', 'close', 'detach', 'rewind',
+ 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
+ 'isReadable', 'read', 'getContents', 'getMetadata',
+ ];
+
+ /** @var array<string, callable> */
+ private $methods;
+
+ /**
+ * @param array<string, callable> $methods Hash of method name to a callable.
+ */
+ public function __construct(array $methods)
+ {
+ $this->methods = $methods;
+
+ // Create the functions on the class
+ foreach ($methods as $name => $fn) {
+ $this->{'_fn_'.$name} = $fn;
+ }
+ }
+
+ /**
+ * Lazily determine which methods are not implemented.
+ *
+ * @throws \BadMethodCallException
+ */
+ public function __get(string $name): void
+ {
+ throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+ .'() is not implemented in the FnStream');
+ }
+
+ /**
+ * The close method is called on the underlying stream only if possible.
+ */
+ public function __destruct()
+ {
+ if (isset($this->_fn_close)) {
+ ($this->_fn_close)();
+ }
+ }
+
+ /**
+ * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
+ *
+ * @throws \LogicException
+ */
+ public function __wakeup(): void
+ {
+ throw new \LogicException('FnStream should never be unserialized');
+ }
+
+ /**
+ * Adds custom functionality to an underlying stream by intercepting
+ * specific method calls.
+ *
+ * @param StreamInterface $stream Stream to decorate
+ * @param array<string, callable> $methods Hash of method name to a closure
+ *
+ * @return FnStream
+ */
+ public static function decorate(StreamInterface $stream, array $methods)
+ {
+ // If any of the required methods were not provided, then simply
+ // proxy to the decorated stream.
+ foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) {
+ /** @var callable $callable */
+ $callable = [$stream, $diff];
+ $methods[$diff] = $callable;
+ }
+
+ return new self($methods);
+ }
+
+ public function __toString(): string
+ {
+ try {
+ /** @var string */
+ return ($this->_fn___toString)();
+ } catch (\Throwable $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+ return '';
+ }
+ }
+
+ public function close(): void
+ {
+ ($this->_fn_close)();
+ }
+
+ public function detach()
+ {
+ return ($this->_fn_detach)();
+ }
+
+ public function getSize(): ?int
+ {
+ return ($this->_fn_getSize)();
+ }
+
+ public function tell(): int
+ {
+ return ($this->_fn_tell)();
+ }
+
+ public function eof(): bool
+ {
+ return ($this->_fn_eof)();
+ }
+
+ public function isSeekable(): bool
+ {
+ return ($this->_fn_isSeekable)();
+ }
+
+ public function rewind(): void
+ {
+ ($this->_fn_rewind)();
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ ($this->_fn_seek)($offset, $whence);
+ }
+
+ public function isWritable(): bool
+ {
+ return ($this->_fn_isWritable)();
+ }
+
+ public function write($string): int
+ {
+ return ($this->_fn_write)($string);
+ }
+
+ public function isReadable(): bool
+ {
+ return ($this->_fn_isReadable)();
+ }
+
+ public function read($length): string
+ {
+ return ($this->_fn_read)($length);
+ }
+
+ public function getContents(): string
+ {
+ return ($this->_fn_getContents)();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ return ($this->_fn_getMetadata)($key);
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Header.php b/vendor/guzzlehttp/psr7/src/Header.php
new file mode 100644
index 000000000..bbce8b03d
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Header.php
@@ -0,0 +1,134 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+final class Header
+{
+ /**
+ * Parse an array of header values containing ";" separated data into an
+ * array of associative arrays representing the header key value pair data
+ * of the header. When a parameter does not contain a value, but just
+ * contains a key, this function will inject a key with a '' string value.
+ *
+ * @param string|array $header Header to parse into components.
+ */
+ public static function parse($header): array
+ {
+ static $trimmed = "\"' \n\t\r";
+ $params = $matches = [];
+
+ foreach ((array) $header as $value) {
+ foreach (self::splitList($value) as $val) {
+ $part = [];
+ foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) {
+ if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
+ $m = $matches[0];
+ if (isset($m[1])) {
+ $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
+ } else {
+ $part[] = trim($m[0], $trimmed);
+ }
+ }
+ }
+ if ($part) {
+ $params[] = $part;
+ }
+ }
+ }
+
+ return $params;
+ }
+
+ /**
+ * Converts an array of header values that may contain comma separated
+ * headers into an array of headers with no comma separated values.
+ *
+ * @param string|array $header Header to normalize.
+ *
+ * @deprecated Use self::splitList() instead.
+ */
+ public static function normalize($header): array
+ {
+ $result = [];
+ foreach ((array) $header as $value) {
+ foreach (self::splitList($value) as $parsed) {
+ $result[] = $parsed;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Splits a HTTP header defined to contain a comma-separated list into
+ * each individual value. Empty values will be removed.
+ *
+ * Example headers include 'accept', 'cache-control' and 'if-none-match'.
+ *
+ * This method must not be used to parse headers that are not defined as
+ * a list, such as 'user-agent' or 'set-cookie'.
+ *
+ * @param string|string[] $values Header value as returned by MessageInterface::getHeader()
+ *
+ * @return string[]
+ */
+ public static function splitList($values): array
+ {
+ if (!\is_array($values)) {
+ $values = [$values];
+ }
+
+ $result = [];
+ foreach ($values as $value) {
+ if (!\is_string($value)) {
+ throw new \TypeError('$header must either be a string or an array containing strings.');
+ }
+
+ $v = '';
+ $isQuoted = false;
+ $isEscaped = false;
+ for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
+ if ($isEscaped) {
+ $v .= $value[$i];
+ $isEscaped = false;
+
+ continue;
+ }
+
+ if (!$isQuoted && $value[$i] === ',') {
+ $v = \trim($v);
+ if ($v !== '') {
+ $result[] = $v;
+ }
+
+ $v = '';
+ continue;
+ }
+
+ if ($isQuoted && $value[$i] === '\\') {
+ $isEscaped = true;
+ $v .= $value[$i];
+
+ continue;
+ }
+ if ($value[$i] === '"') {
+ $isQuoted = !$isQuoted;
+ $v .= $value[$i];
+
+ continue;
+ }
+
+ $v .= $value[$i];
+ }
+
+ $v = \trim($v);
+ if ($v !== '') {
+ $result[] = $v;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/HttpFactory.php b/vendor/guzzlehttp/psr7/src/HttpFactory.php
new file mode 100644
index 000000000..3ef15103a
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/HttpFactory.php
@@ -0,0 +1,94 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\RequestFactoryInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseFactoryInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestFactoryInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamFactoryInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileFactoryInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use Psr\Http\Message\UriFactoryInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Implements all of the PSR-17 interfaces.
+ *
+ * Note: in consuming code it is recommended to require the implemented interfaces
+ * and inject the instance of this class multiple times.
+ */
+final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
+{
+ public function createUploadedFile(
+ StreamInterface $stream,
+ ?int $size = null,
+ int $error = \UPLOAD_ERR_OK,
+ ?string $clientFilename = null,
+ ?string $clientMediaType = null
+ ): UploadedFileInterface {
+ if ($size === null) {
+ $size = $stream->getSize();
+ }
+
+ return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType);
+ }
+
+ public function createStream(string $content = ''): StreamInterface
+ {
+ return Utils::streamFor($content);
+ }
+
+ public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface
+ {
+ try {
+ $resource = Utils::tryFopen($file, $mode);
+ } catch (\RuntimeException $e) {
+ if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) {
+ throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e);
+ }
+
+ throw $e;
+ }
+
+ return Utils::streamFor($resource);
+ }
+
+ public function createStreamFromResource($resource): StreamInterface
+ {
+ return Utils::streamFor($resource);
+ }
+
+ public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
+ {
+ if (empty($method)) {
+ if (!empty($serverParams['REQUEST_METHOD'])) {
+ $method = $serverParams['REQUEST_METHOD'];
+ } else {
+ throw new \InvalidArgumentException('Cannot determine HTTP method');
+ }
+ }
+
+ return new ServerRequest($method, $uri, [], null, '1.1', $serverParams);
+ }
+
+ public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface
+ {
+ return new Response($code, [], null, '1.1', $reasonPhrase);
+ }
+
+ public function createRequest(string $method, $uri): RequestInterface
+ {
+ return new Request($method, $uri);
+ }
+
+ public function createUri(string $uri = ''): UriInterface
+ {
+ return new Uri($uri);
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/InflateStream.php b/vendor/guzzlehttp/psr7/src/InflateStream.php
new file mode 100644
index 000000000..e674c9ab6
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
+ *
+ * This stream decorator converts the provided stream to a PHP stream resource,
+ * then appends the zlib.inflate filter. The stream is then converted back
+ * to a Guzzle stream resource to be used as a Guzzle stream.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc1950
+ * @see https://datatracker.ietf.org/doc/html/rfc1952
+ * @see https://www.php.net/manual/en/filters.compression.php
+ */
+final class InflateStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ public function __construct(StreamInterface $stream)
+ {
+ $resource = StreamWrapper::getResource($stream);
+ // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
+ // See https://www.zlib.net/manual.html#Advanced definition of inflateInit2
+ // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
+ // Default window size is 15.
+ stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);
+ $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
new file mode 100644
index 000000000..f6c84904e
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Lazily reads or writes to a file that is opened only after an IO operation
+ * take place on the stream.
+ */
+final class LazyOpenStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var string */
+ private $filename;
+
+ /** @var string */
+ private $mode;
+
+ /**
+ * @var StreamInterface
+ */
+ private $stream;
+
+ /**
+ * @param string $filename File to lazily open
+ * @param string $mode fopen mode to use when opening the stream
+ */
+ public function __construct(string $filename, string $mode)
+ {
+ $this->filename = $filename;
+ $this->mode = $mode;
+
+ // unsetting the property forces the first access to go through
+ // __get().
+ unset($this->stream);
+ }
+
+ /**
+ * Creates the underlying stream lazily when required.
+ */
+ protected function createStream(): StreamInterface
+ {
+ return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/LimitStream.php b/vendor/guzzlehttp/psr7/src/LimitStream.php
new file mode 100644
index 000000000..fb2232557
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/LimitStream.php
@@ -0,0 +1,157 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Decorator used to return only a subset of a stream.
+ */
+final class LimitStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var int Offset to start reading from */
+ private $offset;
+
+ /** @var int Limit the number of bytes that can be read */
+ private $limit;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ /**
+ * @param StreamInterface $stream Stream to wrap
+ * @param int $limit Total number of bytes to allow to be read
+ * from the stream. Pass -1 for no limit.
+ * @param int $offset Position to seek to before reading (only
+ * works on seekable streams).
+ */
+ public function __construct(
+ StreamInterface $stream,
+ int $limit = -1,
+ int $offset = 0
+ ) {
+ $this->stream = $stream;
+ $this->setLimit($limit);
+ $this->setOffset($offset);
+ }
+
+ public function eof(): bool
+ {
+ // Always return true if the underlying stream is EOF
+ if ($this->stream->eof()) {
+ return true;
+ }
+
+ // No limit and the underlying stream is not at EOF
+ if ($this->limit === -1) {
+ return false;
+ }
+
+ return $this->stream->tell() >= $this->offset + $this->limit;
+ }
+
+ /**
+ * Returns the size of the limited subset of data
+ */
+ public function getSize(): ?int
+ {
+ if (null === ($length = $this->stream->getSize())) {
+ return null;
+ } elseif ($this->limit === -1) {
+ return $length - $this->offset;
+ } else {
+ return min($this->limit, $length - $this->offset);
+ }
+ }
+
+ /**
+ * Allow for a bounded seek on the read limited stream
+ */
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ if ($whence !== SEEK_SET || $offset < 0) {
+ throw new \RuntimeException(sprintf(
+ 'Cannot seek to offset %s with whence %s',
+ $offset,
+ $whence
+ ));
+ }
+
+ $offset += $this->offset;
+
+ if ($this->limit !== -1) {
+ if ($offset > $this->offset + $this->limit) {
+ $offset = $this->offset + $this->limit;
+ }
+ }
+
+ $this->stream->seek($offset);
+ }
+
+ /**
+ * Give a relative tell()
+ */
+ public function tell(): int
+ {
+ return $this->stream->tell() - $this->offset;
+ }
+
+ /**
+ * Set the offset to start limiting from
+ *
+ * @param int $offset Offset to seek to and begin byte limiting from
+ *
+ * @throws \RuntimeException if the stream cannot be seeked.
+ */
+ public function setOffset(int $offset): void
+ {
+ $current = $this->stream->tell();
+
+ if ($current !== $offset) {
+ // If the stream cannot seek to the offset position, then read to it
+ if ($this->stream->isSeekable()) {
+ $this->stream->seek($offset);
+ } elseif ($current > $offset) {
+ throw new \RuntimeException("Could not seek to stream offset $offset");
+ } else {
+ $this->stream->read($offset - $current);
+ }
+ }
+
+ $this->offset = $offset;
+ }
+
+ /**
+ * Set the limit of bytes that the decorator allows to be read from the
+ * stream.
+ *
+ * @param int $limit Number of bytes to allow to be read from the stream.
+ * Use -1 for no limit.
+ */
+ public function setLimit(int $limit): void
+ {
+ $this->limit = $limit;
+ }
+
+ public function read($length): string
+ {
+ if ($this->limit === -1) {
+ return $this->stream->read($length);
+ }
+
+ // Check if the current position is less than the total allowed
+ // bytes + original offset
+ $remaining = ($this->offset + $this->limit) - $this->stream->tell();
+ if ($remaining > 0) {
+ // Only return the amount of requested data, ensuring that the byte
+ // limit is not exceeded
+ return $this->stream->read(min($remaining, $length));
+ }
+
+ return '';
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Message.php b/vendor/guzzlehttp/psr7/src/Message.php
new file mode 100644
index 000000000..5561a5130
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Message.php
@@ -0,0 +1,246 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+final class Message
+{
+ /**
+ * Returns the string representation of an HTTP message.
+ *
+ * @param MessageInterface $message Message to convert to a string.
+ */
+ public static function toString(MessageInterface $message): string
+ {
+ if ($message instanceof RequestInterface) {
+ $msg = trim($message->getMethod().' '
+ .$message->getRequestTarget())
+ .' HTTP/'.$message->getProtocolVersion();
+ if (!$message->hasHeader('host')) {
+ $msg .= "\r\nHost: ".$message->getUri()->getHost();
+ }
+ } elseif ($message instanceof ResponseInterface) {
+ $msg = 'HTTP/'.$message->getProtocolVersion().' '
+ .$message->getStatusCode().' '
+ .$message->getReasonPhrase();
+ } else {
+ throw new \InvalidArgumentException('Unknown message type');
+ }
+
+ foreach ($message->getHeaders() as $name => $values) {
+ if (is_string($name) && strtolower($name) === 'set-cookie') {
+ foreach ($values as $value) {
+ $msg .= "\r\n{$name}: ".$value;
+ }
+ } else {
+ $msg .= "\r\n{$name}: ".implode(', ', $values);
+ }
+ }
+
+ return "{$msg}\r\n\r\n".$message->getBody();
+ }
+
+ /**
+ * Get a short summary of the message body.
+ *
+ * Will return `null` if the response is not printable.
+ *
+ * @param MessageInterface $message The message to get the body summary
+ * @param int $truncateAt The maximum allowed size of the summary
+ */
+ public static function bodySummary(MessageInterface $message, int $truncateAt = 120): ?string
+ {
+ $body = $message->getBody();
+
+ if (!$body->isSeekable() || !$body->isReadable()) {
+ return null;
+ }
+
+ $size = $body->getSize();
+
+ if ($size === 0) {
+ return null;
+ }
+
+ $body->rewind();
+ $summary = $body->read($truncateAt);
+ $body->rewind();
+
+ if ($size > $truncateAt) {
+ $summary .= ' (truncated...)';
+ }
+
+ // Matches any printable character, including unicode characters:
+ // letters, marks, numbers, punctuation, spacing, and separators.
+ if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
+ return null;
+ }
+
+ return $summary;
+ }
+
+ /**
+ * Attempts to rewind a message body and throws an exception on failure.
+ *
+ * The body of the message will only be rewound if a call to `tell()`
+ * returns a value other than `0`.
+ *
+ * @param MessageInterface $message Message to rewind
+ *
+ * @throws \RuntimeException
+ */
+ public static function rewindBody(MessageInterface $message): void
+ {
+ $body = $message->getBody();
+
+ if ($body->tell()) {
+ $body->rewind();
+ }
+ }
+
+ /**
+ * Parses an HTTP message into an associative array.
+ *
+ * The array contains the "start-line" key containing the start line of
+ * the message, "headers" key containing an associative array of header
+ * array values, and a "body" key containing the body of the message.
+ *
+ * @param string $message HTTP request or response to parse.
+ */
+ public static function parseMessage(string $message): array
+ {
+ if (!$message) {
+ throw new \InvalidArgumentException('Invalid message');
+ }
+
+ $message = ltrim($message, "\r\n");
+
+ $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
+
+ if ($messageParts === false || count($messageParts) !== 2) {
+ throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
+ }
+
+ [$rawHeaders, $body] = $messageParts;
+ $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
+ $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
+
+ if ($headerParts === false || count($headerParts) !== 2) {
+ throw new \InvalidArgumentException('Invalid message: Missing status line');
+ }
+
+ [$startLine, $rawHeaders] = $headerParts;
+
+ if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
+ // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
+ $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
+ }
+
+ /** @var array[] $headerLines */
+ $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
+
+ // If these aren't the same, then one line didn't match and there's an invalid header.
+ if ($count !== substr_count($rawHeaders, "\n")) {
+ // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
+ if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
+ throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
+ }
+
+ throw new \InvalidArgumentException('Invalid header syntax');
+ }
+
+ $headers = [];
+
+ foreach ($headerLines as $headerLine) {
+ $headers[$headerLine[1]][] = $headerLine[2];
+ }
+
+ return [
+ 'start-line' => $startLine,
+ 'headers' => $headers,
+ 'body' => $body,
+ ];
+ }
+
+ /**
+ * Constructs a URI for an HTTP request message.
+ *
+ * @param string $path Path from the start-line
+ * @param array $headers Array of headers (each value an array).
+ */
+ public static function parseRequestUri(string $path, array $headers): string
+ {
+ $hostKey = array_filter(array_keys($headers), function ($k) {
+ // Numeric array keys are converted to int by PHP.
+ $k = (string) $k;
+
+ return strtolower($k) === 'host';
+ });
+
+ // If no host is found, then a full URI cannot be constructed.
+ if (!$hostKey) {
+ return $path;
+ }
+
+ $host = $headers[reset($hostKey)][0];
+ $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
+
+ return $scheme.'://'.$host.'/'.ltrim($path, '/');
+ }
+
+ /**
+ * Parses a request message string into a request object.
+ *
+ * @param string $message Request message string.
+ */
+ public static function parseRequest(string $message): RequestInterface
+ {
+ $data = self::parseMessage($message);
+ $matches = [];
+ if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+ throw new \InvalidArgumentException('Invalid request string');
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+ $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
+
+ $request = new Request(
+ $parts[0],
+ $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
+ $data['headers'],
+ $data['body'],
+ $version
+ );
+
+ return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
+ }
+
+ /**
+ * Parses a response message string into a response object.
+ *
+ * @param string $message Response message string.
+ */
+ public static function parseResponse(string $message): ResponseInterface
+ {
+ $data = self::parseMessage($message);
+ // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+ // the space between status-code and reason-phrase is required. But
+ // browsers accept responses without space and reason as well.
+ if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
+ throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+
+ return new Response(
+ (int) $parts[1],
+ $data['headers'],
+ $data['body'],
+ explode('/', $parts[0])[1],
+ $parts[2] ?? null
+ );
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php
new file mode 100644
index 000000000..65dbc4ba0
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php
@@ -0,0 +1,265 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Trait implementing functionality common to requests and responses.
+ */
+trait MessageTrait
+{
+ /** @var string[][] Map of all registered headers, as original name => array of values */
+ private $headers = [];
+
+ /** @var string[] Map of lowercase header name => original name at registration */
+ private $headerNames = [];
+
+ /** @var string */
+ private $protocol = '1.1';
+
+ /** @var StreamInterface|null */
+ private $stream;
+
+ public function getProtocolVersion(): string
+ {
+ return $this->protocol;
+ }
+
+ public function withProtocolVersion($version): MessageInterface
+ {
+ if ($this->protocol === $version) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->protocol = $version;
+
+ return $new;
+ }
+
+ public function getHeaders(): array
+ {
+ return $this->headers;
+ }
+
+ public function hasHeader($header): bool
+ {
+ return isset($this->headerNames[strtolower($header)]);
+ }
+
+ public function getHeader($header): array
+ {
+ $header = strtolower($header);
+
+ if (!isset($this->headerNames[$header])) {
+ return [];
+ }
+
+ $header = $this->headerNames[$header];
+
+ return $this->headers[$header];
+ }
+
+ public function getHeaderLine($header): string
+ {
+ return implode(', ', $this->getHeader($header));
+ }
+
+ public function withHeader($header, $value): MessageInterface
+ {
+ $this->assertHeader($header);
+ $value = $this->normalizeHeaderValue($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ unset($new->headers[$new->headerNames[$normalized]]);
+ }
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+
+ return $new;
+ }
+
+ public function withAddedHeader($header, $value): MessageInterface
+ {
+ $this->assertHeader($header);
+ $value = $this->normalizeHeaderValue($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $new->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+ }
+
+ return $new;
+ }
+
+ public function withoutHeader($header): MessageInterface
+ {
+ $normalized = strtolower($header);
+
+ if (!isset($this->headerNames[$normalized])) {
+ return $this;
+ }
+
+ $header = $this->headerNames[$normalized];
+
+ $new = clone $this;
+ unset($new->headers[$header], $new->headerNames[$normalized]);
+
+ return $new;
+ }
+
+ public function getBody(): StreamInterface
+ {
+ if (!$this->stream) {
+ $this->stream = Utils::streamFor('');
+ }
+
+ return $this->stream;
+ }
+
+ public function withBody(StreamInterface $body): MessageInterface
+ {
+ if ($body === $this->stream) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->stream = $body;
+
+ return $new;
+ }
+
+ /**
+ * @param (string|string[])[] $headers
+ */
+ private function setHeaders(array $headers): void
+ {
+ $this->headerNames = $this->headers = [];
+ foreach ($headers as $header => $value) {
+ // Numeric array keys are converted to int by PHP.
+ $header = (string) $header;
+
+ $this->assertHeader($header);
+ $value = $this->normalizeHeaderValue($value);
+ $normalized = strtolower($header);
+ if (isset($this->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $this->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $this->headerNames[$normalized] = $header;
+ $this->headers[$header] = $value;
+ }
+ }
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return string[]
+ */
+ private function normalizeHeaderValue($value): array
+ {
+ if (!is_array($value)) {
+ return $this->trimAndValidateHeaderValues([$value]);
+ }
+
+ if (count($value) === 0) {
+ throw new \InvalidArgumentException('Header value can not be an empty array.');
+ }
+
+ return $this->trimAndValidateHeaderValues($value);
+ }
+
+ /**
+ * Trims whitespace from the header values.
+ *
+ * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+ *
+ * header-field = field-name ":" OWS field-value OWS
+ * OWS = *( SP / HTAB )
+ *
+ * @param mixed[] $values Header values
+ *
+ * @return string[] Trimmed header values
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
+ */
+ private function trimAndValidateHeaderValues(array $values): array
+ {
+ return array_map(function ($value) {
+ if (!is_scalar($value) && null !== $value) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Header value must be scalar or null but %s provided.',
+ is_object($value) ? get_class($value) : gettype($value)
+ ));
+ }
+
+ $trimmed = trim((string) $value, " \t");
+ $this->assertValue($trimmed);
+
+ return $trimmed;
+ }, array_values($values));
+ }
+
+ /**
+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+ *
+ * @param mixed $header
+ */
+ private function assertHeader($header): void
+ {
+ if (!is_string($header)) {
+ throw new \InvalidArgumentException(sprintf(
+ 'Header name must be a string but %s provided.',
+ is_object($header) ? get_class($header) : gettype($header)
+ ));
+ }
+
+ if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
+ throw new \InvalidArgumentException(
+ sprintf('"%s" is not valid header name.', $header)
+ );
+ }
+ }
+
+ /**
+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+ *
+ * field-value = *( field-content / obs-fold )
+ * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+ * field-vchar = VCHAR / obs-text
+ * VCHAR = %x21-7E
+ * obs-text = %x80-FF
+ * obs-fold = CRLF 1*( SP / HTAB )
+ */
+ private function assertValue(string $value): void
+ {
+ // The regular expression intentionally does not support the obs-fold production, because as
+ // per RFC 7230#3.2.4:
+ //
+ // A sender MUST NOT generate a message that includes
+ // line folding (i.e., that has any field-value that contains a match to
+ // the obs-fold rule) unless the message is intended for packaging
+ // within the message/http media type.
+ //
+ // Clients must not send a request with line folding and a server sending folded headers is
+ // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
+ // folding is not likely to break any legitimate use case.
+ if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
+ throw new \InvalidArgumentException(
+ sprintf('"%s" is not valid header value.', $value)
+ );
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MimeType.php b/vendor/guzzlehttp/psr7/src/MimeType.php
new file mode 100644
index 000000000..b131bdbe7
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MimeType.php
@@ -0,0 +1,1259 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+final class MimeType
+{
+ private const MIME_TYPES = [
+ '1km' => 'application/vnd.1000minds.decision-model+xml',
+ '3dml' => 'text/vnd.in3d.3dml',
+ '3ds' => 'image/x-3ds',
+ '3g2' => 'video/3gpp2',
+ '3gp' => 'video/3gp',
+ '3gpp' => 'video/3gpp',
+ '3mf' => 'model/3mf',
+ '7z' => 'application/x-7z-compressed',
+ '7zip' => 'application/x-7z-compressed',
+ '123' => 'application/vnd.lotus-1-2-3',
+ 'aab' => 'application/x-authorware-bin',
+ 'aac' => 'audio/aac',
+ 'aam' => 'application/x-authorware-map',
+ 'aas' => 'application/x-authorware-seg',
+ 'abw' => 'application/x-abiword',
+ 'ac' => 'application/vnd.nokia.n-gage.ac+xml',
+ 'ac3' => 'audio/ac3',
+ 'acc' => 'application/vnd.americandynamics.acc',
+ 'ace' => 'application/x-ace-compressed',
+ 'acu' => 'application/vnd.acucobol',
+ 'acutc' => 'application/vnd.acucorp',
+ 'adp' => 'audio/adpcm',
+ 'adts' => 'audio/aac',
+ 'aep' => 'application/vnd.audiograph',
+ 'afm' => 'application/x-font-type1',
+ 'afp' => 'application/vnd.ibm.modcap',
+ 'age' => 'application/vnd.age',
+ 'ahead' => 'application/vnd.ahead.space',
+ 'ai' => 'application/pdf',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'air' => 'application/vnd.adobe.air-application-installer-package+zip',
+ 'ait' => 'application/vnd.dvb.ait',
+ 'ami' => 'application/vnd.amiga.ami',
+ 'aml' => 'application/automationml-aml+xml',
+ 'amlx' => 'application/automationml-amlx+zip',
+ 'amr' => 'audio/amr',
+ 'apk' => 'application/vnd.android.package-archive',
+ 'apng' => 'image/apng',
+ 'appcache' => 'text/cache-manifest',
+ 'appinstaller' => 'application/appinstaller',
+ 'application' => 'application/x-ms-application',
+ 'appx' => 'application/appx',
+ 'appxbundle' => 'application/appxbundle',
+ 'apr' => 'application/vnd.lotus-approach',
+ 'arc' => 'application/x-freearc',
+ 'arj' => 'application/x-arj',
+ 'asc' => 'application/pgp-signature',
+ 'asf' => 'video/x-ms-asf',
+ 'asm' => 'text/x-asm',
+ 'aso' => 'application/vnd.accpac.simply.aso',
+ 'asx' => 'video/x-ms-asf',
+ 'atc' => 'application/vnd.acucorp',
+ 'atom' => 'application/atom+xml',
+ 'atomcat' => 'application/atomcat+xml',
+ 'atomdeleted' => 'application/atomdeleted+xml',
+ 'atomsvc' => 'application/atomsvc+xml',
+ 'atx' => 'application/vnd.antix.game-component',
+ 'au' => 'audio/x-au',
+ 'avci' => 'image/avci',
+ 'avcs' => 'image/avcs',
+ 'avi' => 'video/x-msvideo',
+ 'avif' => 'image/avif',
+ 'aw' => 'application/applixware',
+ 'azf' => 'application/vnd.airzip.filesecure.azf',
+ 'azs' => 'application/vnd.airzip.filesecure.azs',
+ 'azv' => 'image/vnd.airzip.accelerator.azv',
+ 'azw' => 'application/vnd.amazon.ebook',
+ 'b16' => 'image/vnd.pco.b16',
+ 'bat' => 'application/x-msdownload',
+ 'bcpio' => 'application/x-bcpio',
+ 'bdf' => 'application/x-font-bdf',
+ 'bdm' => 'application/vnd.syncml.dm+wbxml',
+ 'bdoc' => 'application/x-bdoc',
+ 'bed' => 'application/vnd.realvnc.bed',
+ 'bh2' => 'application/vnd.fujitsu.oasysprs',
+ 'bin' => 'application/octet-stream',
+ 'blb' => 'application/x-blorb',
+ 'blorb' => 'application/x-blorb',
+ 'bmi' => 'application/vnd.bmi',
+ 'bmml' => 'application/vnd.balsamiq.bmml+xml',
+ 'bmp' => 'image/bmp',
+ 'book' => 'application/vnd.framemaker',
+ 'box' => 'application/vnd.previewsystems.box',
+ 'boz' => 'application/x-bzip2',
+ 'bpk' => 'application/octet-stream',
+ 'bpmn' => 'application/octet-stream',
+ 'bsp' => 'model/vnd.valve.source.compiled-map',
+ 'btf' => 'image/prs.btif',
+ 'btif' => 'image/prs.btif',
+ 'buffer' => 'application/octet-stream',
+ 'bz' => 'application/x-bzip',
+ 'bz2' => 'application/x-bzip2',
+ 'c' => 'text/x-c',
+ 'c4d' => 'application/vnd.clonk.c4group',
+ 'c4f' => 'application/vnd.clonk.c4group',
+ 'c4g' => 'application/vnd.clonk.c4group',
+ 'c4p' => 'application/vnd.clonk.c4group',
+ 'c4u' => 'application/vnd.clonk.c4group',
+ 'c11amc' => 'application/vnd.cluetrust.cartomobile-config',
+ 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg',
+ 'cab' => 'application/vnd.ms-cab-compressed',
+ 'caf' => 'audio/x-caf',
+ 'cap' => 'application/vnd.tcpdump.pcap',
+ 'car' => 'application/vnd.curl.car',
+ 'cat' => 'application/vnd.ms-pki.seccat',
+ 'cb7' => 'application/x-cbr',
+ 'cba' => 'application/x-cbr',
+ 'cbr' => 'application/x-cbr',
+ 'cbt' => 'application/x-cbr',
+ 'cbz' => 'application/x-cbr',
+ 'cc' => 'text/x-c',
+ 'cco' => 'application/x-cocoa',
+ 'cct' => 'application/x-director',
+ 'ccxml' => 'application/ccxml+xml',
+ 'cdbcmsg' => 'application/vnd.contact.cmsg',
+ 'cdf' => 'application/x-netcdf',
+ 'cdfx' => 'application/cdfx+xml',
+ 'cdkey' => 'application/vnd.mediastation.cdkey',
+ 'cdmia' => 'application/cdmi-capability',
+ 'cdmic' => 'application/cdmi-container',
+ 'cdmid' => 'application/cdmi-domain',
+ 'cdmio' => 'application/cdmi-object',
+ 'cdmiq' => 'application/cdmi-queue',
+ 'cdr' => 'application/cdr',
+ 'cdx' => 'chemical/x-cdx',
+ 'cdxml' => 'application/vnd.chemdraw+xml',
+ 'cdy' => 'application/vnd.cinderella',
+ 'cer' => 'application/pkix-cert',
+ 'cfs' => 'application/x-cfs-compressed',
+ 'cgm' => 'image/cgm',
+ 'chat' => 'application/x-chat',
+ 'chm' => 'application/vnd.ms-htmlhelp',
+ 'chrt' => 'application/vnd.kde.kchart',
+ 'cif' => 'chemical/x-cif',
+ 'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
+ 'cil' => 'application/vnd.ms-artgalry',
+ 'cjs' => 'application/node',
+ 'cla' => 'application/vnd.claymore',
+ 'class' => 'application/octet-stream',
+ 'cld' => 'model/vnd.cld',
+ 'clkk' => 'application/vnd.crick.clicker.keyboard',
+ 'clkp' => 'application/vnd.crick.clicker.palette',
+ 'clkt' => 'application/vnd.crick.clicker.template',
+ 'clkw' => 'application/vnd.crick.clicker.wordbank',
+ 'clkx' => 'application/vnd.crick.clicker',
+ 'clp' => 'application/x-msclip',
+ 'cmc' => 'application/vnd.cosmocaller',
+ 'cmdf' => 'chemical/x-cmdf',
+ 'cml' => 'chemical/x-cml',
+ 'cmp' => 'application/vnd.yellowriver-custom-menu',
+ 'cmx' => 'image/x-cmx',
+ 'cod' => 'application/vnd.rim.cod',
+ 'coffee' => 'text/coffeescript',
+ 'com' => 'application/x-msdownload',
+ 'conf' => 'text/plain',
+ 'cpio' => 'application/x-cpio',
+ 'cpl' => 'application/cpl+xml',
+ 'cpp' => 'text/x-c',
+ 'cpt' => 'application/mac-compactpro',
+ 'crd' => 'application/x-mscardfile',
+ 'crl' => 'application/pkix-crl',
+ 'crt' => 'application/x-x509-ca-cert',
+ 'crx' => 'application/x-chrome-extension',
+ 'cryptonote' => 'application/vnd.rig.cryptonote',
+ 'csh' => 'application/x-csh',
+ 'csl' => 'application/vnd.citationstyles.style+xml',
+ 'csml' => 'chemical/x-csml',
+ 'csp' => 'application/vnd.commonspace',
+ 'csr' => 'application/octet-stream',
+ 'css' => 'text/css',
+ 'cst' => 'application/x-director',
+ 'csv' => 'text/csv',
+ 'cu' => 'application/cu-seeme',
+ 'curl' => 'text/vnd.curl',
+ 'cwl' => 'application/cwl',
+ 'cww' => 'application/prs.cww',
+ 'cxt' => 'application/x-director',
+ 'cxx' => 'text/x-c',
+ 'dae' => 'model/vnd.collada+xml',
+ 'daf' => 'application/vnd.mobius.daf',
+ 'dart' => 'application/vnd.dart',
+ 'dataless' => 'application/vnd.fdsn.seed',
+ 'davmount' => 'application/davmount+xml',
+ 'dbf' => 'application/vnd.dbf',
+ 'dbk' => 'application/docbook+xml',
+ 'dcr' => 'application/x-director',
+ 'dcurl' => 'text/vnd.curl.dcurl',
+ 'dd2' => 'application/vnd.oma.dd2+xml',
+ 'ddd' => 'application/vnd.fujixerox.ddd',
+ 'ddf' => 'application/vnd.syncml.dmddf+xml',
+ 'dds' => 'image/vnd.ms-dds',
+ 'deb' => 'application/x-debian-package',
+ 'def' => 'text/plain',
+ 'deploy' => 'application/octet-stream',
+ 'der' => 'application/x-x509-ca-cert',
+ 'dfac' => 'application/vnd.dreamfactory',
+ 'dgc' => 'application/x-dgc-compressed',
+ 'dib' => 'image/bmp',
+ 'dic' => 'text/x-c',
+ 'dir' => 'application/x-director',
+ 'dis' => 'application/vnd.mobius.dis',
+ 'disposition-notification' => 'message/disposition-notification',
+ 'dist' => 'application/octet-stream',
+ 'distz' => 'application/octet-stream',
+ 'djv' => 'image/vnd.djvu',
+ 'djvu' => 'image/vnd.djvu',
+ 'dll' => 'application/octet-stream',
+ 'dmg' => 'application/x-apple-diskimage',
+ 'dmn' => 'application/octet-stream',
+ 'dmp' => 'application/vnd.tcpdump.pcap',
+ 'dms' => 'application/octet-stream',
+ 'dna' => 'application/vnd.dna',
+ 'doc' => 'application/msword',
+ 'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dot' => 'application/msword',
+ 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'dp' => 'application/vnd.osgi.dp',
+ 'dpg' => 'application/vnd.dpgraph',
+ 'dpx' => 'image/dpx',
+ 'dra' => 'audio/vnd.dra',
+ 'drle' => 'image/dicom-rle',
+ 'dsc' => 'text/prs.lines.tag',
+ 'dssc' => 'application/dssc+der',
+ 'dtb' => 'application/x-dtbook+xml',
+ 'dtd' => 'application/xml-dtd',
+ 'dts' => 'audio/vnd.dts',
+ 'dtshd' => 'audio/vnd.dts.hd',
+ 'dump' => 'application/octet-stream',
+ 'dvb' => 'video/vnd.dvb.file',
+ 'dvi' => 'application/x-dvi',
+ 'dwd' => 'application/atsc-dwd+xml',
+ 'dwf' => 'model/vnd.dwf',
+ 'dwg' => 'image/vnd.dwg',
+ 'dxf' => 'image/vnd.dxf',
+ 'dxp' => 'application/vnd.spotfire.dxp',
+ 'dxr' => 'application/x-director',
+ 'ear' => 'application/java-archive',
+ 'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
+ 'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
+ 'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
+ 'ecma' => 'application/ecmascript',
+ 'edm' => 'application/vnd.novadigm.edm',
+ 'edx' => 'application/vnd.novadigm.edx',
+ 'efif' => 'application/vnd.picsel',
+ 'ei6' => 'application/vnd.pg.osasli',
+ 'elc' => 'application/octet-stream',
+ 'emf' => 'image/emf',
+ 'eml' => 'message/rfc822',
+ 'emma' => 'application/emma+xml',
+ 'emotionml' => 'application/emotionml+xml',
+ 'emz' => 'application/x-msmetafile',
+ 'eol' => 'audio/vnd.digital-winds',
+ 'eot' => 'application/vnd.ms-fontobject',
+ 'eps' => 'application/postscript',
+ 'epub' => 'application/epub+zip',
+ 'es3' => 'application/vnd.eszigno3+xml',
+ 'esa' => 'application/vnd.osgi.subsystem',
+ 'esf' => 'application/vnd.epson.esf',
+ 'et3' => 'application/vnd.eszigno3+xml',
+ 'etx' => 'text/x-setext',
+ 'eva' => 'application/x-eva',
+ 'evy' => 'application/x-envoy',
+ 'exe' => 'application/octet-stream',
+ 'exi' => 'application/exi',
+ 'exp' => 'application/express',
+ 'exr' => 'image/aces',
+ 'ext' => 'application/vnd.novadigm.ext',
+ 'ez' => 'application/andrew-inset',
+ 'ez2' => 'application/vnd.ezpix-album',
+ 'ez3' => 'application/vnd.ezpix-package',
+ 'f' => 'text/x-fortran',
+ 'f4v' => 'video/mp4',
+ 'f77' => 'text/x-fortran',
+ 'f90' => 'text/x-fortran',
+ 'fbs' => 'image/vnd.fastbidsheet',
+ 'fcdt' => 'application/vnd.adobe.formscentral.fcdt',
+ 'fcs' => 'application/vnd.isac.fcs',
+ 'fdf' => 'application/vnd.fdf',
+ 'fdt' => 'application/fdt+xml',
+ 'fe_launch' => 'application/vnd.denovo.fcselayout-link',
+ 'fg5' => 'application/vnd.fujitsu.oasysgp',
+ 'fgd' => 'application/x-director',
+ 'fh' => 'image/x-freehand',
+ 'fh4' => 'image/x-freehand',
+ 'fh5' => 'image/x-freehand',
+ 'fh7' => 'image/x-freehand',
+ 'fhc' => 'image/x-freehand',
+ 'fig' => 'application/x-xfig',
+ 'fits' => 'image/fits',
+ 'flac' => 'audio/x-flac',
+ 'fli' => 'video/x-fli',
+ 'flo' => 'application/vnd.micrografx.flo',
+ 'flv' => 'video/x-flv',
+ 'flw' => 'application/vnd.kde.kivio',
+ 'flx' => 'text/vnd.fmi.flexstor',
+ 'fly' => 'text/vnd.fly',
+ 'fm' => 'application/vnd.framemaker',
+ 'fnc' => 'application/vnd.frogans.fnc',
+ 'fo' => 'application/vnd.software602.filler.form+xml',
+ 'for' => 'text/x-fortran',
+ 'fpx' => 'image/vnd.fpx',
+ 'frame' => 'application/vnd.framemaker',
+ 'fsc' => 'application/vnd.fsc.weblaunch',
+ 'fst' => 'image/vnd.fst',
+ 'ftc' => 'application/vnd.fluxtime.clip',
+ 'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
+ 'fvt' => 'video/vnd.fvt',
+ 'fxp' => 'application/vnd.adobe.fxp',
+ 'fxpl' => 'application/vnd.adobe.fxp',
+ 'fzs' => 'application/vnd.fuzzysheet',
+ 'g2w' => 'application/vnd.geoplan',
+ 'g3' => 'image/g3fax',
+ 'g3w' => 'application/vnd.geospace',
+ 'gac' => 'application/vnd.groove-account',
+ 'gam' => 'application/x-tads',
+ 'gbr' => 'application/rpki-ghostbusters',
+ 'gca' => 'application/x-gca-compressed',
+ 'gdl' => 'model/vnd.gdl',
+ 'gdoc' => 'application/vnd.google-apps.document',
+ 'ged' => 'text/vnd.familysearch.gedcom',
+ 'geo' => 'application/vnd.dynageo',
+ 'geojson' => 'application/geo+json',
+ 'gex' => 'application/vnd.geometry-explorer',
+ 'ggb' => 'application/vnd.geogebra.file',
+ 'ggt' => 'application/vnd.geogebra.tool',
+ 'ghf' => 'application/vnd.groove-help',
+ 'gif' => 'image/gif',
+ 'gim' => 'application/vnd.groove-identity-message',
+ 'glb' => 'model/gltf-binary',
+ 'gltf' => 'model/gltf+json',
+ 'gml' => 'application/gml+xml',
+ 'gmx' => 'application/vnd.gmx',
+ 'gnumeric' => 'application/x-gnumeric',
+ 'gpg' => 'application/gpg-keys',
+ 'gph' => 'application/vnd.flographit',
+ 'gpx' => 'application/gpx+xml',
+ 'gqf' => 'application/vnd.grafeq',
+ 'gqs' => 'application/vnd.grafeq',
+ 'gram' => 'application/srgs',
+ 'gramps' => 'application/x-gramps-xml',
+ 'gre' => 'application/vnd.geometry-explorer',
+ 'grv' => 'application/vnd.groove-injector',
+ 'grxml' => 'application/srgs+xml',
+ 'gsf' => 'application/x-font-ghostscript',
+ 'gsheet' => 'application/vnd.google-apps.spreadsheet',
+ 'gslides' => 'application/vnd.google-apps.presentation',
+ 'gtar' => 'application/x-gtar',
+ 'gtm' => 'application/vnd.groove-tool-message',
+ 'gtw' => 'model/vnd.gtw',
+ 'gv' => 'text/vnd.graphviz',
+ 'gxf' => 'application/gxf',
+ 'gxt' => 'application/vnd.geonext',
+ 'gz' => 'application/gzip',
+ 'gzip' => 'application/gzip',
+ 'h' => 'text/x-c',
+ 'h261' => 'video/h261',
+ 'h263' => 'video/h263',
+ 'h264' => 'video/h264',
+ 'hal' => 'application/vnd.hal+xml',
+ 'hbci' => 'application/vnd.hbci',
+ 'hbs' => 'text/x-handlebars-template',
+ 'hdd' => 'application/x-virtualbox-hdd',
+ 'hdf' => 'application/x-hdf',
+ 'heic' => 'image/heic',
+ 'heics' => 'image/heic-sequence',
+ 'heif' => 'image/heif',
+ 'heifs' => 'image/heif-sequence',
+ 'hej2' => 'image/hej2k',
+ 'held' => 'application/atsc-held+xml',
+ 'hh' => 'text/x-c',
+ 'hjson' => 'application/hjson',
+ 'hlp' => 'application/winhlp',
+ 'hpgl' => 'application/vnd.hp-hpgl',
+ 'hpid' => 'application/vnd.hp-hpid',
+ 'hps' => 'application/vnd.hp-hps',
+ 'hqx' => 'application/mac-binhex40',
+ 'hsj2' => 'image/hsj2',
+ 'htc' => 'text/x-component',
+ 'htke' => 'application/vnd.kenameaapp',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'hvd' => 'application/vnd.yamaha.hv-dic',
+ 'hvp' => 'application/vnd.yamaha.hv-voice',
+ 'hvs' => 'application/vnd.yamaha.hv-script',
+ 'i2g' => 'application/vnd.intergeo',
+ 'icc' => 'application/vnd.iccprofile',
+ 'ice' => 'x-conference/x-cooltalk',
+ 'icm' => 'application/vnd.iccprofile',
+ 'ico' => 'image/x-icon',
+ 'ics' => 'text/calendar',
+ 'ief' => 'image/ief',
+ 'ifb' => 'text/calendar',
+ 'ifm' => 'application/vnd.shana.informed.formdata',
+ 'iges' => 'model/iges',
+ 'igl' => 'application/vnd.igloader',
+ 'igm' => 'application/vnd.insors.igm',
+ 'igs' => 'model/iges',
+ 'igx' => 'application/vnd.micrografx.igx',
+ 'iif' => 'application/vnd.shana.informed.interchange',
+ 'img' => 'application/octet-stream',
+ 'imp' => 'application/vnd.accpac.simply.imp',
+ 'ims' => 'application/vnd.ms-ims',
+ 'in' => 'text/plain',
+ 'ini' => 'text/plain',
+ 'ink' => 'application/inkml+xml',
+ 'inkml' => 'application/inkml+xml',
+ 'install' => 'application/x-install-instructions',
+ 'iota' => 'application/vnd.astraea-software.iota',
+ 'ipfix' => 'application/ipfix',
+ 'ipk' => 'application/vnd.shana.informed.package',
+ 'irm' => 'application/vnd.ibm.rights-management',
+ 'irp' => 'application/vnd.irepository.package+xml',
+ 'iso' => 'application/x-iso9660-image',
+ 'itp' => 'application/vnd.shana.informed.formtemplate',
+ 'its' => 'application/its+xml',
+ 'ivp' => 'application/vnd.immervision-ivp',
+ 'ivu' => 'application/vnd.immervision-ivu',
+ 'jad' => 'text/vnd.sun.j2me.app-descriptor',
+ 'jade' => 'text/jade',
+ 'jam' => 'application/vnd.jam',
+ 'jar' => 'application/java-archive',
+ 'jardiff' => 'application/x-java-archive-diff',
+ 'java' => 'text/x-java-source',
+ 'jhc' => 'image/jphc',
+ 'jisp' => 'application/vnd.jisp',
+ 'jls' => 'image/jls',
+ 'jlt' => 'application/vnd.hp-jlyt',
+ 'jng' => 'image/x-jng',
+ 'jnlp' => 'application/x-java-jnlp-file',
+ 'joda' => 'application/vnd.joost.joda-archive',
+ 'jp2' => 'image/jp2',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpf' => 'image/jpx',
+ 'jpg' => 'image/jpeg',
+ 'jpg2' => 'image/jp2',
+ 'jpgm' => 'video/jpm',
+ 'jpgv' => 'video/jpeg',
+ 'jph' => 'image/jph',
+ 'jpm' => 'video/jpm',
+ 'jpx' => 'image/jpx',
+ 'js' => 'application/javascript',
+ 'json' => 'application/json',
+ 'json5' => 'application/json5',
+ 'jsonld' => 'application/ld+json',
+ 'jsonml' => 'application/jsonml+json',
+ 'jsx' => 'text/jsx',
+ 'jt' => 'model/jt',
+ 'jxr' => 'image/jxr',
+ 'jxra' => 'image/jxra',
+ 'jxrs' => 'image/jxrs',
+ 'jxs' => 'image/jxs',
+ 'jxsc' => 'image/jxsc',
+ 'jxsi' => 'image/jxsi',
+ 'jxss' => 'image/jxss',
+ 'kar' => 'audio/midi',
+ 'karbon' => 'application/vnd.kde.karbon',
+ 'kdb' => 'application/octet-stream',
+ 'kdbx' => 'application/x-keepass2',
+ 'key' => 'application/x-iwork-keynote-sffkey',
+ 'kfo' => 'application/vnd.kde.kformula',
+ 'kia' => 'application/vnd.kidspiration',
+ 'kml' => 'application/vnd.google-earth.kml+xml',
+ 'kmz' => 'application/vnd.google-earth.kmz',
+ 'kne' => 'application/vnd.kinar',
+ 'knp' => 'application/vnd.kinar',
+ 'kon' => 'application/vnd.kde.kontour',
+ 'kpr' => 'application/vnd.kde.kpresenter',
+ 'kpt' => 'application/vnd.kde.kpresenter',
+ 'kpxx' => 'application/vnd.ds-keypoint',
+ 'ksp' => 'application/vnd.kde.kspread',
+ 'ktr' => 'application/vnd.kahootz',
+ 'ktx' => 'image/ktx',
+ 'ktx2' => 'image/ktx2',
+ 'ktz' => 'application/vnd.kahootz',
+ 'kwd' => 'application/vnd.kde.kword',
+ 'kwt' => 'application/vnd.kde.kword',
+ 'lasxml' => 'application/vnd.las.las+xml',
+ 'latex' => 'application/x-latex',
+ 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
+ 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
+ 'les' => 'application/vnd.hhe.lesson-player',
+ 'less' => 'text/less',
+ 'lgr' => 'application/lgr+xml',
+ 'lha' => 'application/octet-stream',
+ 'link66' => 'application/vnd.route66.link66+xml',
+ 'list' => 'text/plain',
+ 'list3820' => 'application/vnd.ibm.modcap',
+ 'listafp' => 'application/vnd.ibm.modcap',
+ 'litcoffee' => 'text/coffeescript',
+ 'lnk' => 'application/x-ms-shortcut',
+ 'log' => 'text/plain',
+ 'lostxml' => 'application/lost+xml',
+ 'lrf' => 'application/octet-stream',
+ 'lrm' => 'application/vnd.ms-lrm',
+ 'ltf' => 'application/vnd.frogans.ltf',
+ 'lua' => 'text/x-lua',
+ 'luac' => 'application/x-lua-bytecode',
+ 'lvp' => 'audio/vnd.lucent.voice',
+ 'lwp' => 'application/vnd.lotus-wordpro',
+ 'lzh' => 'application/octet-stream',
+ 'm1v' => 'video/mpeg',
+ 'm2a' => 'audio/mpeg',
+ 'm2v' => 'video/mpeg',
+ 'm3a' => 'audio/mpeg',
+ 'm3u' => 'text/plain',
+ 'm3u8' => 'application/vnd.apple.mpegurl',
+ 'm4a' => 'audio/x-m4a',
+ 'm4p' => 'application/mp4',
+ 'm4s' => 'video/iso.segment',
+ 'm4u' => 'application/vnd.mpegurl',
+ 'm4v' => 'video/x-m4v',
+ 'm13' => 'application/x-msmediaview',
+ 'm14' => 'application/x-msmediaview',
+ 'm21' => 'application/mp21',
+ 'ma' => 'application/mathematica',
+ 'mads' => 'application/mads+xml',
+ 'maei' => 'application/mmt-aei+xml',
+ 'mag' => 'application/vnd.ecowin.chart',
+ 'maker' => 'application/vnd.framemaker',
+ 'man' => 'text/troff',
+ 'manifest' => 'text/cache-manifest',
+ 'map' => 'application/json',
+ 'mar' => 'application/octet-stream',
+ 'markdown' => 'text/markdown',
+ 'mathml' => 'application/mathml+xml',
+ 'mb' => 'application/mathematica',
+ 'mbk' => 'application/vnd.mobius.mbk',
+ 'mbox' => 'application/mbox',
+ 'mc1' => 'application/vnd.medcalcdata',
+ 'mcd' => 'application/vnd.mcd',
+ 'mcurl' => 'text/vnd.curl.mcurl',
+ 'md' => 'text/markdown',
+ 'mdb' => 'application/x-msaccess',
+ 'mdi' => 'image/vnd.ms-modi',
+ 'mdx' => 'text/mdx',
+ 'me' => 'text/troff',
+ 'mesh' => 'model/mesh',
+ 'meta4' => 'application/metalink4+xml',
+ 'metalink' => 'application/metalink+xml',
+ 'mets' => 'application/mets+xml',
+ 'mfm' => 'application/vnd.mfmp',
+ 'mft' => 'application/rpki-manifest',
+ 'mgp' => 'application/vnd.osgeo.mapguide.package',
+ 'mgz' => 'application/vnd.proteus.magazine',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mie' => 'application/x-mie',
+ 'mif' => 'application/vnd.mif',
+ 'mime' => 'message/rfc822',
+ 'mj2' => 'video/mj2',
+ 'mjp2' => 'video/mj2',
+ 'mjs' => 'text/javascript',
+ 'mk3d' => 'video/x-matroska',
+ 'mka' => 'audio/x-matroska',
+ 'mkd' => 'text/x-markdown',
+ 'mks' => 'video/x-matroska',
+ 'mkv' => 'video/x-matroska',
+ 'mlp' => 'application/vnd.dolby.mlp',
+ 'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
+ 'mmf' => 'application/vnd.smaf',
+ 'mml' => 'text/mathml',
+ 'mmr' => 'image/vnd.fujixerox.edmics-mmr',
+ 'mng' => 'video/x-mng',
+ 'mny' => 'application/x-msmoney',
+ 'mobi' => 'application/x-mobipocket-ebook',
+ 'mods' => 'application/mods+xml',
+ 'mov' => 'video/quicktime',
+ 'movie' => 'video/x-sgi-movie',
+ 'mp2' => 'audio/mpeg',
+ 'mp2a' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mp4' => 'video/mp4',
+ 'mp4a' => 'audio/mp4',
+ 'mp4s' => 'application/mp4',
+ 'mp4v' => 'video/mp4',
+ 'mp21' => 'application/mp21',
+ 'mpc' => 'application/vnd.mophun.certificate',
+ 'mpd' => 'application/dash+xml',
+ 'mpe' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mpf' => 'application/media-policy-dataset+xml',
+ 'mpg' => 'video/mpeg',
+ 'mpg4' => 'video/mp4',
+ 'mpga' => 'audio/mpeg',
+ 'mpkg' => 'application/vnd.apple.installer+xml',
+ 'mpm' => 'application/vnd.blueice.multipass',
+ 'mpn' => 'application/vnd.mophun.application',
+ 'mpp' => 'application/vnd.ms-project',
+ 'mpt' => 'application/vnd.ms-project',
+ 'mpy' => 'application/vnd.ibm.minipay',
+ 'mqy' => 'application/vnd.mobius.mqy',
+ 'mrc' => 'application/marc',
+ 'mrcx' => 'application/marcxml+xml',
+ 'ms' => 'text/troff',
+ 'mscml' => 'application/mediaservercontrol+xml',
+ 'mseed' => 'application/vnd.fdsn.mseed',
+ 'mseq' => 'application/vnd.mseq',
+ 'msf' => 'application/vnd.epson.msf',
+ 'msg' => 'application/vnd.ms-outlook',
+ 'msh' => 'model/mesh',
+ 'msi' => 'application/x-msdownload',
+ 'msix' => 'application/msix',
+ 'msixbundle' => 'application/msixbundle',
+ 'msl' => 'application/vnd.mobius.msl',
+ 'msm' => 'application/octet-stream',
+ 'msp' => 'application/octet-stream',
+ 'msty' => 'application/vnd.muvee.style',
+ 'mtl' => 'model/mtl',
+ 'mts' => 'model/vnd.mts',
+ 'mus' => 'application/vnd.musician',
+ 'musd' => 'application/mmt-usd+xml',
+ 'musicxml' => 'application/vnd.recordare.musicxml+xml',
+ 'mvb' => 'application/x-msmediaview',
+ 'mvt' => 'application/vnd.mapbox-vector-tile',
+ 'mwf' => 'application/vnd.mfer',
+ 'mxf' => 'application/mxf',
+ 'mxl' => 'application/vnd.recordare.musicxml',
+ 'mxmf' => 'audio/mobile-xmf',
+ 'mxml' => 'application/xv+xml',
+ 'mxs' => 'application/vnd.triscape.mxs',
+ 'mxu' => 'video/vnd.mpegurl',
+ 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
+ 'n3' => 'text/n3',
+ 'nb' => 'application/mathematica',
+ 'nbp' => 'application/vnd.wolfram.player',
+ 'nc' => 'application/x-netcdf',
+ 'ncx' => 'application/x-dtbncx+xml',
+ 'nfo' => 'text/x-nfo',
+ 'ngdat' => 'application/vnd.nokia.n-gage.data',
+ 'nitf' => 'application/vnd.nitf',
+ 'nlu' => 'application/vnd.neurolanguage.nlu',
+ 'nml' => 'application/vnd.enliven',
+ 'nnd' => 'application/vnd.noblenet-directory',
+ 'nns' => 'application/vnd.noblenet-sealer',
+ 'nnw' => 'application/vnd.noblenet-web',
+ 'npx' => 'image/vnd.net-fpx',
+ 'nq' => 'application/n-quads',
+ 'nsc' => 'application/x-conference',
+ 'nsf' => 'application/vnd.lotus-notes',
+ 'nt' => 'application/n-triples',
+ 'ntf' => 'application/vnd.nitf',
+ 'numbers' => 'application/x-iwork-numbers-sffnumbers',
+ 'nzb' => 'application/x-nzb',
+ 'oa2' => 'application/vnd.fujitsu.oasys2',
+ 'oa3' => 'application/vnd.fujitsu.oasys3',
+ 'oas' => 'application/vnd.fujitsu.oasys',
+ 'obd' => 'application/x-msbinder',
+ 'obgx' => 'application/vnd.openblox.game+xml',
+ 'obj' => 'model/obj',
+ 'oda' => 'application/oda',
+ 'odb' => 'application/vnd.oasis.opendocument.database',
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
+ 'odft' => 'application/vnd.oasis.opendocument.formula-template',
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
+ 'odi' => 'application/vnd.oasis.opendocument.image',
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'oga' => 'audio/ogg',
+ 'ogex' => 'model/vnd.opengex',
+ 'ogg' => 'audio/ogg',
+ 'ogv' => 'video/ogg',
+ 'ogx' => 'application/ogg',
+ 'omdoc' => 'application/omdoc+xml',
+ 'onepkg' => 'application/onenote',
+ 'onetmp' => 'application/onenote',
+ 'onetoc' => 'application/onenote',
+ 'onetoc2' => 'application/onenote',
+ 'opf' => 'application/oebps-package+xml',
+ 'opml' => 'text/x-opml',
+ 'oprc' => 'application/vnd.palm',
+ 'opus' => 'audio/ogg',
+ 'org' => 'text/x-org',
+ 'osf' => 'application/vnd.yamaha.openscoreformat',
+ 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml',
+ 'osm' => 'application/vnd.openstreetmap.data+xml',
+ 'otc' => 'application/vnd.oasis.opendocument.chart-template',
+ 'otf' => 'font/otf',
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+ 'oth' => 'application/vnd.oasis.opendocument.text-web',
+ 'oti' => 'application/vnd.oasis.opendocument.image-template',
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
+ 'ova' => 'application/x-virtualbox-ova',
+ 'ovf' => 'application/x-virtualbox-ovf',
+ 'owl' => 'application/rdf+xml',
+ 'oxps' => 'application/oxps',
+ 'oxt' => 'application/vnd.openofficeorg.extension',
+ 'p' => 'text/x-pascal',
+ 'p7a' => 'application/x-pkcs7-signature',
+ 'p7b' => 'application/x-pkcs7-certificates',
+ 'p7c' => 'application/pkcs7-mime',
+ 'p7m' => 'application/pkcs7-mime',
+ 'p7r' => 'application/x-pkcs7-certreqresp',
+ 'p7s' => 'application/pkcs7-signature',
+ 'p8' => 'application/pkcs8',
+ 'p10' => 'application/x-pkcs10',
+ 'p12' => 'application/x-pkcs12',
+ 'pac' => 'application/x-ns-proxy-autoconfig',
+ 'pages' => 'application/x-iwork-pages-sffpages',
+ 'pas' => 'text/x-pascal',
+ 'paw' => 'application/vnd.pawaafile',
+ 'pbd' => 'application/vnd.powerbuilder6',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pcap' => 'application/vnd.tcpdump.pcap',
+ 'pcf' => 'application/x-font-pcf',
+ 'pcl' => 'application/vnd.hp-pcl',
+ 'pclxl' => 'application/vnd.hp-pclxl',
+ 'pct' => 'image/x-pict',
+ 'pcurl' => 'application/vnd.curl.pcurl',
+ 'pcx' => 'image/x-pcx',
+ 'pdb' => 'application/x-pilot',
+ 'pde' => 'text/x-processing',
+ 'pdf' => 'application/pdf',
+ 'pem' => 'application/x-x509-user-cert',
+ 'pfa' => 'application/x-font-type1',
+ 'pfb' => 'application/x-font-type1',
+ 'pfm' => 'application/x-font-type1',
+ 'pfr' => 'application/font-tdpfr',
+ 'pfx' => 'application/x-pkcs12',
+ 'pgm' => 'image/x-portable-graymap',
+ 'pgn' => 'application/x-chess-pgn',
+ 'pgp' => 'application/pgp',
+ 'phar' => 'application/octet-stream',
+ 'php' => 'application/x-httpd-php',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'phtml' => 'application/x-httpd-php',
+ 'pic' => 'image/x-pict',
+ 'pkg' => 'application/octet-stream',
+ 'pki' => 'application/pkixcmp',
+ 'pkipath' => 'application/pkix-pkipath',
+ 'pkpass' => 'application/vnd.apple.pkpass',
+ 'pl' => 'application/x-perl',
+ 'plb' => 'application/vnd.3gpp.pic-bw-large',
+ 'plc' => 'application/vnd.mobius.plc',
+ 'plf' => 'application/vnd.pocketlearn',
+ 'pls' => 'application/pls+xml',
+ 'pm' => 'application/x-perl',
+ 'pml' => 'application/vnd.ctc-posml',
+ 'png' => 'image/png',
+ 'pnm' => 'image/x-portable-anymap',
+ 'portpkg' => 'application/vnd.macports.portpkg',
+ 'pot' => 'application/vnd.ms-powerpoint',
+ 'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppa' => 'application/vnd.ms-powerpoint',
+ 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
+ 'ppd' => 'application/vnd.cups-ppd',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'pps' => 'application/vnd.ms-powerpoint',
+ 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'ppt' => 'application/powerpoint',
+ 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'pqa' => 'application/vnd.palm',
+ 'prc' => 'model/prc',
+ 'pre' => 'application/vnd.lotus-freelance',
+ 'prf' => 'application/pics-rules',
+ 'provx' => 'application/provenance+xml',
+ 'ps' => 'application/postscript',
+ 'psb' => 'application/vnd.3gpp.pic-bw-small',
+ 'psd' => 'application/x-photoshop',
+ 'psf' => 'application/x-font-linux-psf',
+ 'pskcxml' => 'application/pskc+xml',
+ 'pti' => 'image/prs.pti',
+ 'ptid' => 'application/vnd.pvi.ptid1',
+ 'pub' => 'application/x-mspublisher',
+ 'pvb' => 'application/vnd.3gpp.pic-bw-var',
+ 'pwn' => 'application/vnd.3m.post-it-notes',
+ 'pya' => 'audio/vnd.ms-playready.media.pya',
+ 'pyo' => 'model/vnd.pytha.pyox',
+ 'pyox' => 'model/vnd.pytha.pyox',
+ 'pyv' => 'video/vnd.ms-playready.media.pyv',
+ 'qam' => 'application/vnd.epson.quickanime',
+ 'qbo' => 'application/vnd.intu.qbo',
+ 'qfx' => 'application/vnd.intu.qfx',
+ 'qps' => 'application/vnd.publishare-delta-tree',
+ 'qt' => 'video/quicktime',
+ 'qwd' => 'application/vnd.quark.quarkxpress',
+ 'qwt' => 'application/vnd.quark.quarkxpress',
+ 'qxb' => 'application/vnd.quark.quarkxpress',
+ 'qxd' => 'application/vnd.quark.quarkxpress',
+ 'qxl' => 'application/vnd.quark.quarkxpress',
+ 'qxt' => 'application/vnd.quark.quarkxpress',
+ 'ra' => 'audio/x-realaudio',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'raml' => 'application/raml+yaml',
+ 'rapd' => 'application/route-apd+xml',
+ 'rar' => 'application/x-rar',
+ 'ras' => 'image/x-cmu-raster',
+ 'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
+ 'rdf' => 'application/rdf+xml',
+ 'rdz' => 'application/vnd.data-vision.rdz',
+ 'relo' => 'application/p2p-overlay+xml',
+ 'rep' => 'application/vnd.businessobjects',
+ 'res' => 'application/x-dtbresource+xml',
+ 'rgb' => 'image/x-rgb',
+ 'rif' => 'application/reginfo+xml',
+ 'rip' => 'audio/vnd.rip',
+ 'ris' => 'application/x-research-info-systems',
+ 'rl' => 'application/resource-lists+xml',
+ 'rlc' => 'image/vnd.fujixerox.edmics-rlc',
+ 'rld' => 'application/resource-lists-diff+xml',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rmi' => 'audio/midi',
+ 'rmp' => 'audio/x-pn-realaudio-plugin',
+ 'rms' => 'application/vnd.jcp.javame.midlet-rms',
+ 'rmvb' => 'application/vnd.rn-realmedia-vbr',
+ 'rnc' => 'application/relax-ng-compact-syntax',
+ 'rng' => 'application/xml',
+ 'roa' => 'application/rpki-roa',
+ 'roff' => 'text/troff',
+ 'rp9' => 'application/vnd.cloanto.rp9',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'rpss' => 'application/vnd.nokia.radio-presets',
+ 'rpst' => 'application/vnd.nokia.radio-preset',
+ 'rq' => 'application/sparql-query',
+ 'rs' => 'application/rls-services+xml',
+ 'rsa' => 'application/x-pkcs7',
+ 'rsat' => 'application/atsc-rsat+xml',
+ 'rsd' => 'application/rsd+xml',
+ 'rsheet' => 'application/urc-ressheet+xml',
+ 'rss' => 'application/rss+xml',
+ 'rtf' => 'text/rtf',
+ 'rtx' => 'text/richtext',
+ 'run' => 'application/x-makeself',
+ 'rusd' => 'application/route-usd+xml',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 's' => 'text/x-asm',
+ 's3m' => 'audio/s3m',
+ 'saf' => 'application/vnd.yamaha.smaf-audio',
+ 'sass' => 'text/x-sass',
+ 'sbml' => 'application/sbml+xml',
+ 'sc' => 'application/vnd.ibm.secure-container',
+ 'scd' => 'application/x-msschedule',
+ 'scm' => 'application/vnd.lotus-screencam',
+ 'scq' => 'application/scvp-cv-request',
+ 'scs' => 'application/scvp-cv-response',
+ 'scss' => 'text/x-scss',
+ 'scurl' => 'text/vnd.curl.scurl',
+ 'sda' => 'application/vnd.stardivision.draw',
+ 'sdc' => 'application/vnd.stardivision.calc',
+ 'sdd' => 'application/vnd.stardivision.impress',
+ 'sdkd' => 'application/vnd.solent.sdkm+xml',
+ 'sdkm' => 'application/vnd.solent.sdkm+xml',
+ 'sdp' => 'application/sdp',
+ 'sdw' => 'application/vnd.stardivision.writer',
+ 'sea' => 'application/octet-stream',
+ 'see' => 'application/vnd.seemail',
+ 'seed' => 'application/vnd.fdsn.seed',
+ 'sema' => 'application/vnd.sema',
+ 'semd' => 'application/vnd.semd',
+ 'semf' => 'application/vnd.semf',
+ 'senmlx' => 'application/senml+xml',
+ 'sensmlx' => 'application/sensml+xml',
+ 'ser' => 'application/java-serialized-object',
+ 'setpay' => 'application/set-payment-initiation',
+ 'setreg' => 'application/set-registration-initiation',
+ 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data',
+ 'sfs' => 'application/vnd.spotfire.sfs',
+ 'sfv' => 'text/x-sfv',
+ 'sgi' => 'image/sgi',
+ 'sgl' => 'application/vnd.stardivision.writer-global',
+ 'sgm' => 'text/sgml',
+ 'sgml' => 'text/sgml',
+ 'sh' => 'application/x-sh',
+ 'shar' => 'application/x-shar',
+ 'shex' => 'text/shex',
+ 'shf' => 'application/shf+xml',
+ 'shtml' => 'text/html',
+ 'sid' => 'image/x-mrsid-image',
+ 'sieve' => 'application/sieve',
+ 'sig' => 'application/pgp-signature',
+ 'sil' => 'audio/silk',
+ 'silo' => 'model/mesh',
+ 'sis' => 'application/vnd.symbian.install',
+ 'sisx' => 'application/vnd.symbian.install',
+ 'sit' => 'application/x-stuffit',
+ 'sitx' => 'application/x-stuffitx',
+ 'siv' => 'application/sieve',
+ 'skd' => 'application/vnd.koan',
+ 'skm' => 'application/vnd.koan',
+ 'skp' => 'application/vnd.koan',
+ 'skt' => 'application/vnd.koan',
+ 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12',
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+ 'slim' => 'text/slim',
+ 'slm' => 'text/slim',
+ 'sls' => 'application/route-s-tsid+xml',
+ 'slt' => 'application/vnd.epson.salt',
+ 'sm' => 'application/vnd.stepmania.stepchart',
+ 'smf' => 'application/vnd.stardivision.math',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'smv' => 'video/x-smv',
+ 'smzip' => 'application/vnd.stepmania.package',
+ 'snd' => 'audio/basic',
+ 'snf' => 'application/x-font-snf',
+ 'so' => 'application/octet-stream',
+ 'spc' => 'application/x-pkcs7-certificates',
+ 'spdx' => 'text/spdx',
+ 'spf' => 'application/vnd.yamaha.smaf-phrase',
+ 'spl' => 'application/x-futuresplash',
+ 'spot' => 'text/vnd.in3d.spot',
+ 'spp' => 'application/scvp-vp-response',
+ 'spq' => 'application/scvp-vp-request',
+ 'spx' => 'audio/ogg',
+ 'sql' => 'application/x-sql',
+ 'src' => 'application/x-wais-source',
+ 'srt' => 'application/x-subrip',
+ 'sru' => 'application/sru+xml',
+ 'srx' => 'application/sparql-results+xml',
+ 'ssdl' => 'application/ssdl+xml',
+ 'sse' => 'application/vnd.kodak-descriptor',
+ 'ssf' => 'application/vnd.epson.ssf',
+ 'ssml' => 'application/ssml+xml',
+ 'sst' => 'application/octet-stream',
+ 'st' => 'application/vnd.sailingtracker.track',
+ 'stc' => 'application/vnd.sun.xml.calc.template',
+ 'std' => 'application/vnd.sun.xml.draw.template',
+ 'step' => 'application/STEP',
+ 'stf' => 'application/vnd.wt.stf',
+ 'sti' => 'application/vnd.sun.xml.impress.template',
+ 'stk' => 'application/hyperstudio',
+ 'stl' => 'model/stl',
+ 'stp' => 'application/STEP',
+ 'stpx' => 'model/step+xml',
+ 'stpxz' => 'model/step-xml+zip',
+ 'stpz' => 'model/step+zip',
+ 'str' => 'application/vnd.pg.format',
+ 'stw' => 'application/vnd.sun.xml.writer.template',
+ 'styl' => 'text/stylus',
+ 'stylus' => 'text/stylus',
+ 'sub' => 'text/vnd.dvb.subtitle',
+ 'sus' => 'application/vnd.sus-calendar',
+ 'susp' => 'application/vnd.sus-calendar',
+ 'sv4cpio' => 'application/x-sv4cpio',
+ 'sv4crc' => 'application/x-sv4crc',
+ 'svc' => 'application/vnd.dvb.service',
+ 'svd' => 'application/vnd.svd',
+ 'svg' => 'image/svg+xml',
+ 'svgz' => 'image/svg+xml',
+ 'swa' => 'application/x-director',
+ 'swf' => 'application/x-shockwave-flash',
+ 'swi' => 'application/vnd.aristanetworks.swi',
+ 'swidtag' => 'application/swid+xml',
+ 'sxc' => 'application/vnd.sun.xml.calc',
+ 'sxd' => 'application/vnd.sun.xml.draw',
+ 'sxg' => 'application/vnd.sun.xml.writer.global',
+ 'sxi' => 'application/vnd.sun.xml.impress',
+ 'sxm' => 'application/vnd.sun.xml.math',
+ 'sxw' => 'application/vnd.sun.xml.writer',
+ 't' => 'text/troff',
+ 't3' => 'application/x-t3vm-image',
+ 't38' => 'image/t38',
+ 'taglet' => 'application/vnd.mynfc',
+ 'tao' => 'application/vnd.tao.intent-module-archive',
+ 'tap' => 'image/vnd.tencent.tap',
+ 'tar' => 'application/x-tar',
+ 'tcap' => 'application/vnd.3gpp2.tcap',
+ 'tcl' => 'application/x-tcl',
+ 'td' => 'application/urc-targetdesc+xml',
+ 'teacher' => 'application/vnd.smart.teacher',
+ 'tei' => 'application/tei+xml',
+ 'teicorpus' => 'application/tei+xml',
+ 'tex' => 'application/x-tex',
+ 'texi' => 'application/x-texinfo',
+ 'texinfo' => 'application/x-texinfo',
+ 'text' => 'text/plain',
+ 'tfi' => 'application/thraud+xml',
+ 'tfm' => 'application/x-tex-tfm',
+ 'tfx' => 'image/tiff-fx',
+ 'tga' => 'image/x-tga',
+ 'tgz' => 'application/x-tar',
+ 'thmx' => 'application/vnd.ms-officetheme',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'tk' => 'application/x-tcl',
+ 'tmo' => 'application/vnd.tmobile-livetv',
+ 'toml' => 'application/toml',
+ 'torrent' => 'application/x-bittorrent',
+ 'tpl' => 'application/vnd.groove-tool-template',
+ 'tpt' => 'application/vnd.trid.tpt',
+ 'tr' => 'text/troff',
+ 'tra' => 'application/vnd.trueapp',
+ 'trig' => 'application/trig',
+ 'trm' => 'application/x-msterminal',
+ 'ts' => 'video/mp2t',
+ 'tsd' => 'application/timestamped-data',
+ 'tsv' => 'text/tab-separated-values',
+ 'ttc' => 'font/collection',
+ 'ttf' => 'font/ttf',
+ 'ttl' => 'text/turtle',
+ 'ttml' => 'application/ttml+xml',
+ 'twd' => 'application/vnd.simtech-mindmapper',
+ 'twds' => 'application/vnd.simtech-mindmapper',
+ 'txd' => 'application/vnd.genomatix.tuxedo',
+ 'txf' => 'application/vnd.mobius.txf',
+ 'txt' => 'text/plain',
+ 'u3d' => 'model/u3d',
+ 'u8dsn' => 'message/global-delivery-status',
+ 'u8hdr' => 'message/global-headers',
+ 'u8mdn' => 'message/global-disposition-notification',
+ 'u8msg' => 'message/global',
+ 'u32' => 'application/x-authorware-bin',
+ 'ubj' => 'application/ubjson',
+ 'udeb' => 'application/x-debian-package',
+ 'ufd' => 'application/vnd.ufdl',
+ 'ufdl' => 'application/vnd.ufdl',
+ 'ulx' => 'application/x-glulx',
+ 'umj' => 'application/vnd.umajin',
+ 'unityweb' => 'application/vnd.unity',
+ 'uo' => 'application/vnd.uoml+xml',
+ 'uoml' => 'application/vnd.uoml+xml',
+ 'uri' => 'text/uri-list',
+ 'uris' => 'text/uri-list',
+ 'urls' => 'text/uri-list',
+ 'usda' => 'model/vnd.usda',
+ 'usdz' => 'model/vnd.usdz+zip',
+ 'ustar' => 'application/x-ustar',
+ 'utz' => 'application/vnd.uiq.theme',
+ 'uu' => 'text/x-uuencode',
+ 'uva' => 'audio/vnd.dece.audio',
+ 'uvd' => 'application/vnd.dece.data',
+ 'uvf' => 'application/vnd.dece.data',
+ 'uvg' => 'image/vnd.dece.graphic',
+ 'uvh' => 'video/vnd.dece.hd',
+ 'uvi' => 'image/vnd.dece.graphic',
+ 'uvm' => 'video/vnd.dece.mobile',
+ 'uvp' => 'video/vnd.dece.pd',
+ 'uvs' => 'video/vnd.dece.sd',
+ 'uvt' => 'application/vnd.dece.ttml+xml',
+ 'uvu' => 'video/vnd.uvvu.mp4',
+ 'uvv' => 'video/vnd.dece.video',
+ 'uvva' => 'audio/vnd.dece.audio',
+ 'uvvd' => 'application/vnd.dece.data',
+ 'uvvf' => 'application/vnd.dece.data',
+ 'uvvg' => 'image/vnd.dece.graphic',
+ 'uvvh' => 'video/vnd.dece.hd',
+ 'uvvi' => 'image/vnd.dece.graphic',
+ 'uvvm' => 'video/vnd.dece.mobile',
+ 'uvvp' => 'video/vnd.dece.pd',
+ 'uvvs' => 'video/vnd.dece.sd',
+ 'uvvt' => 'application/vnd.dece.ttml+xml',
+ 'uvvu' => 'video/vnd.uvvu.mp4',
+ 'uvvv' => 'video/vnd.dece.video',
+ 'uvvx' => 'application/vnd.dece.unspecified',
+ 'uvvz' => 'application/vnd.dece.zip',
+ 'uvx' => 'application/vnd.dece.unspecified',
+ 'uvz' => 'application/vnd.dece.zip',
+ 'vbox' => 'application/x-virtualbox-vbox',
+ 'vbox-extpack' => 'application/x-virtualbox-vbox-extpack',
+ 'vcard' => 'text/vcard',
+ 'vcd' => 'application/x-cdlink',
+ 'vcf' => 'text/x-vcard',
+ 'vcg' => 'application/vnd.groove-vcard',
+ 'vcs' => 'text/x-vcalendar',
+ 'vcx' => 'application/vnd.vcx',
+ 'vdi' => 'application/x-virtualbox-vdi',
+ 'vds' => 'model/vnd.sap.vds',
+ 'vhd' => 'application/x-virtualbox-vhd',
+ 'vis' => 'application/vnd.visionary',
+ 'viv' => 'video/vnd.vivo',
+ 'vlc' => 'application/videolan',
+ 'vmdk' => 'application/x-virtualbox-vmdk',
+ 'vob' => 'video/x-ms-vob',
+ 'vor' => 'application/vnd.stardivision.writer',
+ 'vox' => 'application/x-authorware-bin',
+ 'vrml' => 'model/vrml',
+ 'vsd' => 'application/vnd.visio',
+ 'vsf' => 'application/vnd.vsf',
+ 'vss' => 'application/vnd.visio',
+ 'vst' => 'application/vnd.visio',
+ 'vsw' => 'application/vnd.visio',
+ 'vtf' => 'image/vnd.valve.source.texture',
+ 'vtt' => 'text/vtt',
+ 'vtu' => 'model/vnd.vtu',
+ 'vxml' => 'application/voicexml+xml',
+ 'w3d' => 'application/x-director',
+ 'wad' => 'application/x-doom',
+ 'wadl' => 'application/vnd.sun.wadl+xml',
+ 'war' => 'application/java-archive',
+ 'wasm' => 'application/wasm',
+ 'wav' => 'audio/x-wav',
+ 'wax' => 'audio/x-ms-wax',
+ 'wbmp' => 'image/vnd.wap.wbmp',
+ 'wbs' => 'application/vnd.criticaltools.wbs+xml',
+ 'wbxml' => 'application/wbxml',
+ 'wcm' => 'application/vnd.ms-works',
+ 'wdb' => 'application/vnd.ms-works',
+ 'wdp' => 'image/vnd.ms-photo',
+ 'weba' => 'audio/webm',
+ 'webapp' => 'application/x-web-app-manifest+json',
+ 'webm' => 'video/webm',
+ 'webmanifest' => 'application/manifest+json',
+ 'webp' => 'image/webp',
+ 'wg' => 'application/vnd.pmi.widget',
+ 'wgsl' => 'text/wgsl',
+ 'wgt' => 'application/widget',
+ 'wif' => 'application/watcherinfo+xml',
+ 'wks' => 'application/vnd.ms-works',
+ 'wm' => 'video/x-ms-wm',
+ 'wma' => 'audio/x-ms-wma',
+ 'wmd' => 'application/x-ms-wmd',
+ 'wmf' => 'image/wmf',
+ 'wml' => 'text/vnd.wap.wml',
+ 'wmlc' => 'application/wmlc',
+ 'wmls' => 'text/vnd.wap.wmlscript',
+ 'wmlsc' => 'application/vnd.wap.wmlscriptc',
+ 'wmv' => 'video/x-ms-wmv',
+ 'wmx' => 'video/x-ms-wmx',
+ 'wmz' => 'application/x-msmetafile',
+ 'woff' => 'font/woff',
+ 'woff2' => 'font/woff2',
+ 'word' => 'application/msword',
+ 'wpd' => 'application/vnd.wordperfect',
+ 'wpl' => 'application/vnd.ms-wpl',
+ 'wps' => 'application/vnd.ms-works',
+ 'wqd' => 'application/vnd.wqd',
+ 'wri' => 'application/x-mswrite',
+ 'wrl' => 'model/vrml',
+ 'wsc' => 'message/vnd.wfa.wsc',
+ 'wsdl' => 'application/wsdl+xml',
+ 'wspolicy' => 'application/wspolicy+xml',
+ 'wtb' => 'application/vnd.webturbo',
+ 'wvx' => 'video/x-ms-wvx',
+ 'x3d' => 'model/x3d+xml',
+ 'x3db' => 'model/x3d+fastinfoset',
+ 'x3dbz' => 'model/x3d+binary',
+ 'x3dv' => 'model/x3d-vrml',
+ 'x3dvz' => 'model/x3d+vrml',
+ 'x3dz' => 'model/x3d+xml',
+ 'x32' => 'application/x-authorware-bin',
+ 'x_b' => 'model/vnd.parasolid.transmit.binary',
+ 'x_t' => 'model/vnd.parasolid.transmit.text',
+ 'xaml' => 'application/xaml+xml',
+ 'xap' => 'application/x-silverlight-app',
+ 'xar' => 'application/vnd.xara',
+ 'xav' => 'application/xcap-att+xml',
+ 'xbap' => 'application/x-ms-xbap',
+ 'xbd' => 'application/vnd.fujixerox.docuworks.binder',
+ 'xbm' => 'image/x-xbitmap',
+ 'xca' => 'application/xcap-caps+xml',
+ 'xcs' => 'application/calendar+xml',
+ 'xdf' => 'application/xcap-diff+xml',
+ 'xdm' => 'application/vnd.syncml.dm+xml',
+ 'xdp' => 'application/vnd.adobe.xdp+xml',
+ 'xdssc' => 'application/dssc+xml',
+ 'xdw' => 'application/vnd.fujixerox.docuworks',
+ 'xel' => 'application/xcap-el+xml',
+ 'xenc' => 'application/xenc+xml',
+ 'xer' => 'application/patch-ops-error+xml',
+ 'xfdf' => 'application/xfdf',
+ 'xfdl' => 'application/vnd.xfdl',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'xhvml' => 'application/xv+xml',
+ 'xif' => 'image/vnd.xiff',
+ 'xl' => 'application/excel',
+ 'xla' => 'application/vnd.ms-excel',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlc' => 'application/vnd.ms-excel',
+ 'xlf' => 'application/xliff+xml',
+ 'xlm' => 'application/vnd.ms-excel',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+ 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xlt' => 'application/vnd.ms-excel',
+ 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'xlw' => 'application/vnd.ms-excel',
+ 'xm' => 'audio/xm',
+ 'xml' => 'application/xml',
+ 'xns' => 'application/xcap-ns+xml',
+ 'xo' => 'application/vnd.olpc-sugar',
+ 'xop' => 'application/xop+xml',
+ 'xpi' => 'application/x-xpinstall',
+ 'xpl' => 'application/xproc+xml',
+ 'xpm' => 'image/x-xpixmap',
+ 'xpr' => 'application/vnd.is-xpr',
+ 'xps' => 'application/vnd.ms-xpsdocument',
+ 'xpw' => 'application/vnd.intercon.formnet',
+ 'xpx' => 'application/vnd.intercon.formnet',
+ 'xsd' => 'application/xml',
+ 'xsf' => 'application/prs.xsf+xml',
+ 'xsl' => 'application/xml',
+ 'xslt' => 'application/xslt+xml',
+ 'xsm' => 'application/vnd.syncml+xml',
+ 'xspf' => 'application/xspf+xml',
+ 'xul' => 'application/vnd.mozilla.xul+xml',
+ 'xvm' => 'application/xv+xml',
+ 'xvml' => 'application/xv+xml',
+ 'xwd' => 'image/x-xwindowdump',
+ 'xyz' => 'chemical/x-xyz',
+ 'xz' => 'application/x-xz',
+ 'yaml' => 'text/yaml',
+ 'yang' => 'application/yang',
+ 'yin' => 'application/yin+xml',
+ 'yml' => 'text/yaml',
+ 'ymp' => 'text/x-suse-ymp',
+ 'z' => 'application/x-compress',
+ 'z1' => 'application/x-zmachine',
+ 'z2' => 'application/x-zmachine',
+ 'z3' => 'application/x-zmachine',
+ 'z4' => 'application/x-zmachine',
+ 'z5' => 'application/x-zmachine',
+ 'z6' => 'application/x-zmachine',
+ 'z7' => 'application/x-zmachine',
+ 'z8' => 'application/x-zmachine',
+ 'zaz' => 'application/vnd.zzazz.deck+xml',
+ 'zip' => 'application/zip',
+ 'zir' => 'application/vnd.zul',
+ 'zirz' => 'application/vnd.zul',
+ 'zmm' => 'application/vnd.handheld-entertainment+xml',
+ 'zsh' => 'text/x-scriptzsh',
+ ];
+
+ /**
+ * Determines the mimetype of a file by looking at its extension.
+ *
+ * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+ */
+ public static function fromFilename(string $filename): ?string
+ {
+ return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
+ }
+
+ /**
+ * Maps a file extensions to a mimetype.
+ *
+ * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+ */
+ public static function fromExtension(string $extension): ?string
+ {
+ return self::MIME_TYPES[strtolower($extension)] ?? null;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/MultipartStream.php b/vendor/guzzlehttp/psr7/src/MultipartStream.php
new file mode 100644
index 000000000..43d718f65
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/MultipartStream.php
@@ -0,0 +1,165 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream that when read returns bytes for a streaming multipart or
+ * multipart/form-data stream.
+ */
+final class MultipartStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var string */
+ private $boundary;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ /**
+ * @param array $elements Array of associative arrays, each containing a
+ * required "name" key mapping to the form field,
+ * name, a required "contents" key mapping to a
+ * StreamInterface/resource/string, an optional
+ * "headers" associative array of custom headers,
+ * and an optional "filename" key mapping to a
+ * string to send as the filename in the part.
+ * @param string $boundary You can optionally provide a specific boundary
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct(array $elements = [], ?string $boundary = null)
+ {
+ $this->boundary = $boundary ?: bin2hex(random_bytes(20));
+ $this->stream = $this->createStream($elements);
+ }
+
+ public function getBoundary(): string
+ {
+ return $this->boundary;
+ }
+
+ public function isWritable(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Get the headers needed before transferring the content of a POST file
+ *
+ * @param string[] $headers
+ */
+ private function getHeaders(array $headers): string
+ {
+ $str = '';
+ foreach ($headers as $key => $value) {
+ $str .= "{$key}: {$value}\r\n";
+ }
+
+ return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n";
+ }
+
+ /**
+ * Create the aggregate stream that will be used to upload the POST data
+ */
+ protected function createStream(array $elements = []): StreamInterface
+ {
+ $stream = new AppendStream();
+
+ foreach ($elements as $element) {
+ if (!is_array($element)) {
+ throw new \UnexpectedValueException('An array is expected');
+ }
+ $this->addElement($stream, $element);
+ }
+
+ // Add the trailing boundary with CRLF
+ $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
+
+ return $stream;
+ }
+
+ private function addElement(AppendStream $stream, array $element): void
+ {
+ foreach (['contents', 'name'] as $key) {
+ if (!array_key_exists($key, $element)) {
+ throw new \InvalidArgumentException("A '{$key}' key is required");
+ }
+ }
+
+ $element['contents'] = Utils::streamFor($element['contents']);
+
+ if (empty($element['filename'])) {
+ $uri = $element['contents']->getMetadata('uri');
+ if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') {
+ $element['filename'] = $uri;
+ }
+ }
+
+ [$body, $headers] = $this->createElement(
+ $element['name'],
+ $element['contents'],
+ $element['filename'] ?? null,
+ $element['headers'] ?? []
+ );
+
+ $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
+ $stream->addStream($body);
+ $stream->addStream(Utils::streamFor("\r\n"));
+ }
+
+ /**
+ * @param string[] $headers
+ *
+ * @return array{0: StreamInterface, 1: string[]}
+ */
+ private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
+ {
+ // Set a default content-disposition header if one was no provided
+ $disposition = self::getHeader($headers, 'content-disposition');
+ if (!$disposition) {
+ $headers['Content-Disposition'] = ($filename === '0' || $filename)
+ ? sprintf(
+ 'form-data; name="%s"; filename="%s"',
+ $name,
+ basename($filename)
+ )
+ : "form-data; name=\"{$name}\"";
+ }
+
+ // Set a default content-length header if one was no provided
+ $length = self::getHeader($headers, 'content-length');
+ if (!$length) {
+ if ($length = $stream->getSize()) {
+ $headers['Content-Length'] = (string) $length;
+ }
+ }
+
+ // Set a default Content-Type if one was not supplied
+ $type = self::getHeader($headers, 'content-type');
+ if (!$type && ($filename === '0' || $filename)) {
+ $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
+ }
+
+ return [$stream, $headers];
+ }
+
+ /**
+ * @param string[] $headers
+ */
+ private static function getHeader(array $headers, string $key): ?string
+ {
+ $lowercaseHeader = strtolower($key);
+ foreach ($headers as $k => $v) {
+ if (strtolower((string) $k) === $lowercaseHeader) {
+ return $v;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/vendor/guzzlehttp/psr7/src/NoSeekStream.php
new file mode 100644
index 000000000..161a224f0
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/NoSeekStream.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that prevents a stream from being seeked.
+ */
+final class NoSeekStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ throw new \RuntimeException('Cannot seek a NoSeekStream');
+ }
+
+ public function isSeekable(): bool
+ {
+ return false;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/PumpStream.php b/vendor/guzzlehttp/psr7/src/PumpStream.php
new file mode 100644
index 000000000..e2040709f
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/PumpStream.php
@@ -0,0 +1,179 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a read only stream that pumps data from a PHP callable.
+ *
+ * When invoking the provided callable, the PumpStream will pass the amount of
+ * data requested to read to the callable. The callable can choose to ignore
+ * this value and return fewer or more bytes than requested. Any extra data
+ * returned by the provided callable is buffered internally until drained using
+ * the read() function of the PumpStream. The provided callable MUST return
+ * false when there is no more data to read.
+ */
+final class PumpStream implements StreamInterface
+{
+ /** @var callable(int): (string|false|null)|null */
+ private $source;
+
+ /** @var int|null */
+ private $size;
+
+ /** @var int */
+ private $tellPos = 0;
+
+ /** @var array */
+ private $metadata;
+
+ /** @var BufferStream */
+ private $buffer;
+
+ /**
+ * @param callable(int): (string|false|null) $source Source of the stream data. The callable MAY
+ * accept an integer argument used to control the
+ * amount of data to return. The callable MUST
+ * return a string when called, or false|null on error
+ * or EOF.
+ * @param array{size?: int, metadata?: array} $options Stream options:
+ * - metadata: Hash of metadata to use with stream.
+ * - size: Size of the stream, if known.
+ */
+ public function __construct(callable $source, array $options = [])
+ {
+ $this->source = $source;
+ $this->size = $options['size'] ?? null;
+ $this->metadata = $options['metadata'] ?? [];
+ $this->buffer = new BufferStream();
+ }
+
+ public function __toString(): string
+ {
+ try {
+ return Utils::copyToString($this);
+ } catch (\Throwable $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+ return '';
+ }
+ }
+
+ public function close(): void
+ {
+ $this->detach();
+ }
+
+ public function detach()
+ {
+ $this->tellPos = 0;
+ $this->source = null;
+
+ return null;
+ }
+
+ public function getSize(): ?int
+ {
+ return $this->size;
+ }
+
+ public function tell(): int
+ {
+ return $this->tellPos;
+ }
+
+ public function eof(): bool
+ {
+ return $this->source === null;
+ }
+
+ public function isSeekable(): bool
+ {
+ return false;
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ throw new \RuntimeException('Cannot seek a PumpStream');
+ }
+
+ public function isWritable(): bool
+ {
+ return false;
+ }
+
+ public function write($string): int
+ {
+ throw new \RuntimeException('Cannot write to a PumpStream');
+ }
+
+ public function isReadable(): bool
+ {
+ return true;
+ }
+
+ public function read($length): string
+ {
+ $data = $this->buffer->read($length);
+ $readLen = strlen($data);
+ $this->tellPos += $readLen;
+ $remaining = $length - $readLen;
+
+ if ($remaining) {
+ $this->pump($remaining);
+ $data .= $this->buffer->read($remaining);
+ $this->tellPos += strlen($data) - $readLen;
+ }
+
+ return $data;
+ }
+
+ public function getContents(): string
+ {
+ $result = '';
+ while (!$this->eof()) {
+ $result .= $this->read(1000000);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ if (!$key) {
+ return $this->metadata;
+ }
+
+ return $this->metadata[$key] ?? null;
+ }
+
+ private function pump(int $length): void
+ {
+ if ($this->source !== null) {
+ do {
+ $data = ($this->source)($length);
+ if ($data === false || $data === null) {
+ $this->source = null;
+
+ return;
+ }
+ $this->buffer->write($data);
+ $length -= strlen($data);
+ } while ($length > 0);
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Query.php b/vendor/guzzlehttp/psr7/src/Query.php
new file mode 100644
index 000000000..ccf867a0b
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Query.php
@@ -0,0 +1,118 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+final class Query
+{
+ /**
+ * Parse a query string into an associative array.
+ *
+ * If multiple values are found for the same key, the value of that key
+ * value pair will become an array. This function does not parse nested
+ * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
+ * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
+ *
+ * @param string $str Query string to parse
+ * @param int|bool $urlEncoding How the query string is encoded
+ */
+ public static function parse(string $str, $urlEncoding = true): array
+ {
+ $result = [];
+
+ if ($str === '') {
+ return $result;
+ }
+
+ if ($urlEncoding === true) {
+ $decoder = function ($value) {
+ return rawurldecode(str_replace('+', ' ', (string) $value));
+ };
+ } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
+ $decoder = 'rawurldecode';
+ } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
+ $decoder = 'urldecode';
+ } else {
+ $decoder = function ($str) {
+ return $str;
+ };
+ }
+
+ foreach (explode('&', $str) as $kvp) {
+ $parts = explode('=', $kvp, 2);
+ $key = $decoder($parts[0]);
+ $value = isset($parts[1]) ? $decoder($parts[1]) : null;
+ if (!array_key_exists($key, $result)) {
+ $result[$key] = $value;
+ } else {
+ if (!is_array($result[$key])) {
+ $result[$key] = [$result[$key]];
+ }
+ $result[$key][] = $value;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Build a query string from an array of key value pairs.
+ *
+ * This function can use the return value of `parse()` to build a query
+ * string. This function does not modify the provided keys when an array is
+ * encountered (like `http_build_query()` would).
+ *
+ * @param array $params Query string parameters.
+ * @param int|false $encoding Set to false to not encode,
+ * PHP_QUERY_RFC3986 to encode using
+ * RFC3986, or PHP_QUERY_RFC1738 to
+ * encode using RFC1738.
+ * @param bool $treatBoolsAsInts Set to true to encode as 0/1, and
+ * false as false/true.
+ */
+ public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string
+ {
+ if (!$params) {
+ return '';
+ }
+
+ if ($encoding === false) {
+ $encoder = function (string $str): string {
+ return $str;
+ };
+ } elseif ($encoding === PHP_QUERY_RFC3986) {
+ $encoder = 'rawurlencode';
+ } elseif ($encoding === PHP_QUERY_RFC1738) {
+ $encoder = 'urlencode';
+ } else {
+ throw new \InvalidArgumentException('Invalid type');
+ }
+
+ $castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; };
+
+ $qs = '';
+ foreach ($params as $k => $v) {
+ $k = $encoder((string) $k);
+ if (!is_array($v)) {
+ $qs .= $k;
+ $v = is_bool($v) ? $castBool($v) : $v;
+ if ($v !== null) {
+ $qs .= '='.$encoder((string) $v);
+ }
+ $qs .= '&';
+ } else {
+ foreach ($v as $vv) {
+ $qs .= $k;
+ $vv = is_bool($vv) ? $castBool($vv) : $vv;
+ if ($vv !== null) {
+ $qs .= '='.$encoder((string) $vv);
+ }
+ $qs .= '&';
+ }
+ }
+ }
+
+ return $qs ? (string) substr($qs, 0, -1) : '';
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Request.php b/vendor/guzzlehttp/psr7/src/Request.php
new file mode 100644
index 000000000..faafe1ad8
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Request.php
@@ -0,0 +1,159 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 request implementation.
+ */
+class Request implements RequestInterface
+{
+ use MessageTrait;
+
+ /** @var string */
+ private $method;
+
+ /** @var string|null */
+ private $requestTarget;
+
+ /** @var UriInterface */
+ private $uri;
+
+ /**
+ * @param string $method HTTP method
+ * @param string|UriInterface $uri URI
+ * @param (string|string[])[] $headers Request headers
+ * @param string|resource|StreamInterface|null $body Request body
+ * @param string $version Protocol version
+ */
+ public function __construct(
+ string $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ string $version = '1.1'
+ ) {
+ $this->assertMethod($method);
+ if (!($uri instanceof UriInterface)) {
+ $uri = new Uri($uri);
+ }
+
+ $this->method = strtoupper($method);
+ $this->uri = $uri;
+ $this->setHeaders($headers);
+ $this->protocol = $version;
+
+ if (!isset($this->headerNames['host'])) {
+ $this->updateHostFromUri();
+ }
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = Utils::streamFor($body);
+ }
+ }
+
+ public function getRequestTarget(): string
+ {
+ if ($this->requestTarget !== null) {
+ return $this->requestTarget;
+ }
+
+ $target = $this->uri->getPath();
+ if ($target === '') {
+ $target = '/';
+ }
+ if ($this->uri->getQuery() != '') {
+ $target .= '?'.$this->uri->getQuery();
+ }
+
+ return $target;
+ }
+
+ public function withRequestTarget($requestTarget): RequestInterface
+ {
+ if (preg_match('#\s#', $requestTarget)) {
+ throw new InvalidArgumentException(
+ 'Invalid request target provided; cannot contain whitespace'
+ );
+ }
+
+ $new = clone $this;
+ $new->requestTarget = $requestTarget;
+
+ return $new;
+ }
+
+ public function getMethod(): string
+ {
+ return $this->method;
+ }
+
+ public function withMethod($method): RequestInterface
+ {
+ $this->assertMethod($method);
+ $new = clone $this;
+ $new->method = strtoupper($method);
+
+ return $new;
+ }
+
+ public function getUri(): UriInterface
+ {
+ return $this->uri;
+ }
+
+ public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface
+ {
+ if ($uri === $this->uri) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->uri = $uri;
+
+ if (!$preserveHost || !isset($this->headerNames['host'])) {
+ $new->updateHostFromUri();
+ }
+
+ return $new;
+ }
+
+ private function updateHostFromUri(): void
+ {
+ $host = $this->uri->getHost();
+
+ if ($host == '') {
+ return;
+ }
+
+ if (($port = $this->uri->getPort()) !== null) {
+ $host .= ':'.$port;
+ }
+
+ if (isset($this->headerNames['host'])) {
+ $header = $this->headerNames['host'];
+ } else {
+ $header = 'Host';
+ $this->headerNames['host'] = 'Host';
+ }
+ // Ensure Host is the first header.
+ // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
+ $this->headers = [$header => [$host]] + $this->headers;
+ }
+
+ /**
+ * @param mixed $method
+ */
+ private function assertMethod($method): void
+ {
+ if (!is_string($method) || $method === '') {
+ throw new InvalidArgumentException('Method must be a non-empty string.');
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Response.php b/vendor/guzzlehttp/psr7/src/Response.php
new file mode 100644
index 000000000..34e612fda
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Response.php
@@ -0,0 +1,161 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PSR-7 response implementation.
+ */
+class Response implements ResponseInterface
+{
+ use MessageTrait;
+
+ /** Map of standard HTTP status code/reason phrases */
+ private const PHRASES = [
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-status',
+ 208 => 'Already Reported',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => 'Switch Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Time-out',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Large',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested range not satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot',
+ 422 => 'Unprocessable Entity',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 425 => 'Unordered Collection',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Time-out',
+ 505 => 'HTTP Version not supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 510 => 'Not Extended',
+ 511 => 'Network Authentication Required',
+ ];
+
+ /** @var string */
+ private $reasonPhrase;
+
+ /** @var int */
+ private $statusCode;
+
+ /**
+ * @param int $status Status code
+ * @param (string|string[])[] $headers Response headers
+ * @param string|resource|StreamInterface|null $body Response body
+ * @param string $version Protocol version
+ * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
+ */
+ public function __construct(
+ int $status = 200,
+ array $headers = [],
+ $body = null,
+ string $version = '1.1',
+ ?string $reason = null
+ ) {
+ $this->assertStatusCodeRange($status);
+
+ $this->statusCode = $status;
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = Utils::streamFor($body);
+ }
+
+ $this->setHeaders($headers);
+ if ($reason == '' && isset(self::PHRASES[$this->statusCode])) {
+ $this->reasonPhrase = self::PHRASES[$this->statusCode];
+ } else {
+ $this->reasonPhrase = (string) $reason;
+ }
+
+ $this->protocol = $version;
+ }
+
+ public function getStatusCode(): int
+ {
+ return $this->statusCode;
+ }
+
+ public function getReasonPhrase(): string
+ {
+ return $this->reasonPhrase;
+ }
+
+ public function withStatus($code, $reasonPhrase = ''): ResponseInterface
+ {
+ $this->assertStatusCodeIsInteger($code);
+ $code = (int) $code;
+ $this->assertStatusCodeRange($code);
+
+ $new = clone $this;
+ $new->statusCode = $code;
+ if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) {
+ $reasonPhrase = self::PHRASES[$new->statusCode];
+ }
+ $new->reasonPhrase = (string) $reasonPhrase;
+
+ return $new;
+ }
+
+ /**
+ * @param mixed $statusCode
+ */
+ private function assertStatusCodeIsInteger($statusCode): void
+ {
+ if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) {
+ throw new \InvalidArgumentException('Status code must be an integer value.');
+ }
+ }
+
+ private function assertStatusCodeRange(int $statusCode): void
+ {
+ if ($statusCode < 100 || $statusCode >= 600) {
+ throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.');
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Rfc7230.php b/vendor/guzzlehttp/psr7/src/Rfc7230.php
new file mode 100644
index 000000000..8219dba4d
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Rfc7230.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+/**
+ * @internal
+ */
+final class Rfc7230
+{
+ /**
+ * Header related regular expressions (based on amphp/http package)
+ *
+ * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
+ *
+ * @see https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
+ *
+ * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
+ */
+ public const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
+ public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
+}
diff --git a/vendor/guzzlehttp/psr7/src/ServerRequest.php b/vendor/guzzlehttp/psr7/src/ServerRequest.php
new file mode 100644
index 000000000..3cc953453
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/ServerRequest.php
@@ -0,0 +1,340 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Server-side HTTP request
+ *
+ * Extends the Request definition to add methods for accessing incoming data,
+ * specifically server parameters, cookies, matched path parameters, query
+ * string arguments, body parameters, and upload file information.
+ *
+ * "Attributes" are discovered via decomposing the request (and usually
+ * specifically the URI path), and typically will be injected by the application.
+ *
+ * Requests are considered immutable; all methods that might change state are
+ * implemented such that they retain the internal state of the current
+ * message and return a new instance that contains the changed state.
+ */
+class ServerRequest extends Request implements ServerRequestInterface
+{
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * @var array
+ */
+ private $cookieParams = [];
+
+ /**
+ * @var array|object|null
+ */
+ private $parsedBody;
+
+ /**
+ * @var array
+ */
+ private $queryParams = [];
+
+ /**
+ * @var array
+ */
+ private $serverParams;
+
+ /**
+ * @var array
+ */
+ private $uploadedFiles = [];
+
+ /**
+ * @param string $method HTTP method
+ * @param string|UriInterface $uri URI
+ * @param (string|string[])[] $headers Request headers
+ * @param string|resource|StreamInterface|null $body Request body
+ * @param string $version Protocol version
+ * @param array $serverParams Typically the $_SERVER superglobal
+ */
+ public function __construct(
+ string $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ string $version = '1.1',
+ array $serverParams = []
+ ) {
+ $this->serverParams = $serverParams;
+
+ parent::__construct($method, $uri, $headers, $body, $version);
+ }
+
+ /**
+ * Return an UploadedFile instance array.
+ *
+ * @param array $files An array which respect $_FILES structure
+ *
+ * @throws InvalidArgumentException for unrecognized values
+ */
+ public static function normalizeFiles(array $files): array
+ {
+ $normalized = [];
+
+ foreach ($files as $key => $value) {
+ if ($value instanceof UploadedFileInterface) {
+ $normalized[$key] = $value;
+ } elseif (is_array($value) && isset($value['tmp_name'])) {
+ $normalized[$key] = self::createUploadedFileFromSpec($value);
+ } elseif (is_array($value)) {
+ $normalized[$key] = self::normalizeFiles($value);
+ continue;
+ } else {
+ throw new InvalidArgumentException('Invalid value in files specification');
+ }
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Create and return an UploadedFile instance from a $_FILES specification.
+ *
+ * If the specification represents an array of values, this method will
+ * delegate to normalizeNestedFileSpec() and return that return value.
+ *
+ * @param array $value $_FILES struct
+ *
+ * @return UploadedFileInterface|UploadedFileInterface[]
+ */
+ private static function createUploadedFileFromSpec(array $value)
+ {
+ if (is_array($value['tmp_name'])) {
+ return self::normalizeNestedFileSpec($value);
+ }
+
+ return new UploadedFile(
+ $value['tmp_name'],
+ (int) $value['size'],
+ (int) $value['error'],
+ $value['name'],
+ $value['type']
+ );
+ }
+
+ /**
+ * Normalize an array of file specifications.
+ *
+ * Loops through all nested files and returns a normalized array of
+ * UploadedFileInterface instances.
+ *
+ * @return UploadedFileInterface[]
+ */
+ private static function normalizeNestedFileSpec(array $files = []): array
+ {
+ $normalizedFiles = [];
+
+ foreach (array_keys($files['tmp_name']) as $key) {
+ $spec = [
+ 'tmp_name' => $files['tmp_name'][$key],
+ 'size' => $files['size'][$key] ?? null,
+ 'error' => $files['error'][$key] ?? null,
+ 'name' => $files['name'][$key] ?? null,
+ 'type' => $files['type'][$key] ?? null,
+ ];
+ $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+ }
+
+ return $normalizedFiles;
+ }
+
+ /**
+ * Return a ServerRequest populated with superglobals:
+ * $_GET
+ * $_POST
+ * $_COOKIE
+ * $_FILES
+ * $_SERVER
+ */
+ public static function fromGlobals(): ServerRequestInterface
+ {
+ $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
+ $headers = getallheaders();
+ $uri = self::getUriFromGlobals();
+ $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
+ $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+ $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+ return $serverRequest
+ ->withCookieParams($_COOKIE)
+ ->withQueryParams($_GET)
+ ->withParsedBody($_POST)
+ ->withUploadedFiles(self::normalizeFiles($_FILES));
+ }
+
+ private static function extractHostAndPortFromAuthority(string $authority): array
+ {
+ $uri = 'http://'.$authority;
+ $parts = parse_url($uri);
+ if (false === $parts) {
+ return [null, null];
+ }
+
+ $host = $parts['host'] ?? null;
+ $port = $parts['port'] ?? null;
+
+ return [$host, $port];
+ }
+
+ /**
+ * Get a Uri populated with values from $_SERVER.
+ */
+ public static function getUriFromGlobals(): UriInterface
+ {
+ $uri = new Uri('');
+
+ $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+ $hasPort = false;
+ if (isset($_SERVER['HTTP_HOST'])) {
+ [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
+ if ($host !== null) {
+ $uri = $uri->withHost($host);
+ }
+
+ if ($port !== null) {
+ $hasPort = true;
+ $uri = $uri->withPort($port);
+ }
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+ } elseif (isset($_SERVER['SERVER_ADDR'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+ }
+
+ if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+ $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+ }
+
+ $hasQuery = false;
+ if (isset($_SERVER['REQUEST_URI'])) {
+ $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
+ $uri = $uri->withPath($requestUriParts[0]);
+ if (isset($requestUriParts[1])) {
+ $hasQuery = true;
+ $uri = $uri->withQuery($requestUriParts[1]);
+ }
+ }
+
+ if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+ $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+ }
+
+ return $uri;
+ }
+
+ public function getServerParams(): array
+ {
+ return $this->serverParams;
+ }
+
+ public function getUploadedFiles(): array
+ {
+ return $this->uploadedFiles;
+ }
+
+ public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
+ {
+ $new = clone $this;
+ $new->uploadedFiles = $uploadedFiles;
+
+ return $new;
+ }
+
+ public function getCookieParams(): array
+ {
+ return $this->cookieParams;
+ }
+
+ public function withCookieParams(array $cookies): ServerRequestInterface
+ {
+ $new = clone $this;
+ $new->cookieParams = $cookies;
+
+ return $new;
+ }
+
+ public function getQueryParams(): array
+ {
+ return $this->queryParams;
+ }
+
+ public function withQueryParams(array $query): ServerRequestInterface
+ {
+ $new = clone $this;
+ $new->queryParams = $query;
+
+ return $new;
+ }
+
+ /**
+ * @return array|object|null
+ */
+ public function getParsedBody()
+ {
+ return $this->parsedBody;
+ }
+
+ public function withParsedBody($data): ServerRequestInterface
+ {
+ $new = clone $this;
+ $new->parsedBody = $data;
+
+ return $new;
+ }
+
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getAttribute($attribute, $default = null)
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $default;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ public function withAttribute($attribute, $value): ServerRequestInterface
+ {
+ $new = clone $this;
+ $new->attributes[$attribute] = $value;
+
+ return $new;
+ }
+
+ public function withoutAttribute($attribute): ServerRequestInterface
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $this;
+ }
+
+ $new = clone $this;
+ unset($new->attributes[$attribute]);
+
+ return $new;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Stream.php b/vendor/guzzlehttp/psr7/src/Stream.php
new file mode 100644
index 000000000..0aff9b2b7
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Stream.php
@@ -0,0 +1,283 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PHP stream implementation.
+ */
+class Stream implements StreamInterface
+{
+ /**
+ * @see https://www.php.net/manual/en/function.fopen.php
+ * @see https://www.php.net/manual/en/function.gzopen.php
+ */
+ private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
+ private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
+
+ /** @var resource */
+ private $stream;
+ /** @var int|null */
+ private $size;
+ /** @var bool */
+ private $seekable;
+ /** @var bool */
+ private $readable;
+ /** @var bool */
+ private $writable;
+ /** @var string|null */
+ private $uri;
+ /** @var mixed[] */
+ private $customMetadata;
+
+ /**
+ * This constructor accepts an associative array of options.
+ *
+ * - size: (int) If a read stream would otherwise have an indeterminate
+ * size, but the size is known due to foreknowledge, then you can
+ * provide that size, in bytes.
+ * - metadata: (array) Any additional metadata to return when the metadata
+ * of the stream is accessed.
+ *
+ * @param resource $stream Stream resource to wrap.
+ * @param array{size?: int, metadata?: array} $options Associative array of options.
+ *
+ * @throws \InvalidArgumentException if the stream is not a stream resource
+ */
+ public function __construct($stream, array $options = [])
+ {
+ if (!is_resource($stream)) {
+ throw new \InvalidArgumentException('Stream must be a resource');
+ }
+
+ if (isset($options['size'])) {
+ $this->size = $options['size'];
+ }
+
+ $this->customMetadata = $options['metadata'] ?? [];
+ $this->stream = $stream;
+ $meta = stream_get_meta_data($this->stream);
+ $this->seekable = $meta['seekable'];
+ $this->readable = (bool) preg_match(self::READABLE_MODES, $meta['mode']);
+ $this->writable = (bool) preg_match(self::WRITABLE_MODES, $meta['mode']);
+ $this->uri = $this->getMetadata('uri');
+ }
+
+ /**
+ * Closes the stream when the destructed
+ */
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ public function __toString(): string
+ {
+ try {
+ if ($this->isSeekable()) {
+ $this->seek(0);
+ }
+
+ return $this->getContents();
+ } catch (\Throwable $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+ return '';
+ }
+ }
+
+ public function getContents(): string
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ if (!$this->readable) {
+ throw new \RuntimeException('Cannot read from non-readable stream');
+ }
+
+ return Utils::tryGetContents($this->stream);
+ }
+
+ public function close(): void
+ {
+ if (isset($this->stream)) {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+ }
+
+ public function detach()
+ {
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ $result = $this->stream;
+ unset($this->stream);
+ $this->size = $this->uri = null;
+ $this->readable = $this->writable = $this->seekable = false;
+
+ return $result;
+ }
+
+ public function getSize(): ?int
+ {
+ if ($this->size !== null) {
+ return $this->size;
+ }
+
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ // Clear the stat cache if the stream has a URI
+ if ($this->uri) {
+ clearstatcache(true, $this->uri);
+ }
+
+ $stats = fstat($this->stream);
+ if (is_array($stats) && isset($stats['size'])) {
+ $this->size = $stats['size'];
+
+ return $this->size;
+ }
+
+ return null;
+ }
+
+ public function isReadable(): bool
+ {
+ return $this->readable;
+ }
+
+ public function isWritable(): bool
+ {
+ return $this->writable;
+ }
+
+ public function isSeekable(): bool
+ {
+ return $this->seekable;
+ }
+
+ public function eof(): bool
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ return feof($this->stream);
+ }
+
+ public function tell(): int
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ $result = ftell($this->stream);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to determine stream position');
+ }
+
+ return $result;
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ $whence = (int) $whence;
+
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->seekable) {
+ throw new \RuntimeException('Stream is not seekable');
+ }
+ if (fseek($this->stream, $offset, $whence) === -1) {
+ throw new \RuntimeException('Unable to seek to stream position '
+ .$offset.' with whence '.var_export($whence, true));
+ }
+ }
+
+ public function read($length): string
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->readable) {
+ throw new \RuntimeException('Cannot read from non-readable stream');
+ }
+ if ($length < 0) {
+ throw new \RuntimeException('Length parameter cannot be negative');
+ }
+
+ if (0 === $length) {
+ return '';
+ }
+
+ try {
+ $string = fread($this->stream, $length);
+ } catch (\Exception $e) {
+ throw new \RuntimeException('Unable to read from stream', 0, $e);
+ }
+
+ if (false === $string) {
+ throw new \RuntimeException('Unable to read from stream');
+ }
+
+ return $string;
+ }
+
+ public function write($string): int
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->writable) {
+ throw new \RuntimeException('Cannot write to a non-writable stream');
+ }
+
+ // We can't know the size after writing anything
+ $this->size = null;
+ $result = fwrite($this->stream, $string);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to write to stream');
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ if (!isset($this->stream)) {
+ return $key ? null : [];
+ } elseif (!$key) {
+ return $this->customMetadata + stream_get_meta_data($this->stream);
+ } elseif (isset($this->customMetadata[$key])) {
+ return $this->customMetadata[$key];
+ }
+
+ $meta = stream_get_meta_data($this->stream);
+
+ return $meta[$key] ?? null;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
new file mode 100644
index 000000000..601c13afb
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
@@ -0,0 +1,156 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator trait
+ *
+ * @property StreamInterface $stream
+ */
+trait StreamDecoratorTrait
+{
+ /**
+ * @param StreamInterface $stream Stream to decorate
+ */
+ public function __construct(StreamInterface $stream)
+ {
+ $this->stream = $stream;
+ }
+
+ /**
+ * Magic method used to create a new stream if streams are not added in
+ * the constructor of a decorator (e.g., LazyOpenStream).
+ *
+ * @return StreamInterface
+ */
+ public function __get(string $name)
+ {
+ if ($name === 'stream') {
+ $this->stream = $this->createStream();
+
+ return $this->stream;
+ }
+
+ throw new \UnexpectedValueException("$name not found on class");
+ }
+
+ public function __toString(): string
+ {
+ try {
+ if ($this->isSeekable()) {
+ $this->seek(0);
+ }
+
+ return $this->getContents();
+ } catch (\Throwable $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+ trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
+ return '';
+ }
+ }
+
+ public function getContents(): string
+ {
+ return Utils::copyToString($this);
+ }
+
+ /**
+ * Allow decorators to implement custom methods
+ *
+ * @return mixed
+ */
+ public function __call(string $method, array $args)
+ {
+ /** @var callable $callable */
+ $callable = [$this->stream, $method];
+ $result = ($callable)(...$args);
+
+ // Always return the wrapped object if the result is a return $this
+ return $result === $this->stream ? $this : $result;
+ }
+
+ public function close(): void
+ {
+ $this->stream->close();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ return $this->stream->getMetadata($key);
+ }
+
+ public function detach()
+ {
+ return $this->stream->detach();
+ }
+
+ public function getSize(): ?int
+ {
+ return $this->stream->getSize();
+ }
+
+ public function eof(): bool
+ {
+ return $this->stream->eof();
+ }
+
+ public function tell(): int
+ {
+ return $this->stream->tell();
+ }
+
+ public function isReadable(): bool
+ {
+ return $this->stream->isReadable();
+ }
+
+ public function isWritable(): bool
+ {
+ return $this->stream->isWritable();
+ }
+
+ public function isSeekable(): bool
+ {
+ return $this->stream->isSeekable();
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ $this->stream->seek($offset, $whence);
+ }
+
+ public function read($length): string
+ {
+ return $this->stream->read($length);
+ }
+
+ public function write($string): int
+ {
+ return $this->stream->write($string);
+ }
+
+ /**
+ * Implement in subclasses to dynamically create streams when requested.
+ *
+ * @throws \BadMethodCallException
+ */
+ protected function createStream(): StreamInterface
+ {
+ throw new \BadMethodCallException('Not implemented');
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
new file mode 100644
index 000000000..77b04d747
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
@@ -0,0 +1,207 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Converts Guzzle streams into PHP stream resources.
+ *
+ * @see https://www.php.net/streamwrapper
+ */
+final class StreamWrapper
+{
+ /** @var resource */
+ public $context;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ /** @var string r, r+, or w */
+ private $mode;
+
+ /**
+ * Returns a resource representing the stream.
+ *
+ * @param StreamInterface $stream The stream to get a resource for
+ *
+ * @return resource
+ *
+ * @throws \InvalidArgumentException if stream is not readable or writable
+ */
+ public static function getResource(StreamInterface $stream)
+ {
+ self::register();
+
+ if ($stream->isReadable()) {
+ $mode = $stream->isWritable() ? 'r+' : 'r';
+ } elseif ($stream->isWritable()) {
+ $mode = 'w';
+ } else {
+ throw new \InvalidArgumentException('The stream must be readable, '
+ .'writable, or both.');
+ }
+
+ return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream));
+ }
+
+ /**
+ * Creates a stream context that can be used to open a stream as a php stream resource.
+ *
+ * @return resource
+ */
+ public static function createStreamContext(StreamInterface $stream)
+ {
+ return stream_context_create([
+ 'guzzle' => ['stream' => $stream],
+ ]);
+ }
+
+ /**
+ * Registers the stream wrapper if needed
+ */
+ public static function register(): void
+ {
+ if (!in_array('guzzle', stream_get_wrappers())) {
+ stream_wrapper_register('guzzle', __CLASS__);
+ }
+ }
+
+ public function stream_open(string $path, string $mode, int $options, ?string &$opened_path = null): bool
+ {
+ $options = stream_context_get_options($this->context);
+
+ if (!isset($options['guzzle']['stream'])) {
+ return false;
+ }
+
+ $this->mode = $mode;
+ $this->stream = $options['guzzle']['stream'];
+
+ return true;
+ }
+
+ public function stream_read(int $count): string
+ {
+ return $this->stream->read($count);
+ }
+
+ public function stream_write(string $data): int
+ {
+ return $this->stream->write($data);
+ }
+
+ public function stream_tell(): int
+ {
+ return $this->stream->tell();
+ }
+
+ public function stream_eof(): bool
+ {
+ return $this->stream->eof();
+ }
+
+ public function stream_seek(int $offset, int $whence): bool
+ {
+ $this->stream->seek($offset, $whence);
+
+ return true;
+ }
+
+ /**
+ * @return resource|false
+ */
+ public function stream_cast(int $cast_as)
+ {
+ $stream = clone $this->stream;
+ $resource = $stream->detach();
+
+ return $resource ?? false;
+ }
+
+ /**
+ * @return array{
+ * dev: int,
+ * ino: int,
+ * mode: int,
+ * nlink: int,
+ * uid: int,
+ * gid: int,
+ * rdev: int,
+ * size: int,
+ * atime: int,
+ * mtime: int,
+ * ctime: int,
+ * blksize: int,
+ * blocks: int
+ * }|false
+ */
+ public function stream_stat()
+ {
+ if ($this->stream->getSize() === null) {
+ return false;
+ }
+
+ static $modeMap = [
+ 'r' => 33060,
+ 'rb' => 33060,
+ 'r+' => 33206,
+ 'w' => 33188,
+ 'wb' => 33188,
+ ];
+
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => $modeMap[$this->mode],
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => $this->stream->getSize() ?: 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0,
+ ];
+ }
+
+ /**
+ * @return array{
+ * dev: int,
+ * ino: int,
+ * mode: int,
+ * nlink: int,
+ * uid: int,
+ * gid: int,
+ * rdev: int,
+ * size: int,
+ * atime: int,
+ * mtime: int,
+ * ctime: int,
+ * blksize: int,
+ * blocks: int
+ * }
+ */
+ public function url_stat(string $path, int $flags): array
+ {
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => 0,
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0,
+ ];
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UploadedFile.php b/vendor/guzzlehttp/psr7/src/UploadedFile.php
new file mode 100644
index 000000000..d9b779f13
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UploadedFile.php
@@ -0,0 +1,211 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use RuntimeException;
+
+class UploadedFile implements UploadedFileInterface
+{
+ private const ERROR_MAP = [
+ UPLOAD_ERR_OK => 'UPLOAD_ERR_OK',
+ UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE',
+ UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE',
+ UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL',
+ UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE',
+ UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR',
+ UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE',
+ UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION',
+ ];
+
+ /**
+ * @var string|null
+ */
+ private $clientFilename;
+
+ /**
+ * @var string|null
+ */
+ private $clientMediaType;
+
+ /**
+ * @var int
+ */
+ private $error;
+
+ /**
+ * @var string|null
+ */
+ private $file;
+
+ /**
+ * @var bool
+ */
+ private $moved = false;
+
+ /**
+ * @var int|null
+ */
+ private $size;
+
+ /**
+ * @var StreamInterface|null
+ */
+ private $stream;
+
+ /**
+ * @param StreamInterface|string|resource $streamOrFile
+ */
+ public function __construct(
+ $streamOrFile,
+ ?int $size,
+ int $errorStatus,
+ ?string $clientFilename = null,
+ ?string $clientMediaType = null
+ ) {
+ $this->setError($errorStatus);
+ $this->size = $size;
+ $this->clientFilename = $clientFilename;
+ $this->clientMediaType = $clientMediaType;
+
+ if ($this->isOk()) {
+ $this->setStreamOrFile($streamOrFile);
+ }
+ }
+
+ /**
+ * Depending on the value set file or stream variable
+ *
+ * @param StreamInterface|string|resource $streamOrFile
+ *
+ * @throws InvalidArgumentException
+ */
+ private function setStreamOrFile($streamOrFile): void
+ {
+ if (is_string($streamOrFile)) {
+ $this->file = $streamOrFile;
+ } elseif (is_resource($streamOrFile)) {
+ $this->stream = new Stream($streamOrFile);
+ } elseif ($streamOrFile instanceof StreamInterface) {
+ $this->stream = $streamOrFile;
+ } else {
+ throw new InvalidArgumentException(
+ 'Invalid stream or file provided for UploadedFile'
+ );
+ }
+ }
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ private function setError(int $error): void
+ {
+ if (!isset(UploadedFile::ERROR_MAP[$error])) {
+ throw new InvalidArgumentException(
+ 'Invalid error status for UploadedFile'
+ );
+ }
+
+ $this->error = $error;
+ }
+
+ private static function isStringNotEmpty($param): bool
+ {
+ return is_string($param) && false === empty($param);
+ }
+
+ /**
+ * Return true if there is no upload error
+ */
+ private function isOk(): bool
+ {
+ return $this->error === UPLOAD_ERR_OK;
+ }
+
+ public function isMoved(): bool
+ {
+ return $this->moved;
+ }
+
+ /**
+ * @throws RuntimeException if is moved or not ok
+ */
+ private function validateActive(): void
+ {
+ if (false === $this->isOk()) {
+ throw new RuntimeException(\sprintf('Cannot retrieve stream due to upload error (%s)', self::ERROR_MAP[$this->error]));
+ }
+
+ if ($this->isMoved()) {
+ throw new RuntimeException('Cannot retrieve stream after it has already been moved');
+ }
+ }
+
+ public function getStream(): StreamInterface
+ {
+ $this->validateActive();
+
+ if ($this->stream instanceof StreamInterface) {
+ return $this->stream;
+ }
+
+ /** @var string $file */
+ $file = $this->file;
+
+ return new LazyOpenStream($file, 'r+');
+ }
+
+ public function moveTo($targetPath): void
+ {
+ $this->validateActive();
+
+ if (false === self::isStringNotEmpty($targetPath)) {
+ throw new InvalidArgumentException(
+ 'Invalid path provided for move operation; must be a non-empty string'
+ );
+ }
+
+ if ($this->file) {
+ $this->moved = PHP_SAPI === 'cli'
+ ? rename($this->file, $targetPath)
+ : move_uploaded_file($this->file, $targetPath);
+ } else {
+ Utils::copyToStream(
+ $this->getStream(),
+ new LazyOpenStream($targetPath, 'w')
+ );
+
+ $this->moved = true;
+ }
+
+ if (false === $this->moved) {
+ throw new RuntimeException(
+ sprintf('Uploaded file could not be moved to %s', $targetPath)
+ );
+ }
+ }
+
+ public function getSize(): ?int
+ {
+ return $this->size;
+ }
+
+ public function getError(): int
+ {
+ return $this->error;
+ }
+
+ public function getClientFilename(): ?string
+ {
+ return $this->clientFilename;
+ }
+
+ public function getClientMediaType(): ?string
+ {
+ return $this->clientMediaType;
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php
new file mode 100644
index 000000000..a7cdfb003
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Uri.php
@@ -0,0 +1,743 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use GuzzleHttp\Psr7\Exception\MalformedUriException;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 URI implementation.
+ *
+ * @author Michael Dowling
+ * @author Tobias Schultze
+ * @author Matthew Weier O'Phinney
+ */
+class Uri implements UriInterface, \JsonSerializable
+{
+ /**
+ * Absolute http and https URIs require a host per RFC 7230 Section 2.7
+ * but in generic URIs the host can be empty. So for http(s) URIs
+ * we apply this default host when no host is given yet to form a
+ * valid URI.
+ */
+ private const HTTP_DEFAULT_HOST = 'localhost';
+
+ private const DEFAULT_PORTS = [
+ 'http' => 80,
+ 'https' => 443,
+ 'ftp' => 21,
+ 'gopher' => 70,
+ 'nntp' => 119,
+ 'news' => 119,
+ 'telnet' => 23,
+ 'tn3270' => 23,
+ 'imap' => 143,
+ 'pop' => 110,
+ 'ldap' => 389,
+ ];
+
+ /**
+ * Unreserved characters for use in a regex.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
+ */
+ private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
+
+ /**
+ * Sub-delims for use in a regex.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
+ */
+ private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
+ private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
+
+ /** @var string Uri scheme. */
+ private $scheme = '';
+
+ /** @var string Uri user info. */
+ private $userInfo = '';
+
+ /** @var string Uri host. */
+ private $host = '';
+
+ /** @var int|null Uri port. */
+ private $port;
+
+ /** @var string Uri path. */
+ private $path = '';
+
+ /** @var string Uri query string. */
+ private $query = '';
+
+ /** @var string Uri fragment. */
+ private $fragment = '';
+
+ /** @var string|null String representation */
+ private $composedComponents;
+
+ public function __construct(string $uri = '')
+ {
+ if ($uri !== '') {
+ $parts = self::parse($uri);
+ if ($parts === false) {
+ throw new MalformedUriException("Unable to parse URI: $uri");
+ }
+ $this->applyParts($parts);
+ }
+ }
+
+ /**
+ * UTF-8 aware \parse_url() replacement.
+ *
+ * The internal function produces broken output for non ASCII domain names
+ * (IDN) when used with locales other than "C".
+ *
+ * On the other hand, cURL understands IDN correctly only when UTF-8 locale
+ * is configured ("C.UTF-8", "en_US.UTF-8", etc.).
+ *
+ * @see https://bugs.php.net/bug.php?id=52923
+ * @see https://www.php.net/manual/en/function.parse-url.php#114817
+ * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING
+ *
+ * @return array|false
+ */
+ private static function parse(string $url)
+ {
+ // If IPv6
+ $prefix = '';
+ if (preg_match('%^(.*://\[[0-9:a-fA-F]+\])(.*?)$%', $url, $matches)) {
+ /** @var array{0:string, 1:string, 2:string} $matches */
+ $prefix = $matches[1];
+ $url = $matches[2];
+ }
+
+ /** @var string */
+ $encodedUrl = preg_replace_callback(
+ '%[^:/@?&=#]+%usD',
+ static function ($matches) {
+ return urlencode($matches[0]);
+ },
+ $url
+ );
+
+ $result = parse_url($prefix.$encodedUrl);
+
+ if ($result === false) {
+ return false;
+ }
+
+ return array_map('urldecode', $result);
+ }
+
+ public function __toString(): string
+ {
+ if ($this->composedComponents === null) {
+ $this->composedComponents = self::composeComponents(
+ $this->scheme,
+ $this->getAuthority(),
+ $this->path,
+ $this->query,
+ $this->fragment
+ );
+ }
+
+ return $this->composedComponents;
+ }
+
+ /**
+ * Composes a URI reference string from its various components.
+ *
+ * Usually this method does not need to be called manually but instead is used indirectly via
+ * `Psr\Http\Message\UriInterface::__toString`.
+ *
+ * PSR-7 UriInterface treats an empty component the same as a missing component as
+ * getQuery(), getFragment() etc. always return a string. This explains the slight
+ * difference to RFC 3986 Section 5.3.
+ *
+ * Another adjustment is that the authority separator is added even when the authority is missing/empty
+ * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
+ * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
+ * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
+ * that format).
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
+ */
+ public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
+ {
+ $uri = '';
+
+ // weak type checks to also accept null until we can add scalar type hints
+ if ($scheme != '') {
+ $uri .= $scheme.':';
+ }
+
+ if ($authority != '' || $scheme === 'file') {
+ $uri .= '//'.$authority;
+ }
+
+ if ($authority != '' && $path != '' && $path[0] != '/') {
+ $path = '/'.$path;
+ }
+
+ $uri .= $path;
+
+ if ($query != '') {
+ $uri .= '?'.$query;
+ }
+
+ if ($fragment != '') {
+ $uri .= '#'.$fragment;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether the URI has the default port of the current scheme.
+ *
+ * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
+ * independently of the implementation.
+ */
+ public static function isDefaultPort(UriInterface $uri): bool
+ {
+ return $uri->getPort() === null
+ || (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]);
+ }
+
+ /**
+ * Whether the URI is absolute, i.e. it has a scheme.
+ *
+ * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
+ * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
+ * to another URI, the base URI. Relative references can be divided into several forms:
+ * - network-path references, e.g. '//example.com/path'
+ * - absolute-path references, e.g. '/path'
+ * - relative-path references, e.g. 'subpath'
+ *
+ * @see Uri::isNetworkPathReference
+ * @see Uri::isAbsolutePathReference
+ * @see Uri::isRelativePathReference
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
+ */
+ public static function isAbsolute(UriInterface $uri): bool
+ {
+ return $uri->getScheme() !== '';
+ }
+
+ /**
+ * Whether the URI is a network-path reference.
+ *
+ * A relative reference that begins with two slash characters is termed an network-path reference.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+ */
+ public static function isNetworkPathReference(UriInterface $uri): bool
+ {
+ return $uri->getScheme() === '' && $uri->getAuthority() !== '';
+ }
+
+ /**
+ * Whether the URI is a absolute-path reference.
+ *
+ * A relative reference that begins with a single slash character is termed an absolute-path reference.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+ */
+ public static function isAbsolutePathReference(UriInterface $uri): bool
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && isset($uri->getPath()[0])
+ && $uri->getPath()[0] === '/';
+ }
+
+ /**
+ * Whether the URI is a relative-path reference.
+ *
+ * A relative reference that does not begin with a slash character is termed a relative-path reference.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
+ */
+ public static function isRelativePathReference(UriInterface $uri): bool
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
+ }
+
+ /**
+ * Whether the URI is a same-document reference.
+ *
+ * A same-document reference refers to a URI that is, aside from its fragment
+ * component, identical to the base URI. When no base URI is given, only an empty
+ * URI reference (apart from its fragment) is considered a same-document reference.
+ *
+ * @param UriInterface $uri The URI to check
+ * @param UriInterface|null $base An optional base URI to compare against
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
+ */
+ public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool
+ {
+ if ($base !== null) {
+ $uri = UriResolver::resolve($base, $uri);
+
+ return ($uri->getScheme() === $base->getScheme())
+ && ($uri->getAuthority() === $base->getAuthority())
+ && ($uri->getPath() === $base->getPath())
+ && ($uri->getQuery() === $base->getQuery());
+ }
+
+ return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
+ }
+
+ /**
+ * Creates a new URI with a specific query string value removed.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed.
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Query string key to remove.
+ */
+ public static function withoutQueryValue(UriInterface $uri, string $key): UriInterface
+ {
+ $result = self::getFilteredQueryString($uri, [$key]);
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a new URI with a specific query string value.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed and replaced with the given key value pair.
+ *
+ * A value of null will set the query string key without a value, e.g. "key"
+ * instead of "key=value".
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Key to set.
+ * @param string|null $value Value to set
+ */
+ public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface
+ {
+ $result = self::getFilteredQueryString($uri, [$key]);
+
+ $result[] = self::generateQueryString($key, $value);
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a new URI with multiple specific query string values.
+ *
+ * It has the same behavior as withQueryValue() but for an associative array of key => value.
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param (string|null)[] $keyValueArray Associative array of key and values
+ */
+ public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
+ {
+ $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
+
+ foreach ($keyValueArray as $key => $value) {
+ $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null);
+ }
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a URI from a hash of `parse_url` components.
+ *
+ * @see https://www.php.net/manual/en/function.parse-url.php
+ *
+ * @throws MalformedUriException If the components do not form a valid URI.
+ */
+ public static function fromParts(array $parts): UriInterface
+ {
+ $uri = new self();
+ $uri->applyParts($parts);
+ $uri->validateState();
+
+ return $uri;
+ }
+
+ public function getScheme(): string
+ {
+ return $this->scheme;
+ }
+
+ public function getAuthority(): string
+ {
+ $authority = $this->host;
+ if ($this->userInfo !== '') {
+ $authority = $this->userInfo.'@'.$authority;
+ }
+
+ if ($this->port !== null) {
+ $authority .= ':'.$this->port;
+ }
+
+ return $authority;
+ }
+
+ public function getUserInfo(): string
+ {
+ return $this->userInfo;
+ }
+
+ public function getHost(): string
+ {
+ return $this->host;
+ }
+
+ public function getPort(): ?int
+ {
+ return $this->port;
+ }
+
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+
+ public function getQuery(): string
+ {
+ return $this->query;
+ }
+
+ public function getFragment(): string
+ {
+ return $this->fragment;
+ }
+
+ public function withScheme($scheme): UriInterface
+ {
+ $scheme = $this->filterScheme($scheme);
+
+ if ($this->scheme === $scheme) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->scheme = $scheme;
+ $new->composedComponents = null;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withUserInfo($user, $password = null): UriInterface
+ {
+ $info = $this->filterUserInfoComponent($user);
+ if ($password !== null) {
+ $info .= ':'.$this->filterUserInfoComponent($password);
+ }
+
+ if ($this->userInfo === $info) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->userInfo = $info;
+ $new->composedComponents = null;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withHost($host): UriInterface
+ {
+ $host = $this->filterHost($host);
+
+ if ($this->host === $host) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->host = $host;
+ $new->composedComponents = null;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPort($port): UriInterface
+ {
+ $port = $this->filterPort($port);
+
+ if ($this->port === $port) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->port = $port;
+ $new->composedComponents = null;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPath($path): UriInterface
+ {
+ $path = $this->filterPath($path);
+
+ if ($this->path === $path) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->path = $path;
+ $new->composedComponents = null;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withQuery($query): UriInterface
+ {
+ $query = $this->filterQueryAndFragment($query);
+
+ if ($this->query === $query) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->query = $query;
+ $new->composedComponents = null;
+
+ return $new;
+ }
+
+ public function withFragment($fragment): UriInterface
+ {
+ $fragment = $this->filterQueryAndFragment($fragment);
+
+ if ($this->fragment === $fragment) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->fragment = $fragment;
+ $new->composedComponents = null;
+
+ return $new;
+ }
+
+ public function jsonSerialize(): string
+ {
+ return $this->__toString();
+ }
+
+ /**
+ * Apply parse_url parts to a URI.
+ *
+ * @param array $parts Array of parse_url parts to apply.
+ */
+ private function applyParts(array $parts): void
+ {
+ $this->scheme = isset($parts['scheme'])
+ ? $this->filterScheme($parts['scheme'])
+ : '';
+ $this->userInfo = isset($parts['user'])
+ ? $this->filterUserInfoComponent($parts['user'])
+ : '';
+ $this->host = isset($parts['host'])
+ ? $this->filterHost($parts['host'])
+ : '';
+ $this->port = isset($parts['port'])
+ ? $this->filterPort($parts['port'])
+ : null;
+ $this->path = isset($parts['path'])
+ ? $this->filterPath($parts['path'])
+ : '';
+ $this->query = isset($parts['query'])
+ ? $this->filterQueryAndFragment($parts['query'])
+ : '';
+ $this->fragment = isset($parts['fragment'])
+ ? $this->filterQueryAndFragment($parts['fragment'])
+ : '';
+ if (isset($parts['pass'])) {
+ $this->userInfo .= ':'.$this->filterUserInfoComponent($parts['pass']);
+ }
+
+ $this->removeDefaultPort();
+ }
+
+ /**
+ * @param mixed $scheme
+ *
+ * @throws \InvalidArgumentException If the scheme is invalid.
+ */
+ private function filterScheme($scheme): string
+ {
+ if (!is_string($scheme)) {
+ throw new \InvalidArgumentException('Scheme must be a string');
+ }
+
+ return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+ }
+
+ /**
+ * @param mixed $component
+ *
+ * @throws \InvalidArgumentException If the user info is invalid.
+ */
+ private function filterUserInfoComponent($component): string
+ {
+ if (!is_string($component)) {
+ throw new \InvalidArgumentException('User info must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $component
+ );
+ }
+
+ /**
+ * @param mixed $host
+ *
+ * @throws \InvalidArgumentException If the host is invalid.
+ */
+ private function filterHost($host): string
+ {
+ if (!is_string($host)) {
+ throw new \InvalidArgumentException('Host must be a string');
+ }
+
+ return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
+ }
+
+ /**
+ * @param mixed $port
+ *
+ * @throws \InvalidArgumentException If the port is invalid.
+ */
+ private function filterPort($port): ?int
+ {
+ if ($port === null) {
+ return null;
+ }
+
+ $port = (int) $port;
+ if (0 > $port || 0xFFFF < $port) {
+ throw new \InvalidArgumentException(
+ sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
+ );
+ }
+
+ return $port;
+ }
+
+ /**
+ * @param (string|int)[] $keys
+ *
+ * @return string[]
+ */
+ private static function getFilteredQueryString(UriInterface $uri, array $keys): array
+ {
+ $current = $uri->getQuery();
+
+ if ($current === '') {
+ return [];
+ }
+
+ $decodedKeys = array_map(function ($k): string {
+ return rawurldecode((string) $k);
+ }, $keys);
+
+ return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
+ return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
+ });
+ }
+
+ private static function generateQueryString(string $key, ?string $value): string
+ {
+ // Query string separators ("=", "&") within the key or value need to be encoded
+ // (while preventing double-encoding) before setting the query string. All other
+ // chars that need percent-encoding will be encoded by withQuery().
+ $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
+
+ if ($value !== null) {
+ $queryString .= '='.strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
+ }
+
+ return $queryString;
+ }
+
+ private function removeDefaultPort(): void
+ {
+ if ($this->port !== null && self::isDefaultPort($this)) {
+ $this->port = null;
+ }
+ }
+
+ /**
+ * Filters the path of a URI
+ *
+ * @param mixed $path
+ *
+ * @throws \InvalidArgumentException If the path is invalid.
+ */
+ private function filterPath($path): string
+ {
+ if (!is_string($path)) {
+ throw new \InvalidArgumentException('Path must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $path
+ );
+ }
+
+ /**
+ * Filters the query string or fragment of a URI.
+ *
+ * @param mixed $str
+ *
+ * @throws \InvalidArgumentException If the query or fragment is invalid.
+ */
+ private function filterQueryAndFragment($str): string
+ {
+ if (!is_string($str)) {
+ throw new \InvalidArgumentException('Query and fragment must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $str
+ );
+ }
+
+ private function rawurlencodeMatchZero(array $match): string
+ {
+ return rawurlencode($match[0]);
+ }
+
+ private function validateState(): void
+ {
+ if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
+ $this->host = self::HTTP_DEFAULT_HOST;
+ }
+
+ if ($this->getAuthority() === '') {
+ if (0 === strpos($this->path, '//')) {
+ throw new MalformedUriException('The path of a URI without an authority must not start with two slashes "//"');
+ }
+ if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
+ throw new MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon');
+ }
+ }
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriComparator.php b/vendor/guzzlehttp/psr7/src/UriComparator.php
new file mode 100644
index 000000000..70c582aa0
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriComparator.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Provides methods to determine if a modified URL should be considered cross-origin.
+ *
+ * @author Graham Campbell
+ */
+final class UriComparator
+{
+ /**
+ * Determines if a modified URL should be considered cross-origin with
+ * respect to an original URL.
+ */
+ public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool
+ {
+ if (\strcasecmp($original->getHost(), $modified->getHost()) !== 0) {
+ return true;
+ }
+
+ if ($original->getScheme() !== $modified->getScheme()) {
+ return true;
+ }
+
+ if (self::computePort($original) !== self::computePort($modified)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static function computePort(UriInterface $uri): int
+ {
+ $port = $uri->getPort();
+
+ if (null !== $port) {
+ return $port;
+ }
+
+ return 'https' === $uri->getScheme() ? 443 : 80;
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
new file mode 100644
index 000000000..e17455737
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
@@ -0,0 +1,220 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Provides methods to normalize and compare URIs.
+ *
+ * @author Tobias Schultze
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6
+ */
+final class UriNormalizer
+{
+ /**
+ * Default normalizations which only include the ones that preserve semantics.
+ */
+ public const PRESERVING_NORMALIZATIONS =
+ self::CAPITALIZE_PERCENT_ENCODING |
+ self::DECODE_UNRESERVED_CHARACTERS |
+ self::CONVERT_EMPTY_PATH |
+ self::REMOVE_DEFAULT_HOST |
+ self::REMOVE_DEFAULT_PORT |
+ self::REMOVE_DOT_SEGMENTS;
+
+ /**
+ * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+ *
+ * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b
+ */
+ public const CAPITALIZE_PERCENT_ENCODING = 1;
+
+ /**
+ * Decodes percent-encoded octets of unreserved characters.
+ *
+ * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39),
+ * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and,
+ * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
+ *
+ * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/
+ */
+ public const DECODE_UNRESERVED_CHARACTERS = 2;
+
+ /**
+ * Converts the empty path to "/" for http and https URIs.
+ *
+ * Example: http://example.org → http://example.org/
+ */
+ public const CONVERT_EMPTY_PATH = 4;
+
+ /**
+ * Removes the default host of the given URI scheme from the URI.
+ *
+ * Only the "file" scheme defines the default host "localhost".
+ * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile`
+ * are equivalent according to RFC 3986. The first format is not accepted
+ * by PHPs stream functions and thus already normalized implicitly to the
+ * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`.
+ *
+ * Example: file://localhost/myfile → file:///myfile
+ */
+ public const REMOVE_DEFAULT_HOST = 8;
+
+ /**
+ * Removes the default port of the given URI scheme from the URI.
+ *
+ * Example: http://example.org:80/ → http://example.org/
+ */
+ public const REMOVE_DEFAULT_PORT = 16;
+
+ /**
+ * Removes unnecessary dot-segments.
+ *
+ * Dot-segments in relative-path references are not removed as it would
+ * change the semantics of the URI reference.
+ *
+ * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html
+ */
+ public const REMOVE_DOT_SEGMENTS = 32;
+
+ /**
+ * Paths which include two or more adjacent slashes are converted to one.
+ *
+ * Webservers usually ignore duplicate slashes and treat those URIs equivalent.
+ * But in theory those URIs do not need to be equivalent. So this normalization
+ * may change the semantics. Encoded slashes (%2F) are not removed.
+ *
+ * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html
+ */
+ public const REMOVE_DUPLICATE_SLASHES = 64;
+
+ /**
+ * Sort query parameters with their values in alphabetical order.
+ *
+ * However, the order of parameters in a URI may be significant (this is not defined by the standard).
+ * So this normalization is not safe and may change the semantics of the URI.
+ *
+ * Example: ?lang=en&article=fred → ?article=fred&lang=en
+ *
+ * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
+ * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
+ */
+ public const SORT_QUERY_PARAMETERS = 128;
+
+ /**
+ * Returns a normalized URI.
+ *
+ * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+ * This methods adds additional normalizations that can be configured with the $flags parameter.
+ *
+ * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as
+ * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are
+ * treated equivalent which is not necessarily true according to RFC 3986. But that difference
+ * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.
+ *
+ * @param UriInterface $uri The URI to normalize
+ * @param int $flags A bitmask of normalizations to apply, see constants
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
+ */
+ public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
+ {
+ if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
+ $uri = self::capitalizePercentEncoding($uri);
+ }
+
+ if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
+ $uri = self::decodeUnreservedCharacters($uri);
+ }
+
+ if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === ''
+ && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+ ) {
+ $uri = $uri->withPath('/');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
+ $uri = $uri->withHost('');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
+ $uri = $uri->withPort(null);
+ }
+
+ if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
+ $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
+ }
+
+ if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
+ $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
+ }
+
+ if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
+ $queryKeyValues = explode('&', $uri->getQuery());
+ sort($queryKeyValues);
+ $uri = $uri->withQuery(implode('&', $queryKeyValues));
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether two URIs can be considered equivalent.
+ *
+ * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
+ * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
+ * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
+ * relative references does not mean anything.
+ *
+ * @param UriInterface $uri1 An URI to compare
+ * @param UriInterface $uri2 An URI to compare
+ * @param int $normalizations A bitmask of normalizations to apply, see constants
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1
+ */
+ public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
+ {
+ return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
+ }
+
+ private static function capitalizePercentEncoding(UriInterface $uri): UriInterface
+ {
+ $regex = '/(?:%[A-Fa-f0-9]{2})++/';
+
+ $callback = function (array $match): string {
+ return strtoupper($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private static function decodeUnreservedCharacters(UriInterface $uri): UriInterface
+ {
+ $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
+
+ $callback = function (array $match): string {
+ return rawurldecode($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/UriResolver.php b/vendor/guzzlehttp/psr7/src/UriResolver.php
new file mode 100644
index 000000000..3737be1e5
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/UriResolver.php
@@ -0,0 +1,211 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Resolves a URI reference in the context of a base URI and the opposite way.
+ *
+ * @author Tobias Schultze
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5
+ */
+final class UriResolver
+{
+ /**
+ * Removes dot segments from a path and returns the new path.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
+ */
+ public static function removeDotSegments(string $path): string
+ {
+ if ($path === '' || $path === '/') {
+ return $path;
+ }
+
+ $results = [];
+ $segments = explode('/', $path);
+ foreach ($segments as $segment) {
+ if ($segment === '..') {
+ array_pop($results);
+ } elseif ($segment !== '.') {
+ $results[] = $segment;
+ }
+ }
+
+ $newPath = implode('/', $results);
+
+ if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
+ // Re-add the leading slash if necessary for cases like "/.."
+ $newPath = '/'.$newPath;
+ } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
+ // Add the trailing slash if necessary
+ // If newPath is not empty, then $segment must be set and is the last segment from the foreach
+ $newPath .= '/';
+ }
+
+ return $newPath;
+ }
+
+ /**
+ * Converts the relative URI into a new URI that is resolved against the base URI.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
+ */
+ public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
+ {
+ if ((string) $rel === '') {
+ // we can simply return the same base URI instance for this same-document reference
+ return $base;
+ }
+
+ if ($rel->getScheme() != '') {
+ return $rel->withPath(self::removeDotSegments($rel->getPath()));
+ }
+
+ if ($rel->getAuthority() != '') {
+ $targetAuthority = $rel->getAuthority();
+ $targetPath = self::removeDotSegments($rel->getPath());
+ $targetQuery = $rel->getQuery();
+ } else {
+ $targetAuthority = $base->getAuthority();
+ if ($rel->getPath() === '') {
+ $targetPath = $base->getPath();
+ $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
+ } else {
+ if ($rel->getPath()[0] === '/') {
+ $targetPath = $rel->getPath();
+ } else {
+ if ($targetAuthority != '' && $base->getPath() === '') {
+ $targetPath = '/'.$rel->getPath();
+ } else {
+ $lastSlashPos = strrpos($base->getPath(), '/');
+ if ($lastSlashPos === false) {
+ $targetPath = $rel->getPath();
+ } else {
+ $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1).$rel->getPath();
+ }
+ }
+ }
+ $targetPath = self::removeDotSegments($targetPath);
+ $targetQuery = $rel->getQuery();
+ }
+ }
+
+ return new Uri(Uri::composeComponents(
+ $base->getScheme(),
+ $targetAuthority,
+ $targetPath,
+ $targetQuery,
+ $rel->getFragment()
+ ));
+ }
+
+ /**
+ * Returns the target URI as a relative reference from the base URI.
+ *
+ * This method is the counterpart to resolve():
+ *
+ * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+ *
+ * One use-case is to use the current request URI as base URI and then generate relative links in your documents
+ * to reduce the document size or offer self-contained downloadable document archives.
+ *
+ * $base = new Uri('http://example.com/a/b/');
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+ * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+ *
+ * This method also accepts a target that is already relative and will try to relativize it further. Only a
+ * relative-path reference will be returned as-is.
+ *
+ * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
+ */
+ public static function relativize(UriInterface $base, UriInterface $target): UriInterface
+ {
+ if ($target->getScheme() !== ''
+ && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+ ) {
+ return $target;
+ }
+
+ if (Uri::isRelativePathReference($target)) {
+ // As the target is already highly relative we return it as-is. It would be possible to resolve
+ // the target with `$target = self::resolve($base, $target);` and then try make it more relative
+ // by removing a duplicate query. But let's not do that automatically.
+ return $target;
+ }
+
+ if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
+ return $target->withScheme('');
+ }
+
+ // We must remove the path before removing the authority because if the path starts with two slashes, the URI
+ // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
+ // invalid.
+ $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
+
+ if ($base->getPath() !== $target->getPath()) {
+ return $emptyPathUri->withPath(self::getRelativePath($base, $target));
+ }
+
+ if ($base->getQuery() === $target->getQuery()) {
+ // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
+ return $emptyPathUri->withQuery('');
+ }
+
+ // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
+ // inherit the base query component when resolving.
+ if ($target->getQuery() === '') {
+ $segments = explode('/', $target->getPath());
+ /** @var string $lastSegment */
+ $lastSegment = end($segments);
+
+ return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
+ }
+
+ return $emptyPathUri;
+ }
+
+ private static function getRelativePath(UriInterface $base, UriInterface $target): string
+ {
+ $sourceSegments = explode('/', $base->getPath());
+ $targetSegments = explode('/', $target->getPath());
+ array_pop($sourceSegments);
+ $targetLastSegment = array_pop($targetSegments);
+ foreach ($sourceSegments as $i => $segment) {
+ if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
+ unset($sourceSegments[$i], $targetSegments[$i]);
+ } else {
+ break;
+ }
+ }
+ $targetSegments[] = $targetLastSegment;
+ $relativePath = str_repeat('../', count($sourceSegments)).implode('/', $targetSegments);
+
+ // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
+ if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
+ $relativePath = "./$relativePath";
+ } elseif ('/' === $relativePath[0]) {
+ if ($base->getAuthority() != '' && $base->getPath() === '') {
+ // In this case an extra slash is added by resolve() automatically. So we must not add one here.
+ $relativePath = ".$relativePath";
+ } else {
+ $relativePath = "./$relativePath";
+ }
+ }
+
+ return $relativePath;
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php
new file mode 100644
index 000000000..7682d2cdc
--- /dev/null
+++ b/vendor/guzzlehttp/psr7/src/Utils.php
@@ -0,0 +1,477 @@
+<?php
+
+declare(strict_types=1);
+
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+final class Utils
+{
+ /**
+ * Remove the items given by the keys, case insensitively from the data.
+ *
+ * @param (string|int)[] $keys
+ */
+ public static function caselessRemove(array $keys, array $data): array
+ {
+ $result = [];
+
+ foreach ($keys as &$key) {
+ $key = strtolower((string) $key);
+ }
+
+ foreach ($data as $k => $v) {
+ if (!in_array(strtolower((string) $k), $keys)) {
+ $result[$k] = $v;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Copy the contents of a stream into another stream until the given number
+ * of bytes have been read.
+ *
+ * @param StreamInterface $source Stream to read from
+ * @param StreamInterface $dest Stream to write to
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ *
+ * @throws \RuntimeException on error.
+ */
+ public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void
+ {
+ $bufferSize = 8192;
+
+ if ($maxLen === -1) {
+ while (!$source->eof()) {
+ if (!$dest->write($source->read($bufferSize))) {
+ break;
+ }
+ }
+ } else {
+ $remaining = $maxLen;
+ while ($remaining > 0 && !$source->eof()) {
+ $buf = $source->read(min($bufferSize, $remaining));
+ $len = strlen($buf);
+ if (!$len) {
+ break;
+ }
+ $remaining -= $len;
+ $dest->write($buf);
+ }
+ }
+ }
+
+ /**
+ * Copy the contents of a stream into a string until the given number of
+ * bytes have been read.
+ *
+ * @param StreamInterface $stream Stream to read
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ *
+ * @throws \RuntimeException on error.
+ */
+ public static function copyToString(StreamInterface $stream, int $maxLen = -1): string
+ {
+ $buffer = '';
+
+ if ($maxLen === -1) {
+ while (!$stream->eof()) {
+ $buf = $stream->read(1048576);
+ if ($buf === '') {
+ break;
+ }
+ $buffer .= $buf;
+ }
+
+ return $buffer;
+ }
+
+ $len = 0;
+ while (!$stream->eof() && $len < $maxLen) {
+ $buf = $stream->read($maxLen - $len);
+ if ($buf === '') {
+ break;
+ }
+ $buffer .= $buf;
+ $len = strlen($buffer);
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Calculate a hash of a stream.
+ *
+ * This method reads the entire stream to calculate a rolling hash, based
+ * on PHP's `hash_init` functions.
+ *
+ * @param StreamInterface $stream Stream to calculate the hash for
+ * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
+ * @param bool $rawOutput Whether or not to use raw output
+ *
+ * @throws \RuntimeException on error.
+ */
+ public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string
+ {
+ $pos = $stream->tell();
+
+ if ($pos > 0) {
+ $stream->rewind();
+ }
+
+ $ctx = hash_init($algo);
+ while (!$stream->eof()) {
+ hash_update($ctx, $stream->read(1048576));
+ }
+
+ $out = hash_final($ctx, $rawOutput);
+ $stream->seek($pos);
+
+ return $out;
+ }
+
+ /**
+ * Clone and modify a request with the given changes.
+ *
+ * This method is useful for reducing the number of clones needed to mutate
+ * a message.
+ *
+ * The changes can be one of:
+ * - method: (string) Changes the HTTP method.
+ * - set_headers: (array) Sets the given headers.
+ * - remove_headers: (array) Remove the given headers.
+ * - body: (mixed) Sets the given body.
+ * - uri: (UriInterface) Set the URI.
+ * - query: (string) Set the query string value of the URI.
+ * - version: (string) Set the protocol version.
+ *
+ * @param RequestInterface $request Request to clone and modify.
+ * @param array $changes Changes to apply.
+ */
+ public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface
+ {
+ if (!$changes) {
+ return $request;
+ }
+
+ $headers = $request->getHeaders();
+
+ if (!isset($changes['uri'])) {
+ $uri = $request->getUri();
+ } else {
+ // Remove the host header if one is on the URI
+ if ($host = $changes['uri']->getHost()) {
+ $changes['set_headers']['Host'] = $host;
+
+ if ($port = $changes['uri']->getPort()) {
+ $standardPorts = ['http' => 80, 'https' => 443];
+ $scheme = $changes['uri']->getScheme();
+ if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
+ $changes['set_headers']['Host'] .= ':'.$port;
+ }
+ }
+ }
+ $uri = $changes['uri'];
+ }
+
+ if (!empty($changes['remove_headers'])) {
+ $headers = self::caselessRemove($changes['remove_headers'], $headers);
+ }
+
+ if (!empty($changes['set_headers'])) {
+ $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
+ $headers = $changes['set_headers'] + $headers;
+ }
+
+ if (isset($changes['query'])) {
+ $uri = $uri->withQuery($changes['query']);
+ }
+
+ if ($request instanceof ServerRequestInterface) {
+ $new = (new ServerRequest(
+ $changes['method'] ?? $request->getMethod(),
+ $uri,
+ $headers,
+ $changes['body'] ?? $request->getBody(),
+ $changes['version'] ?? $request->getProtocolVersion(),
+ $request->getServerParams()
+ ))
+ ->withParsedBody($request->getParsedBody())
+ ->withQueryParams($request->getQueryParams())
+ ->withCookieParams($request->getCookieParams())
+ ->withUploadedFiles($request->getUploadedFiles());
+
+ foreach ($request->getAttributes() as $key => $value) {
+ $new = $new->withAttribute($key, $value);
+ }
+
+ return $new;
+ }
+
+ return new Request(
+ $changes['method'] ?? $request->getMethod(),
+ $uri,
+ $headers,
+ $changes['body'] ?? $request->getBody(),
+ $changes['version'] ?? $request->getProtocolVersion()
+ );
+ }
+
+ /**
+ * Read a line from the stream up to the maximum allowed buffer length.
+ *
+ * @param StreamInterface $stream Stream to read from
+ * @param int|null $maxLength Maximum buffer length
+ */
+ public static function readLine(StreamInterface $stream, ?int $maxLength = null): string
+ {
+ $buffer = '';
+ $size = 0;
+
+ while (!$stream->eof()) {
+ if ('' === ($byte = $stream->read(1))) {
+ return $buffer;
+ }
+ $buffer .= $byte;
+ // Break when a new line is found or the max length - 1 is reached
+ if ($byte === "\n" || ++$size === $maxLength - 1) {
+ break;
+ }
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Redact the password in the user info part of a URI.
+ */
+ public static function redactUserInfo(UriInterface $uri): UriInterface
+ {
+ $userInfo = $uri->getUserInfo();
+
+ if (false !== ($pos = \strpos($userInfo, ':'))) {
+ return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Create a new stream based on the input type.
+ *
+ * Options is an associative array that can contain the following keys:
+ * - metadata: Array of custom metadata.
+ * - size: Size of the stream.
+ *
+ * This method accepts the following `$resource` types:
+ * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+ * - `string`: Creates a stream object that uses the given string as the contents.
+ * - `resource`: Creates a stream object that wraps the given PHP stream resource.
+ * - `Iterator`: If the provided value implements `Iterator`, then a read-only
+ * stream object will be created that wraps the given iterable. Each time the
+ * stream is read from, data from the iterator will fill a buffer and will be
+ * continuously called until the buffer is equal to the requested read size.
+ * Subsequent read calls will first read from the buffer and then call `next`
+ * on the underlying iterator until it is exhausted.
+ * - `object` with `__toString()`: If the object has the `__toString()` method,
+ * the object will be cast to a string and then a stream will be returned that
+ * uses the string value.
+ * - `NULL`: When `null` is passed, an empty stream object is returned.
+ * - `callable` When a callable is passed, a read-only stream object will be
+ * created that invokes the given callable. The callable is invoked with the
+ * number of suggested bytes to read. The callable can return any number of
+ * bytes, but MUST return `false` when there is no more data to return. The
+ * stream object that wraps the callable will invoke the callable until the
+ * number of requested bytes are available. Any additional bytes will be
+ * buffered and used in subsequent reads.
+ *
+ * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
+ * @param array{size?: int, metadata?: array} $options Additional options
+ *
+ * @throws \InvalidArgumentException if the $resource arg is not valid.
+ */
+ public static function streamFor($resource = '', array $options = []): StreamInterface
+ {
+ if (is_scalar($resource)) {
+ $stream = self::tryFopen('php://temp', 'r+');
+ if ($resource !== '') {
+ fwrite($stream, (string) $resource);
+ fseek($stream, 0);
+ }
+
+ return new Stream($stream, $options);
+ }
+
+ switch (gettype($resource)) {
+ case 'resource':
+ /*
+ * The 'php://input' is a special stream with quirks and inconsistencies.
+ * We avoid using that stream by reading it into php://temp
+ */
+
+ /** @var resource $resource */
+ if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') {
+ $stream = self::tryFopen('php://temp', 'w+');
+ stream_copy_to_stream($resource, $stream);
+ fseek($stream, 0);
+ $resource = $stream;
+ }
+
+ return new Stream($resource, $options);
+ case 'object':
+ /** @var object $resource */
+ if ($resource instanceof StreamInterface) {
+ return $resource;
+ } elseif ($resource instanceof \Iterator) {
+ return new PumpStream(function () use ($resource) {
+ if (!$resource->valid()) {
+ return false;
+ }
+ $result = $resource->current();
+ $resource->next();
+
+ return $result;
+ }, $options);
+ } elseif (method_exists($resource, '__toString')) {
+ return self::streamFor((string) $resource, $options);
+ }
+ break;
+ case 'NULL':
+ return new Stream(self::tryFopen('php://temp', 'r+'), $options);
+ }
+
+ if (is_callable($resource)) {
+ return new PumpStream($resource, $options);
+ }
+
+ throw new \InvalidArgumentException('Invalid resource type: '.gettype($resource));
+ }
+
+ /**
+ * Safely opens a PHP stream resource using a filename.
+ *
+ * When fopen fails, PHP normally raises a warning. This function adds an
+ * error handler that checks for errors and throws an exception instead.
+ *
+ * @param string $filename File to open
+ * @param string $mode Mode used to open the file
+ *
+ * @return resource
+ *
+ * @throws \RuntimeException if the file cannot be opened
+ */
+ public static function tryFopen(string $filename, string $mode)
+ {
+ $ex = null;
+ set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to open "%s" using mode "%s": %s',
+ $filename,
+ $mode,
+ $errstr
+ ));
+
+ return true;
+ });
+
+ try {
+ /** @var resource $handle */
+ $handle = fopen($filename, $mode);
+ } catch (\Throwable $e) {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to open "%s" using mode "%s": %s',
+ $filename,
+ $mode,
+ $e->getMessage()
+ ), 0, $e);
+ }
+
+ restore_error_handler();
+
+ if ($ex) {
+ /** @var $ex \RuntimeException */
+ throw $ex;
+ }
+
+ return $handle;
+ }
+
+ /**
+ * Safely gets the contents of a given stream.
+ *
+ * When stream_get_contents fails, PHP normally raises a warning. This
+ * function adds an error handler that checks for errors and throws an
+ * exception instead.
+ *
+ * @param resource $stream
+ *
+ * @throws \RuntimeException if the stream cannot be read
+ */
+ public static function tryGetContents($stream): string
+ {
+ $ex = null;
+ set_error_handler(static function (int $errno, string $errstr) use (&$ex): bool {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to read stream contents: %s',
+ $errstr
+ ));
+
+ return true;
+ });
+
+ try {
+ /** @var string|false $contents */
+ $contents = stream_get_contents($stream);
+
+ if ($contents === false) {
+ $ex = new \RuntimeException('Unable to read stream contents');
+ }
+ } catch (\Throwable $e) {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to read stream contents: %s',
+ $e->getMessage()
+ ), 0, $e);
+ }
+
+ restore_error_handler();
+
+ if ($ex) {
+ /** @var $ex \RuntimeException */
+ throw $ex;
+ }
+
+ return $contents;
+ }
+
+ /**
+ * Returns a UriInterface for the given value.
+ *
+ * This function accepts a string or UriInterface and returns a
+ * UriInterface for the given value. If the value is already a
+ * UriInterface, it is returned as-is.
+ *
+ * @param string|UriInterface $uri
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function uriFor($uri): UriInterface
+ {
+ if ($uri instanceof UriInterface) {
+ return $uri;
+ }
+
+ if (is_string($uri)) {
+ return new Uri($uri);
+ }
+
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
+ }
+}
diff --git a/vendor/macgirvin/http-message-signer/LICENSE b/vendor/macgirvin/http-message-signer/LICENSE
new file mode 100644
index 000000000..36f87d3af
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/LICENSE
@@ -0,0 +1,30 @@
+BSD 3-Clause License
+
+Copyright (c) 2025, Quantificant LLC <waitman@quantificant.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/vendor/macgirvin/http-message-signer/README.md b/vendor/macgirvin/http-message-signer/README.md
new file mode 100644
index 000000000..cd1aab1aa
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/README.md
@@ -0,0 +1,130 @@
+# HTTP Message Signer (RFC 9421)
+
+
+A PHP 8.1+ library for signing and verifying HTTP messages (requests or responses) per [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421).
+
+This is a fork of quantificant/http-message-signer
+
+Supports:
+- RSA-SHA256
+- Ed25519
+- HMAC-SHA256
+- PSR-7 requests (e.g., Guzzle)
+- Optionally (recommended) calculate and verify body digest (content-digest header)
+
+Requirements:
+- bakame/http-structured-fields
+- psr/http-message
+
+## Note
+
+This is Alpha version please report issues. Thanks. Tested on PHP 8.4, should run fine on 8.1+
+
+2025-05-28: Partially reversed the constructor change.
+
+
+## Installation
+
+```bash
+composer require macgirvin/http-message-signer
+```
+
+
+## Notes
+
+An instance of a PSR-7 MessageInterface is passed to the sign and verify functions. This can be a RequestInterface or a ResponseInterface. Typically, this will be a RequestInterface. If your web framework does not supply a pre-populated PSR7-compatible request interface, you can quickly generate one using
+
+```
+use GuzzleHttp\Psr7\ServerRequest;
+
+$request = ServerRequest::fromGlobals();
+```
+
+This would typically be used to verify a message.
+
+To sign a message, install the composer package guzzlehttp/psr7 and create an instance of `Request`.
+
+## Usage
+
+```php
+use HttpSignature\HttpMessageSigner;
+use HttpSignature\UnProcessableSignatureException;
+use GuzzleHttp\Psr7\Request;
+
+$request = new Request(
+ 'GET',
+ 'https://api.example.com/resource?bat&baz=3',
+ [
+ 'Host' => 'api.example.com',
+ 'Date' => gmdate('D, d M Y H:i:s T'),
+ ...additional headers
+ ]
+);
+
+$signer = (new HttpMessageSigner())
+ ->setPrivateKey($privateKey) // only needed for signing
+ ->setPublicKey($publicKey) // only needed for verifying
+ ->setKeyId('https://example.com/dave#rsaKey') // required
+ ->setAlgorithm('rsa-sha256') // required
+ ->setCreated(time()) // recommended
+ ->setExpires(time() + 300) // optional, contentious
+ ->setNonce('xJJ9;ro.3*kidney`') // optional one-time token
+ ->setTag('fediverse') // optional app profile name
+ ->setSignatureId('sig1') // optional, default is sig1
+
+try {
+ $request = $signer->signRequest('("@method" "@path" "host" "date")', $request);
+}
+catch (UnProcessableSignatureException $exception) {
+ $whatHappened = $exception->getMessage();
+}
+try {
+ $isValid = $signer->verifyRequest($request);
+} catch (UnProcessableSignatureException $exception) {
+ $isValid = false;
+ $whatHappened = $exception->getMessage();
+}
+```
+
+See full examples in `/tests`.
+
+## Structured Fields
+
+RFC9421 makes heavy use of HTTP Structured Fields (RFC8941/RFC9651). The syntax is very precise and unforgiving.
+
+The signRequest() method takes a structured InnerList of components to sign. These may be headers or derived fields.
+The string will look something like the following (where `...` represents additional components):
+
+```
+'("header1" "header2" "@method" ...)'
+```
+
+and may include modifier parameters. These are represented as
+
+```
+'("@query-param";name="foo" "header2";sf "header3" ...)'
+```
+
+Field names beginning with '@' are components derived from the HTTP request but may not be represented in the headers. Please review RFC9421 for precise definitions.
+
+Using the 'sf' parameter on a component will treat a signature component as a Structured Field when normalising the string.
+
+However, parsing Structured Fields by adding the 'sf' parameter is likely to fail unless you know what `type` it is. A built-in table contains the type definition for a number of known stuctured header types. This list is probably incomplete. A method `addStructuredFieldTypes()` is available to add the type information so it can be successfully parsed. This takes an array with key of the lowercase header name and a value; which is one of 'list', 'innerlist', 'parameters, 'dictionary', 'item'. If the header name is in the list and the 'sf' modifier is used, the header will be parsed as the Structured Field type indicated.
+
+If a Structured Field is declared as type 'dictionary'; it is suitable for use with the RFC9421 `key` parameter. Using this parameter will fail if the Structured Field type is unknown or has not been registered.
+
+The signRequest() and verifyRequest() methods both use an instance of MessageInterface. In nearly all cases, this will be the RequestInterface. However, when signing responses, the default will be the ResponseInterface, and if components are required from the RequestInterface, the :req parameter must be added to the field definition.
+
+To sign or verify an HTTP Response, use a ResponseInterface as the provided `$interface`, and provide the RequestInterface in `$originalRequest`. This is optional but will allow the `req` modifier to work correctly when signing Responses.
+
+## Known issues
+Currently not implemented is the special handling of the `cookie` and `set-cookie` headers when using the `sf` modifier. For further information please see https://httpwg.org/http-extensions/draft-ietf-httpbis-retrofit.html and https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-20 (or later). It is planned to implement this once RFC6265bis is finalised as a new RFC.
+
+Also not currently implemented are some of the many signature algorithms; as we're currently focused primarily on rsa-sha256 and ed25519.
+
+Pull requests welcome.
+
+
+## License
+
+BSD 3-Clause
diff --git a/vendor/macgirvin/http-message-signer/TESTING b/vendor/macgirvin/http-message-signer/TESTING
new file mode 100644
index 000000000..a04c516a2
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/TESTING
@@ -0,0 +1,5 @@
+Run Tests:
+
+$ composer require --dev phpunit/phpunit
+$ vendor/bin/phpunit
+
diff --git a/vendor/macgirvin/http-message-signer/composer.json b/vendor/macgirvin/http-message-signer/composer.json
new file mode 100644
index 000000000..dfb735938
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "macgirvin/http-message-signer",
+ "description": "RFC 9421 HTTP Message Signer and Verifier for PSR-7 requests",
+ "type": "library",
+ "require": {
+ "php": "^8.1",
+ "psr/http-message": "^2.0",
+ "guzzlehttp/psr7": "^2.0",
+ "bakame/http-structured-fields": "^2.0",
+ "ext-openssl": "*",
+ "phpseclib/phpseclib": "~3.0",
+ "phpseclib/phpseclib2_compat": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "HttpSignature\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "HttpSignature\\Tests\\": "tests/"
+ }
+ },
+ "license": "BSD-3-Clause",
+ "minimum-stability": "stable"
+}
diff --git a/vendor/macgirvin/http-message-signer/phpunit.xml b/vendor/macgirvin/http-message-signer/phpunit.xml
new file mode 100644
index 000000000..4149ce1a9
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/phpunit.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/12.1/phpunit.xsd"
+ bootstrap="vendor/autoload.php"
+ displayDetailsOnTestsThatTriggerWarnings="true"
+ colors="true">
+
+ <testsuites>
+ <testsuite name="HttpSignature Test Suite">
+ <directory>tests</directory>
+ </testsuite>
+ </testsuites>
+
+ <source>
+ <include>
+ <directory>src</directory>
+ </include>
+ </source>
+
+</phpunit>
diff --git a/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php b/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
new file mode 100644
index 000000000..31c481f22
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
@@ -0,0 +1,876 @@
+<?php
+
+namespace HttpSignature;
+
+use Bakame\Http\StructuredFields\Dictionary;
+use Bakame\Http\StructuredFields\InnerList;
+use Bakame\Http\StructuredFields\Item;
+use Bakame\Http\StructuredFields\OuterList;
+use Bakame\Http\StructuredFields\Parameters;
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use phpseclib\Crypt\RSA;
+
+class HttpMessageSigner
+{
+ private string $keyId;
+ private string $privateKey;
+ private string $publicKey;
+ private string $algorithm;
+ private string $signatureId = 'sig1';
+ private string $created = '';
+ private string $expires = '';
+ private string $nonce = '';
+ private string $tag = '';
+
+ private array $structuredFieldTypes = [];
+
+ private $originalRequest;
+
+
+ public function __construct()
+ {
+ $this->setStructuredFieldTypes((new StructuredFieldTypes())->getFields());
+ return $this;
+ }
+
+ public function getHeaders($interface): array
+ {
+ $headers = [];
+ foreach ($interface->getHeaders() as $name => $values) {
+ $headers[strtolower($name)] = implode(', ', $values);
+ }
+ return $headers;
+ }
+
+
+ /* PSR-7 interface to signing function */
+ /**
+ * @param string $coveredFields
+ * @param MessageInterface $interface
+ * @param RequestInterface|null $originalRequest
+ * @return MessageInterface
+ * @throws UnProcessableSignatureException
+ */
+
+ public function signRequest(string $coveredFields, MessageInterface $interface, RequestInterface $originalRequest = null): MessageInterface
+ {
+ $headers = $this->getHeaders($interface);
+ if ($originalRequest) {
+ $this->setOriginalRequest($originalRequest);
+ }
+
+ $signedHeaders = $this->sign(
+ $headers,
+ $coveredFields,
+ $interface
+ );
+
+ foreach (['signature-input', 'signature'] as $header) {
+ $interface = $interface->withHeader($header, $signedHeaders[$header]);
+ }
+ return $interface;
+ }
+
+ /* PSR-7 verify interface and also check body digest if included */
+ /**
+ * @param MessageInterface $interface
+ * @param RequestInterface|null $originalRequest
+ * @return bool
+ *
+ * @throws UnProcessableSignatureException
+ */
+
+ public function verifyRequest(MessageInterface $interface, RequestInterface $originalRequest = null): bool
+ {
+ $headers = [];
+ if ($originalRequest) {
+ $this->setOriginalRequest($originalRequest);
+ }
+ foreach ($interface->getHeaders() as $name => $values) {
+ $headers[strtolower($name)] = implode(', ', $values);
+ }
+
+ /* check the body digest if it's present */
+
+ if (isset($headers['content-digest'])) {
+ $body = (string)$interface->getBody();
+ if (!$this->isBodyDigestValid($body, $headers['content-digest'])) {
+ return false;
+ }
+ }
+
+ return $this->verify($headers, $interface);
+ }
+
+ /**
+ * check body digest
+ *
+ * From https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Digest
+ * The algorithm used to create a digest of the message content. Only two registered digest algorithms are
+ * considered secure: sha-512 and sha-256. The insecure (legacy) registered digest algorithms
+ * are: md5, sha (SHA-1), unixsum, unixcksum, adler (ADLER32) and crc32c.
+ */
+
+ private function isBodyDigestValid(string $body, string $headerValue): bool
+ {
+ if (!preg_match('/sha-(.*?)=:(.*?):/', $headerValue, $matches)) {
+ return false;
+ }
+ if (!in_array($matches[1], ['256', '512'], true)) {
+ return false;
+ }
+
+ $algorithm = 'sha' . $matches[1];
+
+ $expectedDigest = base64_decode($matches[2]);
+ $actualDigest = hash($algorithm, $body, true);
+
+ return hash_equals($expectedDigest, $actualDigest);
+ }
+
+ /**
+ * @param array $headers
+ * @param string $coveredFields
+ *
+ * Can be used as a development function to peek into the internals of the exact things
+ * that were signed and/or test the internal results of data normalisation.
+ * This matches the sign() function except that it just returns the serialised string
+ * and does not sign it.
+ */
+ public function calculateSignatureBase(array $headers, string $coveredFields, $interface)
+ {
+ $signatureComponents = [];
+ $processedComponents = [];
+ $dict = $this->parseStructuredDict($coveredFields);
+
+ if ($dict->isNotEmpty()) {
+ $coveredStructuredFields = $dict->__toString();
+ $indices = $dict->indices();
+ foreach ($indices as $index) {
+ $member = $dict->getByIndex($index);
+ if (!$member) {
+ throw new UnProcessableSignatureException('Index ' . $index . ' not found');
+ }
+ if (in_array($member, $processedComponents, true)) {
+ throw new UnProcessableSignatureException('Duplicate member found');
+ }
+ $processedComponents[] = $member;
+ $signatureComponents[] = $this->canonicalizeComponent($member, $headers, $interface);
+ }
+ }
+
+ $signatureInput = $coveredStructuredFields . ';keyid="'
+ . $this->keyId . '";alg="' . $this->algorithm . '"';
+
+ if ($this->created) {
+ $signatureInput .= ';created=' . $this->created;
+ }
+ if ($this->expires) {
+ $signatureInput .= ';expires=' . $this->expires;
+ }
+ if ($this->nonce) {
+ $signatureInput .= ';nonce="' . $this->nonce . '"';
+ }
+ if ($this->tag) {
+ $signatureInput .= ';tag="' . $this->tag . '"';
+ }
+
+
+ /**
+ * Always include @signature-params in the result.
+ */
+ $signatureComponents[] = '"@signature-params": ' . $signatureInput;
+
+ $signatureBase = implode("\n", $signatureComponents);
+ return $signatureBase;
+
+ }
+
+ public function sign(array $headers, string $coveredFields, MessageInterface $interface): array
+ {
+ $signatureComponents = [];
+ $processedComponents = [];
+
+ $dict = $this->parseStructuredDict($coveredFields);
+
+ if ($dict->isNotEmpty()) {
+ $coveredStructuredFields = $dict->__toString();
+ $indices = $dict->indices();
+ foreach ($indices as $index) {
+ $member = $dict->getByIndex($index);
+ if (!$member) {
+ throw new UnProcessableSignatureException('Index ' . $index . ' not found');
+ }
+ if (in_array($member, $processedComponents, true)) {
+ throw new UnProcessableSignatureException('Duplicate member found');
+ }
+ $processedComponents[] = $member;
+ $signatureComponents[] = $this->canonicalizeComponent($member, $headers, $interface);
+ }
+ }
+
+ $signatureInput = $coveredStructuredFields . ';keyid="'
+ . $this->keyId . '";alg="' . $this->algorithm . '"'
+ . (($this->created) ? ';created=' . $this->created : '')
+ . (($this->expires) ? ';expires=' . $this->expires : '')
+ . (($this->nonce) ? ';nonce="' . $this->nonce . '"' : '')
+ . (($this->tag) ? ';tag="' . $this->tag . '"' : '');
+
+ /**
+ * Always include @signature-params in the result.
+ */
+ $signatureComponents[] = '"@signature-params": ' . $signatureInput;
+
+ $signatureBase = implode("\n", $signatureComponents);
+ $signature = $this->createSignature($signatureBase);
+
+ $headers['signature-input'] = "$this->signatureId=$signatureInput";
+ $headers['signature'] = "$this->signatureId=:$signature:";
+
+ return $headers;
+ }
+
+ public function verify(array $headers, $interface): bool
+ {
+ if (!isset($headers['signature-input'], $headers['signature'])) {
+ return false;
+ }
+ $headers[] = 'signature-params';
+
+ $sigInputDict = $this->parseStructuredDict($headers['signature-input']);
+
+ $signatureComponents = [];
+
+ if ($sigInputDict->isNotEmpty()) {
+ $indices = $sigInputDict->indices();
+ foreach ($indices as $index) {
+ [$dictName, $members] = $sigInputDict->getByIndex($index);
+ if ($members instanceof InnerList) {
+ $innerIndices = $members->indices();
+ foreach ($innerIndices as $innerIndex) {
+ $member = $members->getByIndex($innerIndex);
+ $signatureComponents[$dictName][] = $this->canonicalizeComponent($member, $headers, $interface);
+ }
+ $parameters = $this->extractParameters($members);
+ if ($parameters) {
+ foreach ($parameters as $key => $value) {
+ if (!in_array($key, ['created', 'expires', 'nonce', 'alg', 'keyid', 'tag'])) {
+ return false;
+ }
+ }
+ }
+ if (isset($parameters['expires'])) {
+ $expires = (int) $parameters['expires'];
+ if ($expires < time()) {
+ return false;
+ }
+ if (isset($parameters['created'])) {
+ $created = (int) $parameters['created'];
+ if ($created >= $expires) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ $sigDict = $this->parseStructuredDict($headers['signature']);
+
+ foreach ($signatureComponents as $dictName => $dictComponents) {
+ $namedSignatureComponents = $signatureComponents[$dictName];
+ $signatureParamsStr = $sigInputDict[$dictName]->toHttpValue();
+ $namedSignatureComponents[] = '"@signature-params": ' . $signatureParamsStr;
+ $signatureBase = implode("\n", $namedSignatureComponents);
+ if (!isset($sigDict[$dictName])) {
+ return false;
+ }
+
+ $decodedSig = base64_decode(trim($sigDict[$dictName]->__toString(), ':'));
+ return $this->verifySignature($signatureBase, $decodedSig, $parameters['alg'] ?? $this->algorithm);
+ }
+ return false;
+ }
+
+ protected function extractParameters($field): array
+ {
+ $parameters = [];
+ $fieldParams = $field->parameters();
+
+ if ($fieldParams->isNotEmpty()) {
+ $indices = $fieldParams->indices();
+ foreach ($indices as $index) {
+ [$name, $item] = $fieldParams->getByIndex($index);
+ $parameters[$name] = $item->value();
+ }
+ }
+ return $parameters;
+ }
+
+ private function canonicalizeComponent($field, array $headers, MessageInterface $interface): string
+ {
+ $fieldName = $field->value();
+ $parameters = $this->extractParameters($field);
+ if (isset($parameters['bs']) && isset($parameters['sf'])) {
+ throw new UnProcessableSignatureException('Cannot use both bs and sf');
+ }
+
+ $whichRequest = $interface;
+ if (isset($parameters['req'])) {
+ if ($interface instanceof ResponseInterface) {
+ $whichRequest = $this->getOriginalRequest();
+ }
+ else {
+ throw new UnProcessableSignatureException('missing request for req parameter');
+ }
+ }
+ $whichHeaders = $headers;
+
+ if (isset($parameters['tr'])) {
+ $whichHeaders = $whichRequest->getTrailers();
+ }
+
+ [$name, $value] = $this->getFieldValue($fieldName, $whichRequest, $whichHeaders, $parameters);
+
+ if (isset($parameters['bs'])) {
+ $result = $name . ';bs: ';
+ $values = $whichRequest->getHeader($fieldName);
+ if (!$values) {
+ return '';
+ }
+ if (!is_array($values)) {
+ $values = [$values];
+ }
+ foreach ($values as $value) {
+ $value = trim($value);
+ $result .= ':' . base64_encode($value) . ':' . ', ';
+ }
+ return $values ? rtrim($result, ', ') : $result;
+ }
+
+ if (isset($parameters['sf'])) {
+ $value = $this->applyStructuredField($name, $value);
+ return $name . ';sf: ' . $value;
+ }
+ if (isset($parameters['key'])) {
+ $childName = $parameters['key'];
+ $value = $this->applySingleKeyValue($name, $childName, $value);
+ return $name . ';key="' . $childName . '": ' . $value;
+ }
+ return $name . ': ' . $value;
+ }
+
+ private function getFieldValue($fieldName, MessageInterface $interface, $headers, $parameters ): array
+ {
+ // The $interface has no single method to extract this, so build it from
+ // the avilable components.
+ $targetUri = $interface->getUri()->getScheme() . '://' . $interface->getUri()->getAuthority()
+ . $interface->getUri()->getPath() . $interface->getUri()->getQuery();
+
+ $value = match ($fieldName) {
+ '@signature-params' => ['', ''],
+ '@method' => ['"@method"', strtoupper($interface->getMethod())],
+ '@authority' => ['"@authority"', $interface->getUri()->getAuthority()],
+ '@scheme' => ['"@scheme"', strtolower($interface->getUri()->getScheme())],
+ '@target-uri' => ['"@target-uri"', $targetUri],
+ '@request-target' => ['"@request-target"', $interface->getRequestTarget()],
+ '@path' => ['"@path"', $interface->getUri()->getPath()],
+ '@query' => ['"@query"', $interface->getUri()->getQuery()],
+ '@query-param' => $this->getQueryParam($interface, $parameters) ?? ['', ''],
+ '@status' => ['"@status"', '"@status": ' . $interface->getStatusCode()],
+ default => ['"' . $fieldName . '"', trim($headers[$fieldName] ?? '')],
+ };
+ return $value;
+ }
+
+ /**
+ * @param string $query
+ * @return array|null
+ *
+ * Should not use PHP's parse_str function here, as it has some issues with
+ * spaces and dots in parameter names. These are unlikely to occur, but the
+ * following function treats them as opaque strings rather than as variable
+ * names.
+ */
+ private function parseQueryString(string $query): array|null
+ {
+ $result = [];
+
+ if (!isset($query)) {
+ return null;
+ }
+
+ $queryParams = explode('&', $query);
+ foreach ($queryParams as $param) {
+ // The '=' character is not required and indicates a boolean true value if unset.
+ $element = explode('=', $param, 2);
+ $result[urldecode($element[0])] = isset($element[1]) ? urldecode($element[1]) : '';
+ }
+ return $result;
+ }
+
+ /**
+ * @param $parameters
+ * @param $name
+ * @return string|null
+ *
+ * Find one query parameter by name (which must supplied as parameters in the (structured) covered field list).
+ */
+ private function getQueryParam($whichRequest, array $parameters): array
+ {
+ $queryString = $whichRequest->getUri()->getQuery();
+ if ($queryString) {
+ $queryParams = $this->parseQueryString($queryString);
+ $fieldName = $parameters['name'];
+ if ($fieldName) {
+ return ['"_' . $fieldName . '_"', $queryParams[$fieldName] ? '"' . $queryParams[$fieldName] . '"' : ''];
+ }
+ }
+ throw new UnProcessableSignatureException('Query string named parameter not set');
+ }
+
+ private function applyStructuredField(string $name, string $fieldValue): string
+ {
+ $type = $this->structuredFieldTypes[trim($name, '"')];
+ switch ($type) {
+ case 'list':
+ $field = OuterList::fromHttpValue($fieldValue);
+ break;
+ case 'innerlist':
+ $field = InnerList::fromHttpValue($fieldValue);
+ break;
+ case 'parameters':
+ $field = Parameters::fromHttpValue($fieldValue);
+ break;
+ case 'dictionary':
+ $field = Dictionary::fromHttpValue($fieldValue);
+ break;
+ case 'item':
+ $field = Item::fromHttpValue($fieldValue);
+ break;
+ case 'url':
+ return '"' . $fieldValue . '"';
+ case 'date':
+ return '@' . strtotime($fieldValue);
+ case 'etag':
+ $result = '';
+ $list = explode(',', $fieldValue);
+ foreach ($list as $item) {
+ if (str_starts_with(trim($item), 'W/')) {
+ $result .= substr(trim($item), 2) . '; w' . ', ';
+ } else {
+ $result .= trim($item) . ', ';
+ }
+ }
+ return rtrim($result, ', ');
+ case 'cookie':
+ // @TODO
+ default:
+ break;
+ }
+ if (!$field) {
+ throw new UnProcessableSignatureException('Unknown or unregistered structured field type');
+ }
+ return $field->toHttpValue();
+ }
+
+ private function applySingleKeyValue(string $name, string $key, string $fieldValue): string
+ {
+ $type = $this->structuredFieldTypes[trim($name, '"')];
+ if (empty($type) || $type === 'dictionary') {
+ $dictionary = Dictionary::fromHttpValue($fieldValue);
+ if ($dictionary->isNotEmpty() && isset($dictionary[$key])) {
+ return $dictionary[$key]->toHttpValue();
+ }
+ }
+ return '';
+ }
+
+ private function createSignature(string $data): string
+ {
+ return match ($this->algorithm) {
+ 'rsa-v1_5-sha256' => $this->rsaSign($data),
+ 'rsa-v1_5-sha512' => $this->rsa512Sign($data),
+ 'rsa-sha256' => $this->rsaSign($data),
+ 'rsa-pss-sha512' => $this->pssSign($data),
+ 'ed25519' => $this->ed25519Sign($data),
+ 'hmac-sha256' => base64_encode(hash_hmac('sha256', $data, $this->privateKey, true)),
+ default => throw new UnProcessableSignatureException("Unsupported algorithm: $this->algorithm")
+ };
+ }
+
+ private function verifySignature(string $data, string $signature, string $alg): bool
+ {
+ return match ($alg) {
+ 'rsa-v1_5-sha256' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA256) === 1,
+ 'rsa-v1_5-sha512' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA512) === 1,
+ 'rsa-sha256' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA256) === 1,
+ 'rsa-pss-sha512' => $this->pssVerify($data, $signature),
+ 'ed25519' => openssl_verify($data, $signature, $this->publicKey, "Ed25519") === 1,
+ 'hmac-sha256' => hash_equals(
+ base64_encode(hash_hmac('sha256', $data, $this->privateKey, true)),
+ base64_encode($signature)
+ ),
+ default => false
+ };
+ }
+
+ /* sign with rsa or ed25519 */
+
+ private function rsaSign(string $data): string
+ {
+ if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA256)) {
+ throw new UnProcessableSignatureException("RSA signing failed");
+ }
+ return base64_encode($signature);
+ }
+ private function rsa512Sign(string $data): string
+ {
+ if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA512)) {
+ throw new UnProcessableSignatureException("RSA signing failed");
+ }
+ return base64_encode($signature);
+ }
+
+ private function pssSign(string $data): string
+ {
+ $rsa = new RSA();
+ if ($rsa->loadKey($this->privateKey) !== true) {
+ throw new UnprocessableSignatureException("PSS loadkey failure");
+ };
+ $rsa->setHash('sha512');
+ $rsa->setMGFHash('sha512');
+ $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+ try {
+ $signatureBytes = $rsa->sign($data);
+ } catch (\Exception $exception) {
+ throw new UnprocessableSignatureException($exception->getMessage());
+ }
+ return base64_encode($signatureBytes);
+ }
+
+ private function ed25519Sign(string $data): string
+ {
+ if (!openssl_sign($data, $signature, $this->privateKey, "Ed25519")) {
+ throw new UnProcessableSignatureException("Ed25519 signing failed");
+ }
+ return base64_encode($signature);
+ }
+
+ private function pssVerify(string $data, $signature): bool
+ {
+ $rsa = new RSA();
+ if (!$rsa->loadKey($this->publicKey)) {
+ throw new UnprocessableSignatureException("PSS loadkey failure");
+ };
+ $rsa->setHash('sha512');
+ $rsa->setMGFHash('sha512');
+ $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+ try {
+ $verified = $rsa->verify($data, $signature);
+ } catch (\Exception $exception) {
+ $verified = false;
+ }
+ return $verified;
+ }
+
+ /* parse a structed dict */
+
+ private function parseStructuredDict(string $headerValue)
+ {
+ if (str_starts_with(trim($headerValue), '(')) {
+ return InnerList::fromHttpValue($headerValue);
+ }
+ else {
+ return Dictionary::fromHttpValue($headerValue);
+ }
+ }
+
+ /* Recommended to calculate the digest of the body and add it to
+ covered headers and sign, but not required. Convenience function
+ to calculate the digest.
+
+ ex:
+
+ $digest = $signer->createContentDigestHeader($body);
+ $request = $request->withHeader('Content-Digest', $digest);
+ */
+
+ /**
+ * @param string $body
+ * @param $algorithm ('sha256' || 'sha512')
+ * @return string
+ *
+ * From https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Digest
+ * The algorithm used to create a digest of the message content. Only two registered digest algorithms are
+ * considered secure: sha-512 and sha-256. The insecure (legacy) registered digest algorithms
+ * are: md5, sha (SHA-1), unixsum, unixcksum, adler (ADLER32) and crc32c.
+ */
+
+ public function createContentDigestHeader(string $body, $algorithm = 'sha256'): string
+ {
+ $supportedAlgorithms = ['sha256' => 'sha-256', 'sha512' => 'sha-512'];
+ foreach ($supportedAlgorithms as $alg => $value) {
+ if ($alg === $algorithm) {
+ $algorithmHeaderString = $value;
+ break;
+ }
+ }
+ if (!isset($algorithmHeaderString)) {
+ throw new UnProcessableSignatureException("Unsupported digest algorithm: $algorithm");
+ }
+ $digest = hash($algorithm, $body, true);
+ /**
+ * Output as structured field.
+ */
+ return $algorithmHeaderString . '=:' . base64_encode($digest) . ':';
+ }
+
+ /* Convenience function, probably want to use a robust PSR7 solution instead */
+
+ public static function parseHttpMessage(string $raw): array
+ {
+ [$headerPart, $body] = explode("\r\n\r\n", $raw, 2);
+ $lines = explode("\r\n", $headerPart);
+ $requestLine = array_shift($lines);
+ [$method, $path] = explode(' ', $requestLine);
+
+ $headers = [];
+ foreach ($lines as $line) {
+ [$name, $value] = explode(':', $line, 2);
+ $headers[strtolower(trim($name))] = trim($value);
+ }
+
+ return [
+ 'method' => $method,
+ 'path' => $path,
+ 'headers' => $headers,
+ 'body' => $body
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ public function getStructuredFieldTypes(): array
+ {
+ return $this->structuredFieldTypes;
+ }
+
+ /**
+ * @param array $structuredFieldTypes
+ * @return HttpMessageSigner
+ */
+ public function setStructuredFieldTypes(array $structuredFieldTypes): HttpMessageSigner
+ {
+ $this->structuredFieldTypes = $structuredFieldTypes;
+ return $this;
+ }
+
+ /**
+ * $structuredFieldType consists of a key named after a specific header field, and a value
+ * which is one of 'list', 'innerlist', 'parameters, 'dictionary', 'item'.
+ *
+ * Example:
+ * ['example-dict' => 'dictionary']
+ *
+ * The 'sf' flag will not be honoured unless the structured type of the header is registered/known.
+ *
+ * @param array $structuredFieldType
+ * @return $this
+ */
+
+ public function addStructuredFieldType(array $structuredFieldType): HttpMessageSigner
+ {
+ foreach ($structuredFieldType as $key => $value) {
+ $this->structuredFieldTypes[$key] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOriginalRequest()
+ {
+ return $this->originalRequest;
+ }
+
+ /**
+ * @param mixed $originalRequest
+ * @return HttpMessageSigner
+ */
+ public function setOriginalRequest($originalRequest)
+ {
+ $this->originalRequest = $originalRequest;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getKeyId(): string
+ {
+ return $this->keyId;
+ }
+
+ /**
+ * @param string $keyId
+ * @return HttpMessageSigner
+ */
+ public function setKeyId(string $keyId): HttpMessageSigner
+ {
+ $this->keyId = $keyId;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPrivateKey(): string
+ {
+ return $this->privateKey;
+ }
+
+ /**
+ * @param string $privateKey
+ * @return HttpMessageSigner
+ */
+ public function setPrivateKey(string $privateKey): HttpMessageSigner
+ {
+ $this->privateKey = $privateKey;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublicKey(): string
+ {
+ return $this->publicKey;
+ }
+
+ /**
+ * @param string $publicKey
+ * @return HttpMessageSigner
+ */
+ public function setPublicKey(string $publicKey): HttpMessageSigner
+ {
+ $this->publicKey = $publicKey;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAlgorithm(): string
+ {
+ return $this->algorithm;
+ }
+
+ /**
+ * @param string $algorithm
+ * @return HttpMessageSigner
+ */
+ public function setAlgorithm(string $algorithm): HttpMessageSigner
+ {
+ $this->algorithm = $algorithm;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSignatureId(): string
+ {
+ return $this->signatureId;
+ }
+
+ /**
+ * @param string $signatureId
+ * @return HttpMessageSigner
+ */
+ public function setSignatureId(string $signatureId): HttpMessageSigner
+ {
+ $this->signatureId = $signatureId;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCreated(): string
+ {
+ return $this->created;
+ }
+
+ /**
+ * @param string $created
+ * @return HttpMessageSigner
+ */
+ public function setCreated(string $created): HttpMessageSigner
+ {
+ $this->created = $created;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getExpires(): string
+ {
+ return $this->expires;
+ }
+
+ /**
+ * @param string $expires
+ * @return HttpMessageSigner
+ */
+ public function setExpires(string $expires): HttpMessageSigner
+ {
+ $this->expires = $expires;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNonce(): string
+ {
+ return $this->nonce;
+ }
+
+ /**
+ * @param string $nonce
+ * @return HttpMessageSigner
+ */
+ public function setNonce(string $nonce): HttpMessageSigner
+ {
+ $this->nonce = $nonce;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTag(): string
+ {
+ return $this->tag;
+ }
+
+ /**
+ * @param string $tag
+ * @return HttpMessageSigner
+ */
+ public function setTag(string $tag): HttpMessageSigner
+ {
+ $this->tag = $tag;
+ return $this;
+ }
+}
+
diff --git a/vendor/macgirvin/http-message-signer/src/StructuredFieldTypes.php b/vendor/macgirvin/http-message-signer/src/StructuredFieldTypes.php
new file mode 100644
index 000000000..df70717f8
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/src/StructuredFieldTypes.php
@@ -0,0 +1,91 @@
+<?php
+namespace HttpSignature;
+
+
+class StructuredFieldTypes
+{
+ public function __construct()
+ {
+ return $this;
+ }
+
+ public function getFields(): array
+ {
+ $returnValue = [];
+ $fields = explode("\n", $this->fieldlist);
+ foreach ($fields as $entry) {
+ $exploded = explode(" ", $entry);
+ $returnValue[$exploded[0]] = trim($exploded[1]);
+ }
+ return $returnValue;
+ }
+
+ public $fieldlist =
+'accept list
+accept-encoding list
+accept-language list
+accept-patch list
+accept-post list
+accept-ranges list
+access-control-allow-credentials item
+access-control-allow-headers list
+access-control-allow-methods list
+access-control-allow-origin item
+access-control-expose-headers list
+access-control-max-age item
+access-control-request-headers list
+access-control-request-method item
+age item
+allow list
+alpn list
+alt-svc dictionary
+alt-used item
+cache-control dictionary
+cdn-loop list
+clear-site-data list
+connection list
+content-encoding list
+content-language list
+content-length list
+content-location url
+content-type item
+cookie cookie
+cross-origin-resource-policy item
+date date
+dnt item
+etag etag
+expect dictionary
+expect-ct dictionary
+expires date
+host item
+if-match etag
+if-modified-since date
+if-none-match etag
+if-unmodified-since date
+keep-alive dictionary
+last-modified date
+location url
+max-forwards item
+origin item
+pragma dictionary
+prefer dictionary
+preference-applied dictionary
+referer url
+retry-after item
+sec-websocket-extensions list
+sec-websocket-protocol list
+sec-websocket-version item
+server-timing list
+set-cookie cookie
+surrogate-control dictionary
+te list
+timing-allow-origin list
+trailer list
+transfer-encoding list
+upgrade-insecure-requests item
+vary list
+x-content-type-options item
+x-frame-options item
+x-xss-protection list';
+
+} \ No newline at end of file
diff --git a/vendor/macgirvin/http-message-signer/src/UnProcessableSignatureException.php b/vendor/macgirvin/http-message-signer/src/UnProcessableSignatureException.php
new file mode 100644
index 000000000..b3dacd8eb
--- /dev/null
+++ b/vendor/macgirvin/http-message-signer/src/UnProcessableSignatureException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace HttpSignature;
+
+class UnProcessableSignatureException extends \Exception
+{
+
+} \ No newline at end of file
diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE
new file mode 100644
index 000000000..45c7017df
--- /dev/null
+++ b/vendor/paragonie/random_compat/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Paragon Initiative Enterprises
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh
new file mode 100755
index 000000000..b4a5ba31c
--- /dev/null
+++ b/vendor/paragonie/random_compat/build-phar.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
+
+php -dphar.readonly=0 "$basedir/other/build_phar.php" $* \ No newline at end of file
diff --git a/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json
new file mode 100644
index 000000000..f2b9c4e51
--- /dev/null
+++ b/vendor/paragonie/random_compat/composer.json
@@ -0,0 +1,34 @@
+{
+ "name": "paragonie/random_compat",
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+ "keywords": [
+ "csprng",
+ "random",
+ "polyfill",
+ "pseudorandom"
+ ],
+ "license": "MIT",
+ "type": "library",
+ "authors": [
+ {
+ "name": "Paragon Initiative Enterprises",
+ "email": "security@paragonie.com",
+ "homepage": "https://paragonie.com"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/paragonie/random_compat/issues",
+ "email": "info@paragonie.com",
+ "source": "https://github.com/paragonie/random_compat"
+ },
+ "require": {
+ "php": ">= 7"
+ },
+ "require-dev": {
+ "vimeo/psalm": "^1",
+ "phpunit/phpunit": "4.*|5.*"
+ },
+ "suggest": {
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+ }
+}
diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey
new file mode 100644
index 000000000..eb50ebfcd
--- /dev/null
+++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
+pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
+-----END PUBLIC KEY-----
diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc
new file mode 100644
index 000000000..6a1d7f300
--- /dev/null
+++ b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.22 (MingW32)
+
+iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
+QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
+1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
+NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
+NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
+JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
+=B6+8
+-----END PGP SIGNATURE-----
diff --git a/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php
new file mode 100644
index 000000000..c7731a56f
--- /dev/null
+++ b/vendor/paragonie/random_compat/lib/random.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Random_* Compatibility Library
+ * for using the new PHP 7 random_* API in PHP 5 projects
+ *
+ * @version 2.99.99
+ * @released 2018-06-06
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// NOP
diff --git a/vendor/paragonie/random_compat/other/build_phar.php b/vendor/paragonie/random_compat/other/build_phar.php
new file mode 100644
index 000000000..70ef4b2ed
--- /dev/null
+++ b/vendor/paragonie/random_compat/other/build_phar.php
@@ -0,0 +1,57 @@
+<?php
+$dist = dirname(__DIR__).'/dist';
+if (!is_dir($dist)) {
+ mkdir($dist, 0755);
+}
+if (file_exists($dist.'/random_compat.phar')) {
+ unlink($dist.'/random_compat.phar');
+}
+$phar = new Phar(
+ $dist.'/random_compat.phar',
+ FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
+ 'random_compat.phar'
+);
+rename(
+ dirname(__DIR__).'/lib/random.php',
+ dirname(__DIR__).'/lib/index.php'
+);
+$phar->buildFromDirectory(dirname(__DIR__).'/lib');
+rename(
+ dirname(__DIR__).'/lib/index.php',
+ dirname(__DIR__).'/lib/random.php'
+);
+
+/**
+ * If we pass an (optional) path to a private key as a second argument, we will
+ * sign the Phar with OpenSSL.
+ *
+ * If you leave this out, it will produce an unsigned .phar!
+ */
+if ($argc > 1) {
+ if (!@is_readable($argv[1])) {
+ echo 'Could not read the private key file:', $argv[1], "\n";
+ exit(255);
+ }
+ $pkeyFile = file_get_contents($argv[1]);
+
+ $private = openssl_get_privatekey($pkeyFile);
+ if ($private !== false) {
+ $pkey = '';
+ openssl_pkey_export($private, $pkey);
+ $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
+
+ /**
+ * Save the corresponding public key to the file
+ */
+ if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
+ $details = openssl_pkey_get_details($private);
+ file_put_contents(
+ $dist.'/random_compat.phar.pubkey',
+ $details['key']
+ );
+ }
+ } else {
+ echo 'An error occurred reading the private key from OpenSSL.', "\n";
+ exit(255);
+ }
+}
diff --git a/vendor/paragonie/random_compat/psalm-autoload.php b/vendor/paragonie/random_compat/psalm-autoload.php
new file mode 100644
index 000000000..d71d1b818
--- /dev/null
+++ b/vendor/paragonie/random_compat/psalm-autoload.php
@@ -0,0 +1,9 @@
+<?php
+
+require_once 'lib/byte_safe_strings.php';
+require_once 'lib/cast_to_int.php';
+require_once 'lib/error_polyfill.php';
+require_once 'other/ide_stubs/libsodium.php';
+require_once 'lib/random.php';
+
+$int = random_int(0, 65536);
diff --git a/vendor/paragonie/random_compat/psalm.xml b/vendor/paragonie/random_compat/psalm.xml
new file mode 100644
index 000000000..596d99dd6
--- /dev/null
+++ b/vendor/paragonie/random_compat/psalm.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<psalm
+ autoloader="psalm-autoload.php"
+ stopOnFirstError="false"
+ useDocblockTypes="true"
+>
+ <projectFiles>
+ <directory name="lib" />
+ </projectFiles>
+ <issueHandlers>
+ <RedundantConditionGivenDocblockType errorLevel="info" />
+ <UnresolvableInclude errorLevel="info" />
+ <DuplicateClass errorLevel="info" />
+ <InvalidOperand errorLevel="info" />
+ <UndefinedConstant errorLevel="info" />
+ <MissingReturnType errorLevel="info" />
+ <InvalidReturnType errorLevel="info" />
+ </issueHandlers>
+</psalm>
diff --git a/vendor/phpseclib/phpseclib/README.md b/vendor/phpseclib/phpseclib/README.md
index 1bdee151d..37cbcb9d5 100644
--- a/vendor/phpseclib/phpseclib/README.md
+++ b/vendor/phpseclib/phpseclib/README.md
@@ -1,6 +1,6 @@
# phpseclib - PHP Secure Communications Library
-[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/github/phpseclib/phpseclib)
+[![CI Status](https://github.com/phpseclib/phpseclib/actions/workflows/ci.yml/badge.svg?branch=3.0&event=push "CI Status")](https://github.com/phpseclib/phpseclib)
## Supporting phpseclib
@@ -19,7 +19,7 @@ SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 /
## Documentation
* [Documentation / Manual](https://phpseclib.com/)
-* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum)
+* [API Documentation](https://api.phpseclib.com/3.0/) (generated by Doctum)
## Branches
@@ -79,22 +79,19 @@ Special Thanks to our $50+ sponsors!:
2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
3. Install Development Dependencies
-
- ``` sh
+ ```sh
composer install
```
4. Create a Feature Branch
-5. (Recommended) Run the Test Suite
-
- ``` sh
- vendor/bin/phpunit
- ```
-6. (Recommended) Check whether your code conforms to our Coding Standards by running
-
- ``` sh
- vendor/bin/phing -f build/build.xml sniff
- ```
-
-7. Send us a Pull Request
+5. Run continuous integration checks:
+ ```sh
+ composer global require php:^8.1 squizlabs/php_codesniffer friendsofphp/php-cs-fixer vimeo/psalm
+ phpcs --standard=build/php_codesniffer.xml
+ php-cs-fixer fix --config=build/php-cs-fixer.php --diff --dry-run --using-cache=no
+ psalm --config=build/psalm.xml --no-cache --long-progress --report-show-info=false --output-format=text
+ vendor/bin/phpunit --verbose --configuration tests/phpunit.xml
+ ```
+
+6. Send us a Pull Request
diff --git a/vendor/phpseclib/phpseclib/appveyor.yml b/vendor/phpseclib/phpseclib/appveyor.yml
deleted file mode 100644
index 210a90347..000000000
--- a/vendor/phpseclib/phpseclib/appveyor.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-build: false
-shallow_clone: false
-platform:
- - x86
- - x64
-clone_folder: C:\projects\phpseclib
-
-install:
- - cinst -y OpenSSL.Light
- - SET PATH=C:\Program Files\OpenSSL;%PATH%
- - sc config wuauserv start= auto
- - net start wuauserv
- - cinst -y php --version 5.6.30
- - cd c:\tools\php56
- - copy php.ini-production php.ini
- - echo date.timezone="UTC" >> php.ini
- - echo extension_dir=ext >> php.ini
- - echo extension=php_openssl.dll >> php.ini
- - echo extension=php_gmp.dll >> php.ini
- - cd C:\projects\phpseclib
- - SET PATH=C:\tools\php56;%PATH%
- - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe
- - php.exe composer.phar install --prefer-source --no-interaction
-
-test_script:
- - cd C:\projects\phpseclib
- - vendor\bin\phpunit.bat tests/Windows32Test.php \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib/composer.json b/vendor/phpseclib/phpseclib/composer.json
index 3fbffa67c..d996700e2 100644
--- a/vendor/phpseclib/phpseclib/composer.json
+++ b/vendor/phpseclib/phpseclib/composer.json
@@ -51,26 +51,34 @@
}
],
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.6.1",
+ "paragonie/constant_time_encoding": "^1|^2|^3",
+ "paragonie/random_compat": "^1.4|^2.0|^9.99.99"
},
"require-dev": {
- "phing/phing": "~2.7",
- "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
- "squizlabs/php_codesniffer": "~2.0"
+ "phpunit/phpunit": "*"
},
"suggest": {
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
- "ext-xml": "Install the XML extension to load XML formatted public keys."
+ "ext-dom": "Install the DOM extension to load XML formatted public keys."
},
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
- "phpseclib\\": "phpseclib/"
+ "phpseclib3\\": "phpseclib/"
}
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "phpseclib3\\Tests\\": "tests/"
+ }
+ },
+ "config": {
+ "sort-packages": true
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php b/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php
new file mode 100644
index 000000000..ad8f63b65
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Common/Functions/Strings.php
@@ -0,0 +1,507 @@
+<?php
+
+/**
+ * Common String Functions
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Common\Functions;
+
+use ParagonIE\ConstantTime\Base64;
+use ParagonIE\ConstantTime\Base64UrlSafe;
+use ParagonIE\ConstantTime\Hex;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\Common\FiniteField;
+
+/**
+ * Common String Functions
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Strings
+{
+ /**
+ * String Shift
+ *
+ * Inspired by array_shift
+ *
+ * @param string $string
+ * @param int $index
+ * @return string
+ */
+ public static function shift(&$string, $index = 1)
+ {
+ $substr = substr($string, 0, $index);
+ $string = substr($string, $index);
+ return $substr;
+ }
+
+ /**
+ * String Pop
+ *
+ * Inspired by array_pop
+ *
+ * @param string $string
+ * @param int $index
+ * @return string
+ */
+ public static function pop(&$string, $index = 1)
+ {
+ $substr = substr($string, -$index);
+ $string = substr($string, 0, -$index);
+ return $substr;
+ }
+
+ /**
+ * Parse SSH2-style string
+ *
+ * Returns either an array or a boolean if $data is malformed.
+ *
+ * Valid characters for $format are as follows:
+ *
+ * C = byte
+ * b = boolean (true/false)
+ * N = uint32
+ * Q = uint64
+ * s = string
+ * i = mpint
+ * L = name-list
+ *
+ * uint64 is not supported.
+ *
+ * @param string $format
+ * @param string $data
+ * @return mixed
+ */
+ public static function unpackSSH2($format, &$data)
+ {
+ $format = self::formatPack($format);
+ $result = [];
+ for ($i = 0; $i < strlen($format); $i++) {
+ switch ($format[$i]) {
+ case 'C':
+ case 'b':
+ if (!strlen($data)) {
+ throw new \LengthException('At least one byte needs to be present for successful C / b decodes');
+ }
+ break;
+ case 'N':
+ case 'i':
+ case 's':
+ case 'L':
+ if (strlen($data) < 4) {
+ throw new \LengthException('At least four byte needs to be present for successful N / i / s / L decodes');
+ }
+ break;
+ case 'Q':
+ if (strlen($data) < 8) {
+ throw new \LengthException('At least eight byte needs to be present for successful N / i / s / L decodes');
+ }
+ break;
+
+ default:
+ throw new \InvalidArgumentException('$format contains an invalid character');
+ }
+ switch ($format[$i]) {
+ case 'C':
+ $result[] = ord(self::shift($data));
+ continue 2;
+ case 'b':
+ $result[] = ord(self::shift($data)) != 0;
+ continue 2;
+ case 'N':
+ list(, $temp) = unpack('N', self::shift($data, 4));
+ $result[] = $temp;
+ continue 2;
+ case 'Q':
+ // pack() added support for Q in PHP 5.6.3 and PHP 5.6 is phpseclib 3's minimum version
+ // so in theory we could support this BUT, "64-bit format codes are not available for
+ // 32-bit versions" and phpseclib works on 32-bit installs. on 32-bit installs
+ // 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow
+ // for. sure, you're not gonna get the full precision of 64-bit numbers but just because
+ // you need > 32-bit precision doesn't mean you need the full 64-bit precision
+ $unpacked = unpack('Nupper/Nlower', self::shift($data, 8));
+ $upper = $unpacked['upper'];
+ $lower = $unpacked['lower'];
+ $temp = $upper ? 4294967296 * $upper : 0;
+ $temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower;
+ // $temp = hexdec(bin2hex(self::shift($data, 8)));
+ $result[] = $temp;
+ continue 2;
+ }
+ list(, $length) = unpack('N', self::shift($data, 4));
+ if (strlen($data) < $length) {
+ throw new \LengthException("$length bytes needed; " . strlen($data) . ' bytes available');
+ }
+ $temp = self::shift($data, $length);
+ switch ($format[$i]) {
+ case 'i':
+ $result[] = new BigInteger($temp, -256);
+ break;
+ case 's':
+ $result[] = $temp;
+ break;
+ case 'L':
+ $result[] = explode(',', $temp);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create SSH2-style string
+ *
+ * @param string $format
+ * @param string|int|float|array|bool ...$elements
+ * @return string
+ */
+ public static function packSSH2($format, ...$elements)
+ {
+ $format = self::formatPack($format);
+ if (strlen($format) != count($elements)) {
+ throw new \InvalidArgumentException('There must be as many arguments as there are characters in the $format string');
+ }
+ $result = '';
+ for ($i = 0; $i < strlen($format); $i++) {
+ $element = $elements[$i];
+ switch ($format[$i]) {
+ case 'C':
+ if (!is_int($element)) {
+ throw new \InvalidArgumentException('Bytes must be represented as an integer between 0 and 255, inclusive.');
+ }
+ $result .= pack('C', $element);
+ break;
+ case 'b':
+ if (!is_bool($element)) {
+ throw new \InvalidArgumentException('A boolean parameter was expected.');
+ }
+ $result .= $element ? "\1" : "\0";
+ break;
+ case 'Q':
+ if (!is_int($element) && !is_float($element)) {
+ throw new \InvalidArgumentException('An integer was expected.');
+ }
+ // 4294967296 == 1 << 32
+ $result .= pack('NN', $element / 4294967296, $element);
+ break;
+ case 'N':
+ if (is_float($element)) {
+ $element = (int) $element;
+ }
+ if (!is_int($element)) {
+ throw new \InvalidArgumentException('An integer was expected.');
+ }
+ $result .= pack('N', $element);
+ break;
+ case 's':
+ if (!self::is_stringable($element)) {
+ throw new \InvalidArgumentException('A string was expected.');
+ }
+ $result .= pack('Na*', strlen($element), $element);
+ break;
+ case 'i':
+ if (!$element instanceof BigInteger && !$element instanceof FiniteField\Integer) {
+ throw new \InvalidArgumentException('A phpseclib3\Math\BigInteger or phpseclib3\Math\Common\FiniteField\Integer object was expected.');
+ }
+ $element = $element->toBytes(true);
+ $result .= pack('Na*', strlen($element), $element);
+ break;
+ case 'L':
+ if (!is_array($element)) {
+ throw new \InvalidArgumentException('An array was expected.');
+ }
+ $element = implode(',', $element);
+ $result .= pack('Na*', strlen($element), $element);
+ break;
+ default:
+ throw new \InvalidArgumentException('$format contains an invalid character');
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Expand a pack string
+ *
+ * Converts C5 to CCCCC, for example.
+ *
+ * @param string $format
+ * @return string
+ */
+ private static function formatPack($format)
+ {
+ $parts = preg_split('#(\d+)#', $format, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $format = '';
+ for ($i = 1; $i < count($parts); $i += 2) {
+ $format .= substr($parts[$i - 1], 0, -1) . str_repeat(substr($parts[$i - 1], -1), $parts[$i]);
+ }
+ $format .= $parts[$i - 1];
+
+ return $format;
+ }
+
+ /**
+ * Convert binary data into bits
+ *
+ * bin2hex / hex2bin refer to base-256 encoded data as binary, whilst
+ * decbin / bindec refer to base-2 encoded data as binary. For the purposes
+ * of this function, bin refers to base-256 encoded data whilst bits refers
+ * to base-2 encoded data
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function bits2bin($x)
+ {
+ /*
+ // the pure-PHP approach is faster than the GMP approach
+ if (function_exists('gmp_export')) {
+ return strlen($x) ? gmp_export(gmp_init($x, 2)) : gmp_init(0);
+ }
+ */
+
+ if (preg_match('#[^01]#', $x)) {
+ throw new \RuntimeException('The only valid characters are 0 and 1');
+ }
+
+ if (!defined('PHP_INT_MIN')) {
+ define('PHP_INT_MIN', ~PHP_INT_MAX);
+ }
+
+ $length = strlen($x);
+ if (!$length) {
+ return '';
+ }
+ $block_size = PHP_INT_SIZE << 3;
+ $pad = $block_size - ($length % $block_size);
+ if ($pad != $block_size) {
+ $x = str_repeat('0', $pad) . $x;
+ }
+
+ $parts = str_split($x, $block_size);
+ $str = '';
+ foreach ($parts as $part) {
+ $xor = $part[0] == '1' ? PHP_INT_MIN : 0;
+ $part[0] = '0';
+ $str .= pack(
+ PHP_INT_SIZE == 4 ? 'N' : 'J',
+ $xor ^ eval('return 0b' . $part . ';')
+ );
+ }
+ return ltrim($str, "\0");
+ }
+
+ /**
+ * Convert bits to binary data
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function bin2bits($x, $trim = true)
+ {
+ /*
+ // the pure-PHP approach is slower than the GMP approach BUT
+ // i want to the pure-PHP version to be easily unit tested as well
+ if (function_exists('gmp_import')) {
+ return gmp_strval(gmp_import($x), 2);
+ }
+ */
+
+ $len = strlen($x);
+ $mod = $len % PHP_INT_SIZE;
+ if ($mod) {
+ $x = str_pad($x, $len + PHP_INT_SIZE - $mod, "\0", STR_PAD_LEFT);
+ }
+
+ $bits = '';
+ if (PHP_INT_SIZE == 4) {
+ $digits = unpack('N*', $x);
+ foreach ($digits as $digit) {
+ $bits .= sprintf('%032b', $digit);
+ }
+ } else {
+ $digits = unpack('J*', $x);
+ foreach ($digits as $digit) {
+ $bits .= sprintf('%064b', $digit);
+ }
+ }
+
+ return $trim ? ltrim($bits, '0') : $bits;
+ }
+
+ /**
+ * Switch Endianness Bit Order
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function switchEndianness($x)
+ {
+ $r = '';
+ for ($i = strlen($x) - 1; $i >= 0; $i--) {
+ $b = ord($x[$i]);
+ if (PHP_INT_SIZE === 8) {
+ // 3 operations
+ // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
+ $r .= chr((($b * 0x0202020202) & 0x010884422010) % 1023);
+ } else {
+ // 7 operations
+ // from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
+ $p1 = ($b * 0x0802) & 0x22110;
+ $p2 = ($b * 0x8020) & 0x88440;
+ $r .= chr(
+ (($p1 | $p2) * 0x10101) >> 16
+ );
+ }
+ }
+ return $r;
+ }
+
+ /**
+ * Increment the current string
+ *
+ * @param string $var
+ * @return string
+ */
+ public static function increment_str(&$var)
+ {
+ if (function_exists('sodium_increment')) {
+ $var = strrev($var);
+ sodium_increment($var);
+ $var = strrev($var);
+ return $var;
+ }
+
+ for ($i = 4; $i <= strlen($var); $i += 4) {
+ $temp = substr($var, -$i, 4);
+ switch ($temp) {
+ case "\xFF\xFF\xFF\xFF":
+ $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
+ break;
+ case "\x7F\xFF\xFF\xFF":
+ $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
+ return $var;
+ default:
+ $temp = unpack('Nnum', $temp);
+ $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
+ return $var;
+ }
+ }
+
+ $remainder = strlen($var) % 4;
+
+ if ($remainder == 0) {
+ return $var;
+ }
+
+ $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
+ $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
+ $var = substr_replace($var, $temp, 0, $remainder);
+
+ return $var;
+ }
+
+ /**
+ * Find whether the type of a variable is string (or could be converted to one)
+ *
+ * @param mixed $var
+ * @return bool
+ * @psalm-assert-if-true string|\Stringable $var
+ */
+ public static function is_stringable($var)
+ {
+ return is_string($var) || (is_object($var) && method_exists($var, '__toString'));
+ }
+
+ /**
+ * Constant Time Base64-decoding
+ *
+ * ParagoneIE\ConstantTime doesn't use libsodium if it's available so we'll do so
+ * ourselves. see https://github.com/paragonie/constant_time_encoding/issues/39
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function base64_decode($data)
+ {
+ return function_exists('sodium_base642bin') ?
+ sodium_base642bin($data, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING, '=') :
+ Base64::decode($data);
+ }
+
+ /**
+ * Constant Time Base64-decoding (URL safe)
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function base64url_decode($data)
+ {
+ // return self::base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
+
+ return function_exists('sodium_base642bin') ?
+ sodium_base642bin($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING, '=') :
+ Base64UrlSafe::decode($data);
+ }
+
+ /**
+ * Constant Time Base64-encoding
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function base64_encode($data)
+ {
+ return function_exists('sodium_bin2base64') ?
+ sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL) :
+ Base64::encode($data);
+ }
+
+ /**
+ * Constant Time Base64-encoding (URL safe)
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function base64url_encode($data)
+ {
+ // return str_replace(['+', '/'], ['-', '_'], self::base64_encode($data));
+
+ return function_exists('sodium_bin2base64') ?
+ sodium_bin2base64($data, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING) :
+ Base64UrlSafe::encode($data);
+ }
+
+ /**
+ * Constant Time Hex Decoder
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function hex2bin($data)
+ {
+ return function_exists('sodium_hex2bin') ?
+ sodium_hex2bin($data) :
+ Hex::decode($data);
+ }
+
+ /**
+ * Constant Time Hex Encoder
+ *
+ * @param string $data
+ * @return string
+ */
+ public static function bin2hex($data)
+ {
+ return function_exists('sodium_bin2hex') ?
+ sodium_bin2hex($data) :
+ Hex::encode($data);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php
index 9903db105..403871627 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php
@@ -16,7 +16,7 @@
* it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
* is called, again, at which point, it'll be recalculated.
*
- * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
+ * Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
* make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function,
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
*
@@ -25,7 +25,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $aes = new \phpseclib\Crypt\AES();
+ * $aes = new \phpseclib3\Crypt\AES('ctr');
*
* $aes->setKey('abcdefghijklmnop');
*
@@ -39,58 +39,78 @@
* ?>
* </code>
*
- * @category Crypt
- * @package AES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
/**
* Pure-PHP implementation of AES.
*
- * @package AES
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class AES extends Rijndael
{
/**
* Dummy function
*
- * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
+ * Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
*
- * @see \phpseclib\Crypt\Rijndael::setBlockLength()
- * @access public
+ * @see \phpseclib3\Crypt\Rijndael::setBlockLength()
* @param int $length
+ * @throws \BadMethodCallException anytime it's called
*/
- function setBlockLength($length)
+ public function setBlockLength($length)
{
- return;
+ throw new \BadMethodCallException('The block length cannot be set for AES.');
}
/**
* Sets the key length
*
- * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to
- * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
+ * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length
*
- * @see \phpseclib\Crypt\Rijndael:setKeyLength()
- * @access public
+ * @see \phpseclib3\Crypt\Rijndael:setKeyLength()
* @param int $length
+ * @throws \LengthException if the key length isn't supported
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
+ switch ($length) {
+ case 128:
+ case 192:
+ case 256:
+ break;
+ default:
+ throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported');
+ }
parent::setKeyLength($length);
- switch ($this->key_length) {
- case 20:
- $this->key_length = 24;
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Rijndael supports five different key lengths, AES only supports three.
+ *
+ * @see \phpseclib3\Crypt\Rijndael:setKey()
+ * @see setKeyLength()
+ * @param string $key
+ * @throws \LengthException if the key length isn't supported
+ */
+ public function setKey($key)
+ {
+ switch (strlen($key)) {
+ case 16:
+ case 24:
+ case 32:
break;
- case 28:
- $this->key_length = 32;
+ default:
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
}
+
+ parent::setKey($key);
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
index 346c064b8..998cf8bb3 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
@@ -59,7 +59,7 @@
*
* This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock()
* implementation can best be understood by doing Ctrl + F and searching for where
- * CRYPT_BASE_USE_REG_INTVAL is defined.
+ * self::$use_reg_intval is defined.
*
* # phpseclib's three different _setupKey() implementations
*
@@ -97,7 +97,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $blowfish = new \phpseclib\Crypt\Blowfish();
+ * $blowfish = new \phpseclib3\Crypt\Blowfish('ctr');
*
* $blowfish->setKey('12345678901234567890123456789012');
*
@@ -107,8 +107,6 @@
* ?>
* </code>
*
- * @category Crypt
- * @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
@@ -116,63 +114,50 @@
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\BlockCipher;
/**
* Pure-PHP implementation of Blowfish.
*
- * @package Blowfish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
- * @access public
*/
-class Blowfish extends Base
+class Blowfish extends BlockCipher
{
/**
* Block Length of the cipher
*
- * @see \phpseclib\Crypt\Base::block_size
+ * @see Common\SymmetricKey::block_size
* @var int
- * @access private
*/
- var $block_size = 8;
+ protected $block_size = 8;
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'blowfish';
+ protected $cipher_name_mcrypt = 'blowfish';
/**
* Optimizing value while CFB-encrypting
*
- * @see \phpseclib\Crypt\Base::cfb_init_len
+ * @see Common\SymmetricKey::cfb_init_len
* @var int
- * @access private
- */
- var $cfb_init_len = 500;
-
- /**
- * SHA512 Object
- *
- * @see self::bcrypt_pbkdf
- * @var object
- * @access private
*/
- var $sha512;
+ protected $cfb_init_len = 500;
/**
- * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
+ * The fixed subkeys boxes
*
- * S-Box 0
+ * S-Box
*
- * @access private
* @var array
*/
- var $sbox0 = array(
+ private static $sbox = [
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
@@ -204,16 +189,8 @@ class Blowfish extends Base
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
- 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
- );
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
- /**
- * S-Box 1
- *
- * @access private
- * @var array
- */
- var $sbox1 = array(
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
@@ -245,16 +222,8 @@ class Blowfish extends Base
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
- 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
- );
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
- /**
- * S-Box 2
- *
- * @access private
- * @var array
- */
- var $sbox2 = array(
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
@@ -286,16 +255,8 @@ class Blowfish extends Base
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
- 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
- );
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
- /**
- * S-Box 3
- *
- * @access private
- * @var array
- */
- var $sbox3 = array(
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
@@ -328,19 +289,18 @@ class Blowfish extends Base
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
- );
+ ];
/**
* P-Array consists of 18 32-bit subkeys
*
* @var array
- * @access private
*/
- var $parray = array(
+ private static $parray = [
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
- );
+ ];
/**
* The BCTX-working Array
@@ -348,64 +308,41 @@ class Blowfish extends Base
* Holds the expanded key [p] and the key-depended s-boxes [sb]
*
* @var array
- * @access private
*/
- var $bctx;
+ private $bctx;
/**
* Holds the last used key
*
* @var array
- * @access private
*/
- var $kl;
+ private $kl;
/**
* The Key Length (in bytes)
- *
- * @see \phpseclib\Crypt\Base::setKeyLength()
- * @var int
- * @access private
- * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
+ * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
- * of that, we'll just precompute it once.
+ * of that, we'll just precompute it once.}
+ *
+ * @see Common\SymmetricKey::setKeyLength()
+ * @var int
*/
- var $key_length = 16;
+ protected $key_length = 16;
/**
* Default Constructor.
*
- * Determines whether or not the mcrypt extension should be used.
- *
- * $mode could be:
- *
- * - CRYPT_MODE_ECB
- *
- * - CRYPT_MODE_CBC
- *
- * - CRYPT_MODE_CTR
- *
- * - CRYPT_MODE_CFB
- *
- * - CRYPT_MODE_OFB
- *
- * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...)
- *
- * If not explicitly set, CRYPT_MODE_CBC will be used.
- *
- * @param int $mode
- * @access public
+ * @param string $mode
+ * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/
- function __construct($mode = self::MODE_CBC)
+ public function __construct($mode)
{
parent::__construct($mode);
- $this->sbox0 = array_map('intval', $this->sbox0);
- $this->sbox1 = array_map('intval', $this->sbox1);
- $this->sbox2 = array_map('intval', $this->sbox2);
- $this->sbox3 = array_map('intval', $this->sbox3);
- $this->parray = array_map('intval', $this->parray);
+ if ($this->mode == self::MODE_STREAM) {
+ throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
+ }
}
/**
@@ -413,78 +350,65 @@ class Blowfish extends Base
*
* Key lengths can be between 32 and 448 bits.
*
- * @access public
* @param int $length
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
- if ($length < 32) {
- $this->key_length = 4;
- } elseif ($length > 448) {
- $this->key_length = 56;
- } else {
- $this->key_length = $length >> 3;
+ if ($length < 32 || $length > 448) {
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported');
}
+ $this->key_length = $length >> 3;
+
parent::setKeyLength($length);
}
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::isValidEngine()
+ * @see Common\SymmetricKey::isValidEngine()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
+ if ($this->key_length < 16) {
+ return false;
+ }
// quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
// "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
// in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
- if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) {
- return false;
- }
- if ($this->key_length < 16) {
- return false;
- }
$this->cipher_name_openssl_ecb = 'bf-ecb';
- $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode();
+ $this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode();
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
* Setup the key (expansion)
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see Common\SymmetricKey::_setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
// already expanded
return;
}
- $this->kl = array('key' => $this->key);
+ $this->kl = ['key' => $this->key];
/* key-expanding p[] and S-Box building sb[] */
- $this->bctx = array(
- 'p' => array(),
- 'sb' => array(
- $this->sbox0,
- $this->sbox1,
- $this->sbox2,
- $this->sbox3
- )
- );
+ $this->bctx = [
+ 'p' => [],
+ 'sb' => self::$sbox
+ ];
// unpack binary string in unsigned chars
$key = array_values(unpack('C*', $this->key));
@@ -498,27 +422,40 @@ class Blowfish extends Base
$j = 0;
}
}
- $this->bctx['p'][] = $this->parray[$i] ^ intval($data);
+ $this->bctx['p'][] = self::$parray[$i] ^ intval($data);
}
// encrypt the zero-string, replace P1 and P2 with the encrypted data,
// encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys
$data = "\0\0\0\0\0\0\0\0";
for ($i = 0; $i < 18; $i += 2) {
- list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
+ list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
$this->bctx['p'][$i ] = $l;
$this->bctx['p'][$i + 1] = $r;
}
- for ($i = 0; $i < 4; ++$i) {
+ for ($i = 0; $i < 0x400; $i += 0x100) {
for ($j = 0; $j < 256; $j += 2) {
- list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data)));
- $this->bctx['sb'][$i][$j ] = $l;
- $this->bctx['sb'][$i][$j + 1] = $r;
+ list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
+ $this->bctx['sb'][$i | $j] = $l;
+ $this->bctx['sb'][$i | ($j + 1)] = $r;
}
}
}
/**
+ * Initialize Static Variables
+ */
+ protected static function initialize_static_variables()
+ {
+ if (is_float(self::$sbox[0x200])) {
+ self::$sbox = array_map('intval', self::$sbox);
+ self::$parray = array_map('intval', self::$parray);
+ }
+
+ parent::initialize_static_variables();
+ }
+
+ /**
* bcrypt
*
* @param string $sha2pass
@@ -526,35 +463,28 @@ class Blowfish extends Base
* @access private
* @return string
*/
- function _bcrypt_hash($sha2pass, $sha2salt)
+ private static function bcrypt_hash($sha2pass, $sha2salt)
{
- $p = $this->parray;
- $sbox0 = $this->sbox0;
- $sbox1 = $this->sbox1;
- $sbox2 = $this->sbox2;
- $sbox3 = $this->sbox3;
+ $p = self::$parray;
+ $sbox = self::$sbox;
$cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
$sha2pass = array_values(unpack('N*', $sha2pass));
$sha2salt = array_values(unpack('N*', $sha2salt));
- $this->_expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ self::expandstate($sha2salt, $sha2pass, $sbox, $p);
for ($i = 0; $i < 64; $i++) {
- $this->_expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p);
- $this->_expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ self::expand0state($sha2salt, $sbox, $p);
+ self::expand0state($sha2pass, $sbox, $p);
}
for ($i = 0; $i < 64; $i++) {
- for ($j = 0; $j < 8; $j+= 2) { // count($cdata) == 8
- list($cdata[$j], $cdata[$j + 1]) = $this->_encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($j = 0; $j < 8; $j += 2) { // count($cdata) == 8
+ list($cdata[$j], $cdata[$j + 1]) = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox, $p);
}
}
- $output = '';
- for ($i = 0; $i < count($cdata); $i++) {
- $output.= pack('L*', $cdata[$i]);
- }
- return $output;
+ return pack('V*', ...$cdata);
}
/**
@@ -565,37 +495,34 @@ class Blowfish extends Base
* @param int $keylen
* @param int $rounds
* @access public
- * @return false|string
+ * @return string
*/
- function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
+ public static function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
{
- if (PHP_INT_SIZE == 4) {
- user_error('bcrypt is far too slow to be practical on 32-bit versions of PHP');
- return false;
- }
+ self::initialize_static_variables();
- if (!isset($this->sha512)) {
- $this->sha512 = new Hash('sha512');
+ if (PHP_INT_SIZE == 4) {
+ throw new \RuntimeException('bcrypt is far too slow to be practical on 32-bit versions of PHP');
}
- $sha2pass = $this->sha512->hash($pass);
- $results = array();
+ $sha2pass = hash('sha512', $pass, true);
+ $results = [];
$count = 1;
while (32 * count($results) < $keylen) {
$countsalt = $salt . pack('N', $count++);
- $sha2salt = $this->sha512->hash($countsalt);
- $out = $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
+ $sha2salt = hash('sha512', $countsalt, true);
+ $out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
for ($i = 1; $i < $rounds; $i++) {
- $sha2salt = $this->sha512->hash($tmpout);
- $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
- $out^= $tmpout;
+ $sha2salt = hash('sha512', $tmpout, true);
+ $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
+ $out ^= $tmpout;
}
$results[] = $out;
}
$output = '';
for ($i = 0; $i < 32; $i++) {
foreach ($results as $result) {
- $output.= $result[$i];
+ $output .= $result[$i];
}
}
return substr($output, 0, $keylen);
@@ -606,20 +533,17 @@ class Blowfish extends Base
*
* @access private
* @param int[] $key
- * @param int[] $sbox0
- * @param int[] $sbox1
- * @param int[] $sbox2
- * @param int[] $sbox3
+ * @param int[] $sbox
* @param int[] $p
* @see self::_bcrypt_hash()
*/
- function _expand0state($key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
+ private static function expand0state(array $key, array &$sbox, array &$p)
{
// expand0state is basically the same thing as this:
- //return $this->_expandstate(array_fill(0, 16, 0), $key);
+ //return self::expandstate(array_fill(0, 16, 0), $key);
// but this separate function eliminates a bunch of XORs and array lookups
- $p = array(
+ $p = [
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
@@ -638,38 +562,23 @@ class Blowfish extends Base
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
- );
+ ];
// @codingStandardsIgnoreStart
- list( $p[0], $p[1]) = $this->_encryptBlockHelperFast( 0, 0, $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[10], $p[11]) = $this->_encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[12], $p[13]) = $this->_encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[14], $p[15]) = $this->_encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[16], $p[17]) = $this->_encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[0], $p[1]) = self::encryptBlockHelperFast( 0, 0, $sbox, $p);
+ list( $p[2], $p[3]) = self::encryptBlockHelperFast($p[ 0], $p[ 1], $sbox, $p);
+ list( $p[4], $p[5]) = self::encryptBlockHelperFast($p[ 2], $p[ 3], $sbox, $p);
+ list( $p[6], $p[7]) = self::encryptBlockHelperFast($p[ 4], $p[ 5], $sbox, $p);
+ list( $p[8], $p[9]) = self::encryptBlockHelperFast($p[ 6], $p[ 7], $sbox, $p);
+ list($p[10], $p[11]) = self::encryptBlockHelperFast($p[ 8], $p[ 9], $sbox, $p);
+ list($p[12], $p[13]) = self::encryptBlockHelperFast($p[10], $p[11], $sbox, $p);
+ list($p[14], $p[15]) = self::encryptBlockHelperFast($p[12], $p[13], $sbox, $p);
+ list($p[16], $p[17]) = self::encryptBlockHelperFast($p[14], $p[15], $sbox, $p);
// @codingStandardsIgnoreEnd
- list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2; $i < 256; $i+= 2) {
- list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2; $i < 256; $i+= 2) {
- list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2; $i < 256; $i+= 2) {
- list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2; $i < 256; $i+= 2) {
- list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($p[16], $p[17], $sbox, $p);
+ for ($i = 2; $i < 1024; $i += 2) {
+ list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($sbox[$i - 2], $sbox[$i - 1], $sbox, $p);
}
}
@@ -679,16 +588,13 @@ class Blowfish extends Base
* @access private
* @param int[] $data
* @param int[] $key
- * @param int[] $sbox0
- * @param int[] $sbox1
- * @param int[] $sbox2
- * @param int[] $sbox3
+ * @param int[] $sbox
* @param int[] $p
* @see self::_bcrypt_hash()
*/
- function _expandstate($data, $key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
+ private static function expandstate(array $data, array $key, array &$sbox, array &$p)
{
- $p = array(
+ $p = [
$p[0] ^ $key[0],
$p[1] ^ $key[1],
$p[2] ^ $key[2],
@@ -707,64 +613,45 @@ class Blowfish extends Base
$p[15] ^ $key[15],
$p[16] ^ $key[0],
$p[17] ^ $key[1]
- );
+ ];
// @codingStandardsIgnoreStart
- list( $p[0], $p[1]) = $this->_encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[10], $p[11]) = $this->_encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[12], $p[13]) = $this->_encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[14], $p[15]) = $this->_encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- list($p[16], $p[17]) = $this->_encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[0], $p[1]) = self::encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox, $p);
+ list( $p[2], $p[3]) = self::encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox, $p);
+ list( $p[4], $p[5]) = self::encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox, $p);
+ list( $p[6], $p[7]) = self::encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox, $p);
+ list( $p[8], $p[9]) = self::encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox, $p);
+ list($p[10], $p[11]) = self::encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox, $p);
+ list($p[12], $p[13]) = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox, $p);
+ list($p[14], $p[15]) = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox, $p);
+ list($p[16], $p[17]) = self::encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox, $p);
// @codingStandardsIgnoreEnd
- list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
- list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
- list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
- list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- }
-
- list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
- for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
- list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($sbox[0], $sbox[1]) = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox, $p);
+ for ($i = 2, $j = 4; $i < 1024; $i += 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
+ list($sbox[$i], $sbox[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox[$i - 2], $data[$j + 1] ^ $sbox[$i - 1], $sbox, $p);
}
}
/**
* Encrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
- $p = $this->bctx["p"];
- // extract($this->bctx["sb"], EXTR_PREFIX_ALL, "sb"); // slower
- $sb_0 = $this->bctx["sb"][0];
- $sb_1 = $this->bctx["sb"][1];
- $sb_2 = $this->bctx["sb"][2];
- $sb_3 = $this->bctx["sb"][3];
-
- $in = unpack("N*", $in);
+ $p = $this->bctx['p'];
+ // extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower
+ $sb = $this->bctx['sb'];
+
+ $in = unpack('N*', $in);
$l = $in[1];
$r = $in[2];
- list($r, $l) = PHP_INT_SIZE === 8 ?
- $this->_encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) :
- $this->_encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p);
+ list($r, $l) = PHP_INT_SIZE == 4 ?
+ self::encryptBlockHelperSlow($l, $r, $sb, $p) :
+ self::encryptBlockHelperFast($l, $r, $sb, $p);
return pack("N*", $r, $l);
}
@@ -775,34 +662,31 @@ class Blowfish extends Base
* @access private
* @param int $x0
* @param int $x1
- * @param int[] $sbox0
- * @param int[] $sbox1
- * @param int[] $sbox2
- * @param int[] $sbox3
+ * @param int[] $sbox
* @param int[] $p
* @return int[]
*/
- function _encryptBlockHelperFast($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
+ private static function encryptBlockHelperFast($x0, $x1, array $sbox, array $p)
{
$x0 ^= $p[0];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
- $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
- $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
-
- return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF);
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
+ $x1 ^= ((($sbox[($x0 & 0xFF000000) >> 24] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
+ $x0 ^= ((($sbox[($x1 & 0xFF000000) >> 24] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];
+
+ return [$x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF];
}
/**
@@ -811,183 +695,141 @@ class Blowfish extends Base
* @access private
* @param int $x0
* @param int $x1
- * @param int[] $sbox0
- * @param int[] $sbox1
- * @param int[] $sbox2
- * @param int[] $sbox3
+ * @param int[] $sbox
* @param int[] $p
* @return int[]
*/
- function _encryptBlockHelperSlow($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
+ private static function encryptBlockHelperSlow($x0, $x1, array $sbox, array $p)
{
// -16777216 == intval(0xFF000000) on 32-bit PHP installs
- $x0^= $p[0];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
- $x1^= $this->safe_intval(($this->safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
- $x0^= $this->safe_intval(($this->safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
-
- return array($x1 ^ $p[17], $x0);
+ $x0 ^= $p[0];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[1];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[2];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[3];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[4];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[5];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[6];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[7];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[8];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[9];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[10];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[11];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[12];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[13];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[14];
+ $x1 ^= self::safe_intval((self::safe_intval($sbox[(($x0 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x0 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x0 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x0 & 0xFF)]) ^ $p[15];
+ $x0 ^= self::safe_intval((self::safe_intval($sbox[(($x1 & -16777216) >> 24) & 0xFF] + $sbox[0x100 | (($x1 & 0xFF0000) >> 16)]) ^ $sbox[0x200 | (($x1 & 0xFF00) >> 8)]) + $sbox[0x300 | ($x1 & 0xFF)]) ^ $p[16];
+
+ return [$x1 ^ $p[17], $x0];
}
/**
* Decrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
- $p = $this->bctx["p"];
- $sb_0 = $this->bctx["sb"][0];
- $sb_1 = $this->bctx["sb"][1];
- $sb_2 = $this->bctx["sb"][2];
- $sb_3 = $this->bctx["sb"][3];
+ $p = $this->bctx['p'];
+ $sb = $this->bctx['sb'];
- $in = unpack("N*", $in);
+ $in = unpack('N*', $in);
$l = $in[1];
$r = $in[2];
- for ($i = 17; $i > 2; $i-= 2) {
- $l^= $p[$i];
- $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
- $sb_2[$l >> 8 & 0xff]) +
- $sb_3[$l & 0xff]);
+ for ($i = 17; $i > 2; $i -= 2) {
+ $l ^= $p[$i];
+ $r ^= self::safe_intval((self::safe_intval($sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]) ^
+ $sb[0x200 + ($l >> 8 & 0xff)]) +
+ $sb[0x300 + ($l & 0xff)]);
- $r^= $p[$i - 1];
- $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
- $sb_2[$r >> 8 & 0xff]) +
- $sb_3[$r & 0xff]);
+ $r ^= $p[$i - 1];
+ $l ^= self::safe_intval((self::safe_intval($sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]) ^
+ $sb[0x200 + ($r >> 8 & 0xff)]) +
+ $sb[0x300 + ($r & 0xff)]);
}
- return pack("N*", $r ^ $p[0], $l ^ $p[1]);
+ return pack('N*', $r ^ $p[0], $l ^ $p[1]);
}
/**
* Setup the performance-optimized function for de/encrypt()
*
- * @see \phpseclib\Crypt\Base::_setupInlineCrypt()
- * @access private
+ * @see Common\SymmetricKey::_setupInlineCrypt()
*/
- function _setupInlineCrypt()
+ protected function setupInlineCrypt()
{
- $lambda_functions =& self::_getLambdaFunctions();
-
- // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
- // (Currently, for Blowfish, one generated $lambda_function cost on php5.5@32bit ~100kb unfreeable mem and ~180kb on php5.5@64bit)
- // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
- $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
-
- // Generation of a unique hash for our generated code
- $code_hash = "Crypt_Blowfish, {$this->mode}";
- if ($gen_hi_opt_code) {
- $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
- }
-
- $safeint = $this->safe_intval_inline();
-
- if (!isset($lambda_functions[$code_hash])) {
- switch (true) {
- case $gen_hi_opt_code:
- $p = $this->bctx['p'];
- $init_crypt = '
- static $sb_0, $sb_1, $sb_2, $sb_3;
- if (!$sb_0) {
- $sb_0 = $self->bctx["sb"][0];
- $sb_1 = $self->bctx["sb"][1];
- $sb_2 = $self->bctx["sb"][2];
- $sb_3 = $self->bctx["sb"][3];
- }
- ';
- break;
- default:
- $p = array();
- for ($i = 0; $i < 18; ++$i) {
- $p[] = '$p_' . $i;
- }
- $init_crypt = '
- list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"];
- list(' . implode(',', $p) . ') = $self->bctx["p"];
-
- ';
+ $p = $this->bctx['p'];
+ $init_crypt = '
+ static $sb;
+ if (!$sb) {
+ $sb = $this->bctx["sb"];
}
-
- // Generating encrypt code:
- $encrypt_block = '
- $in = unpack("N*", $in);
- $l = $in[1];
- $r = $in[2];
- ';
- for ($i = 0; $i < 16; $i+= 2) {
- $encrypt_block.= '
- $l^= ' . $p[$i] . ';
- $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
- $sb_2[$l >> 8 & 0xff]) +
- $sb_3[$l & 0xff]') . ';
-
- $r^= ' . $p[$i + 1] . ';
- $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
- $sb_2[$r >> 8 & 0xff]) +
- $sb_3[$r & 0xff]') . ';
- ';
- }
- $encrypt_block.= '
- $in = pack("N*",
- $r ^ ' . $p[17] . ',
- $l ^ ' . $p[16] . '
- );
+ ';
+
+ $safeint = self::safe_intval_inline();
+
+ // Generating encrypt code:
+ $encrypt_block = '
+ $in = unpack("N*", $in);
+ $l = $in[1];
+ $r = $in[2];
+ ';
+ for ($i = 0; $i < 16; $i += 2) {
+ $encrypt_block .= '
+ $l^= ' . $p[$i] . ';
+ $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
+ $sb[0x200 + ($l >> 8 & 0xff)]) +
+ $sb[0x300 + ($l & 0xff)]') . ';
+
+ $r^= ' . $p[$i + 1] . ';
+ $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^
+ $sb[0x200 + ($r >> 8 & 0xff)]) +
+ $sb[0x300 + ($r & 0xff)]') . ';
';
-
- // Generating decrypt code:
- $decrypt_block = '
- $in = unpack("N*", $in);
- $l = $in[1];
- $r = $in[2];
- ';
-
- for ($i = 17; $i > 2; $i-= 2) {
- $decrypt_block.= '
- $l^= ' . $p[$i] . ';
- $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
- $sb_2[$l >> 8 & 0xff]) +
- $sb_3[$l & 0xff]') . ';
-
- $r^= ' . $p[$i - 1] . ';
- $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
- $sb_2[$r >> 8 & 0xff]) +
- $sb_3[$r & 0xff]') . ';
- ';
- }
-
- $decrypt_block.= '
- $in = pack("N*",
- $r ^ ' . $p[0] . ',
- $l ^ ' . $p[1] . '
- );
+ }
+ $encrypt_block .= '
+ $in = pack("N*",
+ $r ^ ' . $p[17] . ',
+ $l ^ ' . $p[16] . '
+ );
+ ';
+ // Generating decrypt code:
+ $decrypt_block = '
+ $in = unpack("N*", $in);
+ $l = $in[1];
+ $r = $in[2];
+ ';
+
+ for ($i = 17; $i > 2; $i -= 2) {
+ $decrypt_block .= '
+ $l^= ' . $p[$i] . ';
+ $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$l >> 24 & 0xff] + $sb[0x100 + ($l >> 16 & 0xff)]') . ' ^
+ $sb[0x200 + ($l >> 8 & 0xff)]) +
+ $sb[0x300 + ($l & 0xff)]') . ';
+
+ $r^= ' . $p[$i - 1] . ';
+ $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb[$r >> 24 & 0xff] + $sb[0x100 + ($r >> 16 & 0xff)]') . ' ^
+ $sb[0x200 + ($r >> 8 & 0xff)]) +
+ $sb[0x300 + ($r & 0xff)]') . ';
';
+ }
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => $init_crypt,
- 'init_encrypt' => '',
- 'init_decrypt' => '',
- 'encrypt_block' => $encrypt_block,
- 'decrypt_block' => $decrypt_block
- )
+ $decrypt_block .= '
+ $in = pack("N*",
+ $r ^ ' . $p[0] . ',
+ $l ^ ' . $p[1] . '
);
- }
- $this->inline_crypt = $lambda_functions[$code_hash];
+ ';
+
+ $this->inline_crypt = $this->createInlineCryptFunction(
+ [
+ 'init_crypt' => $init_crypt,
+ 'init_encrypt' => '',
+ 'init_decrypt' => '',
+ 'encrypt_block' => $encrypt_block,
+ 'decrypt_block' => $decrypt_block
+ ]
+ );
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php
new file mode 100644
index 000000000..b2691b5dd
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/ChaCha20.php
@@ -0,0 +1,799 @@
+<?php
+
+/**
+ * Pure-PHP implementation of ChaCha20.
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2019 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Exception\BadDecryptionException;
+use phpseclib3\Exception\InsufficientSetupException;
+
+/**
+ * Pure-PHP implementation of ChaCha20.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class ChaCha20 extends Salsa20
+{
+ /**
+ * The OpenSSL specific name of the cipher
+ *
+ * @var string
+ */
+ protected $cipher_name_openssl = 'chacha20';
+
+ /**
+ * Test for engine validity
+ *
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ * @param int $engine
+ * @return bool
+ */
+ protected function isValidEngineHelper($engine)
+ {
+ switch ($engine) {
+ case self::ENGINE_LIBSODIUM:
+ // PHP 7.2.0 (30 Nov 2017) added support for libsodium
+
+ // we could probably make it so that if $this->counter == 0 then the first block would be done with either OpenSSL
+ // or PHP and then subsequent blocks would then be done with libsodium but idk - it's not a high priority atm
+
+ // we could also make it so that if $this->counter == 0 and $this->continuousBuffer then do the first string
+ // with libsodium and subsequent strings with openssl or pure-PHP but again not a high priority
+ return function_exists('sodium_crypto_aead_chacha20poly1305_ietf_encrypt') &&
+ $this->key_length == 32 &&
+ (($this->usePoly1305 && !isset($this->poly1305Key) && $this->counter == 0) || $this->counter == 1) &&
+ !$this->continuousBuffer;
+ case self::ENGINE_OPENSSL:
+ // OpenSSL 1.1.0 (released 25 Aug 2016) added support for chacha20.
+ // PHP didn't support OpenSSL 1.1.0 until 7.0.19 (11 May 2017)
+
+ // if you attempt to provide openssl with a 128 bit key (as opposed to a 256 bit key) openssl will null
+ // pad the key to 256 bits and still use the expansion constant for 256-bit keys. the fact that
+ // openssl treats the IV as both the counter and nonce, however, let's us use openssl in continuous mode
+ // whereas libsodium does not
+ if ($this->key_length != 32) {
+ return false;
+ }
+ }
+
+ return parent::isValidEngineHelper($engine);
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
+ * @see self::crypt()
+ * @param string $plaintext
+ * @return string $ciphertext
+ */
+ public function encrypt($plaintext)
+ {
+ $this->setup();
+
+ if ($this->engine == self::ENGINE_LIBSODIUM) {
+ return $this->encrypt_with_libsodium($plaintext);
+ }
+
+ return parent::encrypt($plaintext);
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
+ * At least if the continuous buffer is disabled.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see self::crypt()
+ * @param string $ciphertext
+ * @return string $plaintext
+ */
+ public function decrypt($ciphertext)
+ {
+ $this->setup();
+
+ if ($this->engine == self::ENGINE_LIBSODIUM) {
+ return $this->decrypt_with_libsodium($ciphertext);
+ }
+
+ return parent::decrypt($ciphertext);
+ }
+
+ /**
+ * Encrypts a message with libsodium
+ *
+ * @see self::encrypt()
+ * @param string $plaintext
+ * @return string $text
+ */
+ private function encrypt_with_libsodium($plaintext)
+ {
+ $params = [$plaintext, $this->aad, $this->nonce, $this->key];
+ $ciphertext = strlen($this->nonce) == 8 ?
+ sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
+ sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
+ if (!$this->usePoly1305) {
+ return substr($ciphertext, 0, strlen($plaintext));
+ }
+
+ $newciphertext = substr($ciphertext, 0, strlen($plaintext));
+
+ $this->newtag = $this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12 ?
+ substr($ciphertext, strlen($plaintext)) :
+ $this->poly1305($newciphertext);
+
+ return $newciphertext;
+ }
+
+ /**
+ * Decrypts a message with libsodium
+ *
+ * @see self::decrypt()
+ * @param string $ciphertext
+ * @return string $text
+ */
+ private function decrypt_with_libsodium($ciphertext)
+ {
+ $params = [$ciphertext, $this->aad, $this->nonce, $this->key];
+
+ if (isset($this->poly1305Key)) {
+ if ($this->oldtag === false) {
+ throw new InsufficientSetupException('Authentication Tag has not been set');
+ }
+ if ($this->usingGeneratedPoly1305Key && strlen($this->nonce) == 12) {
+ $plaintext = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(...$params);
+ $this->oldtag = false;
+ if ($plaintext === false) {
+ throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
+ }
+ return $plaintext;
+ }
+ $newtag = $this->poly1305($ciphertext);
+ if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
+ $this->oldtag = false;
+ throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
+ }
+ $this->oldtag = false;
+ }
+
+ $plaintext = strlen($this->nonce) == 8 ?
+ sodium_crypto_aead_chacha20poly1305_encrypt(...$params) :
+ sodium_crypto_aead_chacha20poly1305_ietf_encrypt(...$params);
+
+ return substr($plaintext, 0, strlen($ciphertext));
+ }
+
+ /**
+ * Sets the nonce.
+ *
+ * @param string $nonce
+ */
+ public function setNonce($nonce)
+ {
+ if (!is_string($nonce)) {
+ throw new \UnexpectedValueException('The nonce should be a string');
+ }
+
+ /*
+ from https://tools.ietf.org/html/rfc7539#page-7
+
+ "Note also that the original ChaCha had a 64-bit nonce and 64-bit
+ block count. We have modified this here to be more consistent with
+ recommendations in Section 3.2 of [RFC5116]."
+ */
+ switch (strlen($nonce)) {
+ case 8: // 64 bits
+ case 12: // 96 bits
+ break;
+ default:
+ throw new \LengthException('Nonce of size ' . strlen($nonce) . ' not supported by this algorithm. Only 64-bit nonces or 96-bit nonces are supported');
+ }
+
+ $this->nonce = $nonce;
+ $this->changed = true;
+ $this->setEngine();
+ }
+
+ /**
+ * Setup the self::ENGINE_INTERNAL $engine
+ *
+ * (re)init, if necessary, the internal cipher $engine
+ *
+ * _setup() will be called each time if $changed === true
+ * typically this happens when using one or more of following public methods:
+ *
+ * - setKey()
+ *
+ * - setNonce()
+ *
+ * - First run of encrypt() / decrypt() with no init-settings
+ *
+ * @see self::setKey()
+ * @see self::setNonce()
+ * @see self::disableContinuousBuffer()
+ */
+ protected function setup()
+ {
+ if (!$this->changed) {
+ return;
+ }
+
+ $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
+
+ $this->changed = $this->nonIVChanged = false;
+
+ if ($this->nonce === false) {
+ throw new InsufficientSetupException('No nonce has been defined');
+ }
+
+ if ($this->key === false) {
+ throw new InsufficientSetupException('No key has been defined');
+ }
+
+ if ($this->usePoly1305 && !isset($this->poly1305Key)) {
+ $this->usingGeneratedPoly1305Key = true;
+ if ($this->engine == self::ENGINE_LIBSODIUM) {
+ return;
+ }
+ $this->createPoly1305Key();
+ }
+
+ $key = $this->key;
+ if (strlen($key) == 16) {
+ $constant = 'expand 16-byte k';
+ $key .= $key;
+ } else {
+ $constant = 'expand 32-byte k';
+ }
+
+ $this->p1 = $constant . $key;
+ $this->p2 = $this->nonce;
+ if (strlen($this->nonce) == 8) {
+ $this->p2 = "\0\0\0\0" . $this->p2;
+ }
+ }
+
+ /**
+ * The quarterround function
+ *
+ * @param int $a
+ * @param int $b
+ * @param int $c
+ * @param int $d
+ */
+ protected static function quarterRound(&$a, &$b, &$c, &$d)
+ {
+ // in https://datatracker.ietf.org/doc/html/rfc7539#section-2.1 the addition,
+ // xor'ing and rotation are all on the same line so i'm keeping it on the same
+ // line here as well
+ // @codingStandardsIgnoreStart
+ $a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 16);
+ $c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 12);
+ $a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 8);
+ $c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 7);
+ // @codingStandardsIgnoreEnd
+ }
+
+ /**
+ * The doubleround function
+ *
+ * @param int $x0 (by reference)
+ * @param int $x1 (by reference)
+ * @param int $x2 (by reference)
+ * @param int $x3 (by reference)
+ * @param int $x4 (by reference)
+ * @param int $x5 (by reference)
+ * @param int $x6 (by reference)
+ * @param int $x7 (by reference)
+ * @param int $x8 (by reference)
+ * @param int $x9 (by reference)
+ * @param int $x10 (by reference)
+ * @param int $x11 (by reference)
+ * @param int $x12 (by reference)
+ * @param int $x13 (by reference)
+ * @param int $x14 (by reference)
+ * @param int $x15 (by reference)
+ */
+ protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
+ {
+ // columnRound
+ static::quarterRound($x0, $x4, $x8, $x12);
+ static::quarterRound($x1, $x5, $x9, $x13);
+ static::quarterRound($x2, $x6, $x10, $x14);
+ static::quarterRound($x3, $x7, $x11, $x15);
+ // rowRound
+ static::quarterRound($x0, $x5, $x10, $x15);
+ static::quarterRound($x1, $x6, $x11, $x12);
+ static::quarterRound($x2, $x7, $x8, $x13);
+ static::quarterRound($x3, $x4, $x9, $x14);
+ }
+
+ /**
+ * The Salsa20 hash function function
+ *
+ * On my laptop this loop unrolled / function dereferenced version of parent::salsa20 encrypts 1mb of text in
+ * 0.65s vs the 0.85s that it takes with the parent method.
+ *
+ * If we were free to assume that the host OS would always be 64-bits then the if condition in leftRotate could
+ * be eliminated and we could knock this done to 0.60s.
+ *
+ * For comparison purposes, RC4 takes 0.16s and AES in CTR mode with the Eval engine takes 0.48s.
+ * AES in CTR mode with the PHP engine takes 1.19s. Salsa20 / ChaCha20 do not benefit as much from the Eval
+ * approach due to the fact that there are a lot less variables to de-reference, fewer loops to unroll, etc
+ *
+ * @param string $x
+ */
+ protected static function salsa20($x)
+ {
+ list(, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15) = unpack('V*', $x);
+ $z0 = $x0;
+ $z1 = $x1;
+ $z2 = $x2;
+ $z3 = $x3;
+ $z4 = $x4;
+ $z5 = $x5;
+ $z6 = $x6;
+ $z7 = $x7;
+ $z8 = $x8;
+ $z9 = $x9;
+ $z10 = $x10;
+ $z11 = $x11;
+ $z12 = $x12;
+ $z13 = $x13;
+ $z14 = $x14;
+ $z15 = $x15;
+
+ // @codingStandardsIgnoreStart
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+
+ // columnRound
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
+ $x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
+ $x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
+
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
+ $x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
+ $x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
+
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
+ $x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
+ $x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
+
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
+ $x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
+ $x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
+
+ // rowRound
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
+ $x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
+ $x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
+
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
+ $x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
+ $x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
+
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
+ $x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
+ $x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
+
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
+ $x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
+ $x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
+ // @codingStandardsIgnoreEnd
+
+ $x0 += $z0;
+ $x1 += $z1;
+ $x2 += $z2;
+ $x3 += $z3;
+ $x4 += $z4;
+ $x5 += $z5;
+ $x6 += $z6;
+ $x7 += $z7;
+ $x8 += $z8;
+ $x9 += $z9;
+ $x10 += $z10;
+ $x11 += $z11;
+ $x12 += $z12;
+ $x13 += $z13;
+ $x14 += $z14;
+ $x15 += $z15;
+
+ return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php
new file mode 100644
index 000000000..a380e43d7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php
@@ -0,0 +1,581 @@
+<?php
+
+/**
+ * Base Class for all asymmetric key ciphers
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common;
+
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Exception\NoKeyLoadedException;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Base Class for all asymmetric cipher classes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AsymmetricKey
+{
+ /**
+ * Precomputed Zero
+ *
+ * @var BigInteger
+ */
+ protected static $zero;
+
+ /**
+ * Precomputed One
+ *
+ * @var BigInteger
+ */
+ protected static $one;
+
+ /**
+ * Format of the loaded key
+ *
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * Hash function
+ *
+ * @var Hash
+ */
+ protected $hash;
+
+ /**
+ * HMAC function
+ *
+ * @var Hash
+ */
+ private $hmac;
+
+ /**
+ * Supported plugins (lower case)
+ *
+ * @see self::initialize_static_variables()
+ * @var array
+ */
+ private static $plugins = [];
+
+ /**
+ * Invisible plugins
+ *
+ * @see self::initialize_static_variables()
+ * @var array
+ */
+ private static $invisiblePlugins = [];
+
+ /**
+ * Available Engines
+ *
+ * @var boolean[]
+ */
+ protected static $engines = [];
+
+ /**
+ * Key Comment
+ *
+ * @var null|string
+ */
+ private $comment;
+
+ /**
+ * @param string $type
+ * @return array|string
+ */
+ abstract public function toString($type, array $options = []);
+
+ /**
+ * The constructor
+ */
+ protected function __construct()
+ {
+ self::initialize_static_variables();
+
+ $this->hash = new Hash('sha256');
+ $this->hmac = new Hash('sha256');
+ }
+
+ /**
+ * Initialize static variables
+ */
+ protected static function initialize_static_variables()
+ {
+ if (!isset(self::$zero)) {
+ self::$zero = new BigInteger(0);
+ self::$one = new BigInteger(1);
+ }
+
+ self::loadPlugins('Keys');
+ if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
+ self::loadPlugins('Signature');
+ }
+ }
+
+ /**
+ * Load the key
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return PublicKey|PrivateKey
+ */
+ public static function load($key, $password = false)
+ {
+ self::initialize_static_variables();
+
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('load() should not be called from final classes (' . static::class . ')');
+ }
+
+ $components = false;
+ foreach (self::$plugins[static::ALGORITHM]['Keys'] as $format) {
+ if (isset(self::$invisiblePlugins[static::ALGORITHM]) && in_array($format, self::$invisiblePlugins[static::ALGORITHM])) {
+ continue;
+ }
+ try {
+ $components = $format::load($key, $password);
+ } catch (\Exception $e) {
+ $components = false;
+ }
+ if ($components !== false) {
+ break;
+ }
+ }
+
+ if ($components === false) {
+ throw new NoKeyLoadedException('Unable to read key');
+ }
+
+ $components['format'] = $format;
+ $components['secret'] = isset($components['secret']) ? $components['secret'] : '';
+ $comment = isset($components['comment']) ? $components['comment'] : null;
+ $new = static::onLoad($components);
+ $new->format = $format;
+ $new->comment = $comment;
+ return $new instanceof PrivateKey ?
+ $new->withPassword($password) :
+ $new;
+ }
+
+ /**
+ * Loads a private key
+ *
+ * @return PrivateKey
+ * @param string|array $key
+ * @param string $password optional
+ */
+ public static function loadPrivateKey($key, $password = '')
+ {
+ $key = self::load($key, $password);
+ if (!$key instanceof PrivateKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a private key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads a public key
+ *
+ * @return PublicKey
+ * @param string|array $key
+ */
+ public static function loadPublicKey($key)
+ {
+ $key = self::load($key);
+ if (!$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a public key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads parameters
+ *
+ * @return AsymmetricKey
+ * @param string|array $key
+ */
+ public static function loadParameters($key)
+ {
+ $key = self::load($key);
+ if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a parameter');
+ }
+ return $key;
+ }
+
+ /**
+ * Load the key, assuming a specific format
+ *
+ * @param string $type
+ * @param string $key
+ * @param string $password optional
+ * @return static
+ */
+ public static function loadFormat($type, $key, $password = false)
+ {
+ self::initialize_static_variables();
+
+ $components = false;
+ $format = strtolower($type);
+ if (isset(self::$plugins[static::ALGORITHM]['Keys'][$format])) {
+ $format = self::$plugins[static::ALGORITHM]['Keys'][$format];
+ $components = $format::load($key, $password);
+ }
+
+ if ($components === false) {
+ throw new NoKeyLoadedException('Unable to read key');
+ }
+
+ $components['format'] = $format;
+ $components['secret'] = isset($components['secret']) ? $components['secret'] : '';
+
+ $new = static::onLoad($components);
+ $new->format = $format;
+ return $new instanceof PrivateKey ?
+ $new->withPassword($password) :
+ $new;
+ }
+
+ /**
+ * Loads a private key
+ *
+ * @return PrivateKey
+ * @param string $type
+ * @param string $key
+ * @param string $password optional
+ */
+ public static function loadPrivateKeyFormat($type, $key, $password = false)
+ {
+ $key = self::loadFormat($type, $key, $password);
+ if (!$key instanceof PrivateKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a private key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads a public key
+ *
+ * @return PublicKey
+ * @param string $type
+ * @param string $key
+ */
+ public static function loadPublicKeyFormat($type, $key)
+ {
+ $key = self::loadFormat($type, $key);
+ if (!$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a public key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads parameters
+ *
+ * @return AsymmetricKey
+ * @param string $type
+ * @param string|array $key
+ */
+ public static function loadParametersFormat($type, $key)
+ {
+ $key = self::loadFormat($type, $key);
+ if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a parameter');
+ }
+ return $key;
+ }
+
+ /**
+ * Validate Plugin
+ *
+ * @param string $format
+ * @param string $type
+ * @param string $method optional
+ * @return mixed
+ */
+ protected static function validatePlugin($format, $type, $method = null)
+ {
+ $type = strtolower($type);
+ if (!isset(self::$plugins[static::ALGORITHM][$format][$type])) {
+ throw new UnsupportedFormatException("$type is not a supported format");
+ }
+ $type = self::$plugins[static::ALGORITHM][$format][$type];
+ if (isset($method) && !method_exists($type, $method)) {
+ throw new UnsupportedFormatException("$type does not implement $method");
+ }
+
+ return $type;
+ }
+
+ /**
+ * Load Plugins
+ *
+ * @param string $format
+ */
+ private static function loadPlugins($format)
+ {
+ if (!isset(self::$plugins[static::ALGORITHM][$format])) {
+ self::$plugins[static::ALGORITHM][$format] = [];
+ foreach (new \DirectoryIterator(__DIR__ . '/../' . static::ALGORITHM . '/Formats/' . $format . '/') as $file) {
+ if ($file->getExtension() != 'php') {
+ continue;
+ }
+ $name = $file->getBasename('.php');
+ if ($name[0] == '.') {
+ continue;
+ }
+ $type = 'phpseclib3\Crypt\\' . static::ALGORITHM . '\\Formats\\' . $format . '\\' . $name;
+ $reflect = new \ReflectionClass($type);
+ if ($reflect->isTrait()) {
+ continue;
+ }
+ self::$plugins[static::ALGORITHM][$format][strtolower($name)] = $type;
+ if ($reflect->hasConstant('IS_INVISIBLE')) {
+ self::$invisiblePlugins[static::ALGORITHM][] = $type;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a list of supported formats.
+ *
+ * @return array
+ */
+ public static function getSupportedKeyFormats()
+ {
+ self::initialize_static_variables();
+
+ return self::$plugins[static::ALGORITHM]['Keys'];
+ }
+
+ /**
+ * Add a fileformat plugin
+ *
+ * The plugin needs to either already be loaded or be auto-loadable.
+ * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin.
+ *
+ * @see self::load()
+ * @param string $fullname
+ * @return bool
+ */
+ public static function addFileFormat($fullname)
+ {
+ self::initialize_static_variables();
+
+ if (class_exists($fullname)) {
+ $meta = new \ReflectionClass($fullname);
+ $shortname = $meta->getShortName();
+ self::$plugins[static::ALGORITHM]['Keys'][strtolower($shortname)] = $fullname;
+ if ($meta->hasConstant('IS_INVISIBLE')) {
+ self::$invisiblePlugins[static::ALGORITHM][] = strtolower($shortname);
+ }
+ }
+ }
+
+ /**
+ * Returns the format of the loaded key.
+ *
+ * If the key that was loaded wasn't in a valid or if the key was auto-generated
+ * with RSA::createKey() then this will throw an exception.
+ *
+ * @see self::load()
+ * @return mixed
+ */
+ public function getLoadedFormat()
+ {
+ if (empty($this->format)) {
+ throw new NoKeyLoadedException('This key was created with createKey - it was not loaded with load. Therefore there is no "loaded format"');
+ }
+
+ $meta = new \ReflectionClass($this->format);
+ return $meta->getShortName();
+ }
+
+ /**
+ * Returns the key's comment
+ *
+ * Not all key formats support comments. If you want to set a comment use toString()
+ *
+ * @return null|string
+ */
+ public function getComment()
+ {
+ return $this->comment;
+ }
+
+ /**
+ * Tests engine validity
+ *
+ */
+ public static function useBestEngine()
+ {
+ static::$engines = [
+ 'PHP' => true,
+ 'OpenSSL' => extension_loaded('openssl'),
+ // this test can be satisfied by either of the following:
+ // http://php.net/manual/en/book.sodium.php
+ // https://github.com/paragonie/sodium_compat
+ 'libsodium' => function_exists('sodium_crypto_sign_keypair')
+ ];
+
+ return static::$engines;
+ }
+
+ /**
+ * Flag to use internal engine only (useful for unit testing)
+ *
+ */
+ public static function useInternalEngine()
+ {
+ static::$engines = [
+ 'PHP' => true,
+ 'OpenSSL' => false,
+ 'libsodium' => false
+ ];
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString('PKCS8');
+ }
+
+ /**
+ * Determines which hashing function should be used
+ *
+ * @param string $hash
+ */
+ public function withHash($hash)
+ {
+ $new = clone $this;
+
+ $new->hash = new Hash($hash);
+ $new->hmac = new Hash($hash);
+
+ return $new;
+ }
+
+ /**
+ * Returns the hash algorithm currently being used
+ *
+ */
+ public function getHash()
+ {
+ return clone $this->hash;
+ }
+
+ /**
+ * Compute the pseudorandom k for signature generation,
+ * using the process specified for deterministic DSA.
+ *
+ * @param string $h1
+ * @return string
+ */
+ protected function computek($h1)
+ {
+ $v = str_repeat("\1", strlen($h1));
+
+ $k = str_repeat("\0", strlen($h1));
+
+ $x = $this->int2octets($this->x);
+ $h1 = $this->bits2octets($h1);
+
+ $this->hmac->setKey($k);
+ $k = $this->hmac->hash($v . "\0" . $x . $h1);
+ $this->hmac->setKey($k);
+ $v = $this->hmac->hash($v);
+ $k = $this->hmac->hash($v . "\1" . $x . $h1);
+ $this->hmac->setKey($k);
+ $v = $this->hmac->hash($v);
+
+ $qlen = $this->q->getLengthInBytes();
+
+ while (true) {
+ $t = '';
+ while (strlen($t) < $qlen) {
+ $v = $this->hmac->hash($v);
+ $t = $t . $v;
+ }
+ $k = $this->bits2int($t);
+
+ if (!$k->equals(self::$zero) && $k->compare($this->q) < 0) {
+ break;
+ }
+ $k = $this->hmac->hash($v . "\0");
+ $this->hmac->setKey($k);
+ $v = $this->hmac->hash($v);
+ }
+
+ return $k;
+ }
+
+ /**
+ * Integer to Octet String
+ *
+ * @param BigInteger $v
+ * @return string
+ */
+ private function int2octets($v)
+ {
+ $out = $v->toBytes();
+ $rolen = $this->q->getLengthInBytes();
+ if (strlen($out) < $rolen) {
+ return str_pad($out, $rolen, "\0", STR_PAD_LEFT);
+ } elseif (strlen($out) > $rolen) {
+ return substr($out, -$rolen);
+ } else {
+ return $out;
+ }
+ }
+
+ /**
+ * Bit String to Integer
+ *
+ * @param string $in
+ * @return BigInteger
+ */
+ protected function bits2int($in)
+ {
+ $v = new BigInteger($in, 256);
+ $vlen = strlen($in) << 3;
+ $qlen = $this->q->getLength();
+ if ($vlen > $qlen) {
+ return $v->bitwise_rightShift($vlen - $qlen);
+ }
+ return $v;
+ }
+
+ /**
+ * Bit String to Octet String
+ *
+ * @param string $in
+ * @return string
+ */
+ private function bits2octets($in)
+ {
+ $z1 = $this->bits2int($in);
+ $z2 = $z1->subtract($this->q);
+ return $z2->compare(self::$zero) < 0 ?
+ $this->int2octets($z1) :
+ $this->int2octets($z2);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php
new file mode 100644
index 000000000..b2642be11
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/BlockCipher.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Base Class for all block ciphers
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common;
+
+/**
+ * Base Class for all block cipher classes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BlockCipher extends SymmetricKey
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php
new file mode 100644
index 000000000..98b8dacc9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/JWK.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * JSON Web Key (RFC7517) Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+
+/**
+ * JSON Web Key Formatted Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class JWK
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ $key = preg_replace('#\s#', '', $key); // remove whitespace
+
+ if (PHP_VERSION_ID >= 73000) {
+ $key = json_decode($key, null, 512, JSON_THROW_ON_ERROR);
+ } else {
+ $key = json_decode($key);
+ if (!$key) {
+ throw new \RuntimeException('Unable to decode JSON');
+ }
+ }
+
+ if (isset($key->kty)) {
+ return $key;
+ }
+
+ if (!is_object($key)) {
+ throw new \RuntimeException('invalid JWK: not an object');
+ }
+
+ if (!isset($key->keys)) {
+ throw new \RuntimeException('invalid JWK: object has no property "keys"');
+ }
+
+ if (count($key->keys) != 1) {
+ throw new \RuntimeException('Although the JWK key format supports multiple keys phpseclib does not');
+ }
+
+ return $key->keys[0];
+ }
+
+ /**
+ * Wrap a key appropriately
+ *
+ * @return string
+ */
+ protected static function wrapKey(array $key, array $options)
+ {
+ return json_encode(['keys' => [$key + $options]]);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php
new file mode 100644
index 000000000..ab7f05045
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/OpenSSH.php
@@ -0,0 +1,224 @@
+<?php
+
+/**
+ * OpenSSH Key Handler
+ *
+ * PHP version 5
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\AES;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Exception\BadDecryptionException;
+
+/**
+ * OpenSSH Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSH
+{
+ /**
+ * Default comment
+ *
+ * @var string
+ */
+ protected static $comment = 'phpseclib-generated-key';
+
+ /**
+ * Binary key flag
+ *
+ * @var bool
+ */
+ protected static $binary = false;
+
+ /**
+ * Sets the default comment
+ *
+ * @param string $comment
+ */
+ public static function setComment($comment)
+ {
+ self::$comment = str_replace(["\r", "\n"], '', $comment);
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * $type can be either ssh-dss or ssh-rsa
+ *
+ * @param string $key
+ * @param string $password
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ // key format is described here:
+ // https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
+
+ if (strpos($key, 'BEGIN OPENSSH PRIVATE KEY') !== false) {
+ $key = preg_replace('#(?:^-.*?-[\r\n]*$)|\s#ms', '', $key);
+ $key = Strings::base64_decode($key);
+ $magic = Strings::shift($key, 15);
+ if ($magic != "openssh-key-v1\0") {
+ throw new \RuntimeException('Expected openssh-key-v1');
+ }
+ list($ciphername, $kdfname, $kdfoptions, $numKeys) = Strings::unpackSSH2('sssN', $key);
+ if ($numKeys != 1) {
+ // if we wanted to support multiple keys we could update PublicKeyLoader to preview what the # of keys
+ // would be; it'd then call Common\Keys\OpenSSH.php::load() and get the paddedKey. it'd then pass
+ // that to the appropriate key loading parser $numKey times or something
+ throw new \RuntimeException('Although the OpenSSH private key format supports multiple keys phpseclib does not');
+ }
+ switch ($ciphername) {
+ case 'none':
+ break;
+ case 'aes256-ctr':
+ if ($kdfname != 'bcrypt') {
+ throw new \RuntimeException('Only the bcrypt kdf is supported (' . $kdfname . ' encountered)');
+ }
+ list($salt, $rounds) = Strings::unpackSSH2('sN', $kdfoptions);
+ $crypto = new AES('ctr');
+ //$crypto->setKeyLength(256);
+ //$crypto->disablePadding();
+ $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
+ break;
+ default:
+ throw new \RuntimeException('The only supported ciphers are: none, aes256-ctr (' . $ciphername . ' is being used)');
+ }
+
+ list($publicKey, $paddedKey) = Strings::unpackSSH2('ss', $key);
+ list($type) = Strings::unpackSSH2('s', $publicKey);
+ if (isset($crypto)) {
+ $paddedKey = $crypto->decrypt($paddedKey);
+ }
+ list($checkint1, $checkint2) = Strings::unpackSSH2('NN', $paddedKey);
+ // any leftover bytes in $paddedKey are for padding? but they should be sequential bytes. eg. 1, 2, 3, etc.
+ if ($checkint1 != $checkint2) {
+ if (isset($crypto)) {
+ throw new BadDecryptionException('Unable to decrypt key - please verify the password you are using');
+ }
+ throw new \RuntimeException("The two checkints do not match ($checkint1 vs. $checkint2)");
+ }
+ self::checkType($type);
+
+ return compact('type', 'publicKey', 'paddedKey');
+ }
+
+ $parts = explode(' ', $key, 3);
+
+ if (!isset($parts[1])) {
+ $key = base64_decode($parts[0]);
+ $comment = false;
+ } else {
+ $asciiType = $parts[0];
+ self::checkType($parts[0]);
+ $key = base64_decode($parts[1]);
+ $comment = isset($parts[2]) ? $parts[2] : false;
+ }
+ if ($key === false) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ list($type) = Strings::unpackSSH2('s', $key);
+ self::checkType($type);
+ if (isset($asciiType) && $asciiType != $type) {
+ throw new \RuntimeException('Two different types of keys are claimed: ' . $asciiType . ' and ' . $type);
+ }
+ if (strlen($key) <= 4) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $publicKey = $key;
+
+ return compact('type', 'publicKey', 'comment');
+ }
+
+ /**
+ * Toggle between binary and printable keys
+ *
+ * Printable keys are what are generated by default. These are the ones that go in
+ * $HOME/.ssh/authorized_key.
+ *
+ * @param bool $enabled
+ */
+ public static function setBinaryOutput($enabled)
+ {
+ self::$binary = $enabled;
+ }
+
+ /**
+ * Checks to see if the type is valid
+ *
+ * @param string $candidate
+ */
+ private static function checkType($candidate)
+ {
+ if (!in_array($candidate, static::$types)) {
+ throw new \RuntimeException("The key type ($candidate) is not equal to: " . implode(',', static::$types));
+ }
+ }
+
+ /**
+ * Wrap a private key appropriately
+ *
+ * @param string $publicKey
+ * @param string $privateKey
+ * @param string $password
+ * @param array $options
+ * @return string
+ */
+ protected static function wrapPrivateKey($publicKey, $privateKey, $password, $options)
+ {
+ list(, $checkint) = unpack('N', Random::string(4));
+
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+ $paddedKey = Strings::packSSH2('NN', $checkint, $checkint) .
+ $privateKey .
+ Strings::packSSH2('s', $comment);
+
+ $usesEncryption = !empty($password) && is_string($password);
+
+ /*
+ from http://tools.ietf.org/html/rfc4253#section-6 :
+
+ Note that the length of the concatenation of 'packet_length',
+ 'padding_length', 'payload', and 'random padding' MUST be a multiple
+ of the cipher block size or 8, whichever is larger.
+ */
+ $blockSize = $usesEncryption ? 16 : 8;
+ $paddingLength = (($blockSize - 1) * strlen($paddedKey)) % $blockSize;
+ for ($i = 1; $i <= $paddingLength; $i++) {
+ $paddedKey .= chr($i);
+ }
+ if (!$usesEncryption) {
+ $key = Strings::packSSH2('sssNss', 'none', 'none', '', 1, $publicKey, $paddedKey);
+ } else {
+ $rounds = isset($options['rounds']) ? $options['rounds'] : 16;
+ $salt = Random::string(16);
+ $kdfoptions = Strings::packSSH2('sN', $salt, $rounds);
+ $crypto = new AES('ctr');
+ $crypto->setPassword($password, 'bcrypt', $salt, $rounds, 32);
+ $paddedKey = $crypto->encrypt($paddedKey);
+ $key = Strings::packSSH2('sssNss', 'aes256-ctr', 'bcrypt', $kdfoptions, 1, $publicKey, $paddedKey);
+ }
+ $key = "openssh-key-v1\0$key";
+
+ return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
+ chunk_split(Strings::base64_encode($key), 70, "\n") .
+ "-----END OPENSSH PRIVATE KEY-----\n";
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php
new file mode 100644
index 000000000..0219400bc
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * PKCS Formatted Key Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+/**
+ * PKCS1 Formatted Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS
+{
+ /**
+ * Auto-detect the format
+ */
+ const MODE_ANY = 0;
+ /**
+ * Require base64-encoded PEM's be supplied
+ */
+ const MODE_PEM = 1;
+ /**
+ * Require raw DER's be supplied
+ */
+ const MODE_DER = 2;
+ /**#@-*/
+
+ /**
+ * Is the key a base-64 encoded PEM, DER or should it be auto-detected?
+ *
+ * @var int
+ */
+ protected static $format = self::MODE_ANY;
+
+ /**
+ * Require base64-encoded PEM's be supplied
+ *
+ */
+ public static function requirePEM()
+ {
+ self::$format = self::MODE_PEM;
+ }
+
+ /**
+ * Require raw DER's be supplied
+ *
+ */
+ public static function requireDER()
+ {
+ self::$format = self::MODE_DER;
+ }
+
+ /**
+ * Accept any format and auto detect the format
+ *
+ * This is the default setting
+ *
+ */
+ public static function requireAny()
+ {
+ self::$format = self::MODE_ANY;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php
new file mode 100644
index 000000000..4c639c05e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS1.php
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * PKCS1 Formatted Key Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\AES;
+use phpseclib3\Crypt\DES;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\TripleDES;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\File\ASN1;
+
+/**
+ * PKCS1 Formatted Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS1 extends PKCS
+{
+ /**
+ * Default encryption algorithm
+ *
+ * @var string
+ */
+ private static $defaultEncryptionAlgorithm = 'AES-128-CBC';
+
+ /**
+ * Sets the default encryption algorithm
+ *
+ * @param string $algo
+ */
+ public static function setEncryptionAlgorithm($algo)
+ {
+ self::$defaultEncryptionAlgorithm = $algo;
+ }
+
+ /**
+ * Returns the mode constant corresponding to the mode string
+ *
+ * @param string $mode
+ * @return int
+ * @throws \UnexpectedValueException if the block cipher mode is unsupported
+ */
+ private static function getEncryptionMode($mode)
+ {
+ switch ($mode) {
+ case 'CBC':
+ case 'ECB':
+ case 'CFB':
+ case 'OFB':
+ case 'CTR':
+ return $mode;
+ }
+ throw new \UnexpectedValueException('Unsupported block cipher mode of operation');
+ }
+
+ /**
+ * Returns a cipher object corresponding to a string
+ *
+ * @param string $algo
+ * @return string
+ * @throws \UnexpectedValueException if the encryption algorithm is unsupported
+ */
+ private static function getEncryptionObject($algo)
+ {
+ $modes = '(CBC|ECB|CFB|OFB|CTR)';
+ switch (true) {
+ case preg_match("#^AES-(128|192|256)-$modes$#", $algo, $matches):
+ $cipher = new AES(self::getEncryptionMode($matches[2]));
+ $cipher->setKeyLength($matches[1]);
+ return $cipher;
+ case preg_match("#^DES-EDE3-$modes$#", $algo, $matches):
+ return new TripleDES(self::getEncryptionMode($matches[1]));
+ case preg_match("#^DES-$modes$#", $algo, $matches):
+ return new DES(self::getEncryptionMode($matches[1]));
+ default:
+ throw new UnsupportedAlgorithmException($algo . ' is not a supported algorithm');
+ }
+ }
+
+ /**
+ * Generate a symmetric key for PKCS#1 keys
+ *
+ * @param string $password
+ * @param string $iv
+ * @param int $length
+ * @return string
+ */
+ private static function generateSymmetricKey($password, $iv, $length)
+ {
+ $symkey = '';
+ $iv = substr($iv, 0, 8);
+ while (strlen($symkey) < $length) {
+ $symkey .= md5($symkey . $password . $iv, true);
+ }
+ return substr($symkey, 0, $length);
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ protected static function load($key, $password)
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
+ "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
+ protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
+ two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
+
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.1
+ http://tools.ietf.org/html/rfc1421#section-4.6.1.3
+
+ DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
+ DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
+ function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
+ own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
+ implementation are part of the standard, as well.
+
+ * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
+ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
+ $iv = Strings::hex2bin(trim($matches[2]));
+ // remove the Proc-Type / DEK-Info sections as they're no longer needed
+ $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
+ $ciphertext = ASN1::extractBER($key);
+ if ($ciphertext === false) {
+ $ciphertext = $key;
+ }
+ $crypto = self::getEncryptionObject($matches[1]);
+ $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3));
+ $crypto->setIV($iv);
+ $key = $crypto->decrypt($ciphertext);
+ } else {
+ if (self::$format != self::MODE_DER) {
+ $decoded = ASN1::extractBER($key);
+ if ($decoded !== false) {
+ $key = $decoded;
+ } elseif (self::$format == self::MODE_PEM) {
+ throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text');
+ }
+ }
+ }
+
+ return $key;
+ }
+
+ /**
+ * Wrap a private key appropriately
+ *
+ * @param string $key
+ * @param string $type
+ * @param string $password
+ * @param array $options optional
+ * @return string
+ */
+ protected static function wrapPrivateKey($key, $type, $password, array $options = [])
+ {
+ if (empty($password) || !is_string($password)) {
+ return "-----BEGIN $type PRIVATE KEY-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END $type PRIVATE KEY-----";
+ }
+
+ $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm;
+
+ $cipher = self::getEncryptionObject($encryptionAlgorithm);
+ $iv = Random::string($cipher->getBlockLength() >> 3);
+ $cipher->setKey(self::generateSymmetricKey($password, $iv, $cipher->getKeyLength() >> 3));
+ $cipher->setIV($iv);
+ $iv = strtoupper(Strings::bin2hex($iv));
+ return "-----BEGIN $type PRIVATE KEY-----\r\n" .
+ "Proc-Type: 4,ENCRYPTED\r\n" .
+ "DEK-Info: " . $encryptionAlgorithm . ",$iv\r\n" .
+ "\r\n" .
+ chunk_split(Strings::base64_encode($cipher->encrypt($key)), 64) .
+ "-----END $type PRIVATE KEY-----";
+ }
+
+ /**
+ * Wrap a public key appropriately
+ *
+ * @param string $key
+ * @param string $type
+ * @return string
+ */
+ protected static function wrapPublicKey($key, $type)
+ {
+ return "-----BEGIN $type PUBLIC KEY-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END $type PUBLIC KEY-----";
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php
new file mode 100644
index 000000000..2211a8747
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php
@@ -0,0 +1,766 @@
+<?php
+
+/**
+ * PKCS#8 Formatted Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
+ * is specific to private keys it's basically creating a DER-encoded wrapper
+ * for keys. This just extends that same concept to public keys (much like ssh-keygen)
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\AES;
+use phpseclib3\Crypt\DES;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\RC2;
+use phpseclib3\Crypt\RC4;
+use phpseclib3\Crypt\TripleDES;
+use phpseclib3\Exception\InsufficientSetupException;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+
+/**
+ * PKCS#8 Formatted Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS8 extends PKCS
+{
+ /**
+ * Default encryption algorithm
+ *
+ * @var string
+ */
+ private static $defaultEncryptionAlgorithm = 'id-PBES2';
+
+ /**
+ * Default encryption scheme
+ *
+ * Only used when defaultEncryptionAlgorithm is id-PBES2
+ *
+ * @var string
+ */
+ private static $defaultEncryptionScheme = 'aes128-CBC-PAD';
+
+ /**
+ * Default PRF
+ *
+ * Only used when defaultEncryptionAlgorithm is id-PBES2
+ *
+ * @var string
+ */
+ private static $defaultPRF = 'id-hmacWithSHA256';
+
+ /**
+ * Default Iteration Count
+ *
+ * @var int
+ */
+ private static $defaultIterationCount = 2048;
+
+ /**
+ * OIDs loaded
+ *
+ * @var bool
+ */
+ private static $oidsLoaded = false;
+
+ /**
+ * Binary key flag
+ *
+ * @var bool
+ */
+ private static $binary = false;
+
+ /**
+ * Sets the default encryption algorithm
+ *
+ * @param string $algo
+ */
+ public static function setEncryptionAlgorithm($algo)
+ {
+ self::$defaultEncryptionAlgorithm = $algo;
+ }
+
+ /**
+ * Sets the default encryption algorithm for PBES2
+ *
+ * @param string $algo
+ */
+ public static function setEncryptionScheme($algo)
+ {
+ self::$defaultEncryptionScheme = $algo;
+ }
+
+ /**
+ * Sets the iteration count
+ *
+ * @param int $count
+ */
+ public static function setIterationCount($count)
+ {
+ self::$defaultIterationCount = $count;
+ }
+
+ /**
+ * Sets the PRF for PBES2
+ *
+ * @param string $algo
+ */
+ public static function setPRF($algo)
+ {
+ self::$defaultPRF = $algo;
+ }
+
+ /**
+ * Returns a SymmetricKey object based on a PBES1 $algo
+ *
+ * @return \phpseclib3\Crypt\Common\SymmetricKey
+ * @param string $algo
+ */
+ private static function getPBES1EncryptionObject($algo)
+ {
+ $algo = preg_match('#^pbeWith(?:MD2|MD5|SHA1|SHA)And(.*?)-CBC$#', $algo, $matches) ?
+ $matches[1] :
+ substr($algo, 13); // strlen('pbeWithSHAAnd') == 13
+
+ switch ($algo) {
+ case 'DES':
+ $cipher = new DES('cbc');
+ break;
+ case 'RC2':
+ $cipher = new RC2('cbc');
+ $cipher->setKeyLength(64);
+ break;
+ case '3-KeyTripleDES':
+ $cipher = new TripleDES('cbc');
+ break;
+ case '2-KeyTripleDES':
+ $cipher = new TripleDES('cbc');
+ $cipher->setKeyLength(128);
+ break;
+ case '128BitRC2':
+ $cipher = new RC2('cbc');
+ $cipher->setKeyLength(128);
+ break;
+ case '40BitRC2':
+ $cipher = new RC2('cbc');
+ $cipher->setKeyLength(40);
+ break;
+ case '128BitRC4':
+ $cipher = new RC4();
+ $cipher->setKeyLength(128);
+ break;
+ case '40BitRC4':
+ $cipher = new RC4();
+ $cipher->setKeyLength(40);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException("$algo is not a supported algorithm");
+ }
+
+ return $cipher;
+ }
+
+ /**
+ * Returns a hash based on a PBES1 $algo
+ *
+ * @return string
+ * @param string $algo
+ */
+ private static function getPBES1Hash($algo)
+ {
+ if (preg_match('#^pbeWith(MD2|MD5|SHA1|SHA)And.*?-CBC$#', $algo, $matches)) {
+ return $matches[1] == 'SHA' ? 'sha1' : $matches[1];
+ }
+
+ return 'sha1';
+ }
+
+ /**
+ * Returns a KDF baesd on a PBES1 $algo
+ *
+ * @return string
+ * @param string $algo
+ */
+ private static function getPBES1KDF($algo)
+ {
+ switch ($algo) {
+ case 'pbeWithMD2AndDES-CBC':
+ case 'pbeWithMD2AndRC2-CBC':
+ case 'pbeWithMD5AndDES-CBC':
+ case 'pbeWithMD5AndRC2-CBC':
+ case 'pbeWithSHA1AndDES-CBC':
+ case 'pbeWithSHA1AndRC2-CBC':
+ return 'pbkdf1';
+ }
+
+ return 'pkcs12';
+ }
+
+ /**
+ * Returns a SymmetricKey object baesd on a PBES2 $algo
+ *
+ * @return SymmetricKey
+ * @param string $algo
+ */
+ private static function getPBES2EncryptionObject($algo)
+ {
+ switch ($algo) {
+ case 'desCBC':
+ $cipher = new DES('cbc');
+ break;
+ case 'des-EDE3-CBC':
+ $cipher = new TripleDES('cbc');
+ break;
+ case 'rc2CBC':
+ $cipher = new RC2('cbc');
+ // in theory this can be changed
+ $cipher->setKeyLength(128);
+ break;
+ case 'rc5-CBC-PAD':
+ throw new UnsupportedAlgorithmException('rc5-CBC-PAD is not supported for PBES2 PKCS#8 keys');
+ case 'aes128-CBC-PAD':
+ case 'aes192-CBC-PAD':
+ case 'aes256-CBC-PAD':
+ $cipher = new AES('cbc');
+ $cipher->setKeyLength(substr($algo, 3, 3));
+ break;
+ default:
+ throw new UnsupportedAlgorithmException("$algo is not supported");
+ }
+
+ return $cipher;
+ }
+
+ /**
+ * Initialize static variables
+ *
+ */
+ private static function initialize_static_variables()
+ {
+ if (!isset(static::$childOIDsLoaded)) {
+ throw new InsufficientSetupException('This class should not be called directly');
+ }
+
+ if (!static::$childOIDsLoaded) {
+ ASN1::loadOIDs(is_array(static::OID_NAME) ?
+ array_combine(static::OID_NAME, static::OID_VALUE) :
+ [static::OID_NAME => static::OID_VALUE]);
+ static::$childOIDsLoaded = true;
+ }
+ if (!self::$oidsLoaded) {
+ // from https://tools.ietf.org/html/rfc2898
+ ASN1::loadOIDs([
+ // PBES1 encryption schemes
+ 'pbeWithMD2AndDES-CBC' => '1.2.840.113549.1.5.1',
+ 'pbeWithMD2AndRC2-CBC' => '1.2.840.113549.1.5.4',
+ 'pbeWithMD5AndDES-CBC' => '1.2.840.113549.1.5.3',
+ 'pbeWithMD5AndRC2-CBC' => '1.2.840.113549.1.5.6',
+ 'pbeWithSHA1AndDES-CBC' => '1.2.840.113549.1.5.10',
+ 'pbeWithSHA1AndRC2-CBC' => '1.2.840.113549.1.5.11',
+
+ // from PKCS#12:
+ // https://tools.ietf.org/html/rfc7292
+ 'pbeWithSHAAnd128BitRC4' => '1.2.840.113549.1.12.1.1',
+ 'pbeWithSHAAnd40BitRC4' => '1.2.840.113549.1.12.1.2',
+ 'pbeWithSHAAnd3-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.3',
+ 'pbeWithSHAAnd2-KeyTripleDES-CBC' => '1.2.840.113549.1.12.1.4',
+ 'pbeWithSHAAnd128BitRC2-CBC' => '1.2.840.113549.1.12.1.5',
+ 'pbeWithSHAAnd40BitRC2-CBC' => '1.2.840.113549.1.12.1.6',
+
+ 'id-PBKDF2' => '1.2.840.113549.1.5.12',
+ 'id-PBES2' => '1.2.840.113549.1.5.13',
+ 'id-PBMAC1' => '1.2.840.113549.1.5.14',
+
+ // from PKCS#5 v2.1:
+ // http://www.rsa.com/rsalabs/pkcs/files/h11302-wp-pkcs5v2-1-password-based-cryptography-standard.pdf
+ 'id-hmacWithSHA1' => '1.2.840.113549.2.7',
+ 'id-hmacWithSHA224' => '1.2.840.113549.2.8',
+ 'id-hmacWithSHA256' => '1.2.840.113549.2.9',
+ 'id-hmacWithSHA384' => '1.2.840.113549.2.10',
+ 'id-hmacWithSHA512' => '1.2.840.113549.2.11',
+ 'id-hmacWithSHA512-224' => '1.2.840.113549.2.12',
+ 'id-hmacWithSHA512-256' => '1.2.840.113549.2.13',
+
+ 'desCBC' => '1.3.14.3.2.7',
+ 'des-EDE3-CBC' => '1.2.840.113549.3.7',
+ 'rc2CBC' => '1.2.840.113549.3.2',
+ 'rc5-CBC-PAD' => '1.2.840.113549.3.9',
+
+ 'aes128-CBC-PAD' => '2.16.840.1.101.3.4.1.2',
+ 'aes192-CBC-PAD' => '2.16.840.1.101.3.4.1.22',
+ 'aes256-CBC-PAD' => '2.16.840.1.101.3.4.1.42'
+ ]);
+ self::$oidsLoaded = true;
+ }
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ protected static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ $isPublic = strpos($key, 'PUBLIC') !== false;
+ $isPrivate = strpos($key, 'PRIVATE') !== false;
+
+ $decoded = self::preParse($key);
+
+ $meta = [];
+
+ $decrypted = ASN1::asn1map($decoded[0], Maps\EncryptedPrivateKeyInfo::MAP);
+ if (strlen($password) && is_array($decrypted)) {
+ $algorithm = $decrypted['encryptionAlgorithm']['algorithm'];
+ switch ($algorithm) {
+ // PBES1
+ case 'pbeWithMD2AndDES-CBC':
+ case 'pbeWithMD2AndRC2-CBC':
+ case 'pbeWithMD5AndDES-CBC':
+ case 'pbeWithMD5AndRC2-CBC':
+ case 'pbeWithSHA1AndDES-CBC':
+ case 'pbeWithSHA1AndRC2-CBC':
+ case 'pbeWithSHAAnd3-KeyTripleDES-CBC':
+ case 'pbeWithSHAAnd2-KeyTripleDES-CBC':
+ case 'pbeWithSHAAnd128BitRC2-CBC':
+ case 'pbeWithSHAAnd40BitRC2-CBC':
+ case 'pbeWithSHAAnd128BitRC4':
+ case 'pbeWithSHAAnd40BitRC4':
+ $cipher = self::getPBES1EncryptionObject($algorithm);
+ $hash = self::getPBES1Hash($algorithm);
+ $kdf = self::getPBES1KDF($algorithm);
+
+ $meta['meta']['algorithm'] = $algorithm;
+
+ $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
+ if (!$temp) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $map = ASN1::asn1map($temp[0], Maps\PBEParameter::MAP);
+ $salt = $map['salt'];
+ $iterationCount = $map['iterationCount'];
+ $iterationCount = (int) $iterationCount->toString();
+ $cipher->setPassword($password, $kdf, $hash, $salt, $iterationCount);
+ $key = $cipher->decrypt($decrypted['encryptedData']);
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER 2');
+ }
+
+ break;
+ case 'id-PBES2':
+ $meta['meta']['algorithm'] = $algorithm;
+
+ $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
+ if (!$temp) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
+ $keyDerivationFunc = $temp['keyDerivationFunc'];
+ $encryptionScheme = $temp['encryptionScheme'];
+
+ $cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']);
+ $meta['meta']['cipher'] = $encryptionScheme['algorithm'];
+
+ $temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
+ if (!$temp) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
+ $keyDerivationFunc = $temp['keyDerivationFunc'];
+ $encryptionScheme = $temp['encryptionScheme'];
+
+ if (!$cipher instanceof RC2) {
+ $cipher->setIV($encryptionScheme['parameters']['octetString']);
+ } else {
+ $temp = ASN1::decodeBER($encryptionScheme['parameters']);
+ if (!$temp) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $map = ASN1::asn1map($temp[0], Maps\RC2CBCParameter::MAP);
+ $rc2ParametersVersion = $map['rc2ParametersVersion'];
+ $iv = $map['iv'];
+ $effectiveKeyLength = (int) $rc2ParametersVersion->toString();
+ switch ($effectiveKeyLength) {
+ case 160:
+ $effectiveKeyLength = 40;
+ break;
+ case 120:
+ $effectiveKeyLength = 64;
+ break;
+ case 58:
+ $effectiveKeyLength = 128;
+ break;
+ //default: // should be >= 256
+ }
+ $cipher->setIV($iv);
+ $cipher->setKeyLength($effectiveKeyLength);
+ }
+
+ $meta['meta']['keyDerivationFunc'] = $keyDerivationFunc['algorithm'];
+ switch ($keyDerivationFunc['algorithm']) {
+ case 'id-PBKDF2':
+ $temp = ASN1::decodeBER($keyDerivationFunc['parameters']);
+ if (!$temp) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $params = ASN1::asn1map($temp[0], Maps\PBKDF2params::MAP);
+ if (empty($params['prf'])) {
+ $params['prf'] = ['algorithm' => 'id-hmacWithSHA1'];
+ }
+ $salt = $params['salt'];
+ $iterationCount = $params['iterationCount'];
+ $prf = $params['prf'];
+ $meta['meta']['prf'] = $prf['algorithm'];
+ $hash = str_replace('-', '/', substr($prf['algorithm'], 11));
+ $params = [
+ $password,
+ 'pbkdf2',
+ $hash,
+ $salt,
+ (int) $iterationCount->toString()
+ ];
+ if (isset($keyLength)) {
+ $params[] = (int) $keyLength->toString();
+ }
+ $cipher->setPassword(...$params);
+ $key = $cipher->decrypt($decrypted['encryptedData']);
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER 3');
+ }
+ break;
+ default:
+ throw new UnsupportedAlgorithmException('Only PBKDF2 is supported for PBES2 PKCS#8 keys');
+ }
+ break;
+ case 'id-PBMAC1':
+ //$temp = ASN1::decodeBER($decrypted['encryptionAlgorithm']['parameters']);
+ //$value = ASN1::asn1map($temp[0], Maps\PBMAC1params::MAP);
+ // since i can't find any implementation that does PBMAC1 it is unsupported
+ throw new UnsupportedAlgorithmException('Only PBES1 and PBES2 PKCS#8 keys are supported.');
+ // at this point we'll assume that the key conforms to PublicKeyInfo
+ }
+ }
+
+ $private = ASN1::asn1map($decoded[0], Maps\OneAsymmetricKey::MAP);
+ if (is_array($private)) {
+ if ($isPublic) {
+ throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key');
+ }
+
+ if (isset($private['privateKeyAlgorithm']['parameters']) && !$private['privateKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][1]['content'][1])) {
+ $temp = $decoded[0]['content'][1]['content'][1];
+ $private['privateKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length']));
+ }
+ if (is_array(static::OID_NAME)) {
+ if (!in_array($private['privateKeyAlgorithm']['algorithm'], static::OID_NAME)) {
+ throw new UnsupportedAlgorithmException($private['privateKeyAlgorithm']['algorithm'] . ' is not a supported key type');
+ }
+ } else {
+ if ($private['privateKeyAlgorithm']['algorithm'] != static::OID_NAME) {
+ throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $private['privateKeyAlgorithm']['algorithm'] . ' key');
+ }
+ }
+ if (isset($private['publicKey'])) {
+ if ($private['publicKey'][0] != "\0") {
+ throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($private['publicKey'][0]));
+ }
+ $private['publicKey'] = substr($private['publicKey'], 1);
+ }
+ return $private + $meta;
+ }
+
+ // EncryptedPrivateKeyInfo and PublicKeyInfo have largely identical "signatures". the only difference
+ // is that the former has an octet string and the later has a bit string. the first byte of a bit
+ // string represents the number of bits in the last byte that are to be ignored but, currently,
+ // bit strings wanting a non-zero amount of bits trimmed are not supported
+ $public = ASN1::asn1map($decoded[0], Maps\PublicKeyInfo::MAP);
+
+ if (is_array($public)) {
+ if ($isPrivate) {
+ throw new \UnexpectedValueException('Human readable string claims private key but DER encoded string claims public key');
+ }
+
+ if ($public['publicKey'][0] != "\0") {
+ throw new \UnexpectedValueException('The first byte of the public key should be null - not ' . bin2hex($public['publicKey'][0]));
+ }
+ if (is_array(static::OID_NAME)) {
+ if (!in_array($public['publicKeyAlgorithm']['algorithm'], static::OID_NAME)) {
+ throw new UnsupportedAlgorithmException($public['publicKeyAlgorithm']['algorithm'] . ' is not a supported key type');
+ }
+ } else {
+ if ($public['publicKeyAlgorithm']['algorithm'] != static::OID_NAME) {
+ throw new UnsupportedAlgorithmException('Only ' . static::OID_NAME . ' keys are supported; this is a ' . $public['publicKeyAlgorithm']['algorithm'] . ' key');
+ }
+ }
+ if (isset($public['publicKeyAlgorithm']['parameters']) && !$public['publicKeyAlgorithm']['parameters'] instanceof ASN1\Element && isset($decoded[0]['content'][0]['content'][1])) {
+ $temp = $decoded[0]['content'][0]['content'][1];
+ $public['publicKeyAlgorithm']['parameters'] = new ASN1\Element(substr($key, $temp['start'], $temp['length']));
+ }
+ $public['publicKey'] = substr($public['publicKey'], 1);
+ return $public;
+ }
+
+ throw new \RuntimeException('Unable to parse using either OneAsymmetricKey or PublicKeyInfo ASN1 maps');
+ }
+
+ /**
+ * Toggle between binary (DER) and printable (PEM) keys
+ *
+ * Printable keys are what are generated by default.
+ *
+ * @param bool $enabled
+ */
+ public static function setBinaryOutput($enabled)
+ {
+ self::$binary = $enabled;
+ }
+
+ /**
+ * Wrap a private key appropriately
+ *
+ * @param string $key
+ * @param string $attr
+ * @param mixed $params
+ * @param string $password
+ * @param string $oid optional
+ * @param string $publicKey optional
+ * @param array $options optional
+ * @return string
+ */
+ protected static function wrapPrivateKey($key, $attr, $params, $password, $oid = null, $publicKey = '', array $options = [])
+ {
+ self::initialize_static_variables();
+
+ $key = [
+ 'version' => 'v1',
+ 'privateKeyAlgorithm' => [
+ 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid
+ ],
+ 'privateKey' => $key
+ ];
+ if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') {
+ $key['privateKeyAlgorithm']['parameters'] = $params;
+ }
+ if (!empty($attr)) {
+ $key['attributes'] = $attr;
+ }
+ if (!empty($publicKey)) {
+ $key['version'] = 'v2';
+ $key['publicKey'] = $publicKey;
+ }
+ $key = ASN1::encodeDER($key, Maps\OneAsymmetricKey::MAP);
+ if (!empty($password) && is_string($password)) {
+ $salt = Random::string(8);
+
+ $iterationCount = isset($options['iterationCount']) ? $options['iterationCount'] : self::$defaultIterationCount;
+ $encryptionAlgorithm = isset($options['encryptionAlgorithm']) ? $options['encryptionAlgorithm'] : self::$defaultEncryptionAlgorithm;
+ $encryptionScheme = isset($options['encryptionScheme']) ? $options['encryptionScheme'] : self::$defaultEncryptionScheme;
+ $prf = isset($options['PRF']) ? $options['PRF'] : self::$defaultPRF;
+
+ if ($encryptionAlgorithm == 'id-PBES2') {
+ $crypto = self::getPBES2EncryptionObject($encryptionScheme);
+ $hash = str_replace('-', '/', substr($prf, 11));
+ $kdf = 'pbkdf2';
+ $iv = Random::string($crypto->getBlockLength() >> 3);
+
+ $PBKDF2params = [
+ 'salt' => $salt,
+ 'iterationCount' => $iterationCount,
+ 'prf' => ['algorithm' => $prf, 'parameters' => null]
+ ];
+ $PBKDF2params = ASN1::encodeDER($PBKDF2params, Maps\PBKDF2params::MAP);
+
+ if (!$crypto instanceof RC2) {
+ $params = ['octetString' => $iv];
+ } else {
+ $params = [
+ 'rc2ParametersVersion' => 58,
+ 'iv' => $iv
+ ];
+ $params = ASN1::encodeDER($params, Maps\RC2CBCParameter::MAP);
+ $params = new ASN1\Element($params);
+ }
+
+ $params = [
+ 'keyDerivationFunc' => [
+ 'algorithm' => 'id-PBKDF2',
+ 'parameters' => new ASN1\Element($PBKDF2params)
+ ],
+ 'encryptionScheme' => [
+ 'algorithm' => $encryptionScheme,
+ 'parameters' => $params
+ ]
+ ];
+ $params = ASN1::encodeDER($params, Maps\PBES2params::MAP);
+
+ $crypto->setIV($iv);
+ } else {
+ $crypto = self::getPBES1EncryptionObject($encryptionAlgorithm);
+ $hash = self::getPBES1Hash($encryptionAlgorithm);
+ $kdf = self::getPBES1KDF($encryptionAlgorithm);
+
+ $params = [
+ 'salt' => $salt,
+ 'iterationCount' => $iterationCount
+ ];
+ $params = ASN1::encodeDER($params, Maps\PBEParameter::MAP);
+ }
+ $crypto->setPassword($password, $kdf, $hash, $salt, $iterationCount);
+ $key = $crypto->encrypt($key);
+
+ $key = [
+ 'encryptionAlgorithm' => [
+ 'algorithm' => $encryptionAlgorithm,
+ 'parameters' => new ASN1\Element($params)
+ ],
+ 'encryptedData' => $key
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\EncryptedPrivateKeyInfo::MAP);
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $key;
+ }
+
+ return "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END ENCRYPTED PRIVATE KEY-----";
+ }
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $key;
+ }
+
+ return "-----BEGIN PRIVATE KEY-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END PRIVATE KEY-----";
+ }
+
+ /**
+ * Wrap a public key appropriately
+ *
+ * @param string $key
+ * @param mixed $params
+ * @param string $oid
+ * @return string
+ */
+ protected static function wrapPublicKey($key, $params, $oid = null, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ $key = [
+ 'publicKeyAlgorithm' => [
+ 'algorithm' => is_string(static::OID_NAME) ? static::OID_NAME : $oid
+ ],
+ 'publicKey' => "\0" . $key
+ ];
+
+ if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') {
+ $key['publicKeyAlgorithm']['parameters'] = $params;
+ }
+
+ $key = ASN1::encodeDER($key, Maps\PublicKeyInfo::MAP);
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $key;
+ }
+
+ return "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END PUBLIC KEY-----";
+ }
+
+ /**
+ * Perform some preliminary parsing of the key
+ *
+ * @param string $key
+ * @return array
+ */
+ private static function preParse(&$key)
+ {
+ self::initialize_static_variables();
+
+ if (self::$format != self::MODE_DER) {
+ $decoded = ASN1::extractBER($key);
+ if ($decoded !== false) {
+ $key = $decoded;
+ } elseif (self::$format == self::MODE_PEM) {
+ throw new \UnexpectedValueException('Expected base64-encoded PEM format but was unable to decode base64 text');
+ }
+ }
+
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ return $decoded;
+ }
+
+ /**
+ * Returns the encryption parameters used by the key
+ *
+ * @param string $key
+ * @return array
+ */
+ public static function extractEncryptionAlgorithm($key)
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ $decoded = self::preParse($key);
+
+ $r = ASN1::asn1map($decoded[0], Maps\EncryptedPrivateKeyInfo::MAP);
+ if (!is_array($r)) {
+ throw new \RuntimeException('Unable to parse using EncryptedPrivateKeyInfo map');
+ }
+
+ if ($r['encryptionAlgorithm']['algorithm'] == 'id-PBES2') {
+ $decoded = ASN1::decodeBER($r['encryptionAlgorithm']['parameters']->element);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $r['encryptionAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\PBES2params::MAP);
+
+ $kdf = &$r['encryptionAlgorithm']['parameters']['keyDerivationFunc'];
+ switch ($kdf['algorithm']) {
+ case 'id-PBKDF2':
+ $decoded = ASN1::decodeBER($kdf['parameters']->element);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $kdf['parameters'] = ASN1::asn1map($decoded[0], Maps\PBKDF2params::MAP);
+ }
+ }
+
+ return $r['encryptionAlgorithm'];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php
new file mode 100644
index 000000000..ff4a95a82
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PuTTY.php
@@ -0,0 +1,380 @@
+<?php
+
+/**
+ * PuTTY Formatted Key Handler
+ *
+ * See PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\AES;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+
+/**
+ * PuTTY Formatted Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PuTTY
+{
+ /**
+ * Default comment
+ *
+ * @var string
+ */
+ private static $comment = 'phpseclib-generated-key';
+
+ /**
+ * Default version
+ *
+ * @var int
+ */
+ private static $version = 2;
+
+ /**
+ * Sets the default comment
+ *
+ * @param string $comment
+ */
+ public static function setComment($comment)
+ {
+ self::$comment = str_replace(["\r", "\n"], '', $comment);
+ }
+
+ /**
+ * Sets the default version
+ *
+ * @param int $version
+ */
+ public static function setVersion($version)
+ {
+ if ($version != 2 && $version != 3) {
+ throw new \RuntimeException('Only supported versions are 2 and 3');
+ }
+ self::$version = $version;
+ }
+
+ /**
+ * Generate a symmetric key for PuTTY v2 keys
+ *
+ * @param string $password
+ * @param int $length
+ * @return string
+ */
+ private static function generateV2Key($password, $length)
+ {
+ $symkey = '';
+ $sequence = 0;
+ while (strlen($symkey) < $length) {
+ $temp = pack('Na*', $sequence++, $password);
+ $symkey .= Strings::hex2bin(sha1($temp));
+ }
+ return substr($symkey, 0, $length);
+ }
+
+ /**
+ * Generate a symmetric key for PuTTY v3 keys
+ *
+ * @param string $password
+ * @param string $flavour
+ * @param int $memory
+ * @param int $passes
+ * @param string $salt
+ * @return array
+ */
+ private static function generateV3Key($password, $flavour, $memory, $passes, $salt)
+ {
+ if (!function_exists('sodium_crypto_pwhash')) {
+ throw new \RuntimeException('sodium_crypto_pwhash needs to exist for Argon2 password hasing');
+ }
+
+ switch ($flavour) {
+ case 'Argon2i':
+ $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
+ break;
+ case 'Argon2id':
+ $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
+ break;
+ default:
+ throw new UnsupportedAlgorithmException('Only Argon2i and Argon2id are supported');
+ }
+
+ $length = 80; // keylen + ivlen + mac_keylen
+ $temp = sodium_crypto_pwhash($length, $password, $salt, $passes, $memory << 10, $flavour);
+
+ $symkey = substr($temp, 0, 32);
+ $symiv = substr($temp, 32, 16);
+ $hashkey = substr($temp, -32);
+
+ return compact('symkey', 'symiv', 'hashkey');
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password
+ * @return array
+ */
+ public static function load($key, $password)
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (strpos($key, 'BEGIN SSH2 PUBLIC KEY') !== false) {
+ $lines = preg_split('#[\r\n]+#', $key);
+ switch (true) {
+ case $lines[0] != '---- BEGIN SSH2 PUBLIC KEY ----':
+ throw new \UnexpectedValueException('Key doesn\'t start with ---- BEGIN SSH2 PUBLIC KEY ----');
+ case $lines[count($lines) - 1] != '---- END SSH2 PUBLIC KEY ----':
+ throw new \UnexpectedValueException('Key doesn\'t end with ---- END SSH2 PUBLIC KEY ----');
+ }
+ $lines = array_splice($lines, 1, -1);
+ $lines = array_map(function ($line) {
+ return rtrim($line, "\r\n");
+ }, $lines);
+ $data = $current = '';
+ $values = [];
+ $in_value = false;
+ foreach ($lines as $line) {
+ switch (true) {
+ case preg_match('#^(.*?): (.*)#', $line, $match):
+ $in_value = $line[strlen($line) - 1] == '\\';
+ $current = strtolower($match[1]);
+ $values[$current] = $in_value ? substr($match[2], 0, -1) : $match[2];
+ break;
+ case $in_value:
+ $in_value = $line[strlen($line) - 1] == '\\';
+ $values[$current] .= $in_value ? substr($line, 0, -1) : $line;
+ break;
+ default:
+ $data .= $line;
+ }
+ }
+
+ $components = call_user_func([static::PUBLIC_HANDLER, 'load'], $data);
+ if ($components === false) {
+ throw new \UnexpectedValueException('Unable to decode public key');
+ }
+ $components += $values;
+ $components['comment'] = str_replace(['\\\\', '\"'], ['\\', '"'], $values['comment']);
+
+ return $components;
+ }
+
+ $components = [];
+
+ $key = preg_split('#\r\n|\r|\n#', trim($key));
+ if (Strings::shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
+ return false;
+ }
+ $version = (int) Strings::shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
+ if ($version != 2 && $version != 3) {
+ throw new \RuntimeException('Only v2 and v3 PuTTY private keys are supported');
+ }
+ $components['type'] = $type = rtrim($key[0]);
+ if (!in_array($type, static::$types)) {
+ $error = count(static::$types) == 1 ?
+ 'Only ' . static::$types[0] . ' keys are supported. ' :
+ '';
+ throw new UnsupportedAlgorithmException($error . 'This is an unsupported ' . $type . ' key');
+ }
+ $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
+ $components['comment'] = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
+
+ $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
+ $public = Strings::base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
+
+ $source = Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public);
+
+ $length = unpack('Nlength', Strings::shift($public, 4))['length'];
+ $newtype = Strings::shift($public, $length);
+ if ($newtype != $type) {
+ throw new \RuntimeException('The binary type does not match the human readable type field');
+ }
+
+ $components['public'] = $public;
+
+ switch ($version) {
+ case 3:
+ $hashkey = '';
+ break;
+ case 2:
+ $hashkey = 'putty-private-key-file-mac-key';
+ }
+
+ $offset = $publicLength + 4;
+ switch ($encryption) {
+ case 'aes256-cbc':
+ $crypto = new AES('cbc');
+ switch ($version) {
+ case 3:
+ $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
+ $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
+ $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
+ $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
+ $salt = Strings::hex2bin(trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
+
+ $v3key = self::generateV3Key($password, $flavour, $memory, $passes, $salt);
+ $symkey = $v3key['symkey'];
+ $symiv = $v3key['symiv'];
+ $hashkey = $v3key['hashkey'];
+
+ break;
+ case 2:
+ $symkey = self::generateV2Key($password, 32);
+ $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3);
+ $hashkey .= $password;
+ }
+ }
+
+ switch ($version) {
+ case 3:
+ $hash = new Hash('sha256');
+ $hash->setKey($hashkey);
+ break;
+ case 2:
+ $hash = new Hash('sha1');
+ $hash->setKey(sha1($hashkey, true));
+ }
+
+ $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
+ $private = Strings::base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));
+
+ if ($encryption != 'none') {
+ $crypto->setKey($symkey);
+ $crypto->setIV($symiv);
+ $crypto->disablePadding();
+ $private = $crypto->decrypt($private);
+ }
+
+ $source .= Strings::packSSH2('s', $private);
+
+ $hmac = trim(preg_replace('#Private-MAC: (.+)#', '$1', $key[$offset + $privateLength]));
+ $hmac = Strings::hex2bin($hmac);
+
+ if (!hash_equals($hash->hash($source), $hmac)) {
+ throw new \UnexpectedValueException('MAC validation error');
+ }
+
+ $components['private'] = $private;
+
+ return $components;
+ }
+
+ /**
+ * Wrap a private key appropriately
+ *
+ * @param string $public
+ * @param string $private
+ * @param string $type
+ * @param string $password
+ * @param array $options optional
+ * @return string
+ */
+ protected static function wrapPrivateKey($public, $private, $type, $password, array $options = [])
+ {
+ $encryption = (!empty($password) || is_string($password)) ? 'aes256-cbc' : 'none';
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+ $version = isset($options['version']) ? $options['version'] : self::$version;
+
+ $key = "PuTTY-User-Key-File-$version: $type\r\n";
+ $key .= "Encryption: $encryption\r\n";
+ $key .= "Comment: $comment\r\n";
+
+ $public = Strings::packSSH2('s', $type) . $public;
+
+ $source = Strings::packSSH2('ssss', $type, $encryption, $comment, $public);
+
+ $public = Strings::base64_encode($public);
+ $key .= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
+ $key .= chunk_split($public, 64);
+
+ if (empty($password) && !is_string($password)) {
+ $source .= Strings::packSSH2('s', $private);
+ switch ($version) {
+ case 3:
+ $hash = new Hash('sha256');
+ $hash->setKey('');
+ break;
+ case 2:
+ $hash = new Hash('sha1');
+ $hash->setKey(sha1('putty-private-key-file-mac-key', true));
+ }
+ } else {
+ $private .= Random::string(16 - (strlen($private) & 15));
+ $source .= Strings::packSSH2('s', $private);
+ $crypto = new AES('cbc');
+
+ switch ($version) {
+ case 3:
+ $salt = Random::string(16);
+ $key .= "Key-Derivation: Argon2id\r\n";
+ $key .= "Argon2-Memory: 8192\r\n";
+ $key .= "Argon2-Passes: 13\r\n";
+ $key .= "Argon2-Parallelism: 1\r\n";
+ $key .= "Argon2-Salt: " . Strings::bin2hex($salt) . "\r\n";
+ $v3key = self::generateV3Key($password, 'Argon2id', 8192, 13, $salt);
+ $symkey = $v3key['symkey'];
+ $symiv = $v3key['symiv'];
+ $hashkey = $v3key['hashkey'];
+
+ $hash = new Hash('sha256');
+ $hash->setKey($hashkey);
+
+ break;
+ case 2:
+ $symkey = self::generateV2Key($password, 32);
+ $symiv = str_repeat("\0", $crypto->getBlockLength() >> 3);
+ $hashkey = 'putty-private-key-file-mac-key' . $password;
+
+ $hash = new Hash('sha1');
+ $hash->setKey(sha1($hashkey, true));
+ }
+
+ $crypto->setKey($symkey);
+ $crypto->setIV($symiv);
+ $crypto->disablePadding();
+ $private = $crypto->encrypt($private);
+ $mac = $hash->hash($source);
+ }
+
+ $private = Strings::base64_encode($private);
+ $key .= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
+ $key .= chunk_split($private, 64);
+ $key .= 'Private-MAC: ' . Strings::bin2hex($hash->hash($source)) . "\r\n";
+
+ return $key;
+ }
+
+ /**
+ * Wrap a public key appropriately
+ *
+ * This is basically the format described in RFC 4716 (https://tools.ietf.org/html/rfc4716)
+ *
+ * @param string $key
+ * @param string $type
+ * @return string
+ */
+ protected static function wrapPublicKey($key, $type)
+ {
+ $key = pack('Na*a*', strlen($type), $type, $key);
+ $key = "---- BEGIN SSH2 PUBLIC KEY ----\r\n" .
+ 'Comment: "' . str_replace(['\\', '"'], ['\\\\', '\"'], self::$comment) . "\"\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ '---- END SSH2 PUBLIC KEY ----';
+ return $key;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php
new file mode 100644
index 000000000..42a65afa4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Signature/Raw.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Raw Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures as arrays
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Formats\Signature;
+
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Raw Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Raw
+{
+ /**
+ * Loads a signature
+ *
+ * @param array $sig
+ * @return array|bool
+ */
+ public static function load($sig)
+ {
+ switch (true) {
+ case !is_array($sig):
+ case !isset($sig['r']) || !isset($sig['s']):
+ case !$sig['r'] instanceof BigInteger:
+ case !$sig['s'] instanceof BigInteger:
+ return false;
+ }
+
+ return [
+ 'r' => $sig['r'],
+ 's' => $sig['s']
+ ];
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s)
+ {
+ return compact('r', 's');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php
new file mode 100644
index 000000000..a6e1eb0b4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PrivateKey.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * PrivateKey interface
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common;
+
+/**
+ * PrivateKey interface
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+interface PrivateKey
+{
+ public function sign($message);
+ //public function decrypt($ciphertext);
+ public function getPublicKey();
+ public function toString($type, array $options = []);
+
+ /**
+ * @param string|false $password
+ * @return mixed
+ */
+ public function withPassword($password = false);
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php
new file mode 100644
index 000000000..48a5875b1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/PublicKey.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * PublicKey interface
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common;
+
+/**
+ * PublicKey interface
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+interface PublicKey
+{
+ public function verify($message, $signature);
+ //public function encrypt($plaintext);
+ public function toString($type, array $options = []);
+ public function getFingerprint($algorithm);
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php
new file mode 100644
index 000000000..c7c080f4e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/StreamCipher.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * Base Class for all stream ciphers
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common;
+
+/**
+ * Base Class for all stream cipher classes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class StreamCipher extends SymmetricKey
+{
+ /**
+ * Block Length of the cipher
+ *
+ * Stream ciphers do not have a block size
+ *
+ * @see SymmetricKey::block_size
+ * @var int
+ */
+ protected $block_size = 0;
+
+ /**
+ * Default Constructor.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ * @return StreamCipher
+ */
+ public function __construct()
+ {
+ parent::__construct('stream');
+ }
+
+ /**
+ * Stream ciphers not use an IV
+ *
+ * @return bool
+ */
+ public function usesIV()
+ {
+ return false;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php
index 2d4225a3f..35d7a7d7a 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php
@@ -1,32 +1,30 @@
<?php
/**
- * Base Class for all \phpseclib\Crypt\* cipher classes
+ * Base Class for all \phpseclib3\Crypt\* cipher classes
*
* PHP version 5
*
* Internally for phpseclib developers:
* If you plan to add a new cipher class, please note following rules:
*
- * - The new \phpseclib\Crypt\* cipher class should extend \phpseclib\Crypt\Base
+ * - The new \phpseclib3\Crypt\* cipher class should extend \phpseclib3\Crypt\Common\SymmetricKey
*
* - Following methods are then required to be overridden/overloaded:
*
- * - _encryptBlock()
+ * - encryptBlock()
*
- * - _decryptBlock()
+ * - decryptBlock()
*
- * - _setupKey()
+ * - setupKey()
*
* - All other methods are optional to be overridden/overloaded
*
- * - Look at the source code of the current ciphers how they extend \phpseclib\Crypt\Base
+ * - Look at the source code of the current ciphers how they extend \phpseclib3\Crypt\Common\SymmetricKey
* and take one of them as a start up for the new cipher class.
*
* - Please read all the other comments/notes/hints here also for each class var/method
*
- * @category Crypt
- * @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
@@ -34,178 +32,250 @@
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt\Common;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Blowfish;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Exception\BadDecryptionException;
+use phpseclib3\Exception\BadModeException;
+use phpseclib3\Exception\InconsistentSetupException;
+use phpseclib3\Exception\InsufficientSetupException;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\BinaryField;
+use phpseclib3\Math\PrimeField;
/**
- * Base Class for all \phpseclib\Crypt\* cipher classes
+ * Base Class for all \phpseclib3\Crypt\* cipher classes
*
- * @package Base
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
*/
-abstract class Base
+abstract class SymmetricKey
{
- /**#@+
- * @access public
- * @see \phpseclib\Crypt\Base::encrypt()
- * @see \phpseclib\Crypt\Base::decrypt()
- */
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CTR = -1;
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_ECB = 1;
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CBC = 2;
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
- const MODE_CFB8 = 6;
+ const MODE_CFB8 = 7;
/**
* Encrypt / decrypt using the Output Feedback mode (8bit)
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
- const MODE_OFB8 = 7;
+ const MODE_OFB8 = 8;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
const MODE_OFB = 4;
/**
+ * Encrypt / decrypt using Galois/Counter mode.
+ *
+ * @link https://en.wikipedia.org/wiki/Galois/Counter_Mode
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
+ */
+ const MODE_GCM = 5;
+ /**
* Encrypt / decrypt using streaming mode.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
*/
- const MODE_STREAM = 5;
- /**#@-*/
+ const MODE_STREAM = 6;
/**
- * Whirlpool available flag
+ * Mode Map
*
- * @see \phpseclib\Crypt\Base::_hashInlineCryptFunction()
- * @var bool
- * @access private
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
- static $WHIRLPOOL_AVAILABLE;
+ const MODE_MAP = [
+ 'ctr' => self::MODE_CTR,
+ 'ecb' => self::MODE_ECB,
+ 'cbc' => self::MODE_CBC,
+ 'cfb' => self::MODE_CFB,
+ 'cfb8' => self::MODE_CFB8,
+ 'ofb' => self::MODE_OFB,
+ 'ofb8' => self::MODE_OFB8,
+ 'gcm' => self::MODE_GCM,
+ 'stream' => self::MODE_STREAM
+ ];
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\Base::__construct()
- */
/**
* Base value for the internal implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
const ENGINE_INTERNAL = 1;
/**
- * Base value for the mcrypt implementation $engine switch
+ * Base value for the eval() implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
*/
- const ENGINE_MCRYPT = 2;
+ const ENGINE_EVAL = 2;
/**
* Base value for the mcrypt implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ */
+ const ENGINE_MCRYPT = 3;
+ /**
+ * Base value for the openssl implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ */
+ const ENGINE_OPENSSL = 4;
+ /**
+ * Base value for the libsodium implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ */
+ const ENGINE_LIBSODIUM = 5;
+ /**
+ * Base value for the openssl / gcm implementation $engine switch
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ */
+ const ENGINE_OPENSSL_GCM = 6;
+
+ /**
+ * Engine Reverse Map
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine()
*/
- const ENGINE_OPENSSL = 3;
- /**#@-*/
+ const ENGINE_MAP = [
+ self::ENGINE_INTERNAL => 'PHP',
+ self::ENGINE_EVAL => 'Eval',
+ self::ENGINE_MCRYPT => 'mcrypt',
+ self::ENGINE_OPENSSL => 'OpenSSL',
+ self::ENGINE_LIBSODIUM => 'libsodium',
+ self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)'
+ ];
/**
* The Encryption Mode
*
* @see self::__construct()
* @var int
- * @access private
*/
- var $mode;
+ protected $mode;
/**
* The Block Length of the block cipher
*
* @var int
- * @access private
*/
- var $block_size = 16;
+ protected $block_size = 16;
/**
* The Key
*
* @see self::setKey()
* @var string
- * @access private
*/
- var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ protected $key = false;
+
+ /**
+ * HMAC Key
+ *
+ * @see self::setupGCM()
+ * @var ?string
+ */
+ protected $hKey = false;
/**
* The Initialization Vector
*
* @see self::setIV()
* @var string
- * @access private
*/
- var $iv = '';
+ protected $iv = false;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
- * @see self::_clearBuffers()
+ * @see self::clearBuffers()
* @var string
- * @access private
*/
- var $encryptIV;
+ protected $encryptIV;
/**
* A "sliding" Initialization Vector
*
* @see self::enableContinuousBuffer()
- * @see self::_clearBuffers()
+ * @see self::clearBuffers()
* @var string
- * @access private
*/
- var $decryptIV;
+ protected $decryptIV;
/**
* Continuous Buffer status
*
* @see self::enableContinuousBuffer()
* @var bool
- * @access private
*/
- var $continuousBuffer = false;
+ protected $continuousBuffer = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see self::encrypt()
- * @see self::_clearBuffers()
+ * @see self::clearBuffers()
* @var array
- * @access private
*/
- var $enbuffer;
+ protected $enbuffer;
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see self::decrypt()
- * @see self::_clearBuffers()
+ * @see self::clearBuffers()
* @var array
- * @access private
*/
- var $debuffer;
+ protected $debuffer;
/**
* mcrypt resource for encryption
@@ -215,9 +285,8 @@ abstract class Base
*
* @see self::encrypt()
* @var resource
- * @access private
*/
- var $enmcrypt;
+ private $enmcrypt;
/**
* mcrypt resource for decryption
@@ -227,29 +296,26 @@ abstract class Base
*
* @see self::decrypt()
* @var resource
- * @access private
*/
- var $demcrypt;
+ private $demcrypt;
/**
* Does the enmcrypt resource need to be (re)initialized?
*
- * @see \phpseclib\Crypt\Twofish::setKey()
- * @see \phpseclib\Crypt\Twofish::setIV()
+ * @see \phpseclib3\Crypt\Twofish::setKey()
+ * @see \phpseclib3\Crypt\Twofish::setIV()
* @var bool
- * @access private
*/
- var $enchanged = true;
+ private $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
- * @see \phpseclib\Crypt\Twofish::setKey()
- * @see \phpseclib\Crypt\Twofish::setIV()
+ * @see \phpseclib3\Crypt\Twofish::setKey()
+ * @see \phpseclib3\Crypt\Twofish::setIV()
* @var bool
- * @access private
*/
- var $dechanged = true;
+ private $dechanged = true;
/**
* mcrypt resource for CFB mode
@@ -264,11 +330,10 @@ abstract class Base
* @link http://phpseclib.sourceforge.net/cfb-demo.phps
* @see self::encrypt()
* @see self::decrypt()
- * @see self::_setupMcrypt()
+ * @see self::setupMcrypt()
* @var resource
- * @access private
*/
- var $ecb;
+ private $ecb;
/**
* Optimizing value while CFB-encrypting
@@ -288,9 +353,8 @@ abstract class Base
*
* @see self::encrypt()
* @var int
- * @access private
*/
- var $cfb_init_len = 600;
+ protected $cfb_init_len = 600;
/**
* Does internal cipher state need to be (re)initialized?
@@ -299,54 +363,60 @@ abstract class Base
* @see self::setIV()
* @see self::disableContinuousBuffer()
* @var bool
- * @access private
*/
- var $changed = true;
+ protected $changed = true;
+
+ /**
+ * Does Eval engie need to be (re)initialized?
+ *
+ * @see self::setup()
+ * @var bool
+ */
+ protected $nonIVChanged = true;
/**
* Padding status
*
* @see self::enablePadding()
* @var bool
- * @access private
*/
- var $padding = true;
+ private $padding = true;
/**
* Is the mode one that is paddable?
*
* @see self::__construct()
* @var bool
- * @access private
*/
- var $paddable = false;
+ private $paddable = false;
/**
* Holds which crypt engine internaly should be use,
* which will be determined automatically on __construct()
*
* Currently available $engines are:
- * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required)
- * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required)
- * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required)
- *
- * @see self::_setEngine()
+ * - self::ENGINE_LIBSODIUM (very fast, php-extension: libsodium, extension_loaded('libsodium') required)
+ * - self::ENGINE_OPENSSL_GCM (very fast, php-extension: openssl, extension_loaded('openssl') required)
+ * - self::ENGINE_OPENSSL (very fast, php-extension: openssl, extension_loaded('openssl') required)
+ * - self::ENGINE_MCRYPT (fast, php-extension: mcrypt, extension_loaded('mcrypt') required)
+ * - self::ENGINE_EVAL (medium, pure php-engine, no php-extension required)
+ * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required)
+ *
+ * @see self::setEngine()
* @see self::encrypt()
* @see self::decrypt()
* @var int
- * @access private
*/
- var $engine;
+ protected $engine;
/**
* Holds the preferred crypt engine
*
- * @see self::_setEngine()
+ * @see self::setEngine()
* @see self::setPreferredEngine()
* @var int
- * @access private
*/
- var $preferredEngine;
+ private $preferredEngine;
/**
* The mcrypt specific name of the cipher
@@ -355,11 +425,10 @@ abstract class Base
*
* @link http://www.php.net/mcrypt_module_open
* @link http://www.php.net/mcrypt_list_algorithms
- * @see self::_setupMcrypt()
+ * @see self::setupMcrypt()
* @var string
- * @access private
*/
- var $cipher_name_mcrypt;
+ protected $cipher_name_mcrypt;
/**
* The openssl specific name of the cipher
@@ -368,9 +437,8 @@ abstract class Base
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
- * @access private
*/
- var $cipher_name_openssl;
+ protected $cipher_name_openssl;
/**
* The openssl specific name of the cipher in ECB mode
@@ -380,18 +448,16 @@ abstract class Base
*
* @link http://www.php.net/openssl-get-cipher-methods
* @var string
- * @access private
*/
- var $cipher_name_openssl_ecb;
+ protected $cipher_name_openssl_ecb;
/**
* The default salt used by setPassword()
*
* @see self::setPassword()
* @var string
- * @access private
*/
- var $password_default_salt = 'phpseclib/salt';
+ private $password_default_salt = 'phpseclib/salt';
/**
* The name of the performance-optimized callback function
@@ -401,124 +467,212 @@ abstract class Base
*
* @see self::encrypt()
* @see self::decrypt()
- * @see self::_setupInlineCrypt()
- * @see self::$use_inline_crypt
+ * @see self::setupInlineCrypt()
* @var Callback
- * @access private
- */
- var $inline_crypt;
-
- /**
- * Holds whether performance-optimized $inline_crypt() can/should be used.
- *
- * @see self::encrypt()
- * @see self::decrypt()
- * @see self::inline_crypt
- * @var mixed
- * @access private
*/
- var $use_inline_crypt = true;
+ protected $inline_crypt;
/**
* If OpenSSL can be used in ECB but not in CTR we can emulate CTR
*
- * @see self::_openssl_ctr_process()
+ * @see self::openssl_ctr_process()
* @var bool
- * @access private
*/
- var $openssl_emulate_ctr = false;
+ private $openssl_emulate_ctr = false;
/**
- * Determines what options are passed to openssl_encrypt/decrypt
+ * Don't truncate / null pad key
*
- * @see self::isValidEngine()
- * @var mixed
- * @access private
+ * @see self::clearBuffers()
+ * @var bool
*/
- var $openssl_options;
+ private $skip_key_adjustment = false;
/**
* Has the key length explicitly been set or should it be derived from the key, itself?
*
* @see self::setKeyLength()
* @var bool
- * @access private
*/
- var $explicit_key_length = false;
+ protected $explicit_key_length = false;
/**
- * Don't truncate / null pad key
+ * Hash subkey for GHASH
*
- * @see self::_clearBuffers()
- * @var bool
- * @access private
+ * @see self::setupGCM()
+ * @see self::ghash()
+ * @var BinaryField\Integer
*/
- var $skip_key_adjustment = false;
+ private $h;
/**
- * Default Constructor.
+ * Additional authenticated data
+ *
+ * @var string
+ */
+ protected $aad = '';
+
+ /**
+ * Authentication Tag produced after a round of encryption
+ *
+ * @var string
+ */
+ protected $newtag = false;
+
+ /**
+ * Authentication Tag to be verified during decryption
+ *
+ * @var string
+ */
+ protected $oldtag = false;
+
+ /**
+ * GCM Binary Field
+ *
+ * @see self::__construct()
+ * @see self::ghash()
+ * @var BinaryField
+ */
+ private static $gcmField;
+
+ /**
+ * Poly1305 Prime Field
+ *
+ * @see self::enablePoly1305()
+ * @see self::poly1305()
+ * @var PrimeField
+ */
+ private static $poly1305Field;
+
+ /**
+ * Flag for using regular vs "safe" intval
+ *
+ * @see self::initialize_static_variables()
+ * @var boolean
+ */
+ protected static $use_reg_intval;
+
+ /**
+ * Poly1305 Key
+ *
+ * @see self::setPoly1305Key()
+ * @see self::poly1305()
+ * @var string
+ */
+ protected $poly1305Key;
+
+ /**
+ * Poly1305 Flag
+ *
+ * @see self::setPoly1305Key()
+ * @see self::enablePoly1305()
+ * @var boolean
+ */
+ protected $usePoly1305 = false;
+
+ /**
+ * The Original Initialization Vector
+ *
+ * GCM uses the nonce to build the IV but we want to be able to distinguish between nonce-derived
+ * IV's and user-set IV's
+ *
+ * @see self::setIV()
+ * @var string
+ */
+ private $origIV = false;
+
+ /**
+ * Nonce
*
- * Determines whether or not the mcrypt extension should be used.
+ * Only used with GCM. We could re-use setIV() but nonce's can be of a different length and
+ * toggling between GCM and other modes could be more complicated if we re-used setIV()
+ *
+ * @see self::setNonce()
+ * @var string
+ */
+ protected $nonce = false;
+
+ /**
+ * Default Constructor.
*
* $mode could be:
*
- * - self::MODE_ECB
+ * - ecb
+ *
+ * - cbc
+ *
+ * - ctr
*
- * - self::MODE_CBC
+ * - cfb
*
- * - self::MODE_CTR
+ * - cfb8
*
- * - self::MODE_CFB
+ * - ofb
*
- * - self::MODE_OFB
+ * - ofb8
*
- * If not explicitly set, self::MODE_CBC will be used.
+ * - gcm
*
- * @param int $mode
- * @access public
+ * @param string $mode
+ * @throws BadModeException if an invalid / unsupported mode is provided
*/
- function __construct($mode = self::MODE_CBC)
+ public function __construct($mode)
{
+ $mode = strtolower($mode);
+ // necessary because of 5.6 compatibility; we can't do isset(self::MODE_MAP[$mode]) in 5.6
+ $map = self::MODE_MAP;
+ if (!isset($map[$mode])) {
+ throw new BadModeException('No valid mode has been specified');
+ }
+
+ $mode = self::MODE_MAP[$mode];
+
// $mode dependent settings
switch ($mode) {
case self::MODE_ECB:
+ case self::MODE_CBC:
$this->paddable = true;
- $this->mode = self::MODE_ECB;
break;
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
- case self::MODE_OFB8:
case self::MODE_OFB:
+ case self::MODE_OFB8:
case self::MODE_STREAM:
- $this->mode = $mode;
+ $this->paddable = false;
+ break;
+ case self::MODE_GCM:
+ if ($this->block_size != 16) {
+ throw new BadModeException('GCM is only valid for block ciphers with a block size of 128 bits');
+ }
+ if (!isset(self::$gcmField)) {
+ self::$gcmField = new BinaryField(128, 7, 2, 1, 0);
+ }
+ $this->paddable = false;
break;
- case self::MODE_CBC:
default:
- $this->paddable = true;
- $this->mode = self::MODE_CBC;
+ throw new BadModeException('No valid mode has been specified');
}
- $this->_setEngine();
-
- // Determining whether inline crypting can be used by the cipher
- if ($this->use_inline_crypt !== false) {
- $this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function');
- }
+ $this->mode = $mode;
- if (!defined('PHP_INT_SIZE')) {
- define('PHP_INT_SIZE', 4);
- }
+ static::initialize_static_variables();
+ }
- if (!defined('CRYPT_BASE_USE_REG_INTVAL')) {
+ /**
+ * Initialize static variables
+ */
+ protected static function initialize_static_variables()
+ {
+ if (!isset(self::$use_reg_intval)) {
switch (true) {
// PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
case !function_exists('php_uname'):
case !is_string(php_uname('m')):
case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
- case PHP_INT_SIZE == 8:
- define('CRYPT_BASE_USE_REG_INTVAL', true);
+ case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8:
+ self::$use_reg_intval = true;
break;
case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM':
switch (true) {
@@ -533,57 +687,154 @@ abstract class Base
affected versions of PHP are: 7.0.x, 7.1.0 - 7.1.23 and 7.2.0 - 7.2.11 */
case PHP_VERSION_ID >= 70000 && PHP_VERSION_ID <= 70123:
case PHP_VERSION_ID >= 70200 && PHP_VERSION_ID <= 70211:
- define('CRYPT_BASE_USE_REG_INTVAL', false);
+ self::$use_reg_intval = false;
break;
default:
- define('CRYPT_BASE_USE_REG_INTVAL', true);
+ self::$use_reg_intval = true;
}
}
}
}
/**
- * Sets the initialization vector. (optional)
+ * Sets the initialization vector.
*
- * SetIV is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set, it'll be assumed
- * to be all zero's.
+ * setIV() is not required when ecb or gcm modes are being used.
+ *
+ * {@internal Can be overwritten by a sub class, but does not have to be}
*
- * @access public
* @param string $iv
- * @internal Can be overwritten by a sub class, but does not have to be
+ * @throws \LengthException if the IV length isn't equal to the block size
+ * @throws \BadMethodCallException if an IV is provided when one shouldn't be
*/
- function setIV($iv)
+ public function setIV($iv)
{
if ($this->mode == self::MODE_ECB) {
- return;
+ throw new \BadMethodCallException('This mode does not require an IV.');
}
- $this->iv = $iv;
+ if ($this->mode == self::MODE_GCM) {
+ throw new \BadMethodCallException('Use setNonce instead');
+ }
+
+ if (!$this->usesIV()) {
+ throw new \BadMethodCallException('This algorithm does not use an IV.');
+ }
+
+ if (strlen($iv) != $this->block_size) {
+ throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required');
+ }
+
+ $this->iv = $this->origIV = $iv;
$this->changed = true;
}
/**
- * Sets the key length.
+ * Enables Poly1305 mode.
*
- * Keys with explicitly set lengths need to be treated accordingly
+ * Once enabled Poly1305 cannot be disabled.
*
- * @access public
- * @param int $length
+ * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode
*/
- function setKeyLength($length)
+ public function enablePoly1305()
{
- $this->explicit_key_length = true;
- $this->changed = true;
- $this->_setEngine();
+ if ($this->mode == self::MODE_GCM) {
+ throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode');
+ }
+
+ $this->usePoly1305 = true;
+ }
+
+ /**
+ * Enables Poly1305 mode.
+ *
+ * Once enabled Poly1305 cannot be disabled. If $key is not passed then an attempt to call createPoly1305Key
+ * will be made.
+ *
+ * @param string $key optional
+ * @throws \LengthException if the key isn't long enough
+ * @throws \BadMethodCallException if Poly1305 is enabled whilst in GCM mode
+ */
+ public function setPoly1305Key($key = null)
+ {
+ if ($this->mode == self::MODE_GCM) {
+ throw new \BadMethodCallException('Poly1305 cannot be used in GCM mode');
+ }
+
+ if (!is_string($key) || strlen($key) != 32) {
+ throw new \LengthException('The Poly1305 key must be 32 bytes long (256 bits)');
+ }
+
+ if (!isset(self::$poly1305Field)) {
+ // 2^130-5
+ self::$poly1305Field = new PrimeField(new BigInteger('3fffffffffffffffffffffffffffffffb', 16));
+ }
+
+ $this->poly1305Key = $key;
+ $this->usePoly1305 = true;
+ }
+
+ /**
+ * Sets the nonce.
+ *
+ * setNonce() is only required when gcm is used
+ *
+ * @param string $nonce
+ * @throws \BadMethodCallException if an nonce is provided when one shouldn't be
+ */
+ public function setNonce($nonce)
+ {
+ if ($this->mode != self::MODE_GCM) {
+ throw new \BadMethodCallException('Nonces are only used in GCM mode.');
+ }
+
+ $this->nonce = $nonce;
+ $this->setEngine();
+ }
+
+ /**
+ * Sets additional authenticated data
+ *
+ * setAAD() is only used by gcm or in poly1305 mode
+ *
+ * @param string $aad
+ * @throws \BadMethodCallException if mode isn't GCM or if poly1305 isn't being utilized
+ */
+ public function setAAD($aad)
+ {
+ if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
+ throw new \BadMethodCallException('Additional authenticated data is only utilized in GCM mode or with Poly1305');
+ }
+
+ $this->aad = $aad;
+ }
+
+ /**
+ * Returns whether or not the algorithm uses an IV
+ *
+ * @return bool
+ */
+ public function usesIV()
+ {
+ return $this->mode != self::MODE_GCM && $this->mode != self::MODE_ECB;
+ }
+
+ /**
+ * Returns whether or not the algorithm uses a nonce
+ *
+ * @return bool
+ */
+ public function usesNonce()
+ {
+ return $this->mode == self::MODE_GCM;
}
/**
* Returns the current key length in bits
*
- * @access public
* @return int
*/
- function getKeyLength()
+ public function getKeyLength()
{
return $this->key_length << 3;
}
@@ -591,15 +842,41 @@ abstract class Base
/**
* Returns the current block length in bits
*
- * @access public
* @return int
*/
- function getBlockLength()
+ public function getBlockLength()
{
return $this->block_size << 3;
}
/**
+ * Returns the current block length in bytes
+ *
+ * @return int
+ */
+ public function getBlockLengthInBytes()
+ {
+ return $this->block_size;
+ }
+
+ /**
+ * Sets the key length.
+ *
+ * Keys with explicitly set lengths need to be treated accordingly
+ *
+ * @param int $length
+ */
+ public function setKeyLength($length)
+ {
+ $this->explicit_key_length = $length >> 3;
+
+ if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) {
+ $this->key = false;
+ throw new InconsistentSetupException('Key has already been set and is not ' . $this->explicit_key_length . ' bytes long');
+ }
+ }
+
+ /**
* Sets the key.
*
* The min/max length(s) of the key depends on the cipher which is used.
@@ -609,19 +886,19 @@ abstract class Base
*
* If the key is not explicitly set, it'll be assumed to be all null bytes.
*
- * @access public
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @param string $key
*/
- function setKey($key)
+ public function setKey($key)
{
- if (!$this->explicit_key_length) {
- $this->setKeyLength(strlen($key) << 3);
- $this->explicit_key_length = false;
+ if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
+ throw new InconsistentSetupException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
}
$this->key = $key;
- $this->changed = true;
- $this->_setEngine();
+ $this->key_length = strlen($key);
+ $this->setEngine();
}
/**
@@ -637,67 +914,123 @@ abstract class Base
*
* This is a modified version of bcrypt used by OpenSSH.
*
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see Crypt/Hash.php
* @param string $password
* @param string $method
+ * @param int|string ...$func_args
+ * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length
+ * @throws \RuntimeException if bcrypt is being used and a salt isn't provided
* @return bool
- * @access public
- * @internal Could, but not must, extend by the child Crypt_* class
*/
- function setPassword($password, $method = 'pbkdf2')
+ public function setPassword($password, $method = 'pbkdf2', ...$func_args)
{
$key = '';
+ $method = strtolower($method);
switch ($method) {
case 'bcrypt':
- $func_args = func_get_args();
-
if (!isset($func_args[2])) {
- return false;
+ throw new \RuntimeException('A salt must be provided for bcrypt to work');
}
- $salt = $func_args[2];
+ $salt = $func_args[0];
- $rounds = isset($func_args[3]) ? $func_args[3] : 16;
- $keylen = isset($func_args[4]) ? $func_args[4] : $this->key_length;
+ $rounds = isset($func_args[1]) ? $func_args[1] : 16;
+ $keylen = isset($func_args[2]) ? $func_args[2] : $this->key_length;
- $bf = new Blowfish();
- $key = $bf->bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds);
- if (!$key) {
- return false;
- }
+ $key = Blowfish::bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds);
$this->setKey(substr($key, 0, $keylen));
$this->setIV(substr($key, $keylen));
return true;
- default: // 'pbkdf2' or 'pbkdf1'
- $func_args = func_get_args();
-
+ case 'pkcs12': // from https://tools.ietf.org/html/rfc7292#appendix-B.2
+ case 'pbkdf1':
+ case 'pbkdf2':
// Hash function
- $hash = isset($func_args[2]) ? $func_args[2] : 'sha1';
+ $hash = isset($func_args[0]) ? strtolower($func_args[0]) : 'sha1';
+ $hashObj = new Hash();
+ $hashObj->setHash($hash);
// WPA and WPA2 use the SSID as the salt
- $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt;
+ $salt = isset($func_args[1]) ? $func_args[1] : $this->password_default_salt;
// RFC2898#section-4.2 uses 1,000 iterations by default
// WPA and WPA2 use 4,096.
- $count = isset($func_args[4]) ? $func_args[4] : 1000;
+ $count = isset($func_args[2]) ? $func_args[2] : 1000;
// Keylength
- if (isset($func_args[5])) {
- $dkLen = $func_args[5];
+ if (isset($func_args[3])) {
+ if ($func_args[3] <= 0) {
+ throw new \LengthException('Derived key length cannot be longer 0 or less');
+ }
+ $dkLen = $func_args[3];
} else {
- $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length;
+ $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length;
+ $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length;
}
switch (true) {
+ case $method == 'pkcs12':
+ /*
+ In this specification, however, all passwords are created from
+ BMPStrings with a NULL terminator. This means that each character in
+ the original BMPString is encoded in 2 bytes in big-endian format
+ (most-significant byte first). There are no Unicode byte order
+ marks. The 2 bytes produced from the last character in the BMPString
+ are followed by 2 additional bytes with the value 0x00.
+
+ -- https://tools.ietf.org/html/rfc7292#appendix-B.1
+ */
+ $password = "\0" . chunk_split($password, 1, "\0") . "\0";
+
+ /*
+ This standard specifies 3 different values for the ID byte mentioned
+ above:
+
+ 1. If ID=1, then the pseudorandom bits being produced are to be used
+ as key material for performing encryption or decryption.
+
+ 2. If ID=2, then the pseudorandom bits being produced are to be used
+ as an IV (Initial Value) for encryption or decryption.
+
+ 3. If ID=3, then the pseudorandom bits being produced are to be used
+ as an integrity key for MACing.
+ */
+ // Construct a string, D (the "diversifier"), by concatenating v/8
+ // copies of ID.
+ $blockLength = $hashObj->getBlockLengthInBytes();
+ $d1 = str_repeat(chr(1), $blockLength);
+ $d2 = str_repeat(chr(2), $blockLength);
+ $s = '';
+ if (strlen($salt)) {
+ while (strlen($s) < $blockLength) {
+ $s .= $salt;
+ }
+ }
+ $s = substr($s, 0, $blockLength);
+
+ $p = '';
+ if (strlen($password)) {
+ while (strlen($p) < $blockLength) {
+ $p .= $password;
+ }
+ }
+ $p = substr($p, 0, $blockLength);
+
+ $i = $s . $p;
+
+ $this->setKey(self::pkcs12helper($dkLen, $hashObj, $i, $d1, $count));
+ if ($this->usesIV()) {
+ $this->setIV(self::pkcs12helper($this->block_size, $hashObj, $i, $d2, $count));
+ }
+
+ return true;
case $method == 'pbkdf1':
- $hashObj = new Hash();
- $hashObj->setHash($hash);
- if ($dkLen > $hashObj->getLength()) {
- user_error('Derived key too long');
- return false;
+ if ($dkLen > $hashObj->getLengthInBytes()) {
+ throw new \LengthException('Derived key length cannot be longer than the hash length');
}
$t = $password . $salt;
for ($i = 0; $i < $count; ++$i) {
@@ -706,30 +1039,30 @@ abstract class Base
$key = substr($t, 0, $dkLen);
$this->setKey(substr($key, 0, $dkLen >> 1));
- $this->setIV(substr($key, $dkLen >> 1));
+ if ($this->usesIV()) {
+ $this->setIV(substr($key, $dkLen >> 1));
+ }
return true;
- // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable
- case !function_exists('hash_pbkdf2'):
- case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()):
$i = 1;
- $hmac = new Hash();
- $hmac->setHash($hash);
- $hmac->setKey($password);
+ $hashObj->setKey($password);
while (strlen($key) < $dkLen) {
- $f = $u = $hmac->hash($salt . pack('N', $i++));
+ $f = $u = $hashObj->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) {
- $u = $hmac->hash($u);
- $f^= $u;
+ $u = $hashObj->hash($u);
+ $f ^= $u;
}
- $key.= $f;
+ $key .= $f;
}
$key = substr($key, 0, $dkLen);
break;
default:
$key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
}
+ break;
+ default:
+ throw new UnsupportedAlgorithmException($method . ' is not a supported password hashing method');
}
$this->setKey($key);
@@ -738,6 +1071,59 @@ abstract class Base
}
/**
+ * PKCS#12 KDF Helper Function
+ *
+ * As discussed here:
+ *
+ * {@link https://tools.ietf.org/html/rfc7292#appendix-B}
+ *
+ * @see self::setPassword()
+ * @param int $n
+ * @param Hash $hashObj
+ * @param string $i
+ * @param string $d
+ * @param int $count
+ * @return string $a
+ */
+ private static function pkcs12helper($n, $hashObj, $i, $d, $count)
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ $blockLength = $hashObj->getBlockLength() >> 3;
+
+ $c = ceil($n / $hashObj->getLengthInBytes());
+ $a = '';
+ for ($j = 1; $j <= $c; $j++) {
+ $ai = $d . $i;
+ for ($k = 0; $k < $count; $k++) {
+ $ai = $hashObj->hash($ai);
+ }
+ $b = '';
+ while (strlen($b) < $blockLength) {
+ $b .= $ai;
+ }
+ $b = substr($b, 0, $blockLength);
+ $b = new BigInteger($b, 256);
+ $newi = '';
+ for ($k = 0; $k < strlen($i); $k += $blockLength) {
+ $temp = substr($i, $k, $blockLength);
+ $temp = new BigInteger($temp, 256);
+ $temp->setPrecision($blockLength << 3);
+ $temp = $temp->add($b);
+ $temp = $temp->add($one);
+ $newi .= $temp->toBytes(false);
+ }
+ $i = $newi;
+ $a .= $ai;
+ }
+
+ return substr($a, 0, $n);
+ }
+
+ /**
* Encrypts a message.
*
* $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher
@@ -751,40 +1137,62 @@ abstract class Base
* strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that
* length.
*
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see self::decrypt()
- * @access public
* @param string $plaintext
* @return string $ciphertext
- * @internal Could, but not must, extend by the child Crypt_* class
*/
- function encrypt($plaintext)
+ public function encrypt($plaintext)
{
if ($this->paddable) {
- $plaintext = $this->_pad($plaintext);
+ $plaintext = $this->pad($plaintext);
+ }
+
+ $this->setup();
+
+ if ($this->mode == self::MODE_GCM) {
+ $oldIV = $this->iv;
+ Strings::increment_str($this->iv);
+ $cipher = new static('ctr');
+ $cipher->setKey($this->key);
+ $cipher->setIV($this->iv);
+ $ciphertext = $cipher->encrypt($plaintext);
+
+ $s = $this->ghash(
+ self::nullPad128($this->aad) .
+ self::nullPad128($ciphertext) .
+ self::len64($this->aad) .
+ self::len64($ciphertext)
+ );
+ $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV;
+ $this->newtag = $cipher->encrypt($s);
+ return $ciphertext;
+ }
+
+ if (isset($this->poly1305Key)) {
+ $cipher = clone $this;
+ unset($cipher->poly1305Key);
+ $this->usePoly1305 = false;
+ $ciphertext = $cipher->encrypt($plaintext);
+ $this->newtag = $this->poly1305($ciphertext);
+ return $ciphertext;
}
if ($this->engine === self::ENGINE_OPENSSL) {
- if ($this->changed) {
- $this->_clearBuffers();
- $this->changed = false;
- }
switch ($this->mode) {
case self::MODE_STREAM:
- return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
+ return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
case self::MODE_ECB:
- $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
- return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
+ return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
case self::MODE_CBC:
- $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
- if (!defined('OPENSSL_RAW_DATA')) {
- $result = substr($result, 0, -$this->block_size);
- }
+ $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->encryptIV);
if ($this->continuousBuffer) {
$this->encryptIV = substr($result, -$this->block_size);
}
return $result;
case self::MODE_CTR:
- return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
+ return $this->openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
// {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
@@ -803,11 +1211,11 @@ abstract class Base
$max = $this->block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
@@ -819,22 +1227,22 @@ abstract class Base
$overflow = $len % $this->block_size;
if ($overflow) {
- $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
- $iv = $this->_string_pop($ciphertext, $this->block_size);
+ $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
+ $iv = Strings::pop($ciphertext, $this->block_size);
$size = $len - $overflow;
$block = $iv ^ substr($plaintext, -$overflow);
$iv = substr_replace($iv, $block, 0, $overflow);
- $ciphertext.= $block;
+ $ciphertext .= $block;
$pos = $overflow;
} elseif ($len) {
- $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
+ $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
$iv = substr($ciphertext, -$this->block_size);
}
return $ciphertext;
case self::MODE_CFB8:
- $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
+ $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->encryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->encryptIV = substr($ciphertext, -$this->block_size);
@@ -844,14 +1252,13 @@ abstract class Base
}
return $ciphertext;
case self::MODE_OFB8:
- // OpenSSL has built in support for cfb8 but not ofb8
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
- $ciphertext.= $plaintext[$i] ^ $xor;
+ $ciphertext .= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
@@ -860,19 +1267,15 @@ abstract class Base
}
break;
case self::MODE_OFB:
- return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
+ return $this->openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
}
}
if ($this->engine === self::ENGINE_MCRYPT) {
- set_error_handler(array($this, 'do_nothing'));
-
- if ($this->changed) {
- $this->_setupMcrypt();
- $this->changed = false;
- }
+ set_error_handler(function () {
+ });
if ($this->enchanged) {
- mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV));
$this->enchanged = false;
}
@@ -891,11 +1294,11 @@ abstract class Base
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
@@ -908,15 +1311,15 @@ abstract class Base
mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
$this->enbuffer['enmcrypt_init'] = false;
}
- $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
+ $ciphertext .= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
$iv = substr($ciphertext, -$block_size);
- $len%= $block_size;
+ $len %= $block_size;
} else {
while ($len >= $block_size) {
$iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
- $ciphertext.= $iv;
- $len-= $block_size;
- $i+= $block_size;
+ $ciphertext .= $iv;
+ $len -= $block_size;
+ $i += $block_size;
}
}
}
@@ -925,7 +1328,7 @@ abstract class Base
$iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len);
- $ciphertext.= $block;
+ $ciphertext .= $block;
$pos = $len;
}
@@ -937,7 +1340,7 @@ abstract class Base
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
- mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->getIV($this->encryptIV));
}
restore_error_handler();
@@ -945,13 +1348,9 @@ abstract class Base
return $ciphertext;
}
- if ($this->changed) {
- $this->_setup();
- $this->changed = false;
- }
- if ($this->use_inline_crypt) {
+ if ($this->engine === self::ENGINE_EVAL) {
$inline = $this->inline_crypt;
- return $inline('encrypt', $this, $plaintext);
+ return $inline('encrypt', $plaintext);
}
$buffer = &$this->enbuffer;
@@ -959,17 +1358,17 @@ abstract class Base
$ciphertext = '';
switch ($this->mode) {
case self::MODE_ECB:
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
- $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
+ $ciphertext .= $this->encryptBlock(substr($plaintext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->encryptIV;
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
- $block = $this->_encryptBlock($block ^ $xor);
+ $block = $this->encryptBlock($block ^ $xor);
$xor = $block;
- $ciphertext.= $block;
+ $ciphertext .= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
@@ -978,21 +1377,21 @@ abstract class Base
case self::MODE_CTR:
$xor = $this->encryptIV;
if (strlen($buffer['ciphertext'])) {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
- $buffer['ciphertext'].= $this->_encryptBlock($xor);
- $this->_increment_str($xor);
+ $buffer['ciphertext'] .= $this->encryptBlock($xor);
+ Strings::increment_str($xor);
}
- $key = $this->_string_shift($buffer['ciphertext'], $block_size);
- $ciphertext.= $block ^ $key;
+ $key = Strings::shift($buffer['ciphertext'], $block_size);
+ $ciphertext .= $block ^ $key;
}
} else {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
- $key = $this->_encryptBlock($xor);
- $this->_increment_str($xor);
- $ciphertext.= $block ^ $key;
+ $key = $this->encryptBlock($xor);
+ Strings::increment_str($xor);
+ $ciphertext .= $block ^ $key;
}
}
if ($this->continuousBuffer) {
@@ -1019,11 +1418,11 @@ abstract class Base
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
@@ -1031,28 +1430,26 @@ abstract class Base
$iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
}
while ($len >= $block_size) {
- $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
- $ciphertext.= $iv;
- $len-= $block_size;
- $i+= $block_size;
+ $iv = $this->encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
+ $ciphertext .= $iv;
+ $len -= $block_size;
+ $i += $block_size;
}
if ($len) {
- $iv = $this->_encryptBlock($iv);
+ $iv = $this->encryptBlock($iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
- $ciphertext.= $block;
+ $ciphertext .= $block;
$pos = $len;
}
break;
case self::MODE_CFB8:
- // compared to regular CFB, which encrypts a block at a time,
- // here, we're encrypting a byte at a time
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
- $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
+ $ciphertext .= ($c = $plaintext[$i] ^ $this->encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
@@ -1070,8 +1467,8 @@ abstract class Base
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
- $xor = $this->_encryptBlock($iv);
- $ciphertext.= $plaintext[$i] ^ $xor;
+ $xor = $this->encryptBlock($iv);
+ $ciphertext .= $plaintext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
@@ -1082,19 +1479,19 @@ abstract class Base
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['xor'])) {
- $xor = $this->_encryptBlock($xor);
- $buffer['xor'].= $xor;
+ $xor = $this->encryptBlock($xor);
+ $buffer['xor'] .= $xor;
}
- $key = $this->_string_shift($buffer['xor'], $block_size);
- $ciphertext.= $block ^ $key;
+ $key = Strings::shift($buffer['xor'], $block_size);
+ $ciphertext .= $block ^ $key;
}
} else {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
- $xor = $this->_encryptBlock($xor);
- $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
+ $xor = $this->encryptBlock($xor);
+ $ciphertext .= substr($plaintext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
@@ -1106,7 +1503,7 @@ abstract class Base
}
break;
case self::MODE_STREAM:
- $ciphertext = $this->_encryptBlock($plaintext);
+ $ciphertext = $this->encryptBlock($plaintext);
break;
}
@@ -1119,50 +1516,73 @@ abstract class Base
* If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
* it is.
*
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see self::encrypt()
- * @access public
* @param string $ciphertext
* @return string $plaintext
- * @internal Could, but not must, extend by the child Crypt_* class
+ * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size
*/
- function decrypt($ciphertext)
+ public function decrypt($ciphertext)
{
- if ($this->paddable) {
- // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}:
- // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
- $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
+ if ($this->paddable && strlen($ciphertext) % $this->block_size) {
+ throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')');
}
+ $this->setup();
- if ($this->engine === self::ENGINE_OPENSSL) {
- if ($this->changed) {
- $this->_clearBuffers();
- $this->changed = false;
+ if ($this->mode == self::MODE_GCM || isset($this->poly1305Key)) {
+ if ($this->oldtag === false) {
+ throw new InsufficientSetupException('Authentication Tag has not been set');
+ }
+
+ if (isset($this->poly1305Key)) {
+ $newtag = $this->poly1305($ciphertext);
+ } else {
+ $oldIV = $this->iv;
+ Strings::increment_str($this->iv);
+ $cipher = new static('ctr');
+ $cipher->setKey($this->key);
+ $cipher->setIV($this->iv);
+ $plaintext = $cipher->decrypt($ciphertext);
+
+ $s = $this->ghash(
+ self::nullPad128($this->aad) .
+ self::nullPad128($ciphertext) .
+ self::len64($this->aad) .
+ self::len64($ciphertext)
+ );
+ $cipher->encryptIV = $this->iv = $this->encryptIV = $this->decryptIV = $oldIV;
+ $newtag = $cipher->encrypt($s);
+ }
+ if ($this->oldtag != substr($newtag, 0, strlen($newtag))) {
+ $cipher = clone $this;
+ unset($cipher->poly1305Key);
+ $this->usePoly1305 = false;
+ $plaintext = $cipher->decrypt($ciphertext);
+ $this->oldtag = false;
+ throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
}
+ $this->oldtag = false;
+ return $plaintext;
+ }
+
+ if ($this->engine === self::ENGINE_OPENSSL) {
switch ($this->mode) {
case self::MODE_STREAM:
- $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
+ $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
break;
case self::MODE_ECB:
- if (!defined('OPENSSL_RAW_DATA')) {
- $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
- }
- $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
+ $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
break;
case self::MODE_CBC:
- if (!defined('OPENSSL_RAW_DATA')) {
- $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
- $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
- $offset = 2 * $this->block_size;
- } else {
- $offset = $this->block_size;
- }
- $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
+ $offset = $this->block_size;
+ $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -$offset, $this->block_size);
}
break;
case self::MODE_CTR:
- $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
+ $plaintext = $this->openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
break;
case self::MODE_CFB:
// cfb loosely routines inspired by openssl's:
@@ -1182,11 +1602,11 @@ abstract class Base
$max = $this->block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize
@@ -1196,21 +1616,21 @@ abstract class Base
}
$overflow = $len % $this->block_size;
if ($overflow) {
- $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
+ $plaintext .= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
if ($len - $overflow) {
$iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow);
}
- $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
- $plaintext.= $iv ^ substr($ciphertext, -$overflow);
+ $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
+ $plaintext .= $iv ^ substr($ciphertext, -$overflow);
$iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow);
$pos = $overflow;
} elseif ($len) {
- $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
+ $plaintext .= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
$iv = substr($ciphertext, -$this->block_size);
}
break;
case self::MODE_CFB8:
- $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
+ $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->decryptIV);
if ($this->continuousBuffer) {
if (($len = strlen($ciphertext)) >= $this->block_size) {
$this->decryptIV = substr($ciphertext, -$this->block_size);
@@ -1226,7 +1646,7 @@ abstract class Base
for ($i = 0; $i < $len; ++$i) {
$xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
- $plaintext.= $ciphertext[$i] ^ $xor;
+ $plaintext .= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
@@ -1235,21 +1655,18 @@ abstract class Base
}
break;
case self::MODE_OFB:
- $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
+ $plaintext = $this->openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
}
- return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ return $this->paddable ? $this->unpad($plaintext) : $plaintext;
}
if ($this->engine === self::ENGINE_MCRYPT) {
- set_error_handler(array($this, 'do_nothing'));
+ set_error_handler(function () {
+ });
$block_size = $this->block_size;
- if ($this->changed) {
- $this->_setupMcrypt();
- $this->changed = false;
- }
if ($this->dechanged) {
- mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV));
$this->dechanged = false;
}
@@ -1264,11 +1681,11 @@ abstract class Base
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
@@ -1277,13 +1694,13 @@ abstract class Base
}
if ($len >= $block_size) {
$cb = substr($ciphertext, $i, $len - $len % $block_size);
- $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
+ $plaintext .= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
$iv = substr($cb, -$block_size);
- $len%= $block_size;
+ $len %= $block_size;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
- $plaintext.= $iv ^ substr($ciphertext, -$len);
+ $plaintext .= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
$pos = $len;
}
@@ -1296,21 +1713,17 @@ abstract class Base
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
- mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->getIV($this->decryptIV));
}
restore_error_handler();
- return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ return $this->paddable ? $this->unpad($plaintext) : $plaintext;
}
- if ($this->changed) {
- $this->_setup();
- $this->changed = false;
- }
- if ($this->use_inline_crypt) {
+ if ($this->engine === self::ENGINE_EVAL) {
$inline = $this->inline_crypt;
- return $inline('decrypt', $this, $ciphertext);
+ return $inline('decrypt', $ciphertext);
}
$block_size = $this->block_size;
@@ -1319,15 +1732,15 @@ abstract class Base
$plaintext = '';
switch ($this->mode) {
case self::MODE_ECB:
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
- $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
+ $plaintext .= $this->decryptBlock(substr($ciphertext, $i, $block_size));
}
break;
case self::MODE_CBC:
$xor = $this->decryptIV;
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
$block = substr($ciphertext, $i, $block_size);
- $plaintext.= $this->_decryptBlock($block) ^ $xor;
+ $plaintext .= $this->decryptBlock($block) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
@@ -1337,21 +1750,21 @@ abstract class Base
case self::MODE_CTR:
$xor = $this->decryptIV;
if (strlen($buffer['ciphertext'])) {
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
- $buffer['ciphertext'].= $this->_encryptBlock($xor);
- $this->_increment_str($xor);
+ $buffer['ciphertext'] .= $this->encryptBlock($xor);
+ Strings::increment_str($xor);
}
- $key = $this->_string_shift($buffer['ciphertext'], $block_size);
- $plaintext.= $block ^ $key;
+ $key = Strings::shift($buffer['ciphertext'], $block_size);
+ $plaintext .= $block ^ $key;
}
} else {
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
$block = substr($ciphertext, $i, $block_size);
- $key = $this->_encryptBlock($xor);
- $this->_increment_str($xor);
- $plaintext.= $block ^ $key;
+ $key = $this->encryptBlock($xor);
+ Strings::increment_str($xor);
+ $plaintext .= $block ^ $key;
}
}
if ($this->continuousBuffer) {
@@ -1376,11 +1789,11 @@ abstract class Base
$max = $block_size - $pos;
if ($len >= $max) {
$i = $max;
- $len-= $max;
+ $len -= $max;
$pos = 0;
} else {
$i = $len;
- $pos+= $len;
+ $pos += $len;
$len = 0;
}
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
@@ -1388,16 +1801,16 @@ abstract class Base
$iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
}
while ($len >= $block_size) {
- $iv = $this->_encryptBlock($iv);
+ $iv = $this->encryptBlock($iv);
$cb = substr($ciphertext, $i, $block_size);
- $plaintext.= $iv ^ $cb;
+ $plaintext .= $iv ^ $cb;
$iv = $cb;
- $len-= $block_size;
- $i+= $block_size;
+ $len -= $block_size;
+ $i += $block_size;
}
if ($len) {
- $iv = $this->_encryptBlock($iv);
- $plaintext.= $iv ^ substr($ciphertext, $i);
+ $iv = $this->encryptBlock($iv);
+ $plaintext .= $iv ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
$pos = $len;
}
@@ -1408,7 +1821,7 @@ abstract class Base
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
- $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv);
+ $plaintext .= $ciphertext[$i] ^ $this->encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
@@ -1426,8 +1839,8 @@ abstract class Base
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
- $xor = $this->_encryptBlock($iv);
- $plaintext.= $ciphertext[$i] ^ $xor;
+ $xor = $this->encryptBlock($iv);
+ $plaintext .= $ciphertext[$i] ^ $xor;
$iv = substr($iv, 1) . $xor[0];
}
@@ -1438,19 +1851,19 @@ abstract class Base
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
$block = substr($ciphertext, $i, $block_size);
if (strlen($block) > strlen($buffer['xor'])) {
- $xor = $this->_encryptBlock($xor);
- $buffer['xor'].= $xor;
+ $xor = $this->encryptBlock($xor);
+ $buffer['xor'] .= $xor;
}
- $key = $this->_string_shift($buffer['xor'], $block_size);
- $plaintext.= $block ^ $key;
+ $key = Strings::shift($buffer['xor'], $block_size);
+ $plaintext .= $block ^ $key;
}
} else {
- for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
- $xor = $this->_encryptBlock($xor);
- $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
+ for ($i = 0; $i < strlen($ciphertext); $i += $block_size) {
+ $xor = $this->encryptBlock($xor);
+ $plaintext .= substr($ciphertext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
@@ -1462,18 +1875,95 @@ abstract class Base
}
break;
case self::MODE_STREAM:
- $plaintext = $this->_decryptBlock($ciphertext);
+ $plaintext = $this->decryptBlock($ciphertext);
break;
}
- return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
+ return $this->paddable ? $this->unpad($plaintext) : $plaintext;
+ }
+
+ /**
+ * Get the authentication tag
+ *
+ * Only used in GCM or Poly1305 mode
+ *
+ * @see self::encrypt()
+ * @param int $length optional
+ * @return string
+ * @throws \LengthException if $length isn't of a sufficient length
+ * @throws \RuntimeException if GCM mode isn't being used
+ */
+ public function getTag($length = 16)
+ {
+ if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
+ throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305');
+ }
+
+ if ($this->newtag === false) {
+ throw new \BadMethodCallException('A tag can only be returned after a round of encryption has been performed');
+ }
+
+ // the tag is 128-bits. it can't be greater than 16 bytes because that's bigger than the tag is. if it
+ // were 0 you might as well be doing CTR and less than 4 provides minimal security that could be trivially
+ // easily brute forced.
+ // see https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=36
+ // for more info
+ if ($length < 4 || $length > 16) {
+ throw new \LengthException('The authentication tag must be between 4 and 16 bytes long');
+ }
+
+ return $length == 16 ?
+ $this->newtag :
+ substr($this->newtag, 0, $length);
+ }
+
+ /**
+ * Sets the authentication tag
+ *
+ * Only used in GCM mode
+ *
+ * @see self::decrypt()
+ * @param string $tag
+ * @throws \LengthException if $length isn't of a sufficient length
+ * @throws \RuntimeException if GCM mode isn't being used
+ */
+ public function setTag($tag)
+ {
+ if ($this->usePoly1305 && !isset($this->poly1305Key) && method_exists($this, 'createPoly1305Key')) {
+ $this->createPoly1305Key();
+ }
+
+ if ($this->mode != self::MODE_GCM && !$this->usePoly1305) {
+ throw new \BadMethodCallException('Authentication tags are only utilized in GCM mode or with Poly1305');
+ }
+
+ $length = strlen($tag);
+ if ($length < 4 || $length > 16) {
+ throw new \LengthException('The authentication tag must be between 4 and 16 bytes long');
+ }
+ $this->oldtag = $tag;
+ }
+
+ /**
+ * Get the IV
+ *
+ * mcrypt requires an IV even if ECB is used
+ *
+ * @see self::encrypt()
+ * @see self::decrypt()
+ * @param string $iv
+ * @return string
+ */
+ protected function getIV($iv)
+ {
+ return $this->mode == self::MODE_ECB ? str_repeat("\0", $this->block_size) : $iv;
}
/**
* OpenSSL CTR Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
- * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt()
- * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this
+ * for CTR is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt()
+ * and SymmetricKey::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this
* function will emulate CTR with ECB when necessary.
*
* @see self::encrypt()
@@ -1482,9 +1972,8 @@ abstract class Base
* @param string $encryptIV
* @param array $buffer
* @return string
- * @access private
*/
- function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer)
+ private function openssl_ctr_process($plaintext, &$encryptIV, &$buffer)
{
$ciphertext = '';
@@ -1494,24 +1983,21 @@ abstract class Base
if ($this->openssl_emulate_ctr) {
$xor = $encryptIV;
if (strlen($buffer['ciphertext'])) {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
- $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
- $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
- $buffer['ciphertext'].= $result;
+ $buffer['ciphertext'] .= openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
}
- $this->_increment_str($xor);
- $otp = $this->_string_shift($buffer['ciphertext'], $block_size);
- $ciphertext.= $block ^ $otp;
+ Strings::increment_str($xor);
+ $otp = Strings::shift($buffer['ciphertext'], $block_size);
+ $ciphertext .= $block ^ $otp;
}
} else {
- for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
+ for ($i = 0; $i < strlen($plaintext); $i += $block_size) {
$block = substr($plaintext, $i, $block_size);
- $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
- $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
- $this->_increment_str($xor);
- $ciphertext.= $block ^ $otp;
+ $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
+ Strings::increment_str($xor);
+ $ciphertext .= $block ^ $otp;
}
}
if ($this->continuousBuffer) {
@@ -1525,7 +2011,7 @@ abstract class Base
}
if (strlen($buffer['ciphertext'])) {
- $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext));
+ $ciphertext = $plaintext ^ Strings::shift($buffer['ciphertext'], strlen($plaintext));
$plaintext = substr($plaintext, strlen($ciphertext));
if (!strlen($plaintext)) {
@@ -1535,28 +2021,25 @@ abstract class Base
$overflow = strlen($plaintext) % $block_size;
if ($overflow) {
- $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2
- $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
- $temp = $this->_string_pop($encrypted, $block_size);
- $ciphertext.= $encrypted . ($plaintext2 ^ $temp);
+ $plaintext2 = Strings::pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2
+ $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
+ $temp = Strings::pop($encrypted, $block_size);
+ $ciphertext .= $encrypted . ($plaintext2 ^ $temp);
if ($this->continuousBuffer) {
$buffer['ciphertext'] = substr($temp, $overflow);
$encryptIV = $temp;
}
} elseif (!strlen($buffer['ciphertext'])) {
- $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
- $temp = $this->_string_pop($ciphertext, $block_size);
+ $ciphertext .= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
+ $temp = Strings::pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $temp;
}
}
if ($this->continuousBuffer) {
- if (!defined('OPENSSL_RAW_DATA')) {
- $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
- }
- $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
+ $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
if ($overflow) {
- $this->_increment_str($encryptIV);
+ Strings::increment_str($encryptIV);
}
}
@@ -1567,8 +2050,8 @@ abstract class Base
* OpenSSL OFB Processor
*
* PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
- * for OFB is the same for both encrypting and decrypting this function is re-used by both Base::encrypt()
- * and Base::decrypt().
+ * for OFB is the same for both encrypting and decrypting this function is re-used by both SymmetricKey::encrypt()
+ * and SymmetricKey::decrypt().
*
* @see self::encrypt()
* @see self::decrypt()
@@ -1576,9 +2059,8 @@ abstract class Base
* @param string $encryptIV
* @param array $buffer
* @return string
- * @access private
*/
- function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer)
+ private function openssl_ofb_process($plaintext, &$encryptIV, &$buffer)
{
if (strlen($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
@@ -1596,17 +2078,17 @@ abstract class Base
if (strlen($plaintext)) {
if ($overflow) {
- $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
- $xor = $this->_string_pop($ciphertext, $block_size);
+ $ciphertext .= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
+ $xor = Strings::pop($ciphertext, $block_size);
if ($this->continuousBuffer) {
$encryptIV = $xor;
}
- $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow);
+ $ciphertext .= Strings::shift($xor, $overflow) ^ substr($plaintext, -$overflow);
if ($this->continuousBuffer) {
$buffer['xor'] = $xor;
}
} else {
- $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
+ $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $encryptIV);
if ($this->continuousBuffer) {
$encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size);
}
@@ -1621,10 +2103,9 @@ abstract class Base
*
* May need to be overwritten by classes extending this one in some cases
*
- * @return int
- * @access private
+ * @return string
*/
- function _openssl_translate_mode()
+ protected function openssl_translate_mode()
{
switch ($this->mode) {
case self::MODE_ECB:
@@ -1632,6 +2113,7 @@ abstract class Base
case self::MODE_CBC:
return 'cbc';
case self::MODE_CTR:
+ case self::MODE_GCM:
return 'ctr';
case self::MODE_CFB:
return 'cfb';
@@ -1655,9 +2137,8 @@ abstract class Base
* transmitted separately)
*
* @see self::disablePadding()
- * @access public
*/
- function enablePadding()
+ public function enablePadding()
{
$this->padding = true;
}
@@ -1666,9 +2147,8 @@ abstract class Base
* Do not pad packets.
*
* @see self::enablePadding()
- * @access public
*/
- function disablePadding()
+ public function disablePadding()
{
$this->padding = false;
}
@@ -1702,24 +2182,28 @@ abstract class Base
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
- * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each
+ * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\*() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see self::disableContinuousBuffer()
- * @access public
- * @internal Could, but not must, extend by the child Crypt_* class
*/
- function enableContinuousBuffer()
+ public function enableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
}
+ if ($this->mode == self::MODE_GCM) {
+ throw new \BadMethodCallException('This mode does not run in continuous mode');
+ }
+
$this->continuousBuffer = true;
- $this->_setEngine();
+ $this->setEngine();
}
/**
@@ -1727,11 +2211,11 @@ abstract class Base
*
* The default behavior.
*
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see self::enableContinuousBuffer()
- * @access public
- * @internal Could, but not must, extend by the child Crypt_* class
*/
- function disableContinuousBuffer()
+ public function disableContinuousBuffer()
{
if ($this->mode == self::MODE_ECB) {
return;
@@ -1741,9 +2225,8 @@ abstract class Base
}
$this->continuousBuffer = false;
- $this->changed = true;
- $this->_setEngine();
+ $this->setEngine();
}
/**
@@ -1751,33 +2234,19 @@ abstract class Base
*
* @see self::__construct()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
- if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) {
- return false;
- }
$this->openssl_emulate_ctr = false;
$result = $this->cipher_name_openssl &&
- extension_loaded('openssl') &&
- // PHP 5.3.0 - 5.3.2 did not let you set IV's
- version_compare(PHP_VERSION, '5.3.3', '>=');
+ extension_loaded('openssl');
if (!$result) {
return false;
}
- // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer
- // $options openssl_encrypt expected a boolean $raw_data.
- if (!defined('OPENSSL_RAW_DATA')) {
- $this->openssl_options = true;
- } else {
- $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
- }
-
$methods = openssl_get_cipher_methods();
if (in_array($this->cipher_name_openssl, $methods)) {
return true;
@@ -1793,12 +2262,15 @@ abstract class Base
}
return false;
case self::ENGINE_MCRYPT:
- set_error_handler(array($this, 'do_nothing'));
+ set_error_handler(function () {
+ });
$result = $this->cipher_name_mcrypt &&
- extension_loaded('mcrypt') &&
- in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms());
+ extension_loaded('mcrypt') &&
+ in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms());
restore_error_handler();
return $result;
+ case self::ENGINE_EVAL:
+ return method_exists($this, 'setupInlineCrypt');
case self::ENGINE_INTERNAL:
return true;
}
@@ -1807,65 +2279,95 @@ abstract class Base
}
/**
+ * Test for engine validity
+ *
+ * @see self::__construct()
+ * @param string $engine
+ * @return bool
+ */
+ public function isValidEngine($engine)
+ {
+ static $reverseMap;
+ if (!isset($reverseMap)) {
+ $reverseMap = array_map('strtolower', self::ENGINE_MAP);
+ $reverseMap = array_flip($reverseMap);
+ }
+ $engine = strtolower($engine);
+ if (!isset($reverseMap[$engine])) {
+ return false;
+ }
+
+ return $this->isValidEngineHelper($reverseMap[$engine]);
+ }
+
+ /**
* Sets the preferred crypt engine
*
* Currently, $engine could be:
*
- * - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast]
+ * - libsodium[very fast]
+ *
+ * - OpenSSL [very fast]
*
- * - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast]
+ * - mcrypt [fast]
*
- * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow]
+ * - Eval [slow]
+ *
+ * - PHP [slowest]
*
* If the preferred crypt engine is not available the fastest available one will be used
*
* @see self::__construct()
- * @param int $engine
- * @access public
+ * @param string $engine
*/
- function setPreferredEngine($engine)
+ public function setPreferredEngine($engine)
{
- switch ($engine) {
- //case self::ENGINE_OPENSSL;
- case self::ENGINE_MCRYPT:
- case self::ENGINE_INTERNAL:
- $this->preferredEngine = $engine;
- break;
- default:
- $this->preferredEngine = self::ENGINE_OPENSSL;
+ static $reverseMap;
+ if (!isset($reverseMap)) {
+ $reverseMap = array_map('strtolower', self::ENGINE_MAP);
+ $reverseMap = array_flip($reverseMap);
}
+ $engine = is_string($engine) ? strtolower($engine) : '';
+ $this->preferredEngine = isset($reverseMap[$engine]) ? $reverseMap[$engine] : self::ENGINE_LIBSODIUM;
- $this->_setEngine();
+ $this->setEngine();
}
/**
* Returns the engine currently being utilized
*
- * @see self::_setEngine()
- * @access public
+ * @see self::setEngine()
*/
- function getEngine()
+ public function getEngine()
{
- return $this->engine;
+ return self::ENGINE_MAP[$this->engine];
}
/**
* Sets the engine as appropriate
*
* @see self::__construct()
- * @access private
*/
- function _setEngine()
+ protected function setEngine()
{
$this->engine = null;
- $candidateEngines = array(
- $this->preferredEngine,
+ $candidateEngines = [
+ self::ENGINE_LIBSODIUM,
+ self::ENGINE_OPENSSL_GCM,
self::ENGINE_OPENSSL,
- self::ENGINE_MCRYPT
- );
+ self::ENGINE_MCRYPT,
+ self::ENGINE_EVAL
+ ];
+ if (isset($this->preferredEngine)) {
+ $temp = [$this->preferredEngine];
+ $candidateEngines = array_merge(
+ $temp,
+ array_diff($candidateEngines, $temp)
+ );
+ }
foreach ($candidateEngines as $engine) {
- if ($this->isValidEngine($engine)) {
+ if ($this->isValidEngineHelper($engine)) {
$this->engine = $engine;
break;
}
@@ -1875,7 +2377,8 @@ abstract class Base
}
if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) {
- set_error_handler(array($this, 'do_nothing'));
+ set_error_handler(function () {
+ });
// Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
// (re)open them with the module named in $this->cipher_name_mcrypt
mcrypt_module_close($this->enmcrypt);
@@ -1890,42 +2393,39 @@ abstract class Base
restore_error_handler();
}
- $this->changed = true;
+ $this->changed = $this->nonIVChanged = true;
}
/**
* Encrypts a block
*
- * Note: Must be extended by the child \phpseclib\Crypt\* class
+ * Note: Must be extended by the child \phpseclib3\Crypt\* class
*
- * @access private
* @param string $in
* @return string
*/
- abstract function _encryptBlock($in);
+ abstract protected function encryptBlock($in);
/**
* Decrypts a block
*
- * Note: Must be extended by the child \phpseclib\Crypt\* class
+ * Note: Must be extended by the child \phpseclib3\Crypt\* class
*
- * @access private
* @param string $in
* @return string
*/
- abstract function _decryptBlock($in);
+ abstract protected function decryptBlock($in);
/**
* Setup the key (expansion)
*
* Only used if $engine == self::ENGINE_INTERNAL
*
- * Note: Must extend by the child \phpseclib\Crypt\* class
+ * Note: Must extend by the child \phpseclib3\Crypt\* class
*
- * @see self::_setup()
- * @access private
+ * @see self::setup()
*/
- abstract function _setupKey();
+ abstract protected function setupKey();
/**
* Setup the self::ENGINE_INTERNAL $engine
@@ -1944,77 +2444,100 @@ abstract class Base
*
* - First run of encrypt() / decrypt() with no init-settings
*
+ * {@internal setup() is always called before en/decryption.}
+ *
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
* @see self::setKey()
* @see self::setIV()
* @see self::disableContinuousBuffer()
- * @access private
- * @internal _setup() is always called before en/decryption.
- * @internal Could, but not must, extend by the child Crypt_* class
*/
- function _setup()
+ protected function setup()
{
- $this->_clearBuffers();
- $this->_setupKey();
+ if (!$this->changed) {
+ return;
+ }
- if ($this->use_inline_crypt) {
- $this->_setupInlineCrypt();
+ $this->changed = false;
+
+ if ($this->usePoly1305 && !isset($this->poly1305Key) && method_exists($this, 'createPoly1305Key')) {
+ $this->createPoly1305Key();
}
- }
- /**
- * Setup the self::ENGINE_MCRYPT $engine
- *
- * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers
- * Used (only) if $engine = self::ENGINE_MCRYPT
- *
- * _setupMcrypt() will be called each time if $changed === true
- * typically this happens when using one or more of following public methods:
- *
- * - setKey()
- *
- * - setIV()
- *
- * - disableContinuousBuffer()
- *
- * - First run of encrypt() / decrypt()
- *
- * @see self::setKey()
- * @see self::setIV()
- * @see self::disableContinuousBuffer()
- * @access private
- * @internal Could, but not must, extend by the child Crypt_* class
- */
- function _setupMcrypt()
- {
- $this->_clearBuffers();
- $this->enchanged = $this->dechanged = true;
-
- if (!isset($this->enmcrypt)) {
- static $mcrypt_modes = array(
- self::MODE_CTR => 'ctr',
- self::MODE_ECB => MCRYPT_MODE_ECB,
- self::MODE_CBC => MCRYPT_MODE_CBC,
- self::MODE_CFB => 'ncfb',
- self::MODE_CFB8 => MCRYPT_MODE_CFB,
- self::MODE_OFB => MCRYPT_MODE_NOFB,
- self::MODE_OFB8 => MCRYPT_MODE_OFB,
- self::MODE_STREAM => MCRYPT_MODE_STREAM,
- );
+ $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true];
+ //$this->newtag = $this->oldtag = false;
- $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
- $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
+ if ($this->usesNonce()) {
+ if ($this->nonce === false) {
+ throw new InsufficientSetupException('No nonce has been defined');
+ }
+ if ($this->mode == self::MODE_GCM && !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) {
+ $this->setupGCM();
+ }
+ } else {
+ $this->iv = $this->origIV;
+ }
- // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
- // to workaround mcrypt's broken ncfb implementation in buffered mode
- // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
- if ($this->mode == self::MODE_CFB) {
- $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
+ if ($this->iv === false && !in_array($this->mode, [self::MODE_STREAM, self::MODE_ECB])) {
+ if ($this->mode != self::MODE_GCM || !in_array($this->engine, [self::ENGINE_LIBSODIUM, self::ENGINE_OPENSSL_GCM])) {
+ throw new InsufficientSetupException('No IV has been defined');
}
- } // else should mcrypt_generic_deinit be called?
+ }
+
+ if ($this->key === false) {
+ throw new InsufficientSetupException('No key has been defined');
+ }
+
+ $this->encryptIV = $this->decryptIV = $this->iv;
+
+ switch ($this->engine) {
+ case self::ENGINE_MCRYPT:
+ $this->enchanged = $this->dechanged = true;
+
+ set_error_handler(function () {
+ });
+
+ if (!isset($this->enmcrypt)) {
+ static $mcrypt_modes = [
+ self::MODE_CTR => 'ctr',
+ self::MODE_ECB => MCRYPT_MODE_ECB,
+ self::MODE_CBC => MCRYPT_MODE_CBC,
+ self::MODE_CFB => 'ncfb',
+ self::MODE_CFB8 => MCRYPT_MODE_CFB,
+ self::MODE_OFB => MCRYPT_MODE_NOFB,
+ self::MODE_OFB8 => MCRYPT_MODE_OFB,
+ self::MODE_STREAM => MCRYPT_MODE_STREAM,
+ ];
+
+ $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
+ $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
+
+ // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
+ // to workaround mcrypt's broken ncfb implementation in buffered mode
+ // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
+ if ($this->mode == self::MODE_CFB) {
+ $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
+ }
+ } // else should mcrypt_generic_deinit be called?
+
+ if ($this->mode == self::MODE_CFB) {
+ mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
+ }
+
+ restore_error_handler();
- if ($this->mode == self::MODE_CFB) {
- mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
+ break;
+ case self::ENGINE_INTERNAL:
+ $this->setupKey();
+ break;
+ case self::ENGINE_EVAL:
+ if ($this->nonIVChanged) {
+ $this->setupKey();
+ $this->setupInlineCrypt();
+ }
}
+
+ $this->nonIVChanged = false;
}
/**
@@ -2027,12 +2550,12 @@ abstract class Base
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
* and padding will, hence forth, be enabled.
*
- * @see self::_unpad()
+ * @see self::unpad()
* @param string $text
- * @access private
+ * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size
* @return string
*/
- function _pad($text)
+ protected function pad($text)
{
$length = strlen($text);
@@ -2040,8 +2563,7 @@ abstract class Base
if ($length % $this->block_size == 0) {
return $text;
} else {
- user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})");
- $this->padding = true;
+ throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding.");
}
}
@@ -2056,12 +2578,12 @@ abstract class Base
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
*
- * @see self::_pad()
+ * @see self::pad()
* @param string $text
- * @access private
+ * @throws \LengthException if the ciphertext's length is not a multiple of the block size
* @return string
*/
- function _unpad($text)
+ protected function unpad($text)
{
if (!$this->padding) {
return $text;
@@ -2070,114 +2592,13 @@ abstract class Base
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) {
- return false;
+ throw new BadDecryptionException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})");
}
return substr($text, 0, -$length);
}
/**
- * Clears internal buffers
- *
- * Clearing/resetting the internal buffers is done everytime
- * after disableContinuousBuffer() or on cipher $engine (re)init
- * ie after setKey() or setIV()
- *
- * @access public
- * @internal Could, but not must, extend by the child Crypt_* class
- */
- function _clearBuffers()
- {
- $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
-
- // mcrypt's handling of invalid's $iv:
- // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size);
- $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
-
- if (!$this->skip_key_adjustment) {
- $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
- }
- }
-
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @access private
- * @return string
- */
- function _string_shift(&$string, $index = 1)
- {
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
- }
-
- /**
- * String Pop
- *
- * Inspired by array_pop
- *
- * @param string $string
- * @param int $index
- * @access private
- * @return string
- */
- function _string_pop(&$string, $index = 1)
- {
- $substr = substr($string, -$index);
- $string = substr($string, 0, -$index);
- return $substr;
- }
-
- /**
- * Increment the current string
- *
- * @see self::decrypt()
- * @see self::encrypt()
- * @param string $var
- * @access private
- */
- function _increment_str(&$var)
- {
- if (function_exists('sodium_increment')) {
- $var = strrev($var);
- sodium_increment($var);
- $var = strrev($var);
- return;
- }
-
- for ($i = 4; $i <= strlen($var); $i+= 4) {
- $temp = substr($var, -$i, 4);
- switch ($temp) {
- case "\xFF\xFF\xFF\xFF":
- $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
- break;
- case "\x7F\xFF\xFF\xFF":
- $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
- return;
- default:
- $temp = unpack('Nnum', $temp);
- $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
- return;
- }
- }
-
- $remainder = strlen($var) % 4;
-
- if ($remainder == 0) {
- return;
- }
-
- $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
- $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
- $var = substr_replace($var, $temp, 0, $remainder);
- }
-
- /**
* Setup the performance-optimized function for de/encrypt()
*
* Stores the created (or existing) callback function-name
@@ -2187,16 +2608,14 @@ abstract class Base
*
* _setupInlineCrypt() would be called only if:
*
- * - $engine == self::ENGINE_INTERNAL and
- *
- * - $use_inline_crypt === true
+ * - $this->engine === self::ENGINE_EVAL
*
* - each time on _setup(), after(!) _setupKey()
*
*
* This ensures that _setupInlineCrypt() has always a
* full ready2go initializated internal cipher $engine state
- * where, for example, the keys allready expanded,
+ * where, for example, the keys already expanded,
* keys/block_size calculated and such.
*
* It is, each time if called, the responsibility of _setupInlineCrypt():
@@ -2222,31 +2641,21 @@ abstract class Base
* - short (as good as possible)
*
* Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code.
- * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib\Crypt\* class.
+ * - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib3\Crypt\* class.
* - The following variable names are reserved:
* - $_* (all variable names prefixed with an underscore)
* - $self (object reference to it self. Do not use $this, but $self instead)
* - $in (the content of $in has to en/decrypt by the generated code)
* - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only
*
+ * {@internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()}
*
- * @see self::_setup()
- * @see self::_createInlineCryptFunction()
+ * @see self::setup()
+ * @see self::createInlineCryptFunction()
* @see self::encrypt()
* @see self::decrypt()
- * @access private
- * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()
*/
- function _setupInlineCrypt()
- {
- // If, for any reason, an extending \phpseclib\Crypt\Base() \phpseclib\Crypt\* class
- // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false
- // ie in the class var declaration of $use_inline_crypt in general for the \phpseclib\Crypt\* class,
- // in the constructor at object instance-time
- // or, if it's runtime-specific, at runtime
-
- $this->use_inline_crypt = false;
- }
+ //protected function setupInlineCrypt();
/**
* Creates the performance-optimized function for en/decrypt()
@@ -2338,28 +2747,27 @@ abstract class Base
* +----------------------------------------------------------------------------------------------+
* </code>
*
- * See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for
+ * See also the \phpseclib3\Crypt\*::_setupInlineCrypt()'s for
* productive inline $cipher_code's how they works.
*
* Structure of:
* <code>
- * $cipher_code = array(
+ * $cipher_code = [
* 'init_crypt' => (string) '', // optional
* 'init_encrypt' => (string) '', // optional
* 'init_decrypt' => (string) '', // optional
* 'encrypt_block' => (string) '', // required
* 'decrypt_block' => (string) '' // required
- * );
+ * ];
* </code>
*
- * @see self::_setupInlineCrypt()
+ * @see self::setupInlineCrypt()
* @see self::encrypt()
* @see self::decrypt()
* @param array $cipher_code
- * @access private
* @return string (the name of the created callback function)
*/
- function _createInlineCryptFunction($cipher_code)
+ protected function createInlineCryptFunction($cipher_code)
{
$block_size = $this->block_size;
@@ -2380,9 +2788,9 @@ abstract class Base
$_ciphertext = "";
$_plaintext_len = strlen($_text);
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
- $in = substr($_text, $_i, '.$block_size.');
- '.$encrypt_block.'
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
+ $in = substr($_text, $_i, ' . $block_size . ');
+ ' . $encrypt_block . '
$_ciphertext.= $in;
}
@@ -2391,49 +2799,49 @@ abstract class Base
$decrypt = $init_decrypt . '
$_plaintext = "";
- $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
+ $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0));
$_ciphertext_len = strlen($_text);
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
- $in = substr($_text, $_i, '.$block_size.');
- '.$decrypt_block.'
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
+ $in = substr($_text, $_i, ' . $block_size . ');
+ ' . $decrypt_block . '
$_plaintext.= $in;
}
- return $self->_unpad($_plaintext);
+ return $this->unpad($_plaintext);
';
break;
case self::MODE_CTR:
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
- $_xor = $self->encryptIV;
- $_buffer = &$self->enbuffer;
+ $_xor = $this->encryptIV;
+ $_buffer = &$this->enbuffer;
if (strlen($_buffer["ciphertext"])) {
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
if (strlen($_block) > strlen($_buffer["ciphertext"])) {
$in = $_xor;
- '.$encrypt_block.'
- $self->_increment_str($_xor);
+ ' . $encrypt_block . '
+ \phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
- $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
+ $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . ');
$_ciphertext.= $_block ^ $_key;
}
} else {
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
$in = $_xor;
- '.$encrypt_block.'
- $self->_increment_str($_xor);
+ ' . $encrypt_block . '
+ \phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_key = $in;
$_ciphertext.= $_block ^ $_key;
}
}
- if ($self->continuousBuffer) {
- $self->encryptIV = $_xor;
- if ($_start = $_plaintext_len % '.$block_size.') {
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $_xor;
+ if ($_start = $_plaintext_len % ' . $block_size . ') {
$_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
}
}
@@ -2444,34 +2852,34 @@ abstract class Base
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
- $_xor = $self->decryptIV;
- $_buffer = &$self->debuffer;
+ $_xor = $this->decryptIV;
+ $_buffer = &$this->debuffer;
if (strlen($_buffer["ciphertext"])) {
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
if (strlen($_block) > strlen($_buffer["ciphertext"])) {
$in = $_xor;
- '.$encrypt_block.'
- $self->_increment_str($_xor);
+ ' . $encrypt_block . '
+ \phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_buffer["ciphertext"].= $in;
}
- $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
+ $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["ciphertext"], ' . $block_size . ');
$_plaintext.= $_block ^ $_key;
}
} else {
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
$in = $_xor;
- '.$encrypt_block.'
- $self->_increment_str($_xor);
+ ' . $encrypt_block . '
+ \phpseclib3\Common\Functions\Strings::increment_str($_xor);
$_key = $in;
$_plaintext.= $_block ^ $_key;
}
}
- if ($self->continuousBuffer) {
- $self->decryptIV = $_xor;
- if ($_start = $_ciphertext_len % '.$block_size.') {
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $_xor;
+ if ($_start = $_ciphertext_len % ' . $block_size . ') {
$_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
}
}
@@ -2482,20 +2890,20 @@ abstract class Base
case self::MODE_CFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
- $_buffer = &$self->enbuffer;
+ $_buffer = &$this->enbuffer;
- if ($self->continuousBuffer) {
- $_iv = &$self->encryptIV;
+ if ($this->continuousBuffer) {
+ $_iv = &$this->encryptIV;
$_pos = &$_buffer["pos"];
} else {
- $_iv = $self->encryptIV;
+ $_iv = $this->encryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
- $_max = '.$block_size.' - $_pos;
+ $_max = ' . $block_size . ' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
@@ -2508,17 +2916,17 @@ abstract class Base
$_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i);
}
- while ($_len >= '.$block_size.') {
+ while ($_len >= ' . $block_size . ') {
$in = $_iv;
- '.$encrypt_block.';
- $_iv = $in ^ substr($_text, $_i, '.$block_size.');
+ ' . $encrypt_block . ';
+ $_iv = $in ^ substr($_text, $_i, ' . $block_size . ');
$_ciphertext.= $_iv;
- $_len-= '.$block_size.';
- $_i+= '.$block_size.';
+ $_len-= ' . $block_size . ';
+ $_i+= ' . $block_size . ';
}
if ($_len) {
$in = $_iv;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_iv = $in;
$_block = $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, $_block, 0, $_len);
@@ -2530,20 +2938,20 @@ abstract class Base
$decrypt = $init_encrypt . '
$_plaintext = "";
- $_buffer = &$self->debuffer;
+ $_buffer = &$this->debuffer;
- if ($self->continuousBuffer) {
- $_iv = &$self->decryptIV;
+ if ($this->continuousBuffer) {
+ $_iv = &$this->decryptIV;
$_pos = &$_buffer["pos"];
} else {
- $_iv = $self->decryptIV;
+ $_iv = $this->decryptIV;
$_pos = 0;
}
$_len = strlen($_text);
$_i = 0;
if ($_pos) {
$_orig_pos = $_pos;
- $_max = '.$block_size.' - $_pos;
+ $_max = ' . $block_size . ' - $_pos;
if ($_len >= $_max) {
$_i = $_max;
$_len-= $_max;
@@ -2556,19 +2964,19 @@ abstract class Base
$_plaintext = substr($_iv, $_orig_pos) ^ $_text;
$_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i);
}
- while ($_len >= '.$block_size.') {
+ while ($_len >= ' . $block_size . ') {
$in = $_iv;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_iv = $in;
- $cb = substr($_text, $_i, '.$block_size.');
+ $cb = substr($_text, $_i, ' . $block_size . ');
$_plaintext.= $_iv ^ $cb;
$_iv = $cb;
- $_len-= '.$block_size.';
- $_i+= '.$block_size.';
+ $_len-= ' . $block_size . ';
+ $_i+= ' . $block_size . ';
}
if ($_len) {
$in = $_iv;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_iv = $in;
$_plaintext.= $_iv ^ substr($_text, $_i);
$_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len);
@@ -2582,20 +2990,20 @@ abstract class Base
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
- $_iv = $self->encryptIV;
+ $_iv = $this->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
- '.$encrypt_block.'
- $_ciphertext.= ($_c = $_text[$_i] ^ $in);
+ ' . $encrypt_block . '
+ $_ciphertext .= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
- if ($self->continuousBuffer) {
- if ($_len >= '.$block_size.') {
- $self->encryptIV = substr($_ciphertext, -'.$block_size.');
+ if ($this->continuousBuffer) {
+ if ($_len >= ' . $block_size . ') {
+ $this->encryptIV = substr($_ciphertext, -' . $block_size . ');
} else {
- $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len);
+ $this->encryptIV = substr($this->encryptIV, $_len - ' . $block_size . ') . substr($_ciphertext, -$_len);
}
}
@@ -2604,20 +3012,20 @@ abstract class Base
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
- $_iv = $self->decryptIV;
+ $_iv = $this->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
- '.$encrypt_block.'
- $_plaintext.= $_text[$_i] ^ $in;
+ ' . $encrypt_block . '
+ $_plaintext .= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
- if ($self->continuousBuffer) {
- if ($_len >= '.$block_size.') {
- $self->decryptIV = substr($_text, -'.$block_size.');
+ if ($this->continuousBuffer) {
+ if ($_len >= ' . $block_size . ') {
+ $this->decryptIV = substr($_text, -' . $block_size . ');
} else {
- $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len);
+ $this->decryptIV = substr($this->decryptIV, $_len - ' . $block_size . ') . substr($_text, -$_len);
}
}
@@ -2628,17 +3036,17 @@ abstract class Base
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_len = strlen($_text);
- $_iv = $self->encryptIV;
+ $_iv = $this->encryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_ciphertext.= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $in[0];
}
- if ($self->continuousBuffer) {
- $self->encryptIV = $_iv;
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $_iv;
}
return $_ciphertext;
@@ -2646,17 +3054,17 @@ abstract class Base
$decrypt = $init_encrypt . '
$_plaintext = "";
$_len = strlen($_text);
- $_iv = $self->decryptIV;
+ $_iv = $this->decryptIV;
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_plaintext.= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $in[0];
}
- if ($self->continuousBuffer) {
- $self->decryptIV = $_iv;
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $_iv;
}
return $_plaintext;
@@ -2666,33 +3074,33 @@ abstract class Base
$encrypt = $init_encrypt . '
$_ciphertext = "";
$_plaintext_len = strlen($_text);
- $_xor = $self->encryptIV;
- $_buffer = &$self->enbuffer;
+ $_xor = $this->encryptIV;
+ $_buffer = &$this->enbuffer;
if (strlen($_buffer["xor"])) {
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
if (strlen($_block) > strlen($_buffer["xor"])) {
$in = $_xor;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_xor = $in;
$_buffer["xor"].= $_xor;
}
- $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
+ $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . ');
$_ciphertext.= $_block ^ $_key;
}
} else {
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
$in = $_xor;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_xor = $in;
- $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
+ $_ciphertext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor;
}
$_key = $_xor;
}
- if ($self->continuousBuffer) {
- $self->encryptIV = $_xor;
- if ($_start = $_plaintext_len % '.$block_size.') {
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $_xor;
+ if ($_start = $_plaintext_len % ' . $block_size . ') {
$_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
}
}
@@ -2702,33 +3110,33 @@ abstract class Base
$decrypt = $init_encrypt . '
$_plaintext = "";
$_ciphertext_len = strlen($_text);
- $_xor = $self->decryptIV;
- $_buffer = &$self->debuffer;
+ $_xor = $this->decryptIV;
+ $_buffer = &$this->debuffer;
if (strlen($_buffer["xor"])) {
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
- $_block = substr($_text, $_i, '.$block_size.');
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
+ $_block = substr($_text, $_i, ' . $block_size . ');
if (strlen($_block) > strlen($_buffer["xor"])) {
$in = $_xor;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_xor = $in;
$_buffer["xor"].= $_xor;
}
- $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
+ $_key = \phpseclib3\Common\Functions\Strings::shift($_buffer["xor"], ' . $block_size . ');
$_plaintext.= $_block ^ $_key;
}
} else {
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
$in = $_xor;
- '.$encrypt_block.'
+ ' . $encrypt_block . '
$_xor = $in;
- $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
+ $_plaintext.= substr($_text, $_i, ' . $block_size . ') ^ $_xor;
}
$_key = $_xor;
}
- if ($self->continuousBuffer) {
- $self->decryptIV = $_xor;
- if ($_start = $_ciphertext_len % '.$block_size.') {
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $_xor;
+ if ($_start = $_ciphertext_len % ' . $block_size . ') {
$_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
}
}
@@ -2738,12 +3146,12 @@ abstract class Base
case self::MODE_STREAM:
$encrypt = $init_encrypt . '
$_ciphertext = "";
- '.$encrypt_block.'
+ ' . $encrypt_block . '
return $_ciphertext;
';
$decrypt = $init_decrypt . '
$_plaintext = "";
- '.$decrypt_block.'
+ ' . $decrypt_block . '
return $_plaintext;
';
break;
@@ -2753,16 +3161,16 @@ abstract class Base
$_ciphertext = "";
$_plaintext_len = strlen($_text);
- $in = $self->encryptIV;
+ $in = $this->encryptIV;
- for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
- $in = substr($_text, $_i, '.$block_size.') ^ $in;
- '.$encrypt_block.'
+ for ($_i = 0; $_i < $_plaintext_len; $_i+= ' . $block_size . ') {
+ $in = substr($_text, $_i, ' . $block_size . ') ^ $in;
+ ' . $encrypt_block . '
$_ciphertext.= $in;
}
- if ($self->continuousBuffer) {
- $self->encryptIV = $in;
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $in;
}
return $_ciphertext;
@@ -2770,138 +3178,220 @@ abstract class Base
$decrypt = $init_decrypt . '
$_plaintext = "";
- $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
+ $_text = str_pad($_text, strlen($_text) + (' . $block_size . ' - strlen($_text) % ' . $block_size . ') % ' . $block_size . ', chr(0));
$_ciphertext_len = strlen($_text);
- $_iv = $self->decryptIV;
+ $_iv = $this->decryptIV;
- for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
- $in = $_block = substr($_text, $_i, '.$block_size.');
- '.$decrypt_block.'
+ for ($_i = 0; $_i < $_ciphertext_len; $_i+= ' . $block_size . ') {
+ $in = $_block = substr($_text, $_i, ' . $block_size . ');
+ ' . $decrypt_block . '
$_plaintext.= $in ^ $_iv;
$_iv = $_block;
}
- if ($self->continuousBuffer) {
- $self->decryptIV = $_iv;
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $_iv;
}
- return $self->_unpad($_plaintext);
+ return $this->unpad($_plaintext);
';
break;
}
- // Create the $inline function and return its name as string. Ready to run!
- eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
- return $func;
+ // Before discrediting this, please read the following:
+ // @see https://github.com/phpseclib/phpseclib/issues/1293
+ // @see https://github.com/phpseclib/phpseclib/pull/1143
+ eval('$func = function ($_action, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }};');
+
+ return \Closure::bind($func, $this, static::class);
}
/**
- * Holds the lambda_functions table (classwide)
- *
- * Each name of the lambda function, created from
- * _setupInlineCrypt() && _createInlineCryptFunction()
- * is stored, classwide (!), here for reusing.
+ * Convert float to int
*
- * The string-based index of $function is a classwide
- * unique value representing, at least, the $mode of
- * operation (or more... depends of the optimizing level)
- * for which $mode the lambda function was created.
+ * On ARM CPUs converting floats to ints doesn't always work
*
- * @access private
- * @return array &$functions
+ * @param string $x
+ * @return int
*/
- function &_getLambdaFunctions()
+ protected static function safe_intval($x)
{
- static $functions = array();
- return $functions;
+ if (is_int($x)) {
+ return $x;
+ }
+
+ if (self::$use_reg_intval) {
+ return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100 ? intval($x) : $x;
+ }
+
+ return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
+ ((fmod(floor($x / 0x80000000), 2) & 1) << 31);
}
/**
- * Generates a digest from $bytes
+ * eval()'able string for in-line float to int
*
- * @see self::_setupInlineCrypt()
- * @access private
- * @param string $bytes
* @return string
*/
- function _hashInlineCryptFunction($bytes)
+ protected static function safe_intval_inline()
{
- if (!isset(self::$WHIRLPOOL_AVAILABLE)) {
- self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos());
+ if (self::$use_reg_intval) {
+ return PHP_INT_SIZE == 4 && PHP_VERSION_ID >= 80100 ? 'intval(%s)' : '%s';
}
- $result = '';
- $hash = $bytes;
+ $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
+ return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
+ }
- switch (true) {
- case self::$WHIRLPOOL_AVAILABLE:
- foreach (str_split($bytes, 64) as $t) {
- $hash = hash('whirlpool', $hash, true);
- $result .= $t ^ $hash;
- }
- return $result . hash('whirlpool', $hash, true);
- default:
- $len = strlen($bytes);
- for ($i = 0; $i < $len; $i+=20) {
- $t = substr($bytes, $i, 20);
- $hash = pack('H*', sha1($hash));
- $result .= $t ^ $hash;
- }
- return $result . pack('H*', sha1($hash));
+ /**
+ * Sets up GCM parameters
+ *
+ * See steps 1-2 of https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=23
+ * for more info
+ *
+ */
+ private function setupGCM()
+ {
+ // don't keep on re-calculating $this->h
+ if (!$this->h || $this->hKey != $this->key) {
+ $cipher = new static('ecb');
+ $cipher->setKey($this->key);
+ $cipher->disablePadding();
+
+ $this->h = self::$gcmField->newInteger(
+ Strings::switchEndianness($cipher->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"))
+ );
+ $this->hKey = $this->key;
+ }
+
+ if (strlen($this->nonce) == 12) {
+ $this->iv = $this->nonce . "\0\0\0\1";
+ } else {
+ $this->iv = $this->ghash(
+ self::nullPad128($this->nonce) . str_repeat("\0", 8) . self::len64($this->nonce)
+ );
}
}
/**
- * Convert float to int
+ * Performs GHASH operation
*
- * On ARM CPUs converting floats to ints doesn't always work
+ * See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=20
+ * for more info
*
- * @access private
+ * @see self::decrypt()
+ * @see self::encrypt()
* @param string $x
- * @return int
+ * @return string
*/
- function safe_intval($x)
+ private function ghash($x)
{
- if (is_int($x)) {
- return $x;
+ $h = $this->h;
+ $y = ["\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"];
+ $x = str_split($x, 16);
+ $n = 0;
+ // the switchEndianness calls are necessary because the multiplication algorithm in BinaryField/Integer
+ // interprets strings as polynomials in big endian order whereas in GCM they're interpreted in little
+ // endian order per https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf#page=19.
+ // big endian order is what binary field elliptic curves use per http://www.secg.org/sec1-v2.pdf#page=18.
+
+ // we could switchEndianness here instead of in the while loop but doing so in the while loop seems like it
+ // might be slightly more performant
+ //$x = Strings::switchEndianness($x);
+ foreach ($x as $xn) {
+ $xn = Strings::switchEndianness($xn);
+ $t = $y[$n] ^ $xn;
+ $temp = self::$gcmField->newInteger($t);
+ $y[++$n] = $temp->multiply($h)->toBytes();
+ $y[$n] = substr($y[$n], 1);
}
- return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
- ((fmod(floor($x / 0x80000000), 2) & 1) << 31);
+ $y[$n] = Strings::switchEndianness($y[$n]);
+ return $y[$n];
}
/**
- * eval()'able string for in-line float to int
+ * Returns the bit length of a string in a packed format
*
- * @access private
+ * @see self::decrypt()
+ * @see self::encrypt()
+ * @see self::setupGCM()
+ * @param string $str
* @return string
*/
- function safe_intval_inline()
+ private static function len64($str)
{
- if (CRYPT_BASE_USE_REG_INTVAL) {
- return PHP_INT_SIZE == 4 ? 'intval(%s)' : '%s';
- }
+ return "\0\0\0\0" . pack('N', 8 * strlen($str));
+ }
- $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
- return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
+ /**
+ * NULL pads a string to be a multiple of 128
+ *
+ * @see self::decrypt()
+ * @see self::encrypt()
+ * @see self::setupGCM()
+ * @param string $str
+ * @return string
+ */
+ protected static function nullPad128($str)
+ {
+ $len = strlen($str);
+ return $str . str_repeat("\0", 16 * ceil($len / 16) - $len);
+ }
+
+ /**
+ * Calculates Poly1305 MAC
+ *
+ * On my system ChaCha20, with libsodium, takes 0.5s. With this custom Poly1305 implementation
+ * it takes 1.2s.
+ *
+ * @see self::decrypt()
+ * @see self::encrypt()
+ * @param string $text
+ * @return string
+ */
+ protected function poly1305($text)
+ {
+ $s = $this->poly1305Key; // strlen($this->poly1305Key) == 32
+ $r = Strings::shift($s, 16);
+ $r = strrev($r);
+ $r &= "\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xfc\x0f\xff\xff\xff";
+ $s = strrev($s);
+
+ $r = self::$poly1305Field->newInteger(new BigInteger($r, 256));
+ $s = self::$poly1305Field->newInteger(new BigInteger($s, 256));
+ $a = self::$poly1305Field->newInteger(new BigInteger());
+
+ $blocks = str_split($text, 16);
+ foreach ($blocks as $block) {
+ $n = strrev($block . chr(1));
+ $n = self::$poly1305Field->newInteger(new BigInteger($n, 256));
+ $a = $a->add($n);
+ $a = $a->multiply($r);
+ }
+ $r = $a->toBigInteger()->add($s->toBigInteger());
+ $mask = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
+ return strrev($r->toBytes()) & $mask;
}
/**
- * Dummy error handler to suppress mcrypt errors
+ * Return the mode
*
- * @access private
+ * You can do $obj instanceof AES or whatever to get the cipher but you can't do that to get the mode
+ *
+ * @return string
*/
- function do_nothing()
+ public function getMode()
{
+ return array_flip(self::MODE_MAP)[$this->mode];
}
/**
* Is the continuous buffer enabled?
*
- * @access public
* @return boolean
*/
- function continuousBufferEnabled()
+ public function continuousBufferEnabled()
{
return $this->continuousBuffer;
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php
new file mode 100644
index 000000000..9ca8926d3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/Fingerprint.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Fingerprint Trait for Public Keys
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Traits;
+
+use phpseclib3\Crypt\Hash;
+
+/**
+ * Fingerprint Trait for Private Keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+trait Fingerprint
+{
+ /**
+ * Returns the public key's fingerprint
+ *
+ * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
+ * no public key currently loaded, false is returned.
+ * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
+ *
+ * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
+ * for invalid values.
+ * @return mixed
+ */
+ public function getFingerprint($algorithm = 'md5')
+ {
+ $type = self::validatePlugin('Keys', 'OpenSSH', 'savePublicKey');
+ if ($type === false) {
+ return false;
+ }
+ $key = $this->toString('OpenSSH', ['binary' => true]);
+ if ($key === false) {
+ return false;
+ }
+ switch ($algorithm) {
+ case 'sha256':
+ $hash = new Hash('sha256');
+ $base = base64_encode($hash->hash($key));
+ return substr($base, 0, strlen($base) - 1);
+ case 'md5':
+ return substr(chunk_split(md5($key), 2, ':'), 0, -1);
+ default:
+ return false;
+ }
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php
new file mode 100644
index 000000000..0ac274e8d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Traits/PasswordProtected.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Password Protected Trait for Private Keys
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\Common\Traits;
+
+/**
+ * Password Protected Trait for Private Keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+trait PasswordProtected
+{
+ /**
+ * Password
+ *
+ * @var string|bool
+ */
+ private $password = false;
+
+ /**
+ * Sets the password
+ *
+ * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
+ * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
+ *
+ * @see self::createKey()
+ * @see self::load()
+ * @param string|bool $password
+ */
+ public function withPassword($password = false)
+ {
+ $new = clone $this;
+ $new->password = $password;
+ return $new;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php
index 26bd385f5..93d7ad2ed 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php
@@ -18,7 +18,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $des = new \phpseclib\Crypt\DES();
+ * $des = new \phpseclib3\Crypt\DES('ctr');
*
* $des->setKey('abcdefgh');
*
@@ -32,120 +32,119 @@
* ?>
* </code>
*
- * @category Crypt
- * @package DES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\BlockCipher;
+use phpseclib3\Exception\BadModeException;
/**
* Pure-PHP implementation of DES.
*
- * @package DES
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class DES extends Base
+class DES extends BlockCipher
{
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\DES::_setupKey()
- * @see \phpseclib\Crypt\DES::_processBlock()
- */
/**
* Contains $keys[self::ENCRYPT]
+ *
+ * @see \phpseclib3\Crypt\DES::setupKey()
+ * @see \phpseclib3\Crypt\DES::processBlock()
*/
const ENCRYPT = 0;
/**
* Contains $keys[self::DECRYPT]
+ *
+ * @see \phpseclib3\Crypt\DES::setupKey()
+ * @see \phpseclib3\Crypt\DES::processBlock()
*/
const DECRYPT = 1;
- /**#@-*/
/**
* Block Length of the cipher
*
- * @see \phpseclib\Crypt\Base::block_size
+ * @see Common\SymmetricKey::block_size
* @var int
- * @access private
*/
- var $block_size = 8;
+ protected $block_size = 8;
/**
* Key Length (in bytes)
*
- * @see \phpseclib\Crypt\Base::setKeyLength()
+ * @see Common\SymmetricKey::setKeyLength()
* @var int
- * @access private
*/
- var $key_length = 8;
+ protected $key_length = 8;
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'des';
+ protected $cipher_name_mcrypt = 'des';
/**
* The OpenSSL names of the cipher / modes
*
- * @see \phpseclib\Crypt\Base::openssl_mode_names
+ * @see Common\SymmetricKey::openssl_mode_names
* @var array
- * @access private
*/
- var $openssl_mode_names = array(
+ protected $openssl_mode_names = [
self::MODE_ECB => 'des-ecb',
self::MODE_CBC => 'des-cbc',
self::MODE_CFB => 'des-cfb',
self::MODE_OFB => 'des-ofb'
// self::MODE_CTR is undefined for DES
- );
+ ];
/**
* Optimizing value while CFB-encrypting
*
- * @see \phpseclib\Crypt\Base::cfb_init_len
+ * @see Common\SymmetricKey::cfb_init_len
* @var int
- * @access private
*/
- var $cfb_init_len = 500;
+ protected $cfb_init_len = 500;
/**
* Switch for DES/3DES encryption
*
* Used only if $engine == self::ENGINE_INTERNAL
*
- * @see self::_setupKey()
- * @see self::_processBlock()
+ * @see self::setupKey()
+ * @see self::processBlock()
* @var int
- * @access private
*/
- var $des_rounds = 1;
+ protected $des_rounds = 1;
/**
* max possible size of $key
*
* @see self::setKey()
* @var string
- * @access private
*/
- var $key_length_max = 8;
+ protected $key_length_max = 8;
/**
* The Key Schedule
*
- * @see self::_setupKey()
+ * @see self::setupKey()
* @var array
- * @access private
*/
- var $keys;
+ private $keys;
+
+ /**
+ * Key Cache "key"
+ *
+ * @see self::setupKey()
+ * @var array
+ */
+ private $kl;
/**
* Shuffle table.
@@ -154,12 +153,11 @@ class DES extends Base
* with each byte containing all bits in the same state as the
* corresponding bit in the index value.
*
- * @see self::_processBlock()
- * @see self::_setupKey()
+ * @see self::processBlock()
+ * @see self::setupKey()
* @var array
- * @access private
*/
- var $shuffle = array(
+ protected static $shuffle = [
"\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF",
"\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF",
"\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF",
@@ -288,7 +286,7 @@ class DES extends Base
"\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF",
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
- );
+ ];
/**
* IP mapping helper table.
@@ -296,9 +294,8 @@ class DES extends Base
* Indexing this table with each source byte performs the initial bit permutation.
*
* @var array
- * @access private
*/
- var $ipmap = array(
+ protected static $ipmap = [
0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31,
0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33,
0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71,
@@ -331,16 +328,15 @@ class DES extends Base
0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF,
0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD,
0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF
- );
+ ];
/**
* Inverse IP mapping helper table.
* Indexing this table with a byte value reverses the bit order.
*
* @var array
- * @access private
*/
- var $invipmap = array(
+ protected static $invipmap = [
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
@@ -373,7 +369,7 @@ class DES extends Base
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
- );
+ ];
/**
* Pre-permuted S-box1
@@ -382,9 +378,8 @@ class DES extends Base
* P table: concatenation can then be replaced by exclusive ORs.
*
* @var array
- * @access private
*/
- var $sbox1 = array(
+ protected static $sbox1 = [
0x00808200, 0x00000000, 0x00008000, 0x00808202,
0x00808002, 0x00008202, 0x00000002, 0x00008000,
0x00000200, 0x00808200, 0x00808202, 0x00000200,
@@ -401,15 +396,14 @@ class DES extends Base
0x00800002, 0x00000202, 0x00008202, 0x00808200,
0x00000202, 0x00800200, 0x00800200, 0x00000000,
0x00008002, 0x00008200, 0x00000000, 0x00808002
- );
+ ];
/**
* Pre-permuted S-box2
*
* @var array
- * @access private
*/
- var $sbox2 = array(
+ protected static $sbox2 = [
0x40084010, 0x40004000, 0x00004000, 0x00084010,
0x00080000, 0x00000010, 0x40080010, 0x40004010,
0x40000010, 0x40084010, 0x40084000, 0x40000000,
@@ -426,15 +420,14 @@ class DES extends Base
0x00080010, 0x40004010, 0x40000010, 0x00080010,
0x00084000, 0x00000000, 0x40004000, 0x00004010,
0x40000000, 0x40080010, 0x40084010, 0x00084000
- );
+ ];
/**
* Pre-permuted S-box3
*
* @var array
- * @access private
*/
- var $sbox3 = array(
+ protected static $sbox3 = [
0x00000104, 0x04010100, 0x00000000, 0x04010004,
0x04000100, 0x00000000, 0x00010104, 0x04000100,
0x00010004, 0x04000004, 0x04000004, 0x00010000,
@@ -451,15 +444,14 @@ class DES extends Base
0x00000004, 0x00010104, 0x00010100, 0x04000004,
0x04010000, 0x04000104, 0x00000104, 0x04010000,
0x00010104, 0x00000004, 0x04010004, 0x00010100
- );
+ ];
/**
* Pre-permuted S-box4
*
* @var array
- * @access private
*/
- var $sbox4 = array(
+ protected static $sbox4 = [
0x80401000, 0x80001040, 0x80001040, 0x00000040,
0x00401040, 0x80400040, 0x80400000, 0x80001000,
0x00000000, 0x00401000, 0x00401000, 0x80401040,
@@ -476,15 +468,14 @@ class DES extends Base
0x80400000, 0x80001000, 0x00401040, 0x80400040,
0x80001000, 0x00001040, 0x00400000, 0x80401000,
0x00000040, 0x00400000, 0x00001000, 0x00401040
- );
+ ];
/**
* Pre-permuted S-box5
*
* @var array
- * @access private
*/
- var $sbox5 = array(
+ protected static $sbox5 = [
0x00000080, 0x01040080, 0x01040000, 0x21000080,
0x00040000, 0x00000080, 0x20000000, 0x01040000,
0x20040080, 0x00040000, 0x01000080, 0x20040080,
@@ -501,15 +492,14 @@ class DES extends Base
0x01040000, 0x00000000, 0x20040000, 0x21000000,
0x00040080, 0x01000080, 0x20000080, 0x00040000,
0x00000000, 0x20040000, 0x01040080, 0x20000080
- );
+ ];
/**
* Pre-permuted S-box6
*
* @var array
- * @access private
*/
- var $sbox6 = array(
+ protected static $sbox6 = [
0x10000008, 0x10200000, 0x00002000, 0x10202008,
0x10200000, 0x00000008, 0x10202008, 0x00200000,
0x10002000, 0x00202008, 0x00200000, 0x10000008,
@@ -526,15 +516,14 @@ class DES extends Base
0x00000008, 0x00002000, 0x10200000, 0x00202008,
0x00002000, 0x00200008, 0x10002008, 0x00000000,
0x10202000, 0x10000000, 0x00200008, 0x10002008
- );
+ ];
/**
* Pre-permuted S-box7
*
* @var array
- * @access private
*/
- var $sbox7 = array(
+ protected static $sbox7 = [
0x00100000, 0x02100001, 0x02000401, 0x00000000,
0x00000400, 0x02000401, 0x00100401, 0x02100400,
0x02100401, 0x00100000, 0x00000000, 0x02000001,
@@ -551,15 +540,14 @@ class DES extends Base
0x00100400, 0x00000000, 0x00000001, 0x02100401,
0x00000000, 0x00100401, 0x02100000, 0x00000400,
0x02000001, 0x02000400, 0x00000400, 0x00100001
- );
+ ];
/**
* Pre-permuted S-box8
*
* @var array
- * @access private
*/
- var $sbox8 = array(
+ protected static $sbox8 = [
0x08000820, 0x00000800, 0x00020000, 0x08020820,
0x08000000, 0x08000820, 0x00000020, 0x08000000,
0x00020020, 0x08020000, 0x08020820, 0x00020800,
@@ -576,19 +564,33 @@ class DES extends Base
0x08020000, 0x08000800, 0x08000820, 0x00000000,
0x08020820, 0x00020800, 0x00020800, 0x00000820,
0x00000820, 0x00020020, 0x08000000, 0x08020800
- );
+ ];
+
+ /**
+ * Default Constructor.
+ *
+ * @param string $mode
+ * @throws BadModeException if an invalid / unsupported mode is provided
+ */
+ public function __construct($mode)
+ {
+ parent::__construct($mode);
+
+ if ($this->mode == self::MODE_STREAM) {
+ throw new BadModeException('Block ciphers cannot be ran in stream mode');
+ }
+ }
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::isValidEngine()
+ * @see Common\SymmetricKey::isValidEngine()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
if ($this->key_length_max == 8) {
if ($engine == self::ENGINE_OPENSSL) {
@@ -599,34 +601,27 @@ class DES extends Base
return false;
}
$this->cipher_name_openssl_ecb = 'des-ecb';
- $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode();
+ $this->cipher_name_openssl = 'des-' . $this->openssl_translate_mode();
}
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
* Sets the key.
*
- * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
- * only use the first eight, if $key has more then eight characters in it, and pad $key with the
- * null byte if it is less then eight characters long.
+ * Keys must be 64-bits long or 8 bytes long.
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
- * If the key is not explicitly set, it'll be assumed to be all zero's.
- *
- * @see \phpseclib\Crypt\Base::setKey()
- * @access public
+ * @see Common\SymmetricKey::setKey()
* @param string $key
*/
- function setKey($key)
+ public function setKey($key)
{
- // We check/cut here only up to max length of the key.
- // Key padding to the proper length will be done in _setupKey()
- if (strlen($key) > $this->key_length_max) {
- $key = substr($key, 0, $this->key_length_max);
+ if (!($this instanceof TripleDES) && strlen($key) != 8) {
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported');
}
// Sets the key
@@ -636,31 +631,29 @@ class DES extends Base
/**
* Encrypts a block
*
- * @see \phpseclib\Crypt\Base::_encryptBlock()
- * @see \phpseclib\Crypt\Base::encrypt()
+ * @see Common\SymmetricKey::encryptBlock()
+ * @see Common\SymmetricKey::encrypt()
* @see self::encrypt()
- * @access private
* @param string $in
* @return string
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
- return $this->_processBlock($in, self::ENCRYPT);
+ return $this->processBlock($in, self::ENCRYPT);
}
/**
* Decrypts a block
*
- * @see \phpseclib\Crypt\Base::_decryptBlock()
- * @see \phpseclib\Crypt\Base::decrypt()
+ * @see Common\SymmetricKey::decryptBlock()
+ * @see Common\SymmetricKey::decrypt()
* @see self::decrypt()
- * @access private
* @param string $in
* @return string
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
- return $this->_processBlock($in, self::DECRYPT);
+ return $this->processBlock($in, self::DECRYPT);
}
/**
@@ -670,29 +663,28 @@ class DES extends Base
* {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
* idea of what this function does.
*
- * @see self::_encryptBlock()
- * @see self::_decryptBlock()
- * @access private
+ * @see self::encryptBlock()
+ * @see self::decryptBlock()
* @param string $block
* @param int $mode
* @return string
*/
- function _processBlock($block, $mode)
+ private function processBlock($block, $mode)
{
static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
- $sbox1 = array_map("intval", $this->sbox1);
- $sbox2 = array_map("intval", $this->sbox2);
- $sbox3 = array_map("intval", $this->sbox3);
- $sbox4 = array_map("intval", $this->sbox4);
- $sbox5 = array_map("intval", $this->sbox5);
- $sbox6 = array_map("intval", $this->sbox6);
- $sbox7 = array_map("intval", $this->sbox7);
- $sbox8 = array_map("intval", $this->sbox8);
+ $sbox1 = array_map('intval', self::$sbox1);
+ $sbox2 = array_map('intval', self::$sbox2);
+ $sbox3 = array_map('intval', self::$sbox3);
+ $sbox4 = array_map('intval', self::$sbox4);
+ $sbox5 = array_map('intval', self::$sbox5);
+ $sbox6 = array_map('intval', self::$sbox6);
+ $sbox7 = array_map('intval', self::$sbox7);
+ $sbox8 = array_map('intval', self::$sbox8);
/* Merge $shuffle with $[inv]ipmap */
for ($i = 0; $i < 256; ++$i) {
- $shuffleip[] = $this->shuffle[$this->ipmap[$i]];
- $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]];
+ $shuffleip[] = self::$shuffle[self::$ipmap[$i]];
+ $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]];
}
}
@@ -701,7 +693,7 @@ class DES extends Base
// Do the initial IP permutation.
$t = unpack('Nl/Nr', $block);
- list($l, $r) = array($t['l'], $t['r']);
+ list($l, $r) = [$t['l'], $t['r']];
$block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
@@ -713,7 +705,7 @@ class DES extends Base
// Extract L0 and R0.
$t = unpack('Nl/Nr', $block);
- list($l, $r) = array($t['l'], $t['r']);
+ list($l, $r) = [$t['l'], $t['r']];
for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
// Perform the 16 steps.
@@ -755,22 +747,21 @@ class DES extends Base
/**
* Creates the key schedule
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see Common\SymmetricKey::setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) {
// already expanded
return;
}
- $this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds);
+ $this->kl = ['key' => $this->key, 'des_rounds' => $this->des_rounds];
- static $shifts = array( // number of key bits shifted per round
+ static $shifts = [ // number of key bits shifted per round
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
- );
+ ];
- static $pc1map = array(
+ static $pc1map = [
0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C,
0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E,
0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C,
@@ -803,16 +794,16 @@ class DES extends Base
0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE,
0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC,
0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE
- );
+ ];
// Mapping tables for the PC-2 transformation.
- static $pc2mapc1 = array(
+ static $pc2mapc1 = [
0x00000000, 0x00000400, 0x00200000, 0x00200400,
0x00000001, 0x00000401, 0x00200001, 0x00200401,
0x02000000, 0x02000400, 0x02200000, 0x02200400,
0x02000001, 0x02000401, 0x02200001, 0x02200401
- );
- static $pc2mapc2 = array(
+ ];
+ static $pc2mapc2 = [
0x00000000, 0x00000800, 0x08000000, 0x08000800,
0x00010000, 0x00010800, 0x08010000, 0x08010800,
0x00000000, 0x00000800, 0x08000000, 0x08000800,
@@ -877,8 +868,8 @@ class DES extends Base
0x01050110, 0x01050910, 0x09050110, 0x09050910,
0x01040110, 0x01040910, 0x09040110, 0x09040910,
0x01050110, 0x01050910, 0x09050110, 0x09050910
- );
- static $pc2mapc3 = array(
+ ];
+ static $pc2mapc3 = [
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x00000000, 0x00000004, 0x00001000, 0x00001004,
0x10000000, 0x10000004, 0x10001000, 0x10001004,
@@ -943,8 +934,8 @@ class DES extends Base
0x20080022, 0x20080026, 0x20081022, 0x20081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026,
0x30080022, 0x30080026, 0x30081022, 0x30081026
- );
- static $pc2mapc4 = array(
+ ];
+ static $pc2mapc4 = [
0x00000000, 0x00100000, 0x00000008, 0x00100008,
0x00000200, 0x00100200, 0x00000208, 0x00100208,
0x00000000, 0x00100000, 0x00000008, 0x00100008,
@@ -1009,14 +1000,14 @@ class DES extends Base
0x04022200, 0x04122200, 0x04022208, 0x04122208,
0x04022000, 0x04122000, 0x04022008, 0x04122008,
0x04022200, 0x04122200, 0x04022208, 0x04122208
- );
- static $pc2mapd1 = array(
+ ];
+ static $pc2mapd1 = [
0x00000000, 0x00000001, 0x08000000, 0x08000001,
0x00200000, 0x00200001, 0x08200000, 0x08200001,
0x00000002, 0x00000003, 0x08000002, 0x08000003,
0x00200002, 0x00200003, 0x08200002, 0x08200003
- );
- static $pc2mapd2 = array(
+ ];
+ static $pc2mapd2 = [
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x00000000, 0x00100000, 0x00000800, 0x00100800,
0x04000000, 0x04100000, 0x04000800, 0x04100800,
@@ -1081,8 +1072,8 @@ class DES extends Base
0x00020204, 0x00120204, 0x00020A04, 0x00120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04,
0x04020204, 0x04120204, 0x04020A04, 0x04120A04
- );
- static $pc2mapd3 = array(
+ ];
+ static $pc2mapd3 = [
0x00000000, 0x00010000, 0x02000000, 0x02010000,
0x00000020, 0x00010020, 0x02000020, 0x02010020,
0x00040000, 0x00050000, 0x02040000, 0x02050000,
@@ -1147,8 +1138,8 @@ class DES extends Base
0x20002030, 0x20012030, 0x22002030, 0x22012030,
0x20042010, 0x20052010, 0x22042010, 0x22052010,
0x20042030, 0x20052030, 0x22042030, 0x22052030
- );
- static $pc2mapd4 = array(
+ ];
+ static $pc2mapd4 = [
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000000, 0x00000400, 0x01000000, 0x01000400,
0x00000100, 0x00000500, 0x01000100, 0x01000500,
@@ -1213,33 +1204,33 @@ class DES extends Base
0x10081008, 0x10081408, 0x11081008, 0x11081408,
0x10081108, 0x10081508, 0x11081108, 0x11081508,
0x10081108, 0x10081508, 0x11081108, 0x11081508
- );
+ ];
- $keys = array();
+ $keys = [];
for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) {
// pad the key and remove extra characters as appropriate.
$key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0");
// Perform the PC/1 transformation and compute C and D.
$t = unpack('Nl/Nr', $key);
- list($l, $r) = array($t['l'], $t['r']);
- $key = ($this->shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") |
- ($this->shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") |
- ($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") |
- ($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") |
- ($this->shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") |
- ($this->shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") |
- ($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") |
- ($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00");
+ list($l, $r) = [$t['l'], $t['r']];
+ $key = (self::$shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") |
+ (self::$shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") |
+ (self::$shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") |
+ (self::$shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") |
+ (self::$shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") |
+ (self::$shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") |
+ (self::$shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") |
+ (self::$shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00");
$key = unpack('Nc/Nd', $key);
$c = ( $key['c'] >> 4) & 0x0FFFFFFF;
$d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F);
- $keys[$des_round] = array(
- self::ENCRYPT => array(),
+ $keys[$des_round] = [
+ self::ENCRYPT => [],
self::DECRYPT => array_fill(0, 32, 0)
- );
- for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) {
+ ];
+ for ($i = 0, $ki = 31; $i < 16; ++$i, $ki -= 2) {
$c <<= $shifts[$i];
$c = ($c | ($c >> 28)) & 0x0FFFFFFF;
$d <<= $shifts[$i];
@@ -1265,7 +1256,7 @@ class DES extends Base
switch ($this->des_rounds) {
case 3: // 3DES keys
- $this->keys = array(
+ $this->keys = [
self::ENCRYPT => array_merge(
$keys[0][self::ENCRYPT],
$keys[1][self::DECRYPT],
@@ -1276,174 +1267,126 @@ class DES extends Base
$keys[1][self::ENCRYPT],
$keys[0][self::DECRYPT]
)
- );
+ ];
break;
// case 1: // DES keys
default:
- $this->keys = array(
+ $this->keys = [
self::ENCRYPT => $keys[0][self::ENCRYPT],
self::DECRYPT => $keys[0][self::DECRYPT]
- );
+ ];
}
}
/**
* Setup the performance-optimized function for de/encrypt()
*
- * @see \phpseclib\Crypt\Base::_setupInlineCrypt()
- * @access private
+ * @see Common\SymmetricKey::setupInlineCrypt()
*/
- function _setupInlineCrypt()
+ protected function setupInlineCrypt()
{
- $lambda_functions =& self::_getLambdaFunctions();
-
// Engine configuration for:
// - DES ($des_rounds == 1) or
// - 3DES ($des_rounds == 3)
$des_rounds = $this->des_rounds;
- // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
- // (Currently, for DES, one generated $lambda_function cost on php5.5@32bit ~135kb unfreeable mem and ~230kb on php5.5@64bit)
- // (Currently, for TripleDES, one generated $lambda_function cost on php5.5@32bit ~240kb unfreeable mem and ~340kb on php5.5@64bit)
- // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one
- $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 );
-
- // Generation of a unique hash for our generated code
- $code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
- if ($gen_hi_opt_code) {
- // For hi-optimized code, we create for each combination of
- // $mode, $des_rounds and $this->key its own encrypt/decrypt function.
- // After max 10 hi-optimized functions, we create generic
- // (still very fast.. but not ultra) functions for each $mode/$des_rounds
- // Currently 2 * 5 generic functions will be then max. possible.
- $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
- }
-
- // Is there a re-usable $lambda_functions in there? If not, we have to create it.
- if (!isset($lambda_functions[$code_hash])) {
- // Init code for both, encrypt and decrypt.
- $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
- if (!$sbox1) {
- $sbox1 = array_map("intval", $self->sbox1);
- $sbox2 = array_map("intval", $self->sbox2);
- $sbox3 = array_map("intval", $self->sbox3);
- $sbox4 = array_map("intval", $self->sbox4);
- $sbox5 = array_map("intval", $self->sbox5);
- $sbox6 = array_map("intval", $self->sbox6);
- $sbox7 = array_map("intval", $self->sbox7);
- $sbox8 = array_map("intval", $self->sbox8);'
- /* Merge $shuffle with $[inv]ipmap */ . '
- for ($i = 0; $i < 256; ++$i) {
- $shuffleip[] = $self->shuffle[$self->ipmap[$i]];
- $shuffleinvip[] = $self->shuffle[$self->invipmap[$i]];
- }
+ $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
+ if (!$sbox1) {
+ $sbox1 = array_map("intval", self::$sbox1);
+ $sbox2 = array_map("intval", self::$sbox2);
+ $sbox3 = array_map("intval", self::$sbox3);
+ $sbox4 = array_map("intval", self::$sbox4);
+ $sbox5 = array_map("intval", self::$sbox5);
+ $sbox6 = array_map("intval", self::$sbox6);
+ $sbox7 = array_map("intval", self::$sbox7);
+ $sbox8 = array_map("intval", self::$sbox8);'
+ /* Merge $shuffle with $[inv]ipmap */ . '
+ for ($i = 0; $i < 256; ++$i) {
+ $shuffleip[] = self::$shuffle[self::$ipmap[$i]];
+ $shuffleinvip[] = self::$shuffle[self::$invipmap[$i]];
}
- ';
-
- switch (true) {
- case $gen_hi_opt_code:
- // In Hi-optimized code mode, we use our [3]DES key schedule as hardcoded integers.
- // No futher initialisation of the $keys schedule is necessary.
- // That is the extra performance boost.
- $k = array(
- self::ENCRYPT => $this->keys[self::ENCRYPT],
- self::DECRYPT => $this->keys[self::DECRYPT]
- );
- $init_encrypt = '';
- $init_decrypt = '';
- break;
- default:
- // In generic optimized code mode, we have to use, as the best compromise [currently],
- // our key schedule as $ke/$kd arrays. (with hardcoded indexes...)
- $k = array(
- self::ENCRYPT => array(),
- self::DECRYPT => array()
- );
- for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) {
- $k[self::ENCRYPT][$i] = '$ke[' . $i . ']';
- $k[self::DECRYPT][$i] = '$kd[' . $i . ']';
- }
- $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];';
- $init_decrypt = '$kd = $self->keys[$self::DECRYPT];';
- break;
}
+ ';
+
+ $k = [
+ self::ENCRYPT => $this->keys[self::ENCRYPT],
+ self::DECRYPT => $this->keys[self::DECRYPT]
+ ];
+ $init_encrypt = '';
+ $init_decrypt = '';
- // Creating code for en- and decryption.
- $crypt_block = array();
- foreach (array(self::ENCRYPT, self::DECRYPT) as $c) {
- /* Do the initial IP permutation. */
- $crypt_block[$c] = '
- $in = unpack("N*", $in);
- $l = $in[1];
- $r = $in[2];
- $in = unpack("N*",
- ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
- ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
- ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
- ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
- ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
- ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
- ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
- ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01")
- );
- ' . /* Extract L0 and R0 */ '
- $l = $in[1];
- $r = $in[2];
- ';
+ // Creating code for en- and decryption.
+ $crypt_block = [];
+ foreach ([self::ENCRYPT, self::DECRYPT] as $c) {
+ /* Do the initial IP permutation. */
+ $crypt_block[$c] = '
+ $in = unpack("N*", $in);
+ $l = $in[1];
+ $r = $in[2];
+ $in = unpack("N*",
+ ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
+ ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
+ ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
+ ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
+ ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
+ ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
+ ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
+ ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01")
+ );
+ ' . /* Extract L0 and R0 */ '
+ $l = $in[1];
+ $r = $in[2];
+ ';
- $l = '$l';
- $r = '$r';
+ $l = '$l';
+ $r = '$r';
- // Perform DES or 3DES.
- for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) {
- // Perform the 16 steps.
- for ($i = 0; $i < 16; ++$i) {
- // start of "the Feistel (F) function" - see the following URL:
- // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
- // Merge key schedule.
- $crypt_block[$c].= '
- $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . ';
- $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' .
- /* S-box indexing. */
- $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
- $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
- $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^
- $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . ';
- ';
- // end of "the Feistel (F) function"
+ // Perform DES or 3DES.
+ for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) {
+ // Perform the 16 steps.
+ for ($i = 0; $i < 16; ++$i) {
+ // start of "the Feistel (F) function" - see the following URL:
+ // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
+ // Merge key schedule.
+ $crypt_block[$c] .= '
+ $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . ';
+ $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' .
+ /* S-box indexing. */
+ $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^
+ $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^
+ $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^
+ $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . ';
+ ';
+ // end of "the Feistel (F) function"
- // swap L & R
- list($l, $r) = array($r, $l);
- }
- list($l, $r) = array($r, $l);
+ // swap L & R
+ list($l, $r) = [$r, $l];
}
-
- // Perform the inverse IP permutation.
- $crypt_block[$c].= '$in =
- ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
- ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
- ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
- ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
- ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
- ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
- ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
- ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
- ';
+ list($l, $r) = [$r, $l];
}
- // Creates the inline-crypt function
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => $init_crypt,
- 'init_encrypt' => $init_encrypt,
- 'init_decrypt' => $init_decrypt,
- 'encrypt_block' => $crypt_block[self::ENCRYPT],
- 'decrypt_block' => $crypt_block[self::DECRYPT]
- )
- );
+ // Perform the inverse IP permutation.
+ $crypt_block[$c] .= '$in =
+ ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") |
+ ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") |
+ ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") |
+ ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") |
+ ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") |
+ ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") |
+ ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") |
+ ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01");
+ ';
}
- // Set the inline-crypt function as callback in: $this->inline_crypt
- $this->inline_crypt = $lambda_functions[$code_hash];
+ // Creates the inline-crypt function
+ $this->inline_crypt = $this->createInlineCryptFunction(
+ [
+ 'init_crypt' => $init_crypt,
+ 'init_encrypt' => $init_encrypt,
+ 'init_decrypt' => $init_decrypt,
+ 'encrypt_block' => $crypt_block[self::ENCRYPT],
+ 'decrypt_block' => $crypt_block[self::DECRYPT]
+ ]
+ );
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php
new file mode 100644
index 000000000..b2301986f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH.php
@@ -0,0 +1,405 @@
+<?php
+
+/**
+ * Pure-PHP (EC)DH implementation
+ *
+ * PHP version 5
+ *
+ * Here's an example of how to compute a shared secret with this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $ourPrivate = \phpseclib3\Crypt\DH::createKey();
+ * $secret = DH::computeSecret($ourPrivate, $theirPublic);
+ *
+ * ?>
+ * </code>
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\DH\Parameters;
+use phpseclib3\Crypt\DH\PrivateKey;
+use phpseclib3\Crypt\DH\PublicKey;
+use phpseclib3\Exception\NoKeyLoadedException;
+use phpseclib3\Exception\UnsupportedOperationException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Pure-PHP (EC)DH implementation
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DH extends AsymmetricKey
+{
+ /**
+ * Algorithm Name
+ *
+ * @var string
+ */
+ const ALGORITHM = 'DH';
+
+ /**
+ * DH prime
+ *
+ * @var BigInteger
+ */
+ protected $prime;
+
+ /**
+ * DH Base
+ *
+ * Prime divisor of p-1
+ *
+ * @var BigInteger
+ */
+ protected $base;
+
+ /**
+ * Public Key
+ *
+ * @var BigInteger
+ */
+ protected $publicKey;
+
+ /**
+ * Create DH parameters
+ *
+ * This method is a bit polymorphic. It can take any of the following:
+ * - two BigInteger's (prime and base)
+ * - an integer representing the size of the prime in bits (the base is assumed to be 2)
+ * - a string (eg. diffie-hellman-group14-sha1)
+ *
+ * @return Parameters
+ */
+ public static function createParameters(...$args)
+ {
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')');
+ }
+
+ $params = new Parameters();
+ if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) {
+ //if (!$args[0]->isPrime()) {
+ // throw new \InvalidArgumentException('The first parameter should be a prime number');
+ //}
+ $params->prime = $args[0];
+ $params->base = $args[1];
+ return $params;
+ } elseif (count($args) == 1 && is_numeric($args[0])) {
+ $params->prime = BigInteger::randomPrime($args[0]);
+ $params->base = new BigInteger(2);
+ return $params;
+ } elseif (count($args) != 1 || !is_string($args[0])) {
+ throw new \InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string');
+ }
+ switch ($args[0]) {
+ // see http://tools.ietf.org/html/rfc2409#section-6.2 and
+ // http://tools.ietf.org/html/rfc2412, appendex E
+ case 'diffie-hellman-group1-sha1':
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
+ break;
+ // see http://tools.ietf.org/html/rfc3526#section-3
+ case 'diffie-hellman-group14-sha1': // 2048-bit MODP Group
+ case 'diffie-hellman-group14-sha256':
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
+ break;
+ // see https://tools.ietf.org/html/rfc3526#section-4
+ case 'diffie-hellman-group15-sha512': // 3072-bit MODP Group
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
+ 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
+ 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
+ '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF';
+ break;
+ // see https://tools.ietf.org/html/rfc3526#section-5
+ case 'diffie-hellman-group16-sha512': // 4096-bit MODP Group
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
+ 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
+ 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
+ '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
+ 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
+ '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF';
+ break;
+ // see https://tools.ietf.org/html/rfc3526#section-6
+ case 'diffie-hellman-group17-sha512': // 6144-bit MODP Group
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
+ 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
+ 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
+ '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
+ 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
+ '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
+ 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
+ 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
+ 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
+ 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
+ '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
+ 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
+ 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
+ '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF';
+ break;
+ // see https://tools.ietf.org/html/rfc3526#section-7
+ case 'diffie-hellman-group18-sha512': // 8192-bit MODP Group
+ $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
+ '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
+ '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
+ '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
+ '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
+ '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
+ 'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
+ 'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
+ '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
+ 'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
+ '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
+ 'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
+ 'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
+ 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
+ 'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
+ '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
+ 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
+ 'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
+ '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' .
+ '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' .
+ '2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' .
+ 'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' .
+ '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' .
+ '6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' .
+ 'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' .
+ '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' .
+ '9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF';
+ break;
+ default:
+ throw new \InvalidArgumentException('Invalid named prime provided');
+ }
+
+ $params->prime = new BigInteger($prime, 16);
+ $params->base = new BigInteger(2);
+
+ return $params;
+ }
+
+ /**
+ * Create public / private key pair.
+ *
+ * The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 :
+ *
+ * "To increase the speed of the key exchange, both client and server may
+ * reduce the size of their private exponents. It should be at least
+ * twice as long as the key material that is generated from the shared
+ * secret. For more details, see the paper by van Oorschot and Wiener
+ * [VAN-OORSCHOT]."
+ *
+ * $length is in bits
+ *
+ * @param Parameters $params
+ * @param int $length optional
+ * @return PrivateKey
+ */
+ public static function createKey(Parameters $params, $length = 0)
+ {
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
+ }
+
+ $one = new BigInteger(1);
+ if ($length) {
+ $max = $one->bitwise_leftShift($length);
+ $max = $max->subtract($one);
+ } else {
+ $max = $params->prime->subtract($one);
+ }
+
+ $key = new PrivateKey();
+ $key->prime = $params->prime;
+ $key->base = $params->base;
+ $key->privateKey = BigInteger::randomRange($one, $max);
+ $key->publicKey = $key->base->powMod($key->privateKey, $key->prime);
+ return $key;
+ }
+
+ /**
+ * Compute Shared Secret
+ *
+ * @param PrivateKey|EC $private
+ * @param PublicKey|BigInteger|string $public
+ * @return mixed
+ */
+ public static function computeSecret($private, $public)
+ {
+ if ($private instanceof PrivateKey) { // DH\PrivateKey
+ switch (true) {
+ case $public instanceof PublicKey:
+ if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) {
+ throw new \InvalidArgumentException('The public and private key do not share the same prime and / or base numbers');
+ }
+ return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(true);
+ case is_string($public):
+ $public = new BigInteger($public, -256);
+ // fall-through
+ case $public instanceof BigInteger:
+ return $public->powMod($private->privateKey, $private->prime)->toBytes(true);
+ default:
+ throw new \InvalidArgumentException('$public needs to be an instance of DH\PublicKey, a BigInteger or a string');
+ }
+ }
+
+ if ($private instanceof EC\PrivateKey) {
+ switch (true) {
+ case $public instanceof EC\PublicKey:
+ $public = $public->getEncodedCoordinates();
+ // fall-through
+ case is_string($public):
+ $point = $private->multiply($public);
+ switch ($private->getCurve()) {
+ case 'Curve25519':
+ case 'Curve448':
+ $secret = $point;
+ break;
+ default:
+ // according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
+ $secret = substr($point, 1, (strlen($point) - 1) >> 1);
+ }
+ /*
+ if (($secret[0] & "\x80") === "\x80") {
+ $secret = "\0$secret";
+ }
+ */
+ return $secret;
+ default:
+ throw new \InvalidArgumentException('$public needs to be an instance of EC\PublicKey or a string (an encoded coordinate)');
+ }
+ }
+ }
+
+ /**
+ * Load the key
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return AsymmetricKey
+ */
+ public static function load($key, $password = false)
+ {
+ try {
+ return EC::load($key, $password);
+ } catch (NoKeyLoadedException $e) {
+ }
+
+ return parent::load($key, $password);
+ }
+
+ /**
+ * OnLoad Handler
+ *
+ * @return bool
+ */
+ protected static function onLoad(array $components)
+ {
+ if (!isset($components['privateKey']) && !isset($components['publicKey'])) {
+ $new = new Parameters();
+ } else {
+ $new = isset($components['privateKey']) ?
+ new PrivateKey() :
+ new PublicKey();
+ }
+
+ $new->prime = $components['prime'];
+ $new->base = $components['base'];
+
+ if (isset($components['privateKey'])) {
+ $new->privateKey = $components['privateKey'];
+ }
+ if (isset($components['publicKey'])) {
+ $new->publicKey = $components['publicKey'];
+ }
+
+ return $new;
+ }
+
+ /**
+ * Determines which hashing function should be used
+ *
+ * @param string $hash
+ */
+ public function withHash($hash)
+ {
+ throw new UnsupportedOperationException('DH does not use a hash algorithm');
+ }
+
+ /**
+ * Returns the hash algorithm currently being used
+ *
+ */
+ public function getHash()
+ {
+ throw new UnsupportedOperationException('DH does not use a hash algorithm');
+ }
+
+ /**
+ * Returns the parameters
+ *
+ * A public / private key is only returned if the currently loaded "key" contains an x or y
+ * value.
+ *
+ * @see self::getPublicKey()
+ * @return mixed
+ */
+ public function getParameters()
+ {
+ $type = DH::validatePlugin('Keys', 'PKCS1', 'saveParameters');
+
+ $key = $type::saveParameters($this->prime, $this->base);
+ return DH::load($key, 'PKCS1');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php
new file mode 100644
index 000000000..65a0a5dbc
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS1.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * "PKCS1" Formatted EC Key Handler
+ *
+ * PHP version 5
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN DH PARAMETERS-----
+ *
+ * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
+ * DSA, whose format isn't really formally described anywhere, so might as well
+ * use it to describe this, too.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DH\Formats\Keys;
+
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * "PKCS1" Formatted DH Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS1 extends Progenitor
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
+ if (!is_array($components)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping on parameters');
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert EC parameters to the appropriate format
+ *
+ * @return string
+ */
+ public static function saveParameters(BigInteger $prime, BigInteger $base, array $options = [])
+ {
+ $params = [
+ 'prime' => $prime,
+ 'base' => $base
+ ];
+ $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
+
+ return "-----BEGIN DH PARAMETERS-----\r\n" .
+ chunk_split(base64_encode($params), 64) .
+ "-----END DH PARAMETERS-----\r\n";
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php
new file mode 100644
index 000000000..3b83a4290
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Formats/Keys/PKCS8.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * PKCS#8 Formatted DH Key Handler
+ *
+ * PHP version 5
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DH\Formats\Keys;
+
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#8 Formatted DH Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS8 extends Progenitor
+{
+ /**
+ * OID Name
+ *
+ * @var string
+ */
+ const OID_NAME = 'dhKeyAgreement';
+
+ /**
+ * OID Value
+ *
+ * @var string
+ */
+ const OID_VALUE = '1.2.840.113549.1.3.1';
+
+ /**
+ * Child OIDs loaded
+ *
+ * @var bool
+ */
+ protected static $childOIDsLoaded = false;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
+
+ $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
+ if (empty($decoded)) {
+ throw new \RuntimeException('Unable to decode BER of parameters');
+ }
+ $components = ASN1::asn1map($decoded[0], Maps\DHParameter::MAP);
+ if (!is_array($components)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping on parameters');
+ }
+
+ $decoded = ASN1::decodeBER($key[$type]);
+ switch (true) {
+ case !isset($decoded):
+ case !isset($decoded[0]['content']):
+ case !$decoded[0]['content'] instanceof BigInteger:
+ throw new \RuntimeException('Unable to decode BER of parameters');
+ }
+ $components[$type] = $decoded[0]['content'];
+
+ return $components;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $prime
+ * @param BigInteger $base
+ * @param BigInteger $privateKey
+ * @param BigInteger $publicKey
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $prime, BigInteger $base, BigInteger $privateKey, BigInteger $publicKey, $password = '', array $options = [])
+ {
+ $params = [
+ 'prime' => $prime,
+ 'base' => $base
+ ];
+ $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
+ $params = new ASN1\Element($params);
+ $key = ASN1::encodeDER($privateKey, ['type' => ASN1::TYPE_INTEGER]);
+ return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $prime
+ * @param BigInteger $base
+ * @param BigInteger $publicKey
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $prime, BigInteger $base, BigInteger $publicKey, array $options = [])
+ {
+ $params = [
+ 'prime' => $prime,
+ 'base' => $base
+ ];
+ $params = ASN1::encodeDER($params, Maps\DHParameter::MAP);
+ $params = new ASN1\Element($params);
+ $key = ASN1::encodeDER($publicKey, ['type' => ASN1::TYPE_INTEGER]);
+ return self::wrapPublicKey($key, $params, null, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php
new file mode 100644
index 000000000..c0ded84cd
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/Parameters.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * DH Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DH;
+
+use phpseclib3\Crypt\DH;
+
+/**
+ * DH Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class Parameters extends DH
+{
+ /**
+ * Returns the parameters
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type = 'PKCS1', array $options = [])
+ {
+ $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
+
+ return $type::saveParameters($this->prime, $this->base, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php
new file mode 100644
index 000000000..e2407e35e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PrivateKey.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * DH Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DH;
+
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\DH;
+
+/**
+ * DH Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PrivateKey extends DH
+{
+ use Common\Traits\PasswordProtected;
+
+ /**
+ * Private Key
+ *
+ * @var \phpseclib3\Math\BigInteger
+ */
+ protected $privateKey;
+
+ /**
+ * Public Key
+ *
+ * @var \phpseclib3\Math\BigInteger
+ */
+ protected $publicKey;
+
+ /**
+ * Returns the public key
+ *
+ * @return PublicKey
+ */
+ public function getPublicKey()
+ {
+ $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
+
+ if (!isset($this->publicKey)) {
+ $this->publicKey = $this->base->powMod($this->privateKey, $this->prime);
+ }
+
+ $key = $type::savePublicKey($this->prime, $this->base, $this->publicKey);
+
+ return DH::loadFormat('PKCS8', $key);
+ }
+
+ /**
+ * Returns the private key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePrivateKey');
+
+ if (!isset($this->publicKey)) {
+ $this->publicKey = $this->base->powMod($this->privateKey, $this->prime);
+ }
+
+ return $type::savePrivateKey($this->prime, $this->base, $this->privateKey, $this->publicKey, $this->password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php
new file mode 100644
index 000000000..87726a5a3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DH/PublicKey.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * DH Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DH;
+
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\DH;
+
+/**
+ * DH Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PublicKey extends DH
+{
+ use Common\Traits\Fingerprint;
+
+ /**
+ * Returns the public key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePublicKey');
+
+ return $type::savePublicKey($this->prime, $this->base, $this->publicKey, $options);
+ }
+
+ /**
+ * Returns the public key as a BigInteger
+ *
+ * @return \phpseclib3\Math\BigInteger
+ */
+ public function toBigInteger()
+ {
+ return $this->publicKey;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php
new file mode 100644
index 000000000..92c777d6a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA.php
@@ -0,0 +1,337 @@
+<?php
+
+/**
+ * Pure-PHP FIPS 186-4 compliant implementation of DSA.
+ *
+ * PHP version 5
+ *
+ * Here's an example of how to create signatures and verify signatures with this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $private = \phpseclib3\Crypt\DSA::createKey();
+ * $public = $private->getPublicKey();
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $signature = $private->sign($plaintext);
+ *
+ * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
+ * ?>
+ * </code>
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\DSA\Parameters;
+use phpseclib3\Crypt\DSA\PrivateKey;
+use phpseclib3\Crypt\DSA\PublicKey;
+use phpseclib3\Exception\InsufficientSetupException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Pure-PHP FIPS 186-4 compliant implementation of DSA.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DSA extends AsymmetricKey
+{
+ /**
+ * Algorithm Name
+ *
+ * @var string
+ */
+ const ALGORITHM = 'DSA';
+
+ /**
+ * DSA Prime P
+ *
+ * @var BigInteger
+ */
+ protected $p;
+
+ /**
+ * DSA Group Order q
+ *
+ * Prime divisor of p-1
+ *
+ * @var BigInteger
+ */
+ protected $q;
+
+ /**
+ * DSA Group Generator G
+ *
+ * @var BigInteger
+ */
+ protected $g;
+
+ /**
+ * DSA public key value y
+ *
+ * @var BigInteger
+ */
+ protected $y;
+
+ /**
+ * Signature Format
+ *
+ * @var string
+ */
+ protected $sigFormat;
+
+ /**
+ * Signature Format (Short)
+ *
+ * @var string
+ */
+ protected $shortFormat;
+
+ /**
+ * Create DSA parameters
+ *
+ * @param int $L
+ * @param int $N
+ * @return DSA|bool
+ */
+ public static function createParameters($L = 2048, $N = 224)
+ {
+ self::initialize_static_variables();
+
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')');
+ }
+
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+
+ switch (true) {
+ case $N == 160:
+ /*
+ in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024.
+ RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most
+ SSH DSA implementations only support keys with an N of 160.
+ puttygen let's you set the size of L (but not the size of N) and uses 2048 as the
+ default L value. that's not really compliant with any of the FIPS standards, however,
+ for the purposes of maintaining compatibility with puttygen, we'll support it
+ */
+ //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160:
+ // FIPS 186-3 changed this as follows:
+ //case $L == 1024 && $N == 160:
+ case $L == 2048 && $N == 224:
+ case $L == 2048 && $N == 256:
+ case $L == 3072 && $N == 256:
+ break;
+ default:
+ throw new \InvalidArgumentException('Invalid values for N and L');
+ }
+
+ $two = new BigInteger(2);
+
+ $q = BigInteger::randomPrime($N);
+ $divisor = $q->multiply($two);
+
+ do {
+ $x = BigInteger::random($L);
+ list(, $c) = $x->divide($divisor);
+ $p = $x->subtract($c->subtract(self::$one));
+ } while ($p->getLength() != $L || !$p->isPrime());
+
+ $p_1 = $p->subtract(self::$one);
+ list($e) = $p_1->divide($q);
+
+ // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 ,
+ // "h could be obtained from a random number generator or from a counter that
+ // changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments
+ // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that
+ $h = clone $two;
+ while (true) {
+ $g = $h->powMod($e, $p);
+ if (!$g->equals(self::$one)) {
+ break;
+ }
+ $h = $h->add(self::$one);
+ }
+
+ $dsa = new Parameters();
+ $dsa->p = $p;
+ $dsa->q = $q;
+ $dsa->g = $g;
+
+ return $dsa;
+ }
+
+ /**
+ * Create public / private key pair.
+ *
+ * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or
+ * no parameters (at which point L and N will be generated with this method)
+ *
+ * Returns the private key, from which the publickey can be extracted
+ *
+ * @param int[] ...$args
+ * @return PrivateKey
+ */
+ public static function createKey(...$args)
+ {
+ self::initialize_static_variables();
+
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
+ }
+
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+
+ if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
+ $params = self::createParameters($args[0], $args[1]);
+ } elseif (count($args) == 1 && $args[0] instanceof Parameters) {
+ $params = $args[0];
+ } elseif (!count($args)) {
+ $params = self::createParameters();
+ } else {
+ throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.');
+ }
+
+ $private = new PrivateKey();
+ $private->p = $params->p;
+ $private->q = $params->q;
+ $private->g = $params->g;
+
+ $private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one));
+ $private->y = $private->g->powMod($private->x, $private->p);
+
+ //$public = clone $private;
+ //unset($public->x);
+
+ return $private
+ ->withHash($params->hash->getHash())
+ ->withSignatureFormat($params->shortFormat);
+ }
+
+ /**
+ * OnLoad Handler
+ *
+ * @return bool
+ */
+ protected static function onLoad(array $components)
+ {
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+
+ if (!isset($components['x']) && !isset($components['y'])) {
+ $new = new Parameters();
+ } elseif (isset($components['x'])) {
+ $new = new PrivateKey();
+ $new->x = $components['x'];
+ } else {
+ $new = new PublicKey();
+ }
+
+ $new->p = $components['p'];
+ $new->q = $components['q'];
+ $new->g = $components['g'];
+
+ if (isset($components['y'])) {
+ $new->y = $components['y'];
+ }
+
+ return $new;
+ }
+
+ /**
+ * Constructor
+ *
+ * PublicKey and PrivateKey objects can only be created from abstract RSA class
+ */
+ protected function __construct()
+ {
+ $this->sigFormat = self::validatePlugin('Signature', 'ASN1');
+ $this->shortFormat = 'ASN1';
+
+ parent::__construct();
+ }
+
+ /**
+ * Returns the key size
+ *
+ * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q)
+ *
+ * @return array
+ */
+ public function getLength()
+ {
+ return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
+ }
+
+ /**
+ * Returns the current engine being used
+ *
+ * @see self::useInternalEngine()
+ * @see self::useBestEngine()
+ * @return string
+ */
+ public function getEngine()
+ {
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+ return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
+ 'OpenSSL' : 'PHP';
+ }
+
+ /**
+ * Returns the parameters
+ *
+ * A public / private key is only returned if the currently loaded "key" contains an x or y
+ * value.
+ *
+ * @see self::getPublicKey()
+ * @return mixed
+ */
+ public function getParameters()
+ {
+ $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
+
+ $key = $type::saveParameters($this->p, $this->q, $this->g);
+ return DSA::load($key, 'PKCS1')
+ ->withHash($this->hash->getHash())
+ ->withSignatureFormat($this->shortFormat);
+ }
+
+ /**
+ * Determines the signature padding mode
+ *
+ * Valid values are: ASN1, SSH2, Raw
+ *
+ * @param string $format
+ */
+ public function withSignatureFormat($format)
+ {
+ $new = clone $this;
+ $new->shortFormat = $format;
+ $new->sigFormat = self::validatePlugin('Signature', $format);
+ return $new;
+ }
+
+ /**
+ * Returns the signature format currently being used
+ *
+ */
+ public function getSignatureFormat()
+ {
+ return $this->shortFormat;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php
new file mode 100644
index 000000000..bc41fcf5e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/OpenSSH.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * OpenSSH Formatted DSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * OpenSSH Formatted DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSH extends Progenitor
+{
+ /**
+ * Supported Key Types
+ *
+ * @var array
+ */
+ protected static $types = ['ssh-dss'];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $parsed = parent::load($key, $password);
+
+ if (isset($parsed['paddedKey'])) {
+ list($type) = Strings::unpackSSH2('s', $parsed['paddedKey']);
+ if ($type != $parsed['type']) {
+ throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
+ }
+
+ list($p, $q, $g, $y, $x, $comment) = Strings::unpackSSH2('i5s', $parsed['paddedKey']);
+
+ return compact('p', 'q', 'g', 'y', 'x', 'comment');
+ }
+
+ list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $parsed['publicKey']);
+
+ $comment = $parsed['comment'];
+
+ return compact('p', 'q', 'g', 'y', 'comment');
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = [])
+ {
+ if ($q->getLength() != 160) {
+ throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
+ }
+
+ // from <http://tools.ietf.org/html/rfc4253#page-15>:
+ // string "ssh-dss"
+ // mpint p
+ // mpint q
+ // mpint g
+ // mpint y
+ $DSAPublicKey = Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y);
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $DSAPublicKey;
+ }
+
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+ $DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey) . ' ' . $comment;
+
+ return $DSAPublicKey;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param BigInteger $x
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = [])
+ {
+ $publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => true]);
+ $privateKey = Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x);
+
+ return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php
new file mode 100644
index 000000000..800cfb38c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS1.php
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * PKCS#1 Formatted DSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by File/X509.php
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN DSA PRIVATE KEY-----
+ * -----BEGIN DSA PUBLIC KEY-----
+ * -----BEGIN DSA PARAMETERS-----
+ *
+ * Analogous to ssh-keygen's pem format (as specified by -m)
+ *
+ * Also, technically, PKCS1 decribes RSA but I am not aware of a formal specification for DSA.
+ * The DSA private key format seems to have been adapted from the RSA private key format so
+ * we're just re-using that as the name.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#1 Formatted DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS1 extends Progenitor
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
+ if (is_array($key)) {
+ return $key;
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\DSAPrivateKey::MAP);
+ if (is_array($key)) {
+ return $key;
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
+ if (is_array($key)) {
+ return $key;
+ }
+
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+
+ /**
+ * Convert DSA parameters to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @return string
+ */
+ public static function saveParameters(BigInteger $p, BigInteger $q, BigInteger $g)
+ {
+ $key = [
+ 'p' => $p,
+ 'q' => $q,
+ 'g' => $g
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\DSAParams::MAP);
+
+ return "-----BEGIN DSA PARAMETERS-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END DSA PARAMETERS-----\r\n";
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param BigInteger $x
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = [])
+ {
+ $key = [
+ 'version' => 0,
+ 'p' => $p,
+ 'q' => $q,
+ 'g' => $g,
+ 'y' => $y,
+ 'x' => $x
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\DSAPrivateKey::MAP);
+
+ return self::wrapPrivateKey($key, 'DSA', $password, $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y)
+ {
+ $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
+
+ return self::wrapPublicKey($key, 'DSA');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php
new file mode 100644
index 000000000..359ed09ea
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PKCS8.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * PKCS#8 Formatted DSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
+ * is specific to private keys it's basically creating a DER-encoded wrapper
+ * for keys. This just extends that same concept to public keys (much like ssh-keygen)
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#8 Formatted DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS8 extends Progenitor
+{
+ /**
+ * OID Name
+ *
+ * @var string
+ */
+ const OID_NAME = 'id-dsa';
+
+ /**
+ * OID Value
+ *
+ * @var string
+ */
+ const OID_VALUE = '1.2.840.10040.4.1';
+
+ /**
+ * Child OIDs loaded
+ *
+ * @var bool
+ */
+ protected static $childOIDsLoaded = false;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
+
+ $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER of parameters');
+ }
+ $components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
+ if (!is_array($components)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping on parameters');
+ }
+
+ $decoded = ASN1::decodeBER($key[$type]);
+ if (empty($decoded)) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $var = $type == 'privateKey' ? 'x' : 'y';
+ $components[$var] = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
+ if (!$components[$var] instanceof BigInteger) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+
+ if (isset($key['meta'])) {
+ $components['meta'] = $key['meta'];
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param BigInteger $x
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = [])
+ {
+ $params = [
+ 'p' => $p,
+ 'q' => $q,
+ 'g' => $g
+ ];
+ $params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
+ $params = new ASN1\Element($params);
+ $key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP);
+ return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = [])
+ {
+ $params = [
+ 'p' => $p,
+ 'q' => $q,
+ 'g' => $g
+ ];
+ $params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
+ $params = new ASN1\Element($params);
+ $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
+ return self::wrapPublicKey($key, $params, null, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php
new file mode 100644
index 000000000..8549a2ec7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/PuTTY.php
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * PuTTY Formatted DSA Key Handler
+ *
+ * puttygen does not generate DSA keys with an N of anything other than 160, however,
+ * it can still load them and convert them. PuTTY will load them, too, but SSH servers
+ * won't accept them. Since PuTTY formatted keys are primarily used with SSH this makes
+ * keys with N > 160 kinda useless, hence this handlers not supporting such keys.
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PuTTY Formatted DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PuTTY extends Progenitor
+{
+ /**
+ * Public Handler
+ *
+ * @var string
+ */
+ const PUBLIC_HANDLER = 'phpseclib3\Crypt\DSA\Formats\Keys\OpenSSH';
+
+ /**
+ * Algorithm Identifier
+ *
+ * @var array
+ */
+ protected static $types = ['ssh-dss'];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $components = parent::load($key, $password);
+ if (!isset($components['private'])) {
+ return $components;
+ }
+ $type = $components['type'];
+ $comment = $components['comment'];
+ $public = $components['public'];
+ $private = $components['private'];
+ unset($components['public'], $components['private']);
+
+ list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $public);
+ list($x) = Strings::unpackSSH2('i', $private);
+
+ return compact('p', 'q', 'g', 'y', 'x', 'comment');
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param BigInteger $x
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = false, array $options = [])
+ {
+ if ($q->getLength() != 160) {
+ throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
+ }
+
+ $public = Strings::packSSH2('iiii', $p, $q, $g, $y);
+ $private = Strings::packSSH2('i', $x);
+
+ return self::wrapPrivateKey($public, $private, 'ssh-dss', $password, $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y)
+ {
+ if ($q->getLength() != 160) {
+ throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
+ }
+
+ return self::wrapPublicKey(Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dss');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php
new file mode 100644
index 000000000..8e2ef01f1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/Raw.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * Raw DSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Reads and creates arrays as DSA keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Raw DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Raw
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param array $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!is_array($key)) {
+ throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key));
+ }
+
+ switch (true) {
+ case !isset($key['p']) || !isset($key['q']) || !isset($key['g']):
+ case !$key['p'] instanceof BigInteger:
+ case !$key['q'] instanceof BigInteger:
+ case !$key['g'] instanceof BigInteger:
+ case !isset($key['x']) && !isset($key['y']):
+ case isset($key['x']) && !$key['x'] instanceof BigInteger:
+ case isset($key['y']) && !$key['y'] instanceof BigInteger:
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1];
+
+ return array_intersect_key($key, $options);
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @param BigInteger $x
+ * @param string $password optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '')
+ {
+ return compact('p', 'q', 'g', 'y', 'x');
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y)
+ {
+ return compact('p', 'q', 'g', 'y');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php
new file mode 100644
index 000000000..f77cbf20d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Keys/XML.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * XML Formatted DSA Key Handler
+ *
+ * While XKMS defines a private key format for RSA it does not do so for DSA. Quoting that standard:
+ *
+ * "[XKMS] does not specify private key parameters for the DSA signature algorithm since the algorithm only
+ * supports signature modes and so the application of server generated keys and key recovery is of limited
+ * value"
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * XML Formatted DSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class XML
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (!class_exists('DOMDocument')) {
+ throw new BadConfigurationException('The dom extension is not setup correctly on this system');
+ }
+
+ $use_errors = libxml_use_internal_errors(true);
+
+ $dom = new \DOMDocument();
+ if (substr($key, 0, 5) != '<?xml') {
+ $key = '<xml>' . $key . '</xml>';
+ }
+ if (!$dom->loadXML($key)) {
+ libxml_use_internal_errors($use_errors);
+ throw new \UnexpectedValueException('Key does not appear to contain XML');
+ }
+ $xpath = new \DOMXPath($dom);
+ $keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter'];
+ foreach ($keys as $key) {
+ // $dom->getElementsByTagName($key) is case-sensitive
+ $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
+ if (!$temp->length) {
+ continue;
+ }
+ $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
+ switch ($key) {
+ case 'p': // a prime modulus meeting the [DSS] requirements
+ // Parameters P, Q, and G can be public and common to a group of users. They might be known
+ // from application context. As such, they are optional but P and Q must either both appear
+ // or both be absent
+ $components['p'] = $value;
+ break;
+ case 'q': // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1
+ $components['q'] = $value;
+ break;
+ case 'g': // an integer with certain properties with respect to P and Q
+ $components['g'] = $value;
+ break;
+ case 'y': // G**X mod P (where X is part of the private key and not made public)
+ $components['y'] = $value;
+ // the remaining options do not do anything
+ case 'j': // (P - 1) / Q
+ // Parameter J is available for inclusion solely for efficiency as it is calculatable from
+ // P and Q
+ case 'seed': // a DSA prime generation seed
+ // Parameters seed and pgenCounter are used in the DSA prime number generation algorithm
+ // specified in [DSS]. As such, they are optional but must either both be present or both
+ // be absent
+ case 'pgencounter': // a DSA prime generation counter
+ }
+ }
+
+ libxml_use_internal_errors($use_errors);
+
+ if (!isset($components['y'])) {
+ throw new \UnexpectedValueException('Key is missing y component');
+ }
+
+ switch (true) {
+ case !isset($components['p']):
+ case !isset($components['q']):
+ case !isset($components['g']):
+ return ['y' => $components['y']];
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue
+ *
+ * @param BigInteger $p
+ * @param BigInteger $q
+ * @param BigInteger $g
+ * @param BigInteger $y
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y)
+ {
+ return "<DSAKeyValue>\r\n" .
+ ' <P>' . Strings::base64_encode($p->toBytes()) . "</P>\r\n" .
+ ' <Q>' . Strings::base64_encode($q->toBytes()) . "</Q>\r\n" .
+ ' <G>' . Strings::base64_encode($g->toBytes()) . "</G>\r\n" .
+ ' <Y>' . Strings::base64_encode($y->toBytes()) . "</Y>\r\n" .
+ '</DSAKeyValue>';
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php
new file mode 100644
index 000000000..f80060286
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/ASN1.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * ASN1 Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures in the format described in
+ * https://tools.ietf.org/html/rfc3279#section-2.2.2
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Signature;
+
+use phpseclib3\File\ASN1 as Encoder;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * ASN1 Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ASN1
+{
+ /**
+ * Loads a signature
+ *
+ * @param string $sig
+ * @return array|bool
+ */
+ public static function load($sig)
+ {
+ if (!is_string($sig)) {
+ return false;
+ }
+
+ $decoded = Encoder::decodeBER($sig);
+ if (empty($decoded)) {
+ return false;
+ }
+ $components = Encoder::asn1map($decoded[0], Maps\DssSigValue::MAP);
+
+ return $components;
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s)
+ {
+ return Encoder::encodeDER(compact('r', 's'), Maps\DssSigValue::MAP);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php
new file mode 100644
index 000000000..2657a2a87
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/Raw.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Raw DSA Signature Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Signature;
+
+use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor;
+
+/**
+ * Raw DSA Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Raw extends Progenitor
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php
new file mode 100644
index 000000000..88807b5b8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Formats/Signature/SSH2.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * SSH2 Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures in the format used by SSH2
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA\Formats\Signature;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * SSH2 Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SSH2
+{
+ /**
+ * Loads a signature
+ *
+ * @param string $sig
+ * @return mixed
+ */
+ public static function load($sig)
+ {
+ if (!is_string($sig)) {
+ return false;
+ }
+
+ $result = Strings::unpackSSH2('ss', $sig);
+ if ($result === false) {
+ return false;
+ }
+ list($type, $blob) = $result;
+ if ($type != 'ssh-dss' || strlen($blob) != 40) {
+ return false;
+ }
+
+ return [
+ 'r' => new BigInteger(substr($blob, 0, 20), 256),
+ 's' => new BigInteger(substr($blob, 20), 256)
+ ];
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s)
+ {
+ if ($r->getLength() > 160 || $s->getLength() > 160) {
+ return false;
+ }
+ return Strings::packSSH2(
+ 'ss',
+ 'ssh-dss',
+ str_pad($r->toBytes(), 20, "\0", STR_PAD_LEFT) .
+ str_pad($s->toBytes(), 20, "\0", STR_PAD_LEFT)
+ );
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php
new file mode 100644
index 000000000..84d16ba68
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/Parameters.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * DSA Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA;
+
+use phpseclib3\Crypt\DSA;
+
+/**
+ * DSA Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class Parameters extends DSA
+{
+ /**
+ * Returns the parameters
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type = 'PKCS1', array $options = [])
+ {
+ $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
+
+ return $type::saveParameters($this->p, $this->q, $this->g, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php
new file mode 100644
index 000000000..90252139d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PrivateKey.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * DSA Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA;
+
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * DSA Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PrivateKey extends DSA implements Common\PrivateKey
+{
+ use Common\Traits\PasswordProtected;
+
+ /**
+ * DSA secret exponent x
+ *
+ * @var BigInteger
+ */
+ protected $x;
+
+ /**
+ * Returns the public key
+ *
+ * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key
+ * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING.
+ * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this
+ * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g
+ * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified
+ * by getting a DSA PKCS8 public key:
+ *
+ * "openssl dsa -in private.dsa -pubout -outform PEM"
+ *
+ * ie. just swap out rsa with dsa in the rsa command above.
+ *
+ * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA
+ * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature
+ * without the parameters and the PKCS1 DSA public key format does not include the parameters.
+ *
+ * @see self::getPrivateKey()
+ * @return mixed
+ */
+ public function getPublicKey()
+ {
+ $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
+
+ if (!isset($this->y)) {
+ $this->y = $this->g->powMod($this->x, $this->p);
+ }
+
+ $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y);
+
+ return DSA::loadFormat('PKCS8', $key)
+ ->withHash($this->hash->getHash())
+ ->withSignatureFormat($this->shortFormat);
+ }
+
+ /**
+ * Create a signature
+ *
+ * @see self::verify()
+ * @param string $message
+ * @return mixed
+ */
+ public function sign($message)
+ {
+ $format = $this->sigFormat;
+
+ if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
+ $signature = '';
+ $result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash());
+
+ if ($result) {
+ if ($this->shortFormat == 'ASN1') {
+ return $signature;
+ }
+
+ $loaded = ASN1Signature::load($signature);
+ $r = $loaded['r'];
+ $s = $loaded['s'];
+
+ return $format::save($r, $s);
+ }
+ }
+
+ $h = $this->hash->hash($message);
+ $h = $this->bits2int($h);
+
+ while (true) {
+ $k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one));
+ $r = $this->g->powMod($k, $this->p);
+ list(, $r) = $r->divide($this->q);
+ if ($r->equals(self::$zero)) {
+ continue;
+ }
+ $kinv = $k->modInverse($this->q);
+ $temp = $h->add($this->x->multiply($r));
+ $temp = $kinv->multiply($temp);
+ list(, $s) = $temp->divide($this->q);
+ if (!$s->equals(self::$zero)) {
+ break;
+ }
+ }
+
+ // the following is an RFC6979 compliant implementation of deterministic DSA
+ // it's unused because it's mainly intended for use when a good CSPRNG isn't
+ // available. if phpseclib's CSPRNG isn't good then even key generation is
+ // suspect
+ /*
+ $h1 = $this->hash->hash($message);
+ $k = $this->computek($h1);
+ $r = $this->g->powMod($k, $this->p);
+ list(, $r) = $r->divide($this->q);
+ $kinv = $k->modInverse($this->q);
+ $h1 = $this->bits2int($h1);
+ $temp = $h1->add($this->x->multiply($r));
+ $temp = $kinv->multiply($temp);
+ list(, $s) = $temp->divide($this->q);
+ */
+
+ return $format::save($r, $s);
+ }
+
+ /**
+ * Returns the private key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePrivateKey');
+
+ if (!isset($this->y)) {
+ $this->y = $this->g->powMod($this->x, $this->p);
+ }
+
+ return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php
new file mode 100644
index 000000000..3e16762b8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DSA/PublicKey.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * DSA Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\DSA;
+
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
+
+/**
+ * DSA Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PublicKey extends DSA implements Common\PublicKey
+{
+ use Common\Traits\Fingerprint;
+
+ /**
+ * Verify a signature
+ *
+ * @see self::verify()
+ * @param string $message
+ * @param string $signature
+ * @return mixed
+ */
+ public function verify($message, $signature)
+ {
+ $format = $this->sigFormat;
+
+ $params = $format::load($signature);
+ if ($params === false || count($params) != 2) {
+ return false;
+ }
+ $r = $params['r'];
+ $s = $params['s'];
+
+ if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
+ $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
+
+ $result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());
+
+ if ($result != -1) {
+ return (bool) $result;
+ }
+ }
+
+ $q_1 = $this->q->subtract(self::$one);
+ if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) {
+ return false;
+ }
+
+ $w = $s->modInverse($this->q);
+ $h = $this->hash->hash($message);
+ $h = $this->bits2int($h);
+ list(, $u1) = $h->multiply($w)->divide($this->q);
+ list(, $u2) = $r->multiply($w)->divide($this->q);
+ $v1 = $this->g->powMod($u1, $this->p);
+ $v2 = $this->y->powMod($u2, $this->p);
+ list(, $v) = $v1->multiply($v2)->divide($this->p);
+ list(, $v) = $v->divide($this->q);
+
+ return $v->equals($r);
+ }
+
+ /**
+ * Returns the public key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePublicKey');
+
+ return $type::savePublicKey($this->p, $this->q, $this->g, $this->y, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php
new file mode 100644
index 000000000..dc82dd049
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC.php
@@ -0,0 +1,480 @@
+<?php
+
+/**
+ * Pure-PHP implementation of EC.
+ *
+ * PHP version 5
+ *
+ * Here's an example of how to create signatures and verify signatures with this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $private = \phpseclib3\Crypt\EC::createKey('secp256k1');
+ * $public = $private->getPublicKey();
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $signature = $private->sign($plaintext);
+ *
+ * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
+ * ?>
+ * </code>
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Crypt\EC\Curves\Curve25519;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Crypt\EC\Curves\Ed448;
+use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
+use phpseclib3\Crypt\EC\Parameters;
+use phpseclib3\Crypt\EC\PrivateKey;
+use phpseclib3\Crypt\EC\PublicKey;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\Exception\UnsupportedOperationException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps\ECParameters;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Pure-PHP implementation of EC.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EC extends AsymmetricKey
+{
+ /**
+ * Algorithm Name
+ *
+ * @var string
+ */
+ const ALGORITHM = 'EC';
+
+ /**
+ * Public Key QA
+ *
+ * @var object[]
+ */
+ protected $QA;
+
+ /**
+ * Curve
+ *
+ * @var EC\BaseCurves\Base
+ */
+ protected $curve;
+
+ /**
+ * Signature Format
+ *
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * Signature Format (Short)
+ *
+ * @var string
+ */
+ protected $shortFormat;
+
+ /**
+ * Curve Name
+ *
+ * @var string
+ */
+ private $curveName;
+
+ /**
+ * Curve Order
+ *
+ * Used for deterministic ECDSA
+ *
+ * @var BigInteger
+ */
+ protected $q;
+
+ /**
+ * Alias for the private key
+ *
+ * Used for deterministic ECDSA. AsymmetricKey expects $x. I don't like x because
+ * with x you have x * the base point yielding an (x, y)-coordinate that is the
+ * public key. But the x is different depending on which side of the equal sign
+ * you're on. It's less ambiguous if you do dA * base point = (x, y)-coordinate.
+ *
+ * @var BigInteger
+ */
+ protected $x;
+
+ /**
+ * Context
+ *
+ * @var string
+ */
+ protected $context;
+
+ /**
+ * Signature Format
+ *
+ * @var string
+ */
+ protected $sigFormat;
+
+ /**
+ * Create public / private key pair.
+ *
+ * @param string $curve
+ * @return PrivateKey
+ */
+ public static function createKey($curve)
+ {
+ self::initialize_static_variables();
+
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
+ }
+
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+
+ $curve = strtolower($curve);
+ if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) {
+ $kp = sodium_crypto_sign_keypair();
+
+ $privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp));
+ //$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp));
+
+ $privatekey->curveName = 'Ed25519';
+ //$publickey->curveName = $curve;
+
+ return $privatekey;
+ }
+
+ $privatekey = new PrivateKey();
+
+ $curveName = $curve;
+ if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
+ $curveName = ucfirst($curveName);
+ } elseif (substr($curveName, 0, 10) == 'brainpoolp') {
+ $curveName = 'brainpoolP' . substr($curveName, 10);
+ }
+ $curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
+
+ if (!class_exists($curve)) {
+ throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
+ }
+
+ $reflect = new \ReflectionClass($curve);
+ $curveName = $reflect->isFinal() ?
+ $reflect->getParentClass()->getShortName() :
+ $reflect->getShortName();
+
+ $curve = new $curve();
+ if ($curve instanceof TwistedEdwardsCurve) {
+ $arr = $curve->extractSecret(Random::string($curve instanceof Ed448 ? 57 : 32));
+ $privatekey->dA = $dA = $arr['dA'];
+ $privatekey->secret = $arr['secret'];
+ } else {
+ $privatekey->dA = $dA = $curve->createRandomMultiplier();
+ }
+ if ($curve instanceof Curve25519 && self::$engines['libsodium']) {
+ //$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
+ //$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
+ $QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes());
+ $privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))];
+ } else {
+ $privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
+ }
+ $privatekey->curve = $curve;
+
+ //$publickey = clone $privatekey;
+ //unset($publickey->dA);
+ //unset($publickey->x);
+
+ $privatekey->curveName = $curveName;
+ //$publickey->curveName = $curveName;
+
+ if ($privatekey->curve instanceof TwistedEdwardsCurve) {
+ return $privatekey->withHash($curve::HASH);
+ }
+
+ return $privatekey;
+ }
+
+ /**
+ * OnLoad Handler
+ *
+ * @return bool
+ */
+ protected static function onLoad(array $components)
+ {
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+
+ if (!isset($components['dA']) && !isset($components['QA'])) {
+ $new = new Parameters();
+ $new->curve = $components['curve'];
+ return $new;
+ }
+
+ $new = isset($components['dA']) ?
+ new PrivateKey() :
+ new PublicKey();
+ $new->curve = $components['curve'];
+ $new->QA = $components['QA'];
+
+ if (isset($components['dA'])) {
+ $new->dA = $components['dA'];
+ $new->secret = $components['secret'];
+ }
+
+ if ($new->curve instanceof TwistedEdwardsCurve) {
+ return $new->withHash($components['curve']::HASH);
+ }
+
+ return $new;
+ }
+
+ /**
+ * Constructor
+ *
+ * PublicKey and PrivateKey objects can only be created from abstract RSA class
+ */
+ protected function __construct()
+ {
+ $this->sigFormat = self::validatePlugin('Signature', 'ASN1');
+ $this->shortFormat = 'ASN1';
+
+ parent::__construct();
+ }
+
+ /**
+ * Returns the curve
+ *
+ * Returns a string if it's a named curve, an array if not
+ *
+ * @return string|array
+ */
+ public function getCurve()
+ {
+ if ($this->curveName) {
+ return $this->curveName;
+ }
+
+ if ($this->curve instanceof MontgomeryCurve) {
+ $this->curveName = $this->curve instanceof Curve25519 ? 'Curve25519' : 'Curve448';
+ return $this->curveName;
+ }
+
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ $this->curveName = $this->curve instanceof Ed25519 ? 'Ed25519' : 'Ed448';
+ return $this->curveName;
+ }
+
+ $params = $this->getParameters()->toString('PKCS8', ['namedCurve' => true]);
+ $decoded = ASN1::extractBER($params);
+ $decoded = ASN1::decodeBER($decoded);
+ $decoded = ASN1::asn1map($decoded[0], ECParameters::MAP);
+ if (isset($decoded['namedCurve'])) {
+ $this->curveName = $decoded['namedCurve'];
+ return $decoded['namedCurve'];
+ }
+
+ if (!$namedCurves) {
+ PKCS1::useSpecifiedCurve();
+ }
+
+ return $decoded;
+ }
+
+ /**
+ * Returns the key size
+ *
+ * Quoting https://tools.ietf.org/html/rfc5656#section-2,
+ *
+ * "The size of a set of elliptic curve domain parameters on a prime
+ * curve is defined as the number of bits in the binary representation
+ * of the field order, commonly denoted by p. Size on a
+ * characteristic-2 curve is defined as the number of bits in the binary
+ * representation of the field, commonly denoted by m. A set of
+ * elliptic curve domain parameters defines a group of order n generated
+ * by a base point P"
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return $this->curve->getLength();
+ }
+
+ /**
+ * Returns the current engine being used
+ *
+ * @see self::useInternalEngine()
+ * @see self::useBestEngine()
+ * @return string
+ */
+ public function getEngine()
+ {
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
+ }
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ?
+ 'libsodium' : 'PHP';
+ }
+
+ return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
+ 'OpenSSL' : 'PHP';
+ }
+
+ /**
+ * Returns the public key coordinates as a string
+ *
+ * Used by ECDH
+ *
+ * @return string
+ */
+ public function getEncodedCoordinates()
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ return strrev($this->QA[0]->toBytes(true));
+ }
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ return $this->curve->encodePoint($this->QA);
+ }
+ return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true);
+ }
+
+ /**
+ * Returns the parameters
+ *
+ * @see self::getPublicKey()
+ * @param string $type optional
+ * @return mixed
+ */
+ public function getParameters($type = 'PKCS1')
+ {
+ $type = self::validatePlugin('Keys', $type, 'saveParameters');
+
+ $key = $type::saveParameters($this->curve);
+
+ return EC::load($key, 'PKCS1')
+ ->withHash($this->hash->getHash())
+ ->withSignatureFormat($this->shortFormat);
+ }
+
+ /**
+ * Determines the signature padding mode
+ *
+ * Valid values are: ASN1, SSH2, Raw
+ *
+ * @param string $format
+ */
+ public function withSignatureFormat($format)
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
+ }
+
+ $new = clone $this;
+ $new->shortFormat = $format;
+ $new->sigFormat = self::validatePlugin('Signature', $format);
+ return $new;
+ }
+
+ /**
+ * Returns the signature format currently being used
+ *
+ */
+ public function getSignatureFormat()
+ {
+ return $this->shortFormat;
+ }
+
+ /**
+ * Sets the context
+ *
+ * Used by Ed25519 / Ed448.
+ *
+ * @see self::sign()
+ * @see self::verify()
+ * @param string $context optional
+ */
+ public function withContext($context = null)
+ {
+ if (!$this->curve instanceof TwistedEdwardsCurve) {
+ throw new UnsupportedCurveException('Only Ed25519 and Ed448 support contexts');
+ }
+
+ $new = clone $this;
+ if (!isset($context)) {
+ $new->context = null;
+ return $new;
+ }
+ if (!is_string($context)) {
+ throw new \InvalidArgumentException('setContext expects a string');
+ }
+ if (strlen($context) > 255) {
+ throw new \LengthException('The context is supposed to be, at most, 255 bytes long');
+ }
+ $new->context = $context;
+ return $new;
+ }
+
+ /**
+ * Returns the signature format currently being used
+ *
+ */
+ public function getContext()
+ {
+ return $this->context;
+ }
+
+ /**
+ * Determines which hashing function should be used
+ *
+ * @param string $hash
+ */
+ public function withHash($hash)
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
+ }
+ if ($this->curve instanceof Ed25519 && $hash != 'sha512') {
+ throw new UnsupportedAlgorithmException('Ed25519 only supports sha512 as a hash');
+ }
+ if ($this->curve instanceof Ed448 && $hash != 'shake256-912') {
+ throw new UnsupportedAlgorithmException('Ed448 only supports shake256 with a length of 114 bytes');
+ }
+
+ return parent::withHash($hash);
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ return '';
+ }
+
+ return parent::__toString();
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php
new file mode 100644
index 000000000..d76562d0d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Base.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * Curve methods common to all curves
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Base
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Base
+{
+ /**
+ * The Order
+ *
+ * @var BigInteger
+ */
+ protected $order;
+
+ /**
+ * Finite Field Integer factory
+ *
+ * @var FiniteField\Integer
+ */
+ protected $factory;
+
+ /**
+ * Returns a random integer
+ *
+ * @return object
+ */
+ public function randomInteger()
+ {
+ return $this->factory->randomInteger();
+ }
+
+ /**
+ * Converts a BigInteger to a FiniteField\Integer integer
+ *
+ * @return object
+ */
+ public function convertInteger(BigInteger $x)
+ {
+ return $this->factory->newInteger($x);
+ }
+
+ /**
+ * Returns the length, in bytes, of the modulo
+ *
+ * @return integer
+ */
+ public function getLengthInBytes()
+ {
+ return $this->factory->getLengthInBytes();
+ }
+
+ /**
+ * Returns the length, in bits, of the modulo
+ *
+ * @return integer
+ */
+ public function getLength()
+ {
+ return $this->factory->getLength();
+ }
+
+ /**
+ * Multiply a point on the curve by a scalar
+ *
+ * Uses the montgomery ladder technique as described here:
+ *
+ * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
+ * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
+ *
+ * @return array
+ */
+ public function multiplyPoint(array $p, BigInteger $d)
+ {
+ $alreadyInternal = isset($p[2]);
+ $r = $alreadyInternal ?
+ [[], $p] :
+ [[], $this->convertToInternal($p)];
+
+ $d = $d->toBits();
+ for ($i = 0; $i < strlen($d); $i++) {
+ $d_i = (int) $d[$i];
+ $r[1 - $d_i] = $this->addPoint($r[0], $r[1]);
+ $r[$d_i] = $this->doublePoint($r[$d_i]);
+ }
+
+ return $alreadyInternal ? $r[0] : $this->convertToAffine($r[0]);
+ }
+
+ /**
+ * Creates a random scalar multiplier
+ *
+ * @return BigInteger
+ */
+ public function createRandomMultiplier()
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ return BigInteger::randomRange($one, $this->order->subtract($one));
+ }
+
+ /**
+ * Performs range check
+ */
+ public function rangeCheck(BigInteger $x)
+ {
+ static $zero;
+ if (!isset($zero)) {
+ $zero = new BigInteger();
+ }
+
+ if (!isset($this->order)) {
+ throw new \RuntimeException('setOrder needs to be called before this method');
+ }
+ if ($x->compare($this->order) > 0 || $x->compare($zero) <= 0) {
+ throw new \RangeException('x must be between 1 and the order of the curve');
+ }
+ }
+
+ /**
+ * Sets the Order
+ */
+ public function setOrder(BigInteger $order)
+ {
+ $this->order = $order;
+ }
+
+ /**
+ * Returns the Order
+ *
+ * @return BigInteger
+ */
+ public function getOrder()
+ {
+ return $this->order;
+ }
+
+ /**
+ * Use a custom defined modular reduction function
+ *
+ * @return object
+ */
+ public function setReduction(callable $func)
+ {
+ $this->factory->setReduction($func);
+ }
+
+ /**
+ * Returns the affine point
+ *
+ * @return object[]
+ */
+ public function convertToAffine(array $p)
+ {
+ return $p;
+ }
+
+ /**
+ * Converts an affine point to a jacobian coordinate
+ *
+ * @return object[]
+ */
+ public function convertToInternal(array $p)
+ {
+ return $p;
+ }
+
+ /**
+ * Negates a point
+ *
+ * @return object[]
+ */
+ public function negatePoint(array $p)
+ {
+ $temp = [
+ $p[0],
+ $p[1]->negate()
+ ];
+ if (isset($p[2])) {
+ $temp[] = $p[2];
+ }
+ return $temp;
+ }
+
+ /**
+ * Multiply and Add Points
+ *
+ * @return int[]
+ */
+ public function multiplyAddPoints(array $points, array $scalars)
+ {
+ $p1 = $this->convertToInternal($points[0]);
+ $p2 = $this->convertToInternal($points[1]);
+ $p1 = $this->multiplyPoint($p1, $scalars[0]);
+ $p2 = $this->multiplyPoint($p2, $scalars[1]);
+ $r = $this->addPoint($p1, $p2);
+ return $this->convertToAffine($r);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php
new file mode 100644
index 000000000..66da11da7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Binary.php
@@ -0,0 +1,373 @@
+<?php
+
+/**
+ * Curves over y^2 + x*y = x^3 + a*x^2 + b
+ *
+ * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf
+ * The curve is a weierstrass curve with a[3] and a[2] set to 0.
+ *
+ * Uses Jacobian Coordinates for speed if able:
+ *
+ * https://en.wikipedia.org/wiki/Jacobian_curve
+ * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\BinaryField;
+use phpseclib3\Math\BinaryField\Integer as BinaryInteger;
+
+/**
+ * Curves over y^2 + x*y = x^3 + a*x^2 + b
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Binary extends Base
+{
+ /**
+ * Binary Field Integer factory
+ *
+ * @var BinaryField
+ */
+ protected $factory;
+
+ /**
+ * Cofficient for x^1
+ *
+ * @var object
+ */
+ protected $a;
+
+ /**
+ * Cofficient for x^0
+ *
+ * @var object
+ */
+ protected $b;
+
+ /**
+ * Base Point
+ *
+ * @var object
+ */
+ protected $p;
+
+ /**
+ * The number one over the specified finite field
+ *
+ * @var object
+ */
+ protected $one;
+
+ /**
+ * The modulo
+ *
+ * @var BigInteger
+ */
+ protected $modulo;
+
+ /**
+ * The Order
+ *
+ * @var BigInteger
+ */
+ protected $order;
+
+ /**
+ * Sets the modulo
+ */
+ public function setModulo(...$modulo)
+ {
+ $this->modulo = $modulo;
+ $this->factory = new BinaryField(...$modulo);
+
+ $this->one = $this->factory->newInteger("\1");
+ }
+
+ /**
+ * Set coefficients a and b
+ *
+ * @param string $a
+ * @param string $b
+ */
+ public function setCoefficients($a, $b)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->a = $this->factory->newInteger(pack('H*', $a));
+ $this->b = $this->factory->newInteger(pack('H*', $b));
+ }
+
+ /**
+ * Set x and y coordinates for the base point
+ *
+ * @param string|BinaryInteger $x
+ * @param string|BinaryInteger $y
+ */
+ public function setBasePoint($x, $y)
+ {
+ switch (true) {
+ case !is_string($x) && !$x instanceof BinaryInteger:
+ throw new \UnexpectedValueException('Argument 1 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer');
+ case !is_string($y) && !$y instanceof BinaryInteger:
+ throw new \UnexpectedValueException('Argument 2 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer');
+ }
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->p = [
+ is_string($x) ? $this->factory->newInteger(pack('H*', $x)) : $x,
+ is_string($y) ? $this->factory->newInteger(pack('H*', $y)) : $y
+ ];
+ }
+
+ /**
+ * Retrieve the base point as an array
+ *
+ * @return array
+ */
+ public function getBasePoint()
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ /*
+ if (!isset($this->p)) {
+ throw new \RuntimeException('setBasePoint needs to be called before this method');
+ }
+ */
+ return $this->p;
+ }
+
+ /**
+ * Adds two points on the curve
+ *
+ * @return FiniteField[]
+ */
+ public function addPoint(array $p, array $q)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p) || !count($q)) {
+ if (count($q)) {
+ return $q;
+ }
+ if (count($p)) {
+ return $p;
+ }
+ return [];
+ }
+
+ if (!isset($p[2]) || !isset($q[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ if ($p[0]->equals($q[0])) {
+ return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
+ }
+
+ // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
+
+ list($x1, $y1, $z1) = $p;
+ list($x2, $y2, $z2) = $q;
+
+ $o1 = $z1->multiply($z1);
+ $b = $x2->multiply($o1);
+
+ if ($z2->equals($this->one)) {
+ $d = $y2->multiply($o1)->multiply($z1);
+ $e = $x1->add($b);
+ $f = $y1->add($d);
+ $z3 = $e->multiply($z1);
+ $h = $f->multiply($x2)->add($z3->multiply($y2));
+ $i = $f->add($z3);
+ $g = $z3->multiply($z3);
+ $p1 = $this->a->multiply($g);
+ $p2 = $f->multiply($i);
+ $p3 = $e->multiply($e)->multiply($e);
+ $x3 = $p1->add($p2)->add($p3);
+ $y3 = $i->multiply($x3)->add($g->multiply($h));
+
+ return [$x3, $y3, $z3];
+ }
+
+ $o2 = $z2->multiply($z2);
+ $a = $x1->multiply($o2);
+ $c = $y1->multiply($o2)->multiply($z2);
+ $d = $y2->multiply($o1)->multiply($z1);
+ $e = $a->add($b);
+ $f = $c->add($d);
+ $g = $e->multiply($z1);
+ $h = $f->multiply($x2)->add($g->multiply($y2));
+ $z3 = $g->multiply($z2);
+ $i = $f->add($z3);
+ $p1 = $this->a->multiply($z3->multiply($z3));
+ $p2 = $f->multiply($i);
+ $p3 = $e->multiply($e)->multiply($e);
+ $x3 = $p1->add($p2)->add($p3);
+ $y3 = $i->multiply($x3)->add($g->multiply($g)->multiply($h));
+
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Doubles a point on a curve
+ *
+ * @return FiniteField[]
+ */
+ public function doublePoint(array $p)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p)) {
+ return [];
+ }
+
+ if (!isset($p[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
+
+ list($x1, $y1, $z1) = $p;
+
+ $a = $x1->multiply($x1);
+ $b = $a->multiply($a);
+
+ if ($z1->equals($this->one)) {
+ $x3 = $b->add($this->b);
+ $z3 = clone $x1;
+ $p1 = $a->add($y1)->add($z3)->multiply($this->b);
+ $p2 = $a->add($y1)->multiply($b);
+ $y3 = $p1->add($p2);
+
+ return [$x3, $y3, $z3];
+ }
+
+ $c = $z1->multiply($z1);
+ $d = $c->multiply($c);
+ $x3 = $b->add($this->b->multiply($d->multiply($d)));
+ $z3 = $x1->multiply($c);
+ $p1 = $b->multiply($z3);
+ $p2 = $a->add($y1->multiply($z1))->add($z3)->multiply($x3);
+ $y3 = $p1->add($p2);
+
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Returns the X coordinate and the derived Y coordinate
+ *
+ * Not supported because it is covered by patents.
+ * Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html ,
+ *
+ * "Due to patent issues the compressed option is disabled by default for binary curves
+ * and can be enabled by defining the preprocessor macro OPENSSL_EC_BIN_PT_COMP at
+ * compile time."
+ *
+ * @return array
+ */
+ public function derivePoint($m)
+ {
+ throw new \RuntimeException('Point compression on binary finite field elliptic curves is not supported');
+ }
+
+ /**
+ * Tests whether or not the x / y values satisfy the equation
+ *
+ * @return boolean
+ */
+ public function verifyPoint(array $p)
+ {
+ list($x, $y) = $p;
+ $lhs = $y->multiply($y);
+ $lhs = $lhs->add($x->multiply($y));
+ $x2 = $x->multiply($x);
+ $x3 = $x2->multiply($x);
+ $rhs = $x3->add($this->a->multiply($x2))->add($this->b);
+
+ return $lhs->equals($rhs);
+ }
+
+ /**
+ * Returns the modulo
+ *
+ * @return BigInteger
+ */
+ public function getModulo()
+ {
+ return $this->modulo;
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer
+ */
+ public function getA()
+ {
+ return $this->a;
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer
+ */
+ public function getB()
+ {
+ return $this->b;
+ }
+
+ /**
+ * Returns the affine point
+ *
+ * A Jacobian Coordinate is of the form (x, y, z).
+ * To convert a Jacobian Coordinate to an Affine Point
+ * you do (x / z^2, y / z^3)
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer[]
+ */
+ public function convertToAffine(array $p)
+ {
+ if (!isset($p[2])) {
+ return $p;
+ }
+ list($x, $y, $z) = $p;
+ $z = $this->one->divide($z);
+ $z2 = $z->multiply($z);
+ return [
+ $x->multiply($z2),
+ $y->multiply($z2)->multiply($z)
+ ];
+ }
+
+ /**
+ * Converts an affine point to a jacobian coordinate
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer[]
+ */
+ public function convertToInternal(array $p)
+ {
+ if (isset($p[2])) {
+ return $p;
+ }
+
+ $p[2] = clone $this->one;
+ $p['fresh'] = true;
+ return $p;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php
new file mode 100644
index 000000000..d8492ebc2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/KoblitzPrime.php
@@ -0,0 +1,335 @@
+<?php
+
+/**
+ * Generalized Koblitz Curves over y^2 = x^3 + b.
+ *
+ * According to http://www.secg.org/SEC2-Ver-1.0.pdf Koblitz curves are over the GF(2**m)
+ * finite field. Both the $a$ and $b$ coefficients are either 0 or 1. However, SEC2
+ * generalizes the definition to include curves over GF(P) "which possess an efficiently
+ * computable endomorphism".
+ *
+ * For these generalized Koblitz curves $b$ doesn't have to be 0 or 1. Whether or not $a$
+ * has any restrictions on it is unclear, however, for all the GF(P) Koblitz curves defined
+ * in SEC2 v1.0 $a$ is $0$ so all of the methods defined herein will assume that it is.
+ *
+ * I suppose we could rename the $b$ coefficient to $a$, however, the documentation refers
+ * to $b$ so we'll just keep it.
+ *
+ * If a later version of SEC2 comes out wherein some $a$ values are non-zero we can create a
+ * new method for those. eg. KoblitzA1Prime.php or something.
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\PrimeField;
+
+/**
+ * Curves over y^2 = x^3 + b
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class KoblitzPrime extends Prime
+{
+ /**
+ * Basis
+ *
+ * @var list<array{a: BigInteger, b: BigInteger}>
+ */
+ protected $basis;
+
+ /**
+ * Beta
+ *
+ * @var PrimeField\Integer
+ */
+ protected $beta;
+
+ // don't overwrite setCoefficients() with one that only accepts one parameter so that
+ // one might be able to switch between KoblitzPrime and Prime more easily (for benchmarking
+ // purposes).
+
+ /**
+ * Multiply and Add Points
+ *
+ * Uses a efficiently computable endomorphism to achieve a slight speedup
+ *
+ * Adapted from:
+ * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/short.js#L219
+ *
+ * @return int[]
+ */
+ public function multiplyAddPoints(array $points, array $scalars)
+ {
+ static $zero, $one, $two;
+ if (!isset($two)) {
+ $two = new BigInteger(2);
+ $one = new BigInteger(1);
+ }
+
+ if (!isset($this->beta)) {
+ // get roots
+ $inv = $this->one->divide($this->two)->negate();
+ $s = $this->three->negate()->squareRoot()->multiply($inv);
+ $betas = [
+ $inv->add($s),
+ $inv->subtract($s)
+ ];
+ $this->beta = $betas[0]->compare($betas[1]) < 0 ? $betas[0] : $betas[1];
+ //echo strtoupper($this->beta->toHex(true)) . "\n"; exit;
+ }
+
+ if (!isset($this->basis)) {
+ $factory = new PrimeField($this->order);
+ $tempOne = $factory->newInteger($one);
+ $tempTwo = $factory->newInteger($two);
+ $tempThree = $factory->newInteger(new BigInteger(3));
+
+ $inv = $tempOne->divide($tempTwo)->negate();
+ $s = $tempThree->negate()->squareRoot()->multiply($inv);
+
+ $lambdas = [
+ $inv->add($s),
+ $inv->subtract($s)
+ ];
+
+ $lhs = $this->multiplyPoint($this->p, $lambdas[0])[0];
+ $rhs = $this->p[0]->multiply($this->beta);
+ $lambda = $lhs->equals($rhs) ? $lambdas[0] : $lambdas[1];
+
+ $this->basis = static::extendedGCD($lambda->toBigInteger(), $this->order);
+ ///*
+ foreach ($this->basis as $basis) {
+ echo strtoupper($basis['a']->toHex(true)) . "\n";
+ echo strtoupper($basis['b']->toHex(true)) . "\n\n";
+ }
+ exit;
+ //*/
+ }
+
+ $npoints = $nscalars = [];
+ for ($i = 0; $i < count($points); $i++) {
+ $p = $points[$i];
+ $k = $scalars[$i]->toBigInteger();
+
+ // begin split
+ list($v1, $v2) = $this->basis;
+
+ $c1 = $v2['b']->multiply($k);
+ list($c1, $r) = $c1->divide($this->order);
+ if ($this->order->compare($r->multiply($two)) <= 0) {
+ $c1 = $c1->add($one);
+ }
+
+ $c2 = $v1['b']->negate()->multiply($k);
+ list($c2, $r) = $c2->divide($this->order);
+ if ($this->order->compare($r->multiply($two)) <= 0) {
+ $c2 = $c2->add($one);
+ }
+
+ $p1 = $c1->multiply($v1['a']);
+ $p2 = $c2->multiply($v2['a']);
+ $q1 = $c1->multiply($v1['b']);
+ $q2 = $c2->multiply($v2['b']);
+
+ $k1 = $k->subtract($p1)->subtract($p2);
+ $k2 = $q1->add($q2)->negate();
+ // end split
+
+ $beta = [
+ $p[0]->multiply($this->beta),
+ $p[1],
+ clone $this->one
+ ];
+
+ if (isset($p['naf'])) {
+ $beta['naf'] = array_map(function ($p) {
+ return [
+ $p[0]->multiply($this->beta),
+ $p[1],
+ clone $this->one
+ ];
+ }, $p['naf']);
+ $beta['nafwidth'] = $p['nafwidth'];
+ }
+
+ if ($k1->isNegative()) {
+ $k1 = $k1->negate();
+ $p = $this->negatePoint($p);
+ }
+
+ if ($k2->isNegative()) {
+ $k2 = $k2->negate();
+ $beta = $this->negatePoint($beta);
+ }
+
+ $pos = 2 * $i;
+ $npoints[$pos] = $p;
+ $nscalars[$pos] = $this->factory->newInteger($k1);
+
+ $pos++;
+ $npoints[$pos] = $beta;
+ $nscalars[$pos] = $this->factory->newInteger($k2);
+ }
+
+ return parent::multiplyAddPoints($npoints, $nscalars);
+ }
+
+ /**
+ * Returns the numerator and denominator of the slope
+ *
+ * @return FiniteField[]
+ */
+ protected function doublePointHelper(array $p)
+ {
+ $numerator = $this->three->multiply($p[0])->multiply($p[0]);
+ $denominator = $this->two->multiply($p[1]);
+ return [$numerator, $denominator];
+ }
+
+ /**
+ * Doubles a jacobian coordinate on the curve
+ *
+ * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianDoublePoint(array $p)
+ {
+ list($x1, $y1, $z1) = $p;
+ $a = $x1->multiply($x1);
+ $b = $y1->multiply($y1);
+ $c = $b->multiply($b);
+ $d = $x1->add($b);
+ $d = $d->multiply($d)->subtract($a)->subtract($c)->multiply($this->two);
+ $e = $this->three->multiply($a);
+ $f = $e->multiply($e);
+ $x3 = $f->subtract($this->two->multiply($d));
+ $y3 = $e->multiply($d->subtract($x3))->subtract(
+ $this->eight->multiply($c)
+ );
+ $z3 = $this->two->multiply($y1)->multiply($z1);
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Doubles a "fresh" jacobian coordinate on the curve
+ *
+ * See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianDoublePointMixed(array $p)
+ {
+ list($x1, $y1) = $p;
+ $xx = $x1->multiply($x1);
+ $yy = $y1->multiply($y1);
+ $yyyy = $yy->multiply($yy);
+ $s = $x1->add($yy);
+ $s = $s->multiply($s)->subtract($xx)->subtract($yyyy)->multiply($this->two);
+ $m = $this->three->multiply($xx);
+ $t = $m->multiply($m)->subtract($this->two->multiply($s));
+ $x3 = $t;
+ $y3 = $s->subtract($t);
+ $y3 = $m->multiply($y3)->subtract($this->eight->multiply($yyyy));
+ $z3 = $this->two->multiply($y1);
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Tests whether or not the x / y values satisfy the equation
+ *
+ * @return boolean
+ */
+ public function verifyPoint(array $p)
+ {
+ list($x, $y) = $p;
+ $lhs = $y->multiply($y);
+ $temp = $x->multiply($x)->multiply($x);
+ $rhs = $temp->add($this->b);
+
+ return $lhs->equals($rhs);
+ }
+
+ /**
+ * Calculates the parameters needed from the Euclidean algorithm as discussed at
+ * http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=148
+ *
+ * @param BigInteger $u
+ * @param BigInteger $v
+ * @return BigInteger[]
+ */
+ protected static function extendedGCD(BigInteger $u, BigInteger $v)
+ {
+ $one = new BigInteger(1);
+ $zero = new BigInteger();
+
+ $a = clone $one;
+ $b = clone $zero;
+ $c = clone $zero;
+ $d = clone $one;
+
+ $stop = $v->bitwise_rightShift($v->getLength() >> 1);
+
+ $a1 = clone $zero;
+ $b1 = clone $zero;
+ $a2 = clone $zero;
+ $b2 = clone $zero;
+
+ $postGreatestIndex = 0;
+
+ while (!$v->equals($zero)) {
+ list($q) = $u->divide($v);
+
+ $temp = $u;
+ $u = $v;
+ $v = $temp->subtract($v->multiply($q));
+
+ $temp = $a;
+ $a = $c;
+ $c = $temp->subtract($a->multiply($q));
+
+ $temp = $b;
+ $b = $d;
+ $d = $temp->subtract($b->multiply($q));
+
+ if ($v->compare($stop) > 0) {
+ $a0 = $v;
+ $b0 = $c;
+ } else {
+ $postGreatestIndex++;
+ }
+
+ if ($postGreatestIndex == 1) {
+ $a1 = $v;
+ $b1 = $c->negate();
+ }
+
+ if ($postGreatestIndex == 2) {
+ $rhs = $a0->multiply($a0)->add($b0->multiply($b0));
+ $lhs = $v->multiply($v)->add($b->multiply($b));
+ if ($lhs->compare($rhs) <= 0) {
+ $a2 = $a0;
+ $b2 = $b0->negate();
+ } else {
+ $a2 = $v;
+ $b2 = $c->negate();
+ }
+
+ break;
+ }
+ }
+
+ return [
+ ['a' => $a1, 'b' => $b1],
+ ['a' => $a2, 'b' => $b2]
+ ];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php
new file mode 100644
index 000000000..431f9575c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Montgomery.php
@@ -0,0 +1,279 @@
+<?php
+
+/**
+ * Curves over y^2 = x^3 + a*x + x
+ *
+ * Technically, a Montgomery curve has a coefficient for y^2 but for Curve25519 and Curve448 that
+ * coefficient is 1.
+ *
+ * Curve25519 and Curve448 do not make use of the y coordinate, which makes it unsuitable for use
+ * with ECDSA / EdDSA. A few other differences between Curve25519 and Ed25519 are discussed at
+ * https://crypto.stackexchange.com/a/43058/4520
+ *
+ * More info:
+ *
+ * https://en.wikipedia.org/wiki/Montgomery_curve
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2019 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Crypt\EC\Curves\Curve25519;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\PrimeField;
+use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
+
+/**
+ * Curves over y^2 = x^3 + a*x + x
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Montgomery extends Base
+{
+ /**
+ * Prime Field Integer factory
+ *
+ * @var PrimeField
+ */
+ protected $factory;
+
+ /**
+ * Cofficient for x
+ *
+ * @var object
+ */
+ protected $a;
+
+ /**
+ * Constant used for point doubling
+ *
+ * @var object
+ */
+ protected $a24;
+
+ /**
+ * The Number Zero
+ *
+ * @var object
+ */
+ protected $zero;
+
+ /**
+ * The Number One
+ *
+ * @var object
+ */
+ protected $one;
+
+ /**
+ * Base Point
+ *
+ * @var object
+ */
+ protected $p;
+
+ /**
+ * The modulo
+ *
+ * @var BigInteger
+ */
+ protected $modulo;
+
+ /**
+ * The Order
+ *
+ * @var BigInteger
+ */
+ protected $order;
+
+ /**
+ * Sets the modulo
+ */
+ public function setModulo(BigInteger $modulo)
+ {
+ $this->modulo = $modulo;
+ $this->factory = new PrimeField($modulo);
+ $this->zero = $this->factory->newInteger(new BigInteger());
+ $this->one = $this->factory->newInteger(new BigInteger(1));
+ }
+
+ /**
+ * Set coefficients a
+ */
+ public function setCoefficients(BigInteger $a)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->a = $this->factory->newInteger($a);
+ $two = $this->factory->newInteger(new BigInteger(2));
+ $four = $this->factory->newInteger(new BigInteger(4));
+ $this->a24 = $this->a->subtract($two)->divide($four);
+ }
+
+ /**
+ * Set x and y coordinates for the base point
+ *
+ * @param BigInteger|PrimeInteger $x
+ * @param BigInteger|PrimeInteger $y
+ * @return PrimeInteger[]
+ */
+ public function setBasePoint($x, $y)
+ {
+ switch (true) {
+ case !$x instanceof BigInteger && !$x instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ case !$y instanceof BigInteger && !$y instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ }
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->p = [
+ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
+ $y instanceof BigInteger ? $this->factory->newInteger($y) : $y
+ ];
+ }
+
+ /**
+ * Retrieve the base point as an array
+ *
+ * @return array
+ */
+ public function getBasePoint()
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ /*
+ if (!isset($this->p)) {
+ throw new \RuntimeException('setBasePoint needs to be called before this method');
+ }
+ */
+ return $this->p;
+ }
+
+ /**
+ * Doubles and adds a point on a curve
+ *
+ * See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3
+ *
+ * @return FiniteField[][]
+ */
+ private function doubleAndAddPoint(array $p, array $q, PrimeInteger $x1)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p) || !count($q)) {
+ return [];
+ }
+
+ if (!isset($p[1])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to XZ coordinates');
+ }
+
+ list($x2, $z2) = $p;
+ list($x3, $z3) = $q;
+
+ $a = $x2->add($z2);
+ $aa = $a->multiply($a);
+ $b = $x2->subtract($z2);
+ $bb = $b->multiply($b);
+ $e = $aa->subtract($bb);
+ $c = $x3->add($z3);
+ $d = $x3->subtract($z3);
+ $da = $d->multiply($a);
+ $cb = $c->multiply($b);
+ $temp = $da->add($cb);
+ $x5 = $temp->multiply($temp);
+ $temp = $da->subtract($cb);
+ $z5 = $x1->multiply($temp->multiply($temp));
+ $x4 = $aa->multiply($bb);
+ $temp = static::class == Curve25519::class ? $bb : $aa;
+ $z4 = $e->multiply($temp->add($this->a24->multiply($e)));
+
+ return [
+ [$x4, $z4],
+ [$x5, $z5]
+ ];
+ }
+
+ /**
+ * Multiply a point on the curve by a scalar
+ *
+ * Uses the montgomery ladder technique as described here:
+ *
+ * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
+ * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
+ *
+ * @return array
+ */
+ public function multiplyPoint(array $p, BigInteger $d)
+ {
+ $p1 = [$this->one, $this->zero];
+ $alreadyInternal = isset($p[1]);
+ $p2 = $this->convertToInternal($p);
+ $x = $p[0];
+
+ $b = $d->toBits();
+ $b = str_pad($b, 256, '0', STR_PAD_LEFT);
+ for ($i = 0; $i < strlen($b); $i++) {
+ $b_i = (int) $b[$i];
+ if ($b_i) {
+ list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x);
+ } else {
+ list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x);
+ }
+ }
+
+ return $alreadyInternal ? $p1 : $this->convertToAffine($p1);
+ }
+
+ /**
+ * Converts an affine point to an XZ coordinate
+ *
+ * From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html
+ *
+ * XZ coordinates represent x y as X Z satsfying the following equations:
+ *
+ * x=X/Z
+ *
+ * @return PrimeInteger[]
+ */
+ public function convertToInternal(array $p)
+ {
+ if (empty($p)) {
+ return [clone $this->zero, clone $this->one];
+ }
+
+ if (isset($p[1])) {
+ return $p;
+ }
+
+ $p[1] = clone $this->one;
+
+ return $p;
+ }
+
+ /**
+ * Returns the affine point
+ *
+ * @return PrimeInteger[]
+ */
+ public function convertToAffine(array $p)
+ {
+ if (!isset($p[1])) {
+ return $p;
+ }
+ list($x, $z) = $p;
+ return [$x->divide($z)];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php
new file mode 100644
index 000000000..b1970557f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/Prime.php
@@ -0,0 +1,785 @@
+<?php
+
+/**
+ * Curves over y^2 = x^3 + a*x + b
+ *
+ * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf
+ * The curve is a weierstrass curve with a[1], a[3] and a[2] set to 0.
+ *
+ * Uses Jacobian Coordinates for speed if able:
+ *
+ * https://en.wikipedia.org/wiki/Jacobian_curve
+ * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\Common\FiniteField\Integer;
+use phpseclib3\Math\PrimeField;
+use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
+
+/**
+ * Curves over y^2 = x^3 + a*x + b
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Prime extends Base
+{
+ /**
+ * Prime Field Integer factory
+ *
+ * @var \phpseclib3\Math\PrimeFields
+ */
+ protected $factory;
+
+ /**
+ * Cofficient for x^1
+ *
+ * @var object
+ */
+ protected $a;
+
+ /**
+ * Cofficient for x^0
+ *
+ * @var object
+ */
+ protected $b;
+
+ /**
+ * Base Point
+ *
+ * @var object
+ */
+ protected $p;
+
+ /**
+ * The number one over the specified finite field
+ *
+ * @var object
+ */
+ protected $one;
+
+ /**
+ * The number two over the specified finite field
+ *
+ * @var object
+ */
+ protected $two;
+
+ /**
+ * The number three over the specified finite field
+ *
+ * @var object
+ */
+ protected $three;
+
+ /**
+ * The number four over the specified finite field
+ *
+ * @var object
+ */
+ protected $four;
+
+ /**
+ * The number eight over the specified finite field
+ *
+ * @var object
+ */
+ protected $eight;
+
+ /**
+ * The modulo
+ *
+ * @var BigInteger
+ */
+ protected $modulo;
+
+ /**
+ * The Order
+ *
+ * @var BigInteger
+ */
+ protected $order;
+
+ /**
+ * Sets the modulo
+ */
+ public function setModulo(BigInteger $modulo)
+ {
+ $this->modulo = $modulo;
+ $this->factory = new PrimeField($modulo);
+ $this->two = $this->factory->newInteger(new BigInteger(2));
+ $this->three = $this->factory->newInteger(new BigInteger(3));
+ // used by jacobian coordinates
+ $this->one = $this->factory->newInteger(new BigInteger(1));
+ $this->four = $this->factory->newInteger(new BigInteger(4));
+ $this->eight = $this->factory->newInteger(new BigInteger(8));
+ }
+
+ /**
+ * Set coefficients a and b
+ */
+ public function setCoefficients(BigInteger $a, BigInteger $b)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->a = $this->factory->newInteger($a);
+ $this->b = $this->factory->newInteger($b);
+ }
+
+ /**
+ * Set x and y coordinates for the base point
+ *
+ * @param BigInteger|PrimeInteger $x
+ * @param BigInteger|PrimeInteger $y
+ * @return PrimeInteger[]
+ */
+ public function setBasePoint($x, $y)
+ {
+ switch (true) {
+ case !$x instanceof BigInteger && !$x instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ case !$y instanceof BigInteger && !$y instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ }
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->p = [
+ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
+ $y instanceof BigInteger ? $this->factory->newInteger($y) : $y
+ ];
+ }
+
+ /**
+ * Retrieve the base point as an array
+ *
+ * @return array
+ */
+ public function getBasePoint()
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ /*
+ if (!isset($this->p)) {
+ throw new \RuntimeException('setBasePoint needs to be called before this method');
+ }
+ */
+ return $this->p;
+ }
+
+ /**
+ * Adds two "fresh" jacobian form on the curve
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianAddPointMixedXY(array $p, array $q)
+ {
+ list($u1, $s1) = $p;
+ list($u2, $s2) = $q;
+ if ($u1->equals($u2)) {
+ if (!$s1->equals($s2)) {
+ return [];
+ } else {
+ return $this->doublePoint($p);
+ }
+ }
+ $h = $u2->subtract($u1);
+ $r = $s2->subtract($s1);
+ $h2 = $h->multiply($h);
+ $h3 = $h2->multiply($h);
+ $v = $u1->multiply($h2);
+ $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
+ $y3 = $r->multiply(
+ $v->subtract($x3)
+ )->subtract(
+ $s1->multiply($h3)
+ );
+ return [$x3, $y3, $h];
+ }
+
+ /**
+ * Adds one "fresh" jacobian form on the curve
+ *
+ * The second parameter should be the "fresh" one
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianAddPointMixedX(array $p, array $q)
+ {
+ list($u1, $s1, $z1) = $p;
+ list($x2, $y2) = $q;
+
+ $z12 = $z1->multiply($z1);
+
+ $u2 = $x2->multiply($z12);
+ $s2 = $y2->multiply($z12->multiply($z1));
+ if ($u1->equals($u2)) {
+ if (!$s1->equals($s2)) {
+ return [];
+ } else {
+ return $this->doublePoint($p);
+ }
+ }
+ $h = $u2->subtract($u1);
+ $r = $s2->subtract($s1);
+ $h2 = $h->multiply($h);
+ $h3 = $h2->multiply($h);
+ $v = $u1->multiply($h2);
+ $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
+ $y3 = $r->multiply(
+ $v->subtract($x3)
+ )->subtract(
+ $s1->multiply($h3)
+ );
+ $z3 = $h->multiply($z1);
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Adds two jacobian coordinates on the curve
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianAddPoint(array $p, array $q)
+ {
+ list($x1, $y1, $z1) = $p;
+ list($x2, $y2, $z2) = $q;
+
+ $z12 = $z1->multiply($z1);
+ $z22 = $z2->multiply($z2);
+
+ $u1 = $x1->multiply($z22);
+ $u2 = $x2->multiply($z12);
+ $s1 = $y1->multiply($z22->multiply($z2));
+ $s2 = $y2->multiply($z12->multiply($z1));
+ if ($u1->equals($u2)) {
+ if (!$s1->equals($s2)) {
+ return [];
+ } else {
+ return $this->doublePoint($p);
+ }
+ }
+ $h = $u2->subtract($u1);
+ $r = $s2->subtract($s1);
+ $h2 = $h->multiply($h);
+ $h3 = $h2->multiply($h);
+ $v = $u1->multiply($h2);
+ $x3 = $r->multiply($r)->subtract($h3)->subtract($v->multiply($this->two));
+ $y3 = $r->multiply(
+ $v->subtract($x3)
+ )->subtract(
+ $s1->multiply($h3)
+ );
+ $z3 = $h->multiply($z1)->multiply($z2);
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Adds two points on the curve
+ *
+ * @return FiniteField[]
+ */
+ public function addPoint(array $p, array $q)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p) || !count($q)) {
+ if (count($q)) {
+ return $q;
+ }
+ if (count($p)) {
+ return $p;
+ }
+ return [];
+ }
+
+ // use jacobian coordinates
+ if (isset($p[2]) && isset($q[2])) {
+ if (isset($p['fresh']) && isset($q['fresh'])) {
+ return $this->jacobianAddPointMixedXY($p, $q);
+ }
+ if (isset($p['fresh'])) {
+ return $this->jacobianAddPointMixedX($q, $p);
+ }
+ if (isset($q['fresh'])) {
+ return $this->jacobianAddPointMixedX($p, $q);
+ }
+ return $this->jacobianAddPoint($p, $q);
+ }
+
+ if (isset($p[2]) || isset($q[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to Jacobi coordinates or vice versa');
+ }
+
+ if ($p[0]->equals($q[0])) {
+ if (!$p[1]->equals($q[1])) {
+ return [];
+ } else { // eg. doublePoint
+ list($numerator, $denominator) = $this->doublePointHelper($p);
+ }
+ } else {
+ $numerator = $q[1]->subtract($p[1]);
+ $denominator = $q[0]->subtract($p[0]);
+ }
+ $slope = $numerator->divide($denominator);
+ $x = $slope->multiply($slope)->subtract($p[0])->subtract($q[0]);
+ $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]);
+
+ return [$x, $y];
+ }
+
+ /**
+ * Returns the numerator and denominator of the slope
+ *
+ * @return FiniteField[]
+ */
+ protected function doublePointHelper(array $p)
+ {
+ $numerator = $this->three->multiply($p[0])->multiply($p[0])->add($this->a);
+ $denominator = $this->two->multiply($p[1]);
+ return [$numerator, $denominator];
+ }
+
+ /**
+ * Doubles a jacobian coordinate on the curve
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianDoublePoint(array $p)
+ {
+ list($x, $y, $z) = $p;
+ $x2 = $x->multiply($x);
+ $y2 = $y->multiply($y);
+ $z2 = $z->multiply($z);
+ $s = $this->four->multiply($x)->multiply($y2);
+ $m1 = $this->three->multiply($x2);
+ $m2 = $this->a->multiply($z2->multiply($z2));
+ $m = $m1->add($m2);
+ $x1 = $m->multiply($m)->subtract($this->two->multiply($s));
+ $y1 = $m->multiply($s->subtract($x1))->subtract(
+ $this->eight->multiply($y2->multiply($y2))
+ );
+ $z1 = $this->two->multiply($y)->multiply($z);
+ return [$x1, $y1, $z1];
+ }
+
+ /**
+ * Doubles a "fresh" jacobian coordinate on the curve
+ *
+ * @return FiniteField[]
+ */
+ protected function jacobianDoublePointMixed(array $p)
+ {
+ list($x, $y) = $p;
+ $x2 = $x->multiply($x);
+ $y2 = $y->multiply($y);
+ $s = $this->four->multiply($x)->multiply($y2);
+ $m1 = $this->three->multiply($x2);
+ $m = $m1->add($this->a);
+ $x1 = $m->multiply($m)->subtract($this->two->multiply($s));
+ $y1 = $m->multiply($s->subtract($x1))->subtract(
+ $this->eight->multiply($y2->multiply($y2))
+ );
+ $z1 = $this->two->multiply($y);
+ return [$x1, $y1, $z1];
+ }
+
+ /**
+ * Doubles a point on a curve
+ *
+ * @return FiniteField[]
+ */
+ public function doublePoint(array $p)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p)) {
+ return [];
+ }
+
+ // use jacobian coordinates
+ if (isset($p[2])) {
+ if (isset($p['fresh'])) {
+ return $this->jacobianDoublePointMixed($p);
+ }
+ return $this->jacobianDoublePoint($p);
+ }
+
+ list($numerator, $denominator) = $this->doublePointHelper($p);
+
+ $slope = $numerator->divide($denominator);
+
+ $x = $slope->multiply($slope)->subtract($p[0])->subtract($p[0]);
+ $y = $slope->multiply($p[0]->subtract($x))->subtract($p[1]);
+
+ return [$x, $y];
+ }
+
+ /**
+ * Returns the X coordinate and the derived Y coordinate
+ *
+ * @return array
+ */
+ public function derivePoint($m)
+ {
+ $y = ord(Strings::shift($m));
+ $x = new BigInteger($m, 256);
+ $xp = $this->convertInteger($x);
+ switch ($y) {
+ case 2:
+ $ypn = false;
+ break;
+ case 3:
+ $ypn = true;
+ break;
+ default:
+ throw new \RuntimeException('Coordinate not in recognized format');
+ }
+ $temp = $xp->multiply($this->a);
+ $temp = $xp->multiply($xp)->multiply($xp)->add($temp);
+ $temp = $temp->add($this->b);
+ $b = $temp->squareRoot();
+ if (!$b) {
+ throw new \RuntimeException('Unable to derive Y coordinate');
+ }
+ $bn = $b->isOdd();
+ $yp = $ypn == $bn ? $b : $b->negate();
+ return [$xp, $yp];
+ }
+
+ /**
+ * Tests whether or not the x / y values satisfy the equation
+ *
+ * @return boolean
+ */
+ public function verifyPoint(array $p)
+ {
+ list($x, $y) = $p;
+ $lhs = $y->multiply($y);
+ $temp = $x->multiply($this->a);
+ $temp = $x->multiply($x)->multiply($x)->add($temp);
+ $rhs = $temp->add($this->b);
+
+ return $lhs->equals($rhs);
+ }
+
+ /**
+ * Returns the modulo
+ *
+ * @return BigInteger
+ */
+ public function getModulo()
+ {
+ return $this->modulo;
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return PrimeInteger
+ */
+ public function getA()
+ {
+ return $this->a;
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return PrimeInteger
+ */
+ public function getB()
+ {
+ return $this->b;
+ }
+
+ /**
+ * Multiply and Add Points
+ *
+ * Adapted from:
+ * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L125
+ *
+ * @return int[]
+ */
+ public function multiplyAddPoints(array $points, array $scalars)
+ {
+ $length = count($points);
+
+ foreach ($points as &$point) {
+ $point = $this->convertToInternal($point);
+ }
+
+ $wnd = [$this->getNAFPoints($points[0], 7)];
+ $wndWidth = [isset($points[0]['nafwidth']) ? $points[0]['nafwidth'] : 7];
+ for ($i = 1; $i < $length; $i++) {
+ $wnd[] = $this->getNAFPoints($points[$i], 1);
+ $wndWidth[] = isset($points[$i]['nafwidth']) ? $points[$i]['nafwidth'] : 1;
+ }
+
+ $naf = [];
+
+ // comb all window NAFs
+
+ $max = 0;
+ for ($i = $length - 1; $i >= 1; $i -= 2) {
+ $a = $i - 1;
+ $b = $i;
+ if ($wndWidth[$a] != 1 || $wndWidth[$b] != 1) {
+ $naf[$a] = $scalars[$a]->getNAF($wndWidth[$a]);
+ $naf[$b] = $scalars[$b]->getNAF($wndWidth[$b]);
+ $max = max(count($naf[$a]), count($naf[$b]), $max);
+ continue;
+ }
+
+ $comb = [
+ $points[$a], // 1
+ null, // 3
+ null, // 5
+ $points[$b] // 7
+ ];
+
+ $comb[1] = $this->addPoint($points[$a], $points[$b]);
+ $comb[2] = $this->addPoint($points[$a], $this->negatePoint($points[$b]));
+
+ $index = [
+ -3, /* -1 -1 */
+ -1, /* -1 0 */
+ -5, /* -1 1 */
+ -7, /* 0 -1 */
+ 0, /* 0 -1 */
+ 7, /* 0 1 */
+ 5, /* 1 -1 */
+ 1, /* 1 0 */
+ 3 /* 1 1 */
+ ];
+
+ $jsf = self::getJSFPoints($scalars[$a], $scalars[$b]);
+
+ $max = max(count($jsf[0]), $max);
+ if ($max > 0) {
+ $naf[$a] = array_fill(0, $max, 0);
+ $naf[$b] = array_fill(0, $max, 0);
+ } else {
+ $naf[$a] = [];
+ $naf[$b] = [];
+ }
+
+ for ($j = 0; $j < $max; $j++) {
+ $ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0;
+ $jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0;
+
+ $naf[$a][$j] = $index[3 * ($ja + 1) + $jb + 1];
+ $naf[$b][$j] = 0;
+ $wnd[$a] = $comb;
+ }
+ }
+
+ $acc = [];
+ $temp = [0, 0, 0, 0];
+ for ($i = $max; $i >= 0; $i--) {
+ $k = 0;
+ while ($i >= 0) {
+ $zero = true;
+ for ($j = 0; $j < $length; $j++) {
+ $temp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0;
+ if ($temp[$j] != 0) {
+ $zero = false;
+ }
+ }
+ if (!$zero) {
+ break;
+ }
+ $k++;
+ $i--;
+ }
+
+ if ($i >= 0) {
+ $k++;
+ }
+ while ($k--) {
+ $acc = $this->doublePoint($acc);
+ }
+
+ if ($i < 0) {
+ break;
+ }
+
+ for ($j = 0; $j < $length; $j++) {
+ $z = $temp[$j];
+ $p = null;
+ if ($z == 0) {
+ continue;
+ }
+ $p = $z > 0 ?
+ $wnd[$j][($z - 1) >> 1] :
+ $this->negatePoint($wnd[$j][(-$z - 1) >> 1]);
+ $acc = $this->addPoint($acc, $p);
+ }
+ }
+
+ return $this->convertToAffine($acc);
+ }
+
+ /**
+ * Precomputes NAF points
+ *
+ * Adapted from:
+ * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/curve/base.js#L351
+ *
+ * @return int[]
+ */
+ private function getNAFPoints(array $point, $wnd)
+ {
+ if (isset($point['naf'])) {
+ return $point['naf'];
+ }
+
+ $res = [$point];
+ $max = (1 << $wnd) - 1;
+ $dbl = $max == 1 ? null : $this->doublePoint($point);
+ for ($i = 1; $i < $max; $i++) {
+ $res[] = $this->addPoint($res[$i - 1], $dbl);
+ }
+
+ $point['naf'] = $res;
+
+ /*
+ $str = '';
+ foreach ($res as $re) {
+ $re[0] = bin2hex($re[0]->toBytes());
+ $re[1] = bin2hex($re[1]->toBytes());
+ $str.= " ['$re[0]', '$re[1]'],\r\n";
+ }
+ file_put_contents('temp.txt', $str);
+ exit;
+ */
+
+ return $res;
+ }
+
+ /**
+ * Precomputes points in Joint Sparse Form
+ *
+ * Adapted from:
+ * https://github.com/indutny/elliptic/blob/725bd91/lib/elliptic/utils.js#L96
+ *
+ * @return int[]
+ */
+ private static function getJSFPoints(Integer $k1, Integer $k2)
+ {
+ static $three;
+ if (!isset($three)) {
+ $three = new BigInteger(3);
+ }
+
+ $jsf = [[], []];
+ $k1 = $k1->toBigInteger();
+ $k2 = $k2->toBigInteger();
+ $d1 = 0;
+ $d2 = 0;
+
+ while ($k1->compare(new BigInteger(-$d1)) > 0 || $k2->compare(new BigInteger(-$d2)) > 0) {
+ // first phase
+ $m14 = $k1->testBit(0) + 2 * $k1->testBit(1);
+ $m14 += $d1;
+ $m14 &= 3;
+
+ $m24 = $k2->testBit(0) + 2 * $k2->testBit(1);
+ $m24 += $d2;
+ $m24 &= 3;
+
+ if ($m14 == 3) {
+ $m14 = -1;
+ }
+ if ($m24 == 3) {
+ $m24 = -1;
+ }
+
+ $u1 = 0;
+ if ($m14 & 1) { // if $m14 is odd
+ $m8 = $k1->testBit(0) + 2 * $k1->testBit(1) + 4 * $k1->testBit(2);
+ $m8 += $d1;
+ $m8 &= 7;
+ $u1 = ($m8 == 3 || $m8 == 5) && $m24 == 2 ? -$m14 : $m14;
+ }
+ $jsf[0][] = $u1;
+
+ $u2 = 0;
+ if ($m24 & 1) { // if $m24 is odd
+ $m8 = $k2->testBit(0) + 2 * $k2->testBit(1) + 4 * $k2->testBit(2);
+ $m8 += $d2;
+ $m8 &= 7;
+ $u2 = ($m8 == 3 || $m8 == 5) && $m14 == 2 ? -$m24 : $m24;
+ }
+ $jsf[1][] = $u2;
+
+ // second phase
+ if (2 * $d1 == $u1 + 1) {
+ $d1 = 1 - $d1;
+ }
+ if (2 * $d2 == $u2 + 1) {
+ $d2 = 1 - $d2;
+ }
+ $k1 = $k1->bitwise_rightShift(1);
+ $k2 = $k2->bitwise_rightShift(1);
+ }
+
+ return $jsf;
+ }
+
+ /**
+ * Returns the affine point
+ *
+ * A Jacobian Coordinate is of the form (x, y, z).
+ * To convert a Jacobian Coordinate to an Affine Point
+ * you do (x / z^2, y / z^3)
+ *
+ * @return PrimeInteger[]
+ */
+ public function convertToAffine(array $p)
+ {
+ if (!isset($p[2])) {
+ return $p;
+ }
+ list($x, $y, $z) = $p;
+ $z = $this->one->divide($z);
+ $z2 = $z->multiply($z);
+ return [
+ $x->multiply($z2),
+ $y->multiply($z2)->multiply($z)
+ ];
+ }
+
+ /**
+ * Converts an affine point to a jacobian coordinate
+ *
+ * @return PrimeInteger[]
+ */
+ public function convertToInternal(array $p)
+ {
+ if (isset($p[2])) {
+ return $p;
+ }
+
+ $p[2] = clone $this->one;
+ $p['fresh'] = true;
+ return $p;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php
new file mode 100644
index 000000000..99aa38b20
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/BaseCurves/TwistedEdwards.php
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
+ *
+ * http://www.secg.org/SEC2-Ver-1.0.pdf provides for curves with custom parameters.
+ * ie. the coefficients can be arbitrary set through specially formatted keys, etc.
+ * As such, Prime.php is built very generically and it's not able to take full
+ * advantage of curves with 0 coefficients to produce simplified point doubling,
+ * point addition. Twisted Edwards curves, in contrast, do not have a way, currently,
+ * to customize them. As such, we can omit the super generic stuff from this class
+ * and let the named curves (Ed25519 and Ed448) define their own custom tailored
+ * point addition and point doubling methods.
+ *
+ * More info:
+ *
+ * https://en.wikipedia.org/wiki/Twisted_Edwards_curve
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\BaseCurves;
+
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\PrimeField;
+use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
+
+/**
+ * Curves over a*x^2 + y^2 = 1 + d*x^2*y^2
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class TwistedEdwards extends Base
+{
+ /**
+ * The modulo
+ *
+ * @var BigInteger
+ */
+ protected $modulo;
+
+ /**
+ * Cofficient for x^2
+ *
+ * @var object
+ */
+ protected $a;
+
+ /**
+ * Cofficient for x^2*y^2
+ *
+ * @var object
+ */
+ protected $d;
+
+ /**
+ * Base Point
+ *
+ * @var object[]
+ */
+ protected $p;
+
+ /**
+ * The number zero over the specified finite field
+ *
+ * @var object
+ */
+ protected $zero;
+
+ /**
+ * The number one over the specified finite field
+ *
+ * @var object
+ */
+ protected $one;
+
+ /**
+ * The number two over the specified finite field
+ *
+ * @var object
+ */
+ protected $two;
+
+ /**
+ * Sets the modulo
+ */
+ public function setModulo(BigInteger $modulo)
+ {
+ $this->modulo = $modulo;
+ $this->factory = new PrimeField($modulo);
+ $this->zero = $this->factory->newInteger(new BigInteger(0));
+ $this->one = $this->factory->newInteger(new BigInteger(1));
+ $this->two = $this->factory->newInteger(new BigInteger(2));
+ }
+
+ /**
+ * Set coefficients a and b
+ */
+ public function setCoefficients(BigInteger $a, BigInteger $d)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->a = $this->factory->newInteger($a);
+ $this->d = $this->factory->newInteger($d);
+ }
+
+ /**
+ * Set x and y coordinates for the base point
+ */
+ public function setBasePoint($x, $y)
+ {
+ switch (true) {
+ case !$x instanceof BigInteger && !$x instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ case !$y instanceof BigInteger && !$y instanceof PrimeInteger:
+ throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
+ }
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ $this->p = [
+ $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
+ $y instanceof BigInteger ? $this->factory->newInteger($y) : $y
+ ];
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return PrimeInteger
+ */
+ public function getA()
+ {
+ return $this->a;
+ }
+
+ /**
+ * Returns the a coefficient
+ *
+ * @return PrimeInteger
+ */
+ public function getD()
+ {
+ return $this->d;
+ }
+
+ /**
+ * Retrieve the base point as an array
+ *
+ * @return array
+ */
+ public function getBasePoint()
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+ /*
+ if (!isset($this->p)) {
+ throw new \RuntimeException('setBasePoint needs to be called before this method');
+ }
+ */
+ return $this->p;
+ }
+
+ /**
+ * Returns the affine point
+ *
+ * @return PrimeInteger[]
+ */
+ public function convertToAffine(array $p)
+ {
+ if (!isset($p[2])) {
+ return $p;
+ }
+ list($x, $y, $z) = $p;
+ $z = $this->one->divide($z);
+ return [
+ $x->multiply($z),
+ $y->multiply($z)
+ ];
+ }
+
+ /**
+ * Returns the modulo
+ *
+ * @return BigInteger
+ */
+ public function getModulo()
+ {
+ return $this->modulo;
+ }
+
+ /**
+ * Tests whether or not the x / y values satisfy the equation
+ *
+ * @return boolean
+ */
+ public function verifyPoint(array $p)
+ {
+ list($x, $y) = $p;
+ $x2 = $x->multiply($x);
+ $y2 = $y->multiply($y);
+
+ $lhs = $this->a->multiply($x2)->add($y2);
+ $rhs = $this->d->multiply($x2)->multiply($y2)->add($this->one);
+
+ return $lhs->equals($rhs);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php
new file mode 100644
index 000000000..0f3f4d827
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve25519.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Curve25519
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2019 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery;
+use phpseclib3\Math\BigInteger;
+
+class Curve25519 extends Montgomery
+{
+ public function __construct()
+ {
+ // 2^255 - 19
+ $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16));
+ $this->a24 = $this->factory->newInteger(new BigInteger('121666'));
+ $this->p = [$this->factory->newInteger(new BigInteger(9))];
+ // 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
+ $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16));
+
+ /*
+ $this->setCoefficients(
+ new BigInteger('486662'), // a
+ );
+ $this->setBasePoint(
+ new BigInteger(9),
+ new BigInteger('14781619447589544791020593568409986887264606134616475288964881837755586237401')
+ );
+ */
+ }
+
+ /**
+ * Multiply a point on the curve by a scalar
+ *
+ * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8
+ *
+ * @return array
+ */
+ public function multiplyPoint(array $p, BigInteger $d)
+ {
+ //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes())));
+ //return [$this->factory->newInteger(new BigInteger($r, 256))];
+
+ $d = $d->toBytes();
+ $d &= "\xF8" . str_repeat("\xFF", 30) . "\x7F";
+ $d = strrev($d);
+ $d |= "\x40";
+ $d = new BigInteger($d, -256);
+
+ return parent::multiplyPoint($p, $d);
+ }
+
+ /**
+ * Creates a random scalar multiplier
+ *
+ * @return BigInteger
+ */
+ public function createRandomMultiplier()
+ {
+ return BigInteger::random(256);
+ }
+
+ /**
+ * Performs range check
+ */
+ public function rangeCheck(BigInteger $x)
+ {
+ if ($x->getLength() > 256 || $x->isNegative()) {
+ throw new \RangeException('x must be a positive integer less than 256 bytes in length');
+ }
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php
new file mode 100644
index 000000000..f4a442315
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Curve448.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Curve448
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2019 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery;
+use phpseclib3\Math\BigInteger;
+
+class Curve448 extends Montgomery
+{
+ public function __construct()
+ {
+ // 2^448 - 2^224 - 1
+ $this->setModulo(new BigInteger(
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
+ 16
+ ));
+ $this->a24 = $this->factory->newInteger(new BigInteger('39081'));
+ $this->p = [$this->factory->newInteger(new BigInteger(5))];
+ // 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
+ $this->setOrder(new BigInteger(
+ '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
+ 16
+ ));
+
+ /*
+ $this->setCoefficients(
+ new BigInteger('156326'), // a
+ );
+ $this->setBasePoint(
+ new BigInteger(5),
+ new BigInteger(
+ '355293926785568175264127502063783334808976399387714271831880898' .
+ '435169088786967410002932673765864550910142774147268105838985595290' .
+ '606362')
+ );
+ */
+ }
+
+ /**
+ * Multiply a point on the curve by a scalar
+ *
+ * Modifies the scalar as described at https://tools.ietf.org/html/rfc7748#page-8
+ *
+ * @return array
+ */
+ public function multiplyPoint(array $p, BigInteger $d)
+ {
+ //$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes())));
+ //return [$this->factory->newInteger(new BigInteger($r, 256))];
+
+ $d = $d->toBytes();
+ $d[0] = $d[0] & "\xFC";
+ $d = strrev($d);
+ $d |= "\x80";
+ $d = new BigInteger($d, 256);
+
+ return parent::multiplyPoint($p, $d);
+ }
+
+ /**
+ * Creates a random scalar multiplier
+ *
+ * @return BigInteger
+ */
+ public function createRandomMultiplier()
+ {
+ return BigInteger::random(446);
+ }
+
+ /**
+ * Performs range check
+ */
+ public function rangeCheck(BigInteger $x)
+ {
+ if ($x->getLength() > 448 || $x->isNegative()) {
+ throw new \RangeException('x must be a positive integer less than 446 bytes in length');
+ }
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php
new file mode 100644
index 000000000..9d3de684f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed25519.php
@@ -0,0 +1,333 @@
+<?php
+
+/**
+ * Ed25519
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Math\BigInteger;
+
+class Ed25519 extends TwistedEdwards
+{
+ const HASH = 'sha512';
+ /*
+ Per https://tools.ietf.org/html/rfc8032#page-6 EdDSA has several parameters, one of which is b:
+
+ 2. An integer b with 2^(b-1) > p. EdDSA public keys have exactly b
+ bits, and EdDSA signatures have exactly 2*b bits. b is
+ recommended to be a multiple of 8, so public key and signature
+ lengths are an integral number of octets.
+
+ SIZE corresponds to b
+ */
+ const SIZE = 32;
+
+ public function __construct()
+ {
+ // 2^255 - 19
+ $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED', 16));
+ $this->setCoefficients(
+ // -1
+ new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC', 16), // a
+ // -121665/121666
+ new BigInteger('52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3', 16) // d
+ );
+ $this->setBasePoint(
+ new BigInteger('216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A', 16),
+ new BigInteger('6666666666666666666666666666666666666666666666666666666666666658', 16)
+ );
+ $this->setOrder(new BigInteger('1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED', 16));
+ // algorithm 14.47 from http://cacr.uwaterloo.ca/hac/about/chap14.pdf#page=16
+ /*
+ $this->setReduction(function($x) {
+ $parts = $x->bitwise_split(255);
+ $className = $this->className;
+
+ if (count($parts) > 2) {
+ list(, $r) = $x->divide($className::$modulo);
+ return $r;
+ }
+
+ $zero = new BigInteger();
+ $c = new BigInteger(19);
+
+ switch (count($parts)) {
+ case 2:
+ list($qi, $ri) = $parts;
+ break;
+ case 1:
+ $qi = $zero;
+ list($ri) = $parts;
+ break;
+ case 0:
+ return $zero;
+ }
+ $r = $ri;
+
+ while ($qi->compare($zero) > 0) {
+ $temp = $qi->multiply($c)->bitwise_split(255);
+ if (count($temp) == 2) {
+ list($qi, $ri) = $temp;
+ } else {
+ $qi = $zero;
+ list($ri) = $temp;
+ }
+ $r = $r->add($ri);
+ }
+
+ while ($r->compare($className::$modulo) > 0) {
+ $r = $r->subtract($className::$modulo);
+ }
+ return $r;
+ });
+ */
+ }
+
+ /**
+ * Recover X from Y
+ *
+ * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.1.3
+ *
+ * Used by EC\Keys\Common.php
+ *
+ * @param BigInteger $y
+ * @param boolean $sign
+ * @return object[]
+ */
+ public function recoverX(BigInteger $y, $sign)
+ {
+ $y = $this->factory->newInteger($y);
+
+ $y2 = $y->multiply($y);
+ $u = $y2->subtract($this->one);
+ $v = $this->d->multiply($y2)->add($this->one);
+ $x2 = $u->divide($v);
+ if ($x2->equals($this->zero)) {
+ if ($sign) {
+ throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)');
+ }
+ return clone $this->zero;
+ }
+ // find the square root
+ /* we don't do $x2->squareRoot() because, quoting from
+ https://tools.ietf.org/html/rfc8032#section-5.1.1:
+
+ "For point decoding or "decompression", square roots modulo p are
+ needed. They can be computed using the Tonelli-Shanks algorithm or
+ the special case for p = 5 (mod 8). To find a square root of a,
+ first compute the candidate root x = a^((p+3)/8) (mod p)."
+ */
+ $exp = $this->getModulo()->add(new BigInteger(3));
+ $exp = $exp->bitwise_rightShift(3);
+ $x = $x2->pow($exp);
+
+ // If v x^2 = -u (mod p), set x <-- x * 2^((p-1)/4), which is a square root.
+ if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
+ $temp = $this->getModulo()->subtract(new BigInteger(1));
+ $temp = $temp->bitwise_rightShift(2);
+ $temp = $this->two->pow($temp);
+ $x = $x->multiply($temp);
+ if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
+ throw new \RuntimeException('Unable to recover X coordinate');
+ }
+ }
+ if ($x->isOdd() != $sign) {
+ $x = $x->negate();
+ }
+
+ return [$x, $y];
+ }
+
+ /**
+ * Extract Secret Scalar
+ *
+ * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.1.5
+ *
+ * Used by the various key handlers
+ *
+ * @param string $str
+ * @return array
+ */
+ public function extractSecret($str)
+ {
+ if (strlen($str) != 32) {
+ throw new \LengthException('Private Key should be 32-bytes long');
+ }
+ // 1. Hash the 32-byte private key using SHA-512, storing the digest in
+ // a 64-octet large buffer, denoted h. Only the lower 32 bytes are
+ // used for generating the public key.
+ $hash = new Hash('sha512');
+ $h = $hash->hash($str);
+ $h = substr($h, 0, 32);
+ // 2. Prune the buffer: The lowest three bits of the first octet are
+ // cleared, the highest bit of the last octet is cleared, and the
+ // second highest bit of the last octet is set.
+ $h[0] = $h[0] & chr(0xF8);
+ $h = strrev($h);
+ $h[0] = ($h[0] & chr(0x3F)) | chr(0x40);
+ // 3. Interpret the buffer as the little-endian integer, forming a
+ // secret scalar s.
+ $dA = new BigInteger($h, 256);
+
+ return [
+ 'dA' => $dA,
+ 'secret' => $str
+ ];
+ }
+
+ /**
+ * Encode a point as a string
+ *
+ * @param array $point
+ * @return string
+ */
+ public function encodePoint($point)
+ {
+ list($x, $y) = $point;
+ $y = $y->toBytes();
+ $y[0] = $y[0] & chr(0x7F);
+ if ($x->isOdd()) {
+ $y[0] = $y[0] | chr(0x80);
+ }
+ $y = strrev($y);
+
+ return $y;
+ }
+
+ /**
+ * Creates a random scalar multiplier
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer
+ */
+ public function createRandomMultiplier()
+ {
+ return $this->extractSecret(Random::string(32))['dA'];
+ }
+
+ /**
+ * Converts an affine point to an extended homogeneous coordinate
+ *
+ * From https://tools.ietf.org/html/rfc8032#section-5.1.4 :
+ *
+ * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T),
+ * with x = X/Z, y = Y/Z, x * y = T/Z.
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer[]
+ */
+ public function convertToInternal(array $p)
+ {
+ if (empty($p)) {
+ return [clone $this->zero, clone $this->one, clone $this->one, clone $this->zero];
+ }
+
+ if (isset($p[2])) {
+ return $p;
+ }
+
+ $p[2] = clone $this->one;
+ $p[3] = $p[0]->multiply($p[1]);
+
+ return $p;
+ }
+
+ /**
+ * Doubles a point on a curve
+ *
+ * @return FiniteField[]
+ */
+ public function doublePoint(array $p)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p)) {
+ return [];
+ }
+
+ if (!isset($p[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ // from https://tools.ietf.org/html/rfc8032#page-12
+
+ list($x1, $y1, $z1, $t1) = $p;
+
+ $a = $x1->multiply($x1);
+ $b = $y1->multiply($y1);
+ $c = $this->two->multiply($z1)->multiply($z1);
+ $h = $a->add($b);
+ $temp = $x1->add($y1);
+ $e = $h->subtract($temp->multiply($temp));
+ $g = $a->subtract($b);
+ $f = $c->add($g);
+
+ $x3 = $e->multiply($f);
+ $y3 = $g->multiply($h);
+ $t3 = $e->multiply($h);
+ $z3 = $f->multiply($g);
+
+ return [$x3, $y3, $z3, $t3];
+ }
+
+ /**
+ * Adds two points on the curve
+ *
+ * @return FiniteField[]
+ */
+ public function addPoint(array $p, array $q)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p) || !count($q)) {
+ if (count($q)) {
+ return $q;
+ }
+ if (count($p)) {
+ return $p;
+ }
+ return [];
+ }
+
+ if (!isset($p[2]) || !isset($q[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ if ($p[0]->equals($q[0])) {
+ return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
+ }
+
+ // from https://tools.ietf.org/html/rfc8032#page-12
+
+ list($x1, $y1, $z1, $t1) = $p;
+ list($x2, $y2, $z2, $t2) = $q;
+
+ $a = $y1->subtract($x1)->multiply($y2->subtract($x2));
+ $b = $y1->add($x1)->multiply($y2->add($x2));
+ $c = $t1->multiply($this->two)->multiply($this->d)->multiply($t2);
+ $d = $z1->multiply($this->two)->multiply($z2);
+ $e = $b->subtract($a);
+ $f = $d->subtract($c);
+ $g = $d->add($c);
+ $h = $b->add($a);
+
+ $x3 = $e->multiply($f);
+ $y3 = $g->multiply($h);
+ $t3 = $e->multiply($h);
+ $z3 = $f->multiply($g);
+
+ return [$x3, $y3, $z3, $t3];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php
new file mode 100644
index 000000000..5451f909f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/Ed448.php
@@ -0,0 +1,273 @@
+<?php
+
+/**
+ * Ed448
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Math\BigInteger;
+
+class Ed448 extends TwistedEdwards
+{
+ const HASH = 'shake256-912';
+ const SIZE = 57;
+
+ public function __construct()
+ {
+ // 2^448 - 2^224 - 1
+ $this->setModulo(new BigInteger(
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(1),
+ // -39081
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE' .
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6756', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('4F1970C66BED0DED221D15A622BF36DA9E146570470F1767EA6DE324' .
+ 'A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05E', 16),
+ new BigInteger('693F46716EB6BC248876203756C9C7624BEA73736CA3984087789C1E' .
+ '05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA14', 16)
+ );
+ $this->setOrder(new BigInteger(
+ '3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ '7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3',
+ 16
+ ));
+ }
+
+ /**
+ * Recover X from Y
+ *
+ * Implements steps 2-4 at https://tools.ietf.org/html/rfc8032#section-5.2.3
+ *
+ * Used by EC\Keys\Common.php
+ *
+ * @param BigInteger $y
+ * @param boolean $sign
+ * @return object[]
+ */
+ public function recoverX(BigInteger $y, $sign)
+ {
+ $y = $this->factory->newInteger($y);
+
+ $y2 = $y->multiply($y);
+ $u = $y2->subtract($this->one);
+ $v = $this->d->multiply($y2)->subtract($this->one);
+ $x2 = $u->divide($v);
+ if ($x2->equals($this->zero)) {
+ if ($sign) {
+ throw new \RuntimeException('Unable to recover X coordinate (x2 = 0)');
+ }
+ return clone $this->zero;
+ }
+ // find the square root
+ $exp = $this->getModulo()->add(new BigInteger(1));
+ $exp = $exp->bitwise_rightShift(2);
+ $x = $x2->pow($exp);
+
+ if (!$x->multiply($x)->subtract($x2)->equals($this->zero)) {
+ throw new \RuntimeException('Unable to recover X coordinate');
+ }
+ if ($x->isOdd() != $sign) {
+ $x = $x->negate();
+ }
+
+ return [$x, $y];
+ }
+
+ /**
+ * Extract Secret Scalar
+ *
+ * Implements steps 1-3 at https://tools.ietf.org/html/rfc8032#section-5.2.5
+ *
+ * Used by the various key handlers
+ *
+ * @param string $str
+ * @return array
+ */
+ public function extractSecret($str)
+ {
+ if (strlen($str) != 57) {
+ throw new \LengthException('Private Key should be 57-bytes long');
+ }
+ // 1. Hash the 57-byte private key using SHAKE256(x, 114), storing the
+ // digest in a 114-octet large buffer, denoted h. Only the lower 57
+ // bytes are used for generating the public key.
+ $hash = new Hash('shake256-912');
+ $h = $hash->hash($str);
+ $h = substr($h, 0, 57);
+ // 2. Prune the buffer: The two least significant bits of the first
+ // octet are cleared, all eight bits the last octet are cleared, and
+ // the highest bit of the second to last octet is set.
+ $h[0] = $h[0] & chr(0xFC);
+ $h = strrev($h);
+ $h[0] = "\0";
+ $h[1] = $h[1] | chr(0x80);
+ // 3. Interpret the buffer as the little-endian integer, forming a
+ // secret scalar s.
+ $dA = new BigInteger($h, 256);
+
+ return [
+ 'dA' => $dA,
+ 'secret' => $str
+ ];
+
+ $dA->secret = $str;
+ return $dA;
+ }
+
+ /**
+ * Encode a point as a string
+ *
+ * @param array $point
+ * @return string
+ */
+ public function encodePoint($point)
+ {
+ list($x, $y) = $point;
+ $y = "\0" . $y->toBytes();
+ if ($x->isOdd()) {
+ $y[0] = $y[0] | chr(0x80);
+ }
+ $y = strrev($y);
+
+ return $y;
+ }
+
+ /**
+ * Creates a random scalar multiplier
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer
+ */
+ public function createRandomMultiplier()
+ {
+ return $this->extractSecret(Random::string(57))['dA'];
+ }
+
+ /**
+ * Converts an affine point to an extended homogeneous coordinate
+ *
+ * From https://tools.ietf.org/html/rfc8032#section-5.2.4 :
+ *
+ * A point (x,y) is represented in extended homogeneous coordinates (X, Y, Z, T),
+ * with x = X/Z, y = Y/Z, x * y = T/Z.
+ *
+ * @return \phpseclib3\Math\PrimeField\Integer[]
+ */
+ public function convertToInternal(array $p)
+ {
+ if (empty($p)) {
+ return [clone $this->zero, clone $this->one, clone $this->one];
+ }
+
+ if (isset($p[2])) {
+ return $p;
+ }
+
+ $p[2] = clone $this->one;
+
+ return $p;
+ }
+
+ /**
+ * Doubles a point on a curve
+ *
+ * @return FiniteField[]
+ */
+ public function doublePoint(array $p)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p)) {
+ return [];
+ }
+
+ if (!isset($p[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ // from https://tools.ietf.org/html/rfc8032#page-18
+
+ list($x1, $y1, $z1) = $p;
+
+ $b = $x1->add($y1);
+ $b = $b->multiply($b);
+ $c = $x1->multiply($x1);
+ $d = $y1->multiply($y1);
+ $e = $c->add($d);
+ $h = $z1->multiply($z1);
+ $j = $e->subtract($this->two->multiply($h));
+
+ $x3 = $b->subtract($e)->multiply($j);
+ $y3 = $c->subtract($d)->multiply($e);
+ $z3 = $e->multiply($j);
+
+ return [$x3, $y3, $z3];
+ }
+
+ /**
+ * Adds two points on the curve
+ *
+ * @return FiniteField[]
+ */
+ public function addPoint(array $p, array $q)
+ {
+ if (!isset($this->factory)) {
+ throw new \RuntimeException('setModulo needs to be called before this method');
+ }
+
+ if (!count($p) || !count($q)) {
+ if (count($q)) {
+ return $q;
+ }
+ if (count($p)) {
+ return $p;
+ }
+ return [];
+ }
+
+ if (!isset($p[2]) || !isset($q[2])) {
+ throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
+ }
+
+ if ($p[0]->equals($q[0])) {
+ return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
+ }
+
+ // from https://tools.ietf.org/html/rfc8032#page-17
+
+ list($x1, $y1, $z1) = $p;
+ list($x2, $y2, $z2) = $q;
+
+ $a = $z1->multiply($z2);
+ $b = $a->multiply($a);
+ $c = $x1->multiply($x2);
+ $d = $y1->multiply($y2);
+ $e = $this->d->multiply($c)->multiply($d);
+ $f = $b->subtract($e);
+ $g = $b->add($e);
+ $h = $x1->add($y1)->multiply($x2->add($y2));
+
+ $x3 = $a->multiply($f)->multiply($h->subtract($c)->subtract($d));
+ $y3 = $a->multiply($g)->multiply($d->subtract($c));
+ $z3 = $f->multiply($g);
+
+ return [$x3, $y3, $z3];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php
new file mode 100644
index 000000000..7bc2272a1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP160r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP160r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
+ $this->setCoefficients(
+ new BigInteger('340E7BE2A280EB74E2BE61BADA745D97E8F7C300', 16),
+ new BigInteger('1E589A8595423412134FAA2DBDEC95C8D8675E58', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3', 16),
+ new BigInteger('1667CB477A1A8EC338F94741669C976316DA6321', 16)
+ );
+ $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php
new file mode 100644
index 000000000..ebfb29aeb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP160t1.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * brainpoolP160t1
+ *
+ * This curve is a twisted version of brainpoolP160r1 with A = -3. With brainpool,
+ * the curves ending in r1 are the "regular" curves and the curves ending in "t1"
+ * are the twisted version of the r1 curves. Per https://tools.ietf.org/html/rfc5639#page-7
+ * you can convert a point on an r1 curve to a point on a t1 curve thusly:
+ *
+ * F(x,y) := (x*Z^2, y*Z^3)
+ *
+ * The advantage of A = -3 is that some of the point doubling and point addition can be
+ * slightly optimized. See http://hyperelliptic.org/EFD/g1p/auto-shortw-projective-3.html
+ * vs http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html for example.
+ *
+ * phpseclib does not currently take advantage of this optimization opportunity
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP160t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620F', 16));
+ $this->setCoefficients(
+ new BigInteger('E95E4A5F737059DC60DFC7AD95B3D8139515620C', 16), // eg. -3
+ new BigInteger('7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('B199B13B9B34EFC1397E64BAEB05ACC265FF2378', 16),
+ new BigInteger('ADD6718B7C7C1961F0991B842443772152C9E0AD', 16)
+ );
+ $this->setOrder(new BigInteger('E95E4A5F737059DC60DF5991D45029409E60FC09', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php
new file mode 100644
index 000000000..6ec848bc9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP192r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP192r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16));
+ $this->setCoefficients(
+ new BigInteger('6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF', 16),
+ new BigInteger('469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6', 16),
+ new BigInteger('14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F', 16)
+ );
+ $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php
new file mode 100644
index 000000000..e6a86bbd3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP192t1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP192t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP192t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297', 16));
+ $this->setCoefficients(
+ new BigInteger('C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294', 16), // eg. -3
+ new BigInteger('13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129', 16),
+ new BigInteger('097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9', 16)
+ );
+ $this->setOrder(new BigInteger('C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php
new file mode 100644
index 000000000..3d7d8726a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP224r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP224r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16));
+ $this->setCoefficients(
+ new BigInteger('68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43', 16),
+ new BigInteger('2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D', 16),
+ new BigInteger('58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD', 16)
+ );
+ $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php
new file mode 100644
index 000000000..3d4f9289c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP224t1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP224t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP224t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF', 16));
+ $this->setCoefficients(
+ new BigInteger('D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC', 16), // eg. -3
+ new BigInteger('4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580', 16),
+ new BigInteger('0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C', 16)
+ );
+ $this->setOrder(new BigInteger('D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php
new file mode 100644
index 000000000..5780da763
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP256r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP256r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16));
+ $this->setCoefficients(
+ new BigInteger('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16),
+ new BigInteger('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262', 16),
+ new BigInteger('547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997', 16)
+ );
+ $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php
new file mode 100644
index 000000000..724d8b8f1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP256t1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * brainpoolP256t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP256t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16));
+ $this->setCoefficients(
+ new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374', 16), // eg. -3
+ new BigInteger('662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4', 16),
+ new BigInteger('2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE', 16)
+ );
+ $this->setOrder(new BigInteger('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php
new file mode 100644
index 000000000..182e62270
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320r1.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * brainpoolP320r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP320r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' .
+ '2B9EC7893EC28FCD412B1F1B32E27', 16));
+ $this->setCoefficients(
+ new BigInteger('3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F4' .
+ '92F375A97D860EB4', 16),
+ new BigInteger('520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD88453981' .
+ '6F5EB4AC8FB1F1A6', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C7' .
+ '10AF8D0D39E20611', 16),
+ new BigInteger('14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7' .
+ 'D35245D1692E8EE1', 16)
+ );
+ $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' .
+ '82EC7EE8658E98691555B44C59311', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php
new file mode 100644
index 000000000..d5a620d8b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP320t1.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * brainpoolP320t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP320t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F9' .
+ '2B9EC7893EC28FCD412B1F1B32E27', 16));
+ $this->setCoefficients(
+ new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28' .
+ 'FCD412B1F1B32E24', 16), // eg. -3
+ new BigInteger('A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CE' .
+ 'B5B4FEF422340353', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF' .
+ '3357F624A21BED52', 16),
+ new BigInteger('63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B' .
+ '1B9BC0455FB0D2C3', 16)
+ );
+ $this->setOrder(new BigInteger('D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D4' .
+ '82EC7EE8658E98691555B44C59311', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php
new file mode 100644
index 000000000..a20b4b446
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384r1.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * brainpoolP384r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP384r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger(
+ '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' .
+ '1874700133107EC53',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(
+ '7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503' .
+ 'AD4EB04A8C7DD22CE2826',
+ 16
+ ),
+ new BigInteger(
+ '4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DB' .
+ 'C9943AB78696FA504C11',
+ 16
+ )
+ );
+ $this->setBasePoint(
+ new BigInteger(
+ '1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D' .
+ '646AAEF87B2E247D4AF1E',
+ 16
+ ),
+ new BigInteger(
+ '8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E464621779' .
+ '1811142820341263C5315',
+ 16
+ )
+ );
+ $this->setOrder(new BigInteger(
+ '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' .
+ '03B883202E9046565',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php
new file mode 100644
index 000000000..366660e68
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP384t1.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * brainpoolP384t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP384t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger(
+ '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A7' .
+ '1874700133107EC53',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(
+ '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901' .
+ 'D1A71874700133107EC50',
+ 16
+ ), // eg. -3
+ new BigInteger(
+ '7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B8' .
+ '8805CED70355A33B471EE',
+ 16
+ )
+ );
+ $this->setBasePoint(
+ new BigInteger(
+ '18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946' .
+ 'A5F54D8D0AA2F418808CC',
+ 16
+ ),
+ new BigInteger(
+ '25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC' .
+ '2B2912675BF5B9E582928',
+ 16
+ )
+ );
+ $this->setOrder(new BigInteger(
+ '8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC31' .
+ '03B883202E9046565',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php
new file mode 100644
index 000000000..5efe5e1ac
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512r1.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * brainpoolP512r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP512r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger(
+ 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
+ '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(
+ '7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA82' .
+ '53AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA',
+ 16
+ ),
+ new BigInteger(
+ '3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C' .
+ '1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723',
+ 16
+ )
+ );
+ $this->setBasePoint(
+ new BigInteger(
+ '81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D' .
+ '0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822',
+ 16
+ ),
+ new BigInteger(
+ '7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5' .
+ 'F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892',
+ 16
+ )
+ );
+ $this->setOrder(new BigInteger(
+ 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' .
+ '92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php
new file mode 100644
index 000000000..745863a63
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/brainpoolP512t1.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * brainpoolP512t1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class brainpoolP512t1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger(
+ 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
+ '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(
+ 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC' .
+ '66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0',
+ 16
+ ), // eg. -3
+ new BigInteger(
+ '7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA23049' .
+ '76540F6450085F2DAE145C22553B465763689180EA2571867423E',
+ 16
+ )
+ );
+ $this->setBasePoint(
+ new BigInteger(
+ '640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CD' .
+ 'B3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA',
+ 16
+ ),
+ new BigInteger(
+ '5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEE' .
+ 'F216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332',
+ 16
+ )
+ );
+ $this->setOrder(new BigInteger(
+ 'AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA' .
+ '92619418661197FAC10471DB1D381085DDADDB58796829CA90069',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php
new file mode 100644
index 000000000..bae12b06f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb233.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistb233
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistb233 extends sect233r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php
new file mode 100644
index 000000000..a46153d3c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistb409.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistb409
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistb409 extends sect409r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php
new file mode 100644
index 000000000..8b2637617
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk163.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistk163
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistk163 extends sect163k1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php
new file mode 100644
index 000000000..69e141382
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk233.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistk233
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistk233 extends sect233k1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php
new file mode 100644
index 000000000..9e95f10e7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk283.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * sect283k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistk283 extends sect283k1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php
new file mode 100644
index 000000000..06bd9af76
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistk409.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistk409
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistk409 extends sect409k1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php
new file mode 100644
index 000000000..ddead3cff
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp192.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistp192
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistp192 extends secp192r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php
new file mode 100644
index 000000000..746571b4d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp224.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistp224
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistp224 extends secp224r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php
new file mode 100644
index 000000000..a26c0f992
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp256.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistp256
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistp256 extends secp256r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php
new file mode 100644
index 000000000..1f20c02d1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp384.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistp384
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistp384 extends secp384r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php
new file mode 100644
index 000000000..86fa05084
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistp521.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistp521
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistp521 extends secp521r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php
new file mode 100644
index 000000000..7908b38b9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/nistt571.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * nistt571
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class nistt571 extends sect571k1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php
new file mode 100644
index 000000000..e9c13cd8c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v1.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * prime192v1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class prime192v1 extends secp192r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php
new file mode 100644
index 000000000..e3e341f26
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * prime192v2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class prime192v2 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
+ new BigInteger('CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A', 16),
+ new BigInteger('6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php
new file mode 100644
index 000000000..1e97992dc
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime192v3.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * prime192v3
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class prime192v3 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
+ new BigInteger('22123DC2395A05CAA7423DAECCC94760A7D462256BD56916', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896', 16),
+ new BigInteger('38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php
new file mode 100644
index 000000000..084be9d7c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * prime239v1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class prime239v1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
+ new BigInteger('6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF', 16),
+ new BigInteger('7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE', 16)
+ );
+ $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php
new file mode 100644
index 000000000..21941b834
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * prime239v2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class prime239v2 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
+ new BigInteger('617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7', 16),
+ new BigInteger('5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA', 16)
+ );
+ $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php
new file mode 100644
index 000000000..78c50f069
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime239v3.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * prime239v3
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class prime239v3 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC', 16),
+ new BigInteger('255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A', 16),
+ new BigInteger('1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3', 16)
+ );
+ $this->setOrder(new BigInteger('7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php
new file mode 100644
index 000000000..c72b22e8a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/prime256v1.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * prime256v1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+final class prime256v1 extends secp256r1
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php
new file mode 100644
index 000000000..d1d3194b2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * secp112r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp112r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
+ $this->setCoefficients(
+ new BigInteger('DB7C2ABF62E35E668076BEAD2088', 16),
+ new BigInteger('659EF8BA043916EEDE8911702B22', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('09487239995A5EE76B55F9C2F098', 16),
+ new BigInteger('A89CE5AF8724C0A23E0E0FF77500', 16)
+ );
+ $this->setOrder(new BigInteger('DB7C2ABF62E35E7628DFAC6561C5', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php
new file mode 100644
index 000000000..da44e7fd8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp112r2.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * secp112r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp112r2 extends Prime
+{
+ public function __construct()
+ {
+ // same modulo as secp112r1
+ $this->setModulo(new BigInteger('DB7C2ABF62E35E668076BEAD208B', 16));
+ $this->setCoefficients(
+ new BigInteger('6127C24C05F38A0AAAF65C0EF02C', 16),
+ new BigInteger('51DEF1815DB5ED74FCC34C85D709', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('4BA30AB5E892B4E1649DD0928643', 16),
+ new BigInteger('ADCD46F5882E3747DEF36E956E97', 16)
+ );
+ $this->setOrder(new BigInteger('36DF0AAFD8B8D7597CA10520D04B', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php
new file mode 100644
index 000000000..34456bc07
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * secp128r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp128r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC', 16),
+ new BigInteger('E87579C11079F43DD824993C2CEE5ED3', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('161FF7528B899B2D0C28607CA52C5B86', 16),
+ new BigInteger('CF5AC8395BAFEB13C02DA292DDED7A83', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFE0000000075A30D1B9038A115', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php
new file mode 100644
index 000000000..e102c3409
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp128r2.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * secp128r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp128r2 extends Prime
+{
+ public function __construct()
+ {
+ // same as secp128r1
+ $this->setModulo(new BigInteger('FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('D6031998D1B3BBFEBF59CC9BBFF9AEE1', 16),
+ new BigInteger('5EEEFCA380D02919DC2C6558BB6D8A5D', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('7B6AA5D85E572983E6FB32A7CDEBC140', 16),
+ new BigInteger('27B6916A894D3AEE7106FE805FC34B44', 16)
+ );
+ $this->setOrder(new BigInteger('3FFFFFFF7FFFFFFFBE0024720613B5A3', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php
new file mode 100644
index 000000000..c6a33344a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160k1.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * secp160k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
+use phpseclib3\Math\BigInteger;
+
+class secp160k1 extends KoblitzPrime
+{
+ public function __construct()
+ {
+ // same as secp160r2
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
+ $this->setCoefficients(
+ new BigInteger('0000000000000000000000000000000000000000', 16),
+ new BigInteger('0000000000000000000000000000000000000007', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('3B4C382CE37AA192A4019E763036F4F5DD4D7EBB', 16),
+ new BigInteger('938CF935318FDCED6BC28286531733C3F03C4FEE', 16)
+ );
+ $this->setOrder(new BigInteger('0100000000000000000001B8FA16DFAB9ACA16B6B3', 16));
+
+ $this->basis = [];
+ $this->basis[] = [
+ 'a' => new BigInteger('0096341F1138933BC2F505', -16),
+ 'b' => new BigInteger('FF6E9D0418C67BB8D5F562', -16)
+ ];
+ $this->basis[] = [
+ 'a' => new BigInteger('01BDCB3A09AAAABEAFF4A8', -16),
+ 'b' => new BigInteger('04D12329FF0EF498EA67', -16)
+ ];
+ $this->beta = $this->factory->newInteger(new BigInteger('645B7345A143464942CC46D7CF4D5D1E1E6CBB68', -16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php
new file mode 100644
index 000000000..af4687749
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * secp160r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp160r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC', 16),
+ new BigInteger('1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('4A96B5688EF573284664698968C38BB913CBFC82', 16),
+ new BigInteger('23A628553168947D59DCC912042351377AC5FB32', 16)
+ );
+ $this->setOrder(new BigInteger('0100000000000000000001F4C8F927AED3CA752257', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php
new file mode 100644
index 000000000..9bd23d23c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp160r2.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * secp160r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp160r2 extends Prime
+{
+ public function __construct()
+ {
+ // same as secp160k1
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70', 16),
+ new BigInteger('B4E134D3FB59EB8BAB57274904664D5AF50388BA', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('52DCB034293A117E1F4FF11B30F7199D3144CE6D', 16),
+ new BigInteger('FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E', 16)
+ );
+ $this->setOrder(new BigInteger('0100000000000000000000351EE786A818F3A1A16B', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php
new file mode 100644
index 000000000..79ff2e097
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192k1.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * secp192k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
+use phpseclib3\Math\BigInteger;
+
+class secp192k1 extends KoblitzPrime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37', 16));
+ $this->setCoefficients(
+ new BigInteger('000000000000000000000000000000000000000000000000', 16),
+ new BigInteger('000000000000000000000000000000000000000000000003', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D', 16),
+ new BigInteger('9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D', 16));
+
+ $this->basis = [];
+ $this->basis[] = [
+ 'a' => new BigInteger('00B3FB3400DEC5C4ADCEB8655C', -16),
+ 'b' => new BigInteger('8EE96418CCF4CFC7124FDA0F', -16)
+ ];
+ $this->basis[] = [
+ 'a' => new BigInteger('01D90D03E8F096B9948B20F0A9', -16),
+ 'b' => new BigInteger('42E49819ABBA9474E1083F6B', -16)
+ ];
+ $this->beta = $this->factory->newInteger(new BigInteger('447A96E6C647963E2F7809FEAAB46947F34B0AA3CA0BBA74', -16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php
new file mode 100644
index 000000000..83ab1c706
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp192r1.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * secp192r1
+ *
+ * This is the NIST P-192 curve
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp192r1 extends Prime
+{
+ public function __construct()
+ {
+ $modulo = new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF', 16);
+ $this->setModulo($modulo);
+
+ // algorithm 2.27 from http://diamond.boisestate.edu/~liljanab/MATH308/GuideToECC.pdf#page=66
+ /* in theory this should be faster than regular modular reductions save for one small issue.
+ to convert to / from base-2**8 with BCMath you have to call bcmul() and bcdiv() a lot.
+ to convert to / from base-2**8 with PHP64 you have to call base256_rshift() a lot.
+ in short, converting to / from base-2**8 is pretty expensive and that expense is
+ enough to offset whatever else might be gained by a simplified reduction algorithm.
+ now, if PHP supported unsigned integers things might be different. no bit-shifting
+ would be required for the PHP engine and it'd be a lot faster. but as is, BigInteger
+ uses base-2**31 or base-2**26 depending on whether or not the system is has a 32-bit
+ or a 64-bit OS.
+ */
+ /*
+ $m_length = $this->getLengthInBytes();
+ $this->setReduction(function($c) use ($m_length) {
+ $cBytes = $c->toBytes();
+ $className = $this->className;
+
+ if (strlen($cBytes) > 2 * $m_length) {
+ list(, $r) = $c->divide($className::$modulo);
+ return $r;
+ }
+
+ $c = str_pad($cBytes, 48, "\0", STR_PAD_LEFT);
+ $c = array_reverse(str_split($c, 8));
+
+ $null = "\0\0\0\0\0\0\0\0";
+ $s1 = new BigInteger($c[2] . $c[1] . $c[0], 256);
+ $s2 = new BigInteger($null . $c[3] . $c[3], 256);
+ $s3 = new BigInteger($c[4] . $c[4] . $null, 256);
+ $s4 = new BigInteger($c[5] . $c[5] . $c[5], 256);
+
+ $r = $s1->add($s2)->add($s3)->add($s4);
+ while ($r->compare($className::$modulo) >= 0) {
+ $r = $r->subtract($className::$modulo);
+ }
+
+ return $r;
+ });
+ */
+
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC', 16),
+ new BigInteger('64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012', 16),
+ new BigInteger('07192B95FFC8DA78631011ED6B24CDD573F977A11E794811', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php
new file mode 100644
index 000000000..79a5c5417
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224k1.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * secp224k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
+use phpseclib3\Math\BigInteger;
+
+class secp224k1 extends KoblitzPrime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D', 16));
+ $this->setCoefficients(
+ new BigInteger('00000000000000000000000000000000000000000000000000000000', 16),
+ new BigInteger('00000000000000000000000000000000000000000000000000000005', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C', 16),
+ new BigInteger('7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5', 16)
+ );
+ $this->setOrder(new BigInteger('010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7', 16));
+
+ $this->basis = [];
+ $this->basis[] = [
+ 'a' => new BigInteger('00B8ADF1378A6EB73409FA6C9C637D', -16),
+ 'b' => new BigInteger('94730F82B358A3776A826298FA6F', -16)
+ ];
+ $this->basis[] = [
+ 'a' => new BigInteger('01DCE8D2EC6184CAF0A972769FCC8B', -16),
+ 'b' => new BigInteger('4D2100BA3DC75AAB747CCF355DEC', -16)
+ ];
+ $this->beta = $this->factory->newInteger(new BigInteger('01F178FFA4B17C89E6F73AECE2AAD57AF4C0A748B63C830947B27E04', -16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php
new file mode 100644
index 000000000..a9e474a3c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp224r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * secp224r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp224r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE', 16),
+ new BigInteger('B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21', 16),
+ new BigInteger('BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php
new file mode 100644
index 000000000..462e7a1ca
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256k1.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * secp256k1
+ *
+ * This is the curve used in Bitcoin
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+//use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Crypt\EC\BaseCurves\KoblitzPrime;
+use phpseclib3\Math\BigInteger;
+
+//class secp256k1 extends Prime
+class secp256k1 extends KoblitzPrime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16));
+ $this->setCoefficients(
+ new BigInteger('0000000000000000000000000000000000000000000000000000000000000000', 16),
+ new BigInteger('0000000000000000000000000000000000000000000000000000000000000007', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16));
+ $this->setBasePoint(
+ new BigInteger('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16),
+ new BigInteger('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
+ );
+
+ $this->basis = [];
+ $this->basis[] = [
+ 'a' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16),
+ 'b' => new BigInteger('FF1BBC8129FEF177D790AB8056F5401B3D', -16)
+ ];
+ $this->basis[] = [
+ 'a' => new BigInteger('114CA50F7A8E2F3F657C1108D9D44CFD8', -16),
+ 'b' => new BigInteger('3086D221A7D46BCDE86C90E49284EB15', -16)
+ ];
+ $this->beta = $this->factory->newInteger(new BigInteger('7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE', -16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php
new file mode 100644
index 000000000..9003373cf
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp256r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * secp256r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp256r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC', 16),
+ new BigInteger('5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296', 16),
+ new BigInteger('4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5', 16)
+ );
+ $this->setOrder(new BigInteger('FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php
new file mode 100644
index 000000000..98764a341
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp384r1.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * secp384r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp384r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger(
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF',
+ 16
+ ));
+ $this->setCoefficients(
+ new BigInteger(
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC',
+ 16
+ ),
+ new BigInteger(
+ 'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF',
+ 16
+ )
+ );
+ $this->setBasePoint(
+ new BigInteger(
+ 'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7',
+ 16
+ ),
+ new BigInteger(
+ '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F',
+ 16
+ )
+ );
+ $this->setOrder(new BigInteger(
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php
new file mode 100644
index 000000000..b89a4ea74
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/secp521r1.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * secp521r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Prime;
+use phpseclib3\Math\BigInteger;
+
+class secp521r1 extends Prime
+{
+ public function __construct()
+ {
+ $this->setModulo(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'FFFF', 16));
+ $this->setCoefficients(
+ new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'FFFC', 16),
+ new BigInteger('0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF1' .
+ '09E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B50' .
+ '3F00', 16)
+ );
+ $this->setBasePoint(
+ new BigInteger('00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D' .
+ '3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5' .
+ 'BD66', 16),
+ new BigInteger('011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E' .
+ '662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD1' .
+ '6650', 16)
+ );
+ $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'FFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E9138' .
+ '6409', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php
new file mode 100644
index 000000000..77ec7603a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect113r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect113r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(113, 9, 0);
+ $this->setCoefficients(
+ '003088250CA6E7C7FE649CE85820F7',
+ '00E8BEE4D3E2260744188BE0E9C723'
+ );
+ $this->setBasePoint(
+ '009D73616F35F4AB1407D73562C10F',
+ '00A52830277958EE84D1315ED31886'
+ );
+ $this->setOrder(new BigInteger('0100000000000000D9CCEC8A39E56F', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php
new file mode 100644
index 000000000..2185d60e3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect113r2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect113r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect113r2 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(113, 9, 0);
+ $this->setCoefficients(
+ '00689918DBEC7E5A0DD6DFC0AA55C7',
+ '0095E9A9EC9B297BD4BF36E059184F'
+ );
+ $this->setBasePoint(
+ '01A57A6A7B26CA5EF52FCDB8164797',
+ '00B3ADC94ED1FE674C06E695BABA1D'
+ );
+ $this->setOrder(new BigInteger('010000000000000108789B2496AF93', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php
new file mode 100644
index 000000000..1365cb601
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect131r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect131r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(131, 8, 3, 2, 0);
+ $this->setCoefficients(
+ '07A11B09A76B562144418FF3FF8C2570B8',
+ '0217C05610884B63B9C6C7291678F9D341'
+ );
+ $this->setBasePoint(
+ '0081BAF91FDF9833C40F9C181343638399',
+ '078C6E7EA38C001F73C8134B1B4EF9E150'
+ );
+ $this->setOrder(new BigInteger('0400000000000000023123953A9464B54D', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php
new file mode 100644
index 000000000..93c11b2a3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect131r2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect131r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect131r2 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(131, 8, 3, 2, 0);
+ $this->setCoefficients(
+ '03E5A88919D7CAFCBF415F07C2176573B2',
+ '04B8266A46C55657AC734CE38F018F2192'
+ );
+ $this->setBasePoint(
+ '0356DCD8F2F95031AD652D23951BB366A8',
+ '0648F06D867940A5366D9E265DE9EB240F'
+ );
+ $this->setOrder(new BigInteger('0400000000000000016954A233049BA98F', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php
new file mode 100644
index 000000000..3c8574bb3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163k1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect163k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect163k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(163, 7, 6, 3, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000001',
+ '000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8',
+ '0289070FB05D38FF58321F2E800536D538CCDAA3D9'
+ );
+ $this->setOrder(new BigInteger('04000000000000000000020108A2E0CC0D99F8A5EF', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php
new file mode 100644
index 000000000..26afd87e4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect163r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect163r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(163, 7, 6, 3, 0);
+ $this->setCoefficients(
+ '07B6882CAAEFA84F9554FF8428BD88E246D2782AE2',
+ '0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9'
+ );
+ $this->setBasePoint(
+ '0369979697AB43897789566789567F787A7876A654',
+ '00435EDB42EFAFB2989D51FEFCE3C80988F41FF883'
+ );
+ $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php
new file mode 100644
index 000000000..38f94661c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect163r2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect163r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect163r2 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(163, 7, 6, 3, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000001',
+ '020A601907B8C953CA1481EB10512F78744A3205FD'
+ );
+ $this->setBasePoint(
+ '03F0EBA16286A2D57EA0991168D4994637E8343E36',
+ '00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1'
+ );
+ $this->setOrder(new BigInteger('040000000000000000000292FE77E70C12A4234C33', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php
new file mode 100644
index 000000000..951f261eb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect193r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect193r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(193, 15, 0);
+ $this->setCoefficients(
+ '0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01',
+ '00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814'
+ );
+ $this->setBasePoint(
+ '01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1',
+ '0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05'
+ );
+ $this->setOrder(new BigInteger('01000000000000000000000000C7F34A778F443ACC920EBA49', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php
new file mode 100644
index 000000000..e3ff47ac7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect193r2.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect193r2
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect193r2 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(193, 15, 0);
+ $this->setCoefficients(
+ '0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B',
+ '00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE'
+ );
+ $this->setBasePoint(
+ '00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F',
+ '01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C'
+ );
+ $this->setOrder(new BigInteger('010000000000000000000000015AAB561B005413CCD4EE99D5', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php
new file mode 100644
index 000000000..eea3f7ad5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233k1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect233k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect233k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(233, 74, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000',
+ '000000000000000000000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126',
+ '01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3'
+ );
+ $this->setOrder(new BigInteger('8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php
new file mode 100644
index 000000000..68219f0ea
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect233r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect233r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect233r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(233, 74, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000001',
+ '0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD'
+ );
+ $this->setBasePoint(
+ '00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B',
+ '01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052'
+ );
+ $this->setOrder(new BigInteger('01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php
new file mode 100644
index 000000000..0e6994ba3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect239k1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect239k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect239k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(239, 158, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000',
+ '000000000000000000000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC',
+ '76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA'
+ );
+ $this->setOrder(new BigInteger('2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php
new file mode 100644
index 000000000..279c24aac
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283k1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect283k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect283k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(283, 12, 7, 5, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000000000000000000000000000000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836',
+ '01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259'
+ );
+ $this->setOrder(new BigInteger('01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php
new file mode 100644
index 000000000..e44a60765
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect283r1.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * sect283r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect283r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(283, 12, 7, 5, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000000000000001',
+ '027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5'
+ );
+ $this->setBasePoint(
+ '05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053',
+ '03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4'
+ );
+ $this->setOrder(new BigInteger('03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307', 16));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php
new file mode 100644
index 000000000..1fe329d8c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409k1.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * sect409k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect409k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(409, 87, 0);
+ $this->setCoefficients(
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746',
+ '01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B'
+ );
+ $this->setOrder(new BigInteger(
+ '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F' .
+ '83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php
new file mode 100644
index 000000000..3e209ef8f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect409r1.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * sect409r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect409r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(409, 87, 0);
+ $this->setCoefficients(
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001',
+ '0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F'
+ );
+ $this->setBasePoint(
+ '015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7',
+ '0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706'
+ );
+ $this->setOrder(new BigInteger(
+ '010000000000000000000000000000000000000000000000000001E2' .
+ 'AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php
new file mode 100644
index 000000000..3c54eabdb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571k1.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * sect571k1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect571k1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(571, 10, 5, 2, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000000000000000' .
+ '000000000000000000000000000000000000000000000000000000000000000000000000',
+ '000000000000000000000000000000000000000000000000000000000000000000000000' .
+ '000000000000000000000000000000000000000000000000000000000000000000000001'
+ );
+ $this->setBasePoint(
+ '026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA443709584' .
+ '93B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972',
+ '0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0' .
+ 'AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3'
+ );
+ $this->setOrder(new BigInteger(
+ '020000000000000000000000000000000000000000000000000000000000000000000000' .
+ '131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php
new file mode 100644
index 000000000..172c1af9c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Curves/sect571r1.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * sect571r1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wiggint on <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Crypt\EC\Curves;
+
+use phpseclib3\Crypt\EC\BaseCurves\Binary;
+use phpseclib3\Math\BigInteger;
+
+class sect571r1 extends Binary
+{
+ public function __construct()
+ {
+ $this->setModulo(571, 10, 5, 2, 0);
+ $this->setCoefficients(
+ '000000000000000000000000000000000000000000000000000000000000000000000000' .
+ '000000000000000000000000000000000000000000000000000000000000000000000001',
+ '02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD' .
+ '8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A'
+ );
+ $this->setBasePoint(
+ '0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950' .
+ 'F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19',
+ '037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43' .
+ 'BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B'
+ );
+ $this->setOrder(new BigInteger(
+ '03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' .
+ 'E661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47',
+ 16
+ ));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php
new file mode 100644
index 000000000..743c07c3e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/Common.php
@@ -0,0 +1,549 @@
+<?php
+
+/**
+ * Generic EC Key Parsing Helper functions
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Generic EC Key Parsing Helper functions
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+trait Common
+{
+ /**
+ * Curve OIDs
+ *
+ * @var array
+ */
+ private static $curveOIDs = [];
+
+ /**
+ * Child OIDs loaded
+ *
+ * @var bool
+ */
+ protected static $childOIDsLoaded = false;
+
+ /**
+ * Use Named Curves
+ *
+ * @var bool
+ */
+ private static $useNamedCurves = true;
+
+ /**
+ * Initialize static variables
+ */
+ private static function initialize_static_variables()
+ {
+ if (empty(self::$curveOIDs)) {
+ // the sec* curves are from the standards for efficient cryptography group
+ // sect* curves are curves over binary finite fields
+ // secp* curves are curves over prime finite fields
+ // sec*r* curves are regular curves; sec*k* curves are koblitz curves
+ // brainpool*r* curves are regular prime finite field curves
+ // brainpool*t* curves are twisted versions of the brainpool*r* curves
+ self::$curveOIDs = [
+ 'prime192v1' => '1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1)
+ 'prime192v2' => '1.2.840.10045.3.1.2', // J.5.1, example 2
+ 'prime192v3' => '1.2.840.10045.3.1.3', // J.5.1, example 3
+ 'prime239v1' => '1.2.840.10045.3.1.4', // J.5.2, example 1
+ 'prime239v2' => '1.2.840.10045.3.1.5', // J.5.2, example 2
+ 'prime239v3' => '1.2.840.10045.3.1.6', // J.5.2, example 3
+ 'prime256v1' => '1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1)
+
+ // https://tools.ietf.org/html/rfc5656#section-10
+ 'nistp256' => '1.2.840.10045.3.1.7', // aka secp256r1
+ 'nistp384' => '1.3.132.0.34', // aka secp384r1
+ 'nistp521' => '1.3.132.0.35', // aka secp521r1
+
+ 'nistk163' => '1.3.132.0.1', // aka sect163k1
+ 'nistp192' => '1.2.840.10045.3.1.1', // aka secp192r1
+ 'nistp224' => '1.3.132.0.33', // aka secp224r1
+ 'nistk233' => '1.3.132.0.26', // aka sect233k1
+ 'nistb233' => '1.3.132.0.27', // aka sect233r1
+ 'nistk283' => '1.3.132.0.16', // aka sect283k1
+ 'nistk409' => '1.3.132.0.36', // aka sect409k1
+ 'nistb409' => '1.3.132.0.37', // aka sect409r1
+ 'nistt571' => '1.3.132.0.38', // aka sect571k1
+
+ // from https://tools.ietf.org/html/rfc5915
+ 'secp192r1' => '1.2.840.10045.3.1.1', // aka prime192v1
+ 'sect163k1' => '1.3.132.0.1',
+ 'sect163r2' => '1.3.132.0.15',
+ 'secp224r1' => '1.3.132.0.33',
+ 'sect233k1' => '1.3.132.0.26',
+ 'sect233r1' => '1.3.132.0.27',
+ 'secp256r1' => '1.2.840.10045.3.1.7', // aka prime256v1
+ 'sect283k1' => '1.3.132.0.16',
+ 'sect283r1' => '1.3.132.0.17',
+ 'secp384r1' => '1.3.132.0.34',
+ 'sect409k1' => '1.3.132.0.36',
+ 'sect409r1' => '1.3.132.0.37',
+ 'secp521r1' => '1.3.132.0.35',
+ 'sect571k1' => '1.3.132.0.38',
+ 'sect571r1' => '1.3.132.0.39',
+ // from http://www.secg.org/SEC2-Ver-1.0.pdf
+ 'secp112r1' => '1.3.132.0.6',
+ 'secp112r2' => '1.3.132.0.7',
+ 'secp128r1' => '1.3.132.0.28',
+ 'secp128r2' => '1.3.132.0.29',
+ 'secp160k1' => '1.3.132.0.9',
+ 'secp160r1' => '1.3.132.0.8',
+ 'secp160r2' => '1.3.132.0.30',
+ 'secp192k1' => '1.3.132.0.31',
+ 'secp224k1' => '1.3.132.0.32',
+ 'secp256k1' => '1.3.132.0.10',
+
+ 'sect113r1' => '1.3.132.0.4',
+ 'sect113r2' => '1.3.132.0.5',
+ 'sect131r1' => '1.3.132.0.22',
+ 'sect131r2' => '1.3.132.0.23',
+ 'sect163r1' => '1.3.132.0.2',
+ 'sect193r1' => '1.3.132.0.24',
+ 'sect193r2' => '1.3.132.0.25',
+ 'sect239k1' => '1.3.132.0.3',
+
+ // from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36
+ /*
+ 'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1
+ 'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2
+ 'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3
+ 'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1
+ 'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1
+ 'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2
+ 'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3
+ 'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4
+ 'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5
+ 'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1
+ 'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1
+ 'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2
+ 'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3
+ 'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4
+ 'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5
+ 'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1
+ 'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1
+ 'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1
+ 'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1
+ 'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1
+ */
+
+ // http://www.ecc-brainpool.org/download/Domain-parameters.pdf
+ // https://tools.ietf.org/html/rfc5639
+ 'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1',
+ 'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2',
+ 'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3',
+ 'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4',
+ 'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5',
+ 'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6',
+ 'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7',
+ 'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8',
+ 'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9',
+ 'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10',
+ 'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11',
+ 'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12',
+ 'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13',
+ 'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14'
+ ];
+ ASN1::loadOIDs([
+ 'prime-field' => '1.2.840.10045.1.1',
+ 'characteristic-two-field' => '1.2.840.10045.1.2',
+ 'characteristic-two-basis' => '1.2.840.10045.1.2.3',
+ // per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here"
+ 'gnBasis' => '1.2.840.10045.1.2.3.1', // NULL
+ 'tpBasis' => '1.2.840.10045.1.2.3.2', // Trinomial
+ 'ppBasis' => '1.2.840.10045.1.2.3.3' // Pentanomial
+ ] + self::$curveOIDs);
+ }
+ }
+
+ /**
+ * Explicitly set the curve
+ *
+ * If the key contains an implicit curve phpseclib needs the curve
+ * to be explicitly provided
+ *
+ * @param BaseCurve $curve
+ */
+ public static function setImplicitCurve(BaseCurve $curve)
+ {
+ self::$implicitCurve = $curve;
+ }
+
+ /**
+ * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
+ * on the curve parameters
+ *
+ * @param array $params
+ * @return BaseCurve|false
+ */
+ protected static function loadCurveByParam(array $params)
+ {
+ if (count($params) > 1) {
+ throw new \RuntimeException('No parameters are present');
+ }
+ if (isset($params['namedCurve'])) {
+ $curve = '\phpseclib3\Crypt\EC\Curves\\' . $params['namedCurve'];
+ if (!class_exists($curve)) {
+ throw new UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported');
+ }
+ return new $curve();
+ }
+ if (isset($params['implicitCurve'])) {
+ if (!isset(self::$implicitCurve)) {
+ throw new \RuntimeException('Implicit curves can be provided by calling setImplicitCurve');
+ }
+ return self::$implicitCurve;
+ }
+ if (isset($params['specifiedCurve'])) {
+ $data = $params['specifiedCurve'];
+ switch ($data['fieldID']['fieldType']) {
+ case 'prime-field':
+ $curve = new PrimeCurve();
+ $curve->setModulo($data['fieldID']['parameters']);
+ $curve->setCoefficients(
+ new BigInteger($data['curve']['a'], 256),
+ new BigInteger($data['curve']['b'], 256)
+ );
+ $point = self::extractPoint("\0" . $data['base'], $curve);
+ $curve->setBasePoint(...$point);
+ $curve->setOrder($data['order']);
+ return $curve;
+ case 'characteristic-two-field':
+ $curve = new BinaryCurve();
+ $params = ASN1::decodeBER($data['fieldID']['parameters']);
+ $params = ASN1::asn1map($params[0], Maps\Characteristic_two::MAP);
+ $modulo = [(int) $params['m']->toString()];
+ switch ($params['basis']) {
+ case 'tpBasis':
+ $modulo[] = (int) $params['parameters']->toString();
+ break;
+ case 'ppBasis':
+ $temp = ASN1::decodeBER($params['parameters']);
+ $temp = ASN1::asn1map($temp[0], Maps\Pentanomial::MAP);
+ $modulo[] = (int) $temp['k3']->toString();
+ $modulo[] = (int) $temp['k2']->toString();
+ $modulo[] = (int) $temp['k1']->toString();
+ }
+ $modulo[] = 0;
+ $curve->setModulo(...$modulo);
+ $len = ceil($modulo[0] / 8);
+ $curve->setCoefficients(
+ Strings::bin2hex($data['curve']['a']),
+ Strings::bin2hex($data['curve']['b'])
+ );
+ $point = self::extractPoint("\0" . $data['base'], $curve);
+ $curve->setBasePoint(...$point);
+ $curve->setOrder($data['order']);
+ return $curve;
+ default:
+ throw new UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported');
+ }
+ }
+ throw new \RuntimeException('No valid parameters are present');
+ }
+
+ /**
+ * Extract points from a string
+ *
+ * Supports both compressed and uncompressed points
+ *
+ * @param string $str
+ * @param BaseCurve $curve
+ * @return object[]
+ */
+ public static function extractPoint($str, BaseCurve $curve)
+ {
+ if ($curve instanceof TwistedEdwardsCurve) {
+ // first step of point deciding as discussed at the following URL's:
+ // https://tools.ietf.org/html/rfc8032#section-5.1.3
+ // https://tools.ietf.org/html/rfc8032#section-5.2.3
+ $y = $str;
+ $y = strrev($y);
+ $sign = (bool) (ord($y[0]) & 0x80);
+ $y[0] = $y[0] & chr(0x7F);
+ $y = new BigInteger($y, 256);
+ if ($y->compare($curve->getModulo()) >= 0) {
+ throw new \RuntimeException('The Y coordinate should not be >= the modulo');
+ }
+ $point = $curve->recoverX($y, $sign);
+ if (!$curve->verifyPoint($point)) {
+ throw new \RuntimeException('Unable to verify that point exists on curve');
+ }
+ return $point;
+ }
+
+ // the first byte of a bit string represents the number of bits in the last byte that are to be ignored but,
+ // currently, bit strings wanting a non-zero amount of bits trimmed are not supported
+ if (($val = Strings::shift($str)) != "\0") {
+ throw new \UnexpectedValueException('extractPoint expects the first byte to be null - not ' . Strings::bin2hex($val));
+ }
+ if ($str == "\0") {
+ return [];
+ }
+
+ $keylen = strlen($str);
+ $order = $curve->getLengthInBytes();
+ // point compression is being used
+ if ($keylen == $order + 1) {
+ return $curve->derivePoint($str);
+ }
+
+ // point compression is not being used
+ if ($keylen == 2 * $order + 1) {
+ preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches);
+ list(, $w, $x, $y) = $matches;
+ if ($w != "\4") {
+ throw new \UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . Strings::bin2hex($val));
+ }
+ $point = [
+ $curve->convertInteger(new BigInteger($x, 256)),
+ $curve->convertInteger(new BigInteger($y, 256))
+ ];
+
+ if (!$curve->verifyPoint($point)) {
+ throw new \RuntimeException('Unable to verify that point exists on curve');
+ }
+
+ return $point;
+ }
+
+ throw new \UnexpectedValueException('The string representation of the points is not of an appropriate length');
+ }
+
+ /**
+ * Encode Parameters
+ *
+ * @todo Maybe at some point this could be moved to __toString() for each of the curves?
+ * @param BaseCurve $curve
+ * @param bool $returnArray optional
+ * @param array $options optional
+ * @return string|false
+ */
+ private static function encodeParameters(BaseCurve $curve, $returnArray = false, array $options = [])
+ {
+ $useNamedCurves = isset($options['namedCurve']) ? $options['namedCurve'] : self::$useNamedCurves;
+
+ $reflect = new \ReflectionClass($curve);
+ $name = $reflect->getShortName();
+ if ($useNamedCurves) {
+ if (isset(self::$curveOIDs[$name])) {
+ if ($reflect->isFinal()) {
+ $reflect = $reflect->getParentClass();
+ $name = $reflect->getShortName();
+ }
+ return $returnArray ?
+ ['namedCurve' => $name] :
+ ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP);
+ }
+ foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) {
+ if ($file->getExtension() != 'php') {
+ continue;
+ }
+ $testName = $file->getBasename('.php');
+ $class = 'phpseclib3\Crypt\EC\Curves\\' . $testName;
+ $reflect = new \ReflectionClass($class);
+ if ($reflect->isFinal()) {
+ continue;
+ }
+ $candidate = new $class();
+ switch ($name) {
+ case 'Prime':
+ if (!$candidate instanceof PrimeCurve) {
+ break;
+ }
+ if (!$candidate->getModulo()->equals($curve->getModulo())) {
+ break;
+ }
+ if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
+ break;
+ }
+ if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
+ break;
+ }
+
+ list($candidateX, $candidateY) = $candidate->getBasePoint();
+ list($curveX, $curveY) = $curve->getBasePoint();
+ if ($candidateX->toBytes() != $curveX->toBytes()) {
+ break;
+ }
+ if ($candidateY->toBytes() != $curveY->toBytes()) {
+ break;
+ }
+
+ return $returnArray ?
+ ['namedCurve' => $testName] :
+ ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
+ case 'Binary':
+ if (!$candidate instanceof BinaryCurve) {
+ break;
+ }
+ if ($candidate->getModulo() != $curve->getModulo()) {
+ break;
+ }
+ if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
+ break;
+ }
+ if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
+ break;
+ }
+
+ list($candidateX, $candidateY) = $candidate->getBasePoint();
+ list($curveX, $curveY) = $curve->getBasePoint();
+ if ($candidateX->toBytes() != $curveX->toBytes()) {
+ break;
+ }
+ if ($candidateY->toBytes() != $curveY->toBytes()) {
+ break;
+ }
+
+ return $returnArray ?
+ ['namedCurve' => $testName] :
+ ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
+ }
+ }
+ }
+
+ $order = $curve->getOrder();
+ // we could try to calculate the order thusly:
+ // https://crypto.stackexchange.com/a/27914/4520
+ // https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm
+ if (!$order) {
+ throw new \RuntimeException('Specified Curves need the order to be specified');
+ }
+ $point = $curve->getBasePoint();
+ $x = $point[0]->toBytes();
+ $y = $point[1]->toBytes();
+
+ if ($curve instanceof PrimeCurve) {
+ /*
+ * valid versions are:
+ *
+ * ecdpVer1:
+ * - neither the curve or the base point are generated verifiably randomly.
+ * ecdpVer2:
+ * - curve and base point are generated verifiably at random and curve.seed is present
+ * ecdpVer3:
+ * - base point is generated verifiably at random but curve is not. curve.seed is present
+ */
+ // other (optional) parameters can be calculated using the methods discused at
+ // https://crypto.stackexchange.com/q/28947/4520
+ $data = [
+ 'version' => 'ecdpVer1',
+ 'fieldID' => [
+ 'fieldType' => 'prime-field',
+ 'parameters' => $curve->getModulo()
+ ],
+ 'curve' => [
+ 'a' => $curve->getA()->toBytes(),
+ 'b' => $curve->getB()->toBytes()
+ ],
+ 'base' => "\4" . $x . $y,
+ 'order' => $order
+ ];
+
+ return $returnArray ?
+ ['specifiedCurve' => $data] :
+ ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
+ }
+ if ($curve instanceof BinaryCurve) {
+ $modulo = $curve->getModulo();
+ $basis = count($modulo);
+ $m = array_shift($modulo);
+ array_pop($modulo); // the last parameter should always be 0
+ //rsort($modulo);
+ switch ($basis) {
+ case 3:
+ $basis = 'tpBasis';
+ $modulo = new BigInteger($modulo[0]);
+ break;
+ case 5:
+ $basis = 'ppBasis';
+ // these should be in strictly ascending order (hence the commented out rsort above)
+ $modulo = [
+ 'k1' => new BigInteger($modulo[2]),
+ 'k2' => new BigInteger($modulo[1]),
+ 'k3' => new BigInteger($modulo[0])
+ ];
+ $modulo = ASN1::encodeDER($modulo, Maps\Pentanomial::MAP);
+ $modulo = new ASN1\Element($modulo);
+ }
+ $params = ASN1::encodeDER([
+ 'm' => new BigInteger($m),
+ 'basis' => $basis,
+ 'parameters' => $modulo
+ ], Maps\Characteristic_two::MAP);
+ $params = new ASN1\Element($params);
+ $a = ltrim($curve->getA()->toBytes(), "\0");
+ if (!strlen($a)) {
+ $a = "\0";
+ }
+ $b = ltrim($curve->getB()->toBytes(), "\0");
+ if (!strlen($b)) {
+ $b = "\0";
+ }
+ $data = [
+ 'version' => 'ecdpVer1',
+ 'fieldID' => [
+ 'fieldType' => 'characteristic-two-field',
+ 'parameters' => $params
+ ],
+ 'curve' => [
+ 'a' => $a,
+ 'b' => $b
+ ],
+ 'base' => "\4" . $x . $y,
+ 'order' => $order
+ ];
+
+ return $returnArray ?
+ ['specifiedCurve' => $data] :
+ ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
+ }
+
+ throw new UnsupportedCurveException('Curve cannot be serialized');
+ }
+
+ /**
+ * Use Specified Curve
+ *
+ * A specified curve has all the coefficients, the base points, etc, explicitely included.
+ * A specified curve is a more verbose way of representing a curve
+ */
+ public static function useSpecifiedCurve()
+ {
+ self::$useNamedCurves = false;
+ }
+
+ /**
+ * Use Named Curve
+ *
+ * A named curve does not include any parameters. It is up to the EC parameters to
+ * know what the coefficients, the base points, etc, are from the name of the curve.
+ * A named curve is a more concise way of representing a curve
+ */
+ public static function useNamedCurve()
+ {
+ self::$useNamedCurves = true;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php
new file mode 100644
index 000000000..5bc5184f7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/JWK.php
@@ -0,0 +1,189 @@
+<?php
+
+/**
+ * JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Crypt\EC\Curves\secp256k1;
+use phpseclib3\Crypt\EC\Curves\secp256r1;
+use phpseclib3\Crypt\EC\Curves\secp384r1;
+use phpseclib3\Crypt\EC\Curves\secp521r1;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * JWK Formatted EC Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class JWK extends Progenitor
+{
+ use Common;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ switch ($key->kty) {
+ case 'EC':
+ switch ($key->crv) {
+ case 'P-256':
+ case 'P-384':
+ case 'P-521':
+ case 'secp256k1':
+ break;
+ default:
+ throw new UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)');
+ }
+ break;
+ case 'OKP':
+ switch ($key->crv) {
+ case 'Ed25519':
+ case 'Ed448':
+ break;
+ default:
+ throw new UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)');
+ }
+ break;
+ default:
+ throw new \Exception('Only EC and OKP JWK keys are supported');
+ }
+
+ $curve = '\phpseclib3\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv);
+ $curve = new $curve();
+
+ if ($curve instanceof TwistedEdwardsCurve) {
+ $QA = self::extractPoint(Strings::base64url_decode($key->x), $curve);
+ if (!isset($key->d)) {
+ return compact('curve', 'QA');
+ }
+ $arr = $curve->extractSecret(Strings::base64url_decode($key->d));
+ return compact('curve', 'QA') + $arr;
+ }
+
+ $QA = [
+ $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->x), 256)),
+ $curve->convertInteger(new BigInteger(Strings::base64url_decode($key->y), 256))
+ ];
+
+ if (!$curve->verifyPoint($QA)) {
+ throw new \RuntimeException('Unable to verify that point exists on curve');
+ }
+
+ if (!isset($key->d)) {
+ return compact('curve', 'QA');
+ }
+
+ $dA = new BigInteger(Strings::base64url_decode($key->d), 256);
+
+ $curve->rangeCheck($dA);
+
+ return compact('curve', 'dA', 'QA');
+ }
+
+ /**
+ * Returns the alias that corresponds to a curve
+ *
+ * @return string
+ */
+ private static function getAlias(BaseCurve $curve)
+ {
+ switch (true) {
+ case $curve instanceof secp256r1:
+ return 'P-256';
+ case $curve instanceof secp384r1:
+ return 'P-384';
+ case $curve instanceof secp521r1:
+ return 'P-521';
+ case $curve instanceof secp256k1:
+ return 'secp256k1';
+ }
+
+ $reflect = new \ReflectionClass($curve);
+ $curveName = $reflect->isFinal() ?
+ $reflect->getParentClass()->getShortName() :
+ $reflect->getShortName();
+ throw new UnsupportedCurveException("$curveName is not a supported curve");
+ }
+
+ /**
+ * Return the array superstructure for an EC public key
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @return array
+ */
+ private static function savePublicKeyHelper(BaseCurve $curve, array $publicKey)
+ {
+ if ($curve instanceof TwistedEdwardsCurve) {
+ return [
+ 'kty' => 'OKP',
+ 'crv' => $curve instanceof Ed25519 ? 'Ed25519' : 'Ed448',
+ 'x' => Strings::base64url_encode($curve->encodePoint($publicKey))
+ ];
+ }
+
+ return [
+ 'kty' => 'EC',
+ 'crv' => self::getAlias($curve),
+ 'x' => Strings::base64url_encode($publicKey[0]->toBytes()),
+ 'y' => Strings::base64url_encode($publicKey[1]->toBytes())
+ ];
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
+ {
+ $key = self::savePublicKeyHelper($curve, $publicKey);
+
+ return self::wrapKey($key, $options);
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param Ed25519 $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
+ {
+ $key = self::savePublicKeyHelper($curve, $publicKey);
+ $key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes();
+ $key['d'] = Strings::base64url_encode($key['d']);
+
+ return self::wrapKey($key, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php
new file mode 100644
index 000000000..aa64f79ab
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPrivate.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * Montgomery Private Key Handler
+ *
+ * "Naked" Curve25519 private keys can pretty much be any sequence of random 32x bytes so unless
+ * we have a "hidden" key handler pretty much every 32 byte string will be loaded as a curve25519
+ * private key even if it probably isn't one by PublicKeyLoader.
+ *
+ * "Naked" Curve25519 public keys also a string of 32 bytes so distinguishing between a "naked"
+ * curve25519 private key and a public key is nigh impossible, hence separate plugins for each
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\Curves\Curve25519;
+use phpseclib3\Crypt\EC\Curves\Curve448;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Montgomery Curve Private Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class MontgomeryPrivate
+{
+ /**
+ * Is invisible flag
+ *
+ */
+ const IS_INVISIBLE = true;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ switch (strlen($key)) {
+ case 32:
+ $curve = new Curve25519();
+ break;
+ case 56:
+ $curve = new Curve448();
+ break;
+ default:
+ throw new \LengthException('The only supported lengths are 32 and 56');
+ }
+
+ $components = ['curve' => $curve];
+ $components['dA'] = new BigInteger($key, 256);
+ $curve->rangeCheck($components['dA']);
+ // note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result)
+ $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
+
+ return $components;
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param MontgomeryCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @return string
+ */
+ public static function savePublicKey(MontgomeryCurve $curve, array $publicKey)
+ {
+ return strrev($publicKey[0]->toBytes());
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param MontgomeryCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, MontgomeryCurve $curve, array $publicKey, $secret = null, $password = '')
+ {
+ if (!empty($password) && is_string($password)) {
+ throw new UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption');
+ }
+
+ return $privateKey->toBytes();
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php
new file mode 100644
index 000000000..257c26e87
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/MontgomeryPublic.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Montgomery Public Key Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\Curves\Curve25519;
+use phpseclib3\Crypt\EC\Curves\Curve448;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Montgomery Public Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class MontgomeryPublic
+{
+ /**
+ * Is invisible flag
+ *
+ */
+ const IS_INVISIBLE = true;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ switch (strlen($key)) {
+ case 32:
+ $curve = new Curve25519();
+ break;
+ case 56:
+ $curve = new Curve448();
+ break;
+ default:
+ throw new \LengthException('The only supported lengths are 32 and 56');
+ }
+
+ $components = ['curve' => $curve];
+ $components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), 256))];
+
+ return $components;
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param MontgomeryCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @return string
+ */
+ public static function savePublicKey(MontgomeryCurve $curve, array $publicKey)
+ {
+ return strrev($publicKey[0]->toBytes());
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php
new file mode 100644
index 000000000..0ef116044
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * OpenSSH Formatted EC Key Handler
+ *
+ * PHP version 5
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * OpenSSH Formatted EC Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSH extends Progenitor
+{
+ use Common;
+
+ /**
+ * Supported Key Types
+ *
+ * @var array
+ */
+ protected static $types = [
+ 'ecdsa-sha2-nistp256',
+ 'ecdsa-sha2-nistp384',
+ 'ecdsa-sha2-nistp521',
+ 'ssh-ed25519'
+ ];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $parsed = parent::load($key, $password);
+
+ if (isset($parsed['paddedKey'])) {
+ $paddedKey = $parsed['paddedKey'];
+ list($type) = Strings::unpackSSH2('s', $paddedKey);
+ if ($type != $parsed['type']) {
+ throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
+ }
+ if ($type == 'ssh-ed25519') {
+ list(, $key, $comment) = Strings::unpackSSH2('sss', $paddedKey);
+ $key = libsodium::load($key);
+ $key['comment'] = $comment;
+ return $key;
+ }
+ list($curveName, $publicKey, $privateKey, $comment) = Strings::unpackSSH2('ssis', $paddedKey);
+ $curve = self::loadCurveByParam(['namedCurve' => $curveName]);
+ $curve->rangeCheck($privateKey);
+ return [
+ 'curve' => $curve,
+ 'dA' => $privateKey,
+ 'QA' => self::extractPoint("\0$publicKey", $curve),
+ 'comment' => $comment
+ ];
+ }
+
+ if ($parsed['type'] == 'ssh-ed25519') {
+ if (Strings::shift($parsed['publicKey'], 4) != "\0\0\0\x20") {
+ throw new \RuntimeException('Length of ssh-ed25519 key should be 32');
+ }
+
+ $curve = new Ed25519();
+ $qa = self::extractPoint($parsed['publicKey'], $curve);
+ } else {
+ list($curveName, $publicKey) = Strings::unpackSSH2('ss', $parsed['publicKey']);
+ $curveName = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
+ $curve = new $curveName();
+
+ $qa = self::extractPoint("\0" . $publicKey, $curve);
+ }
+
+ return [
+ 'curve' => $curve,
+ 'QA' => $qa,
+ 'comment' => $parsed['comment']
+ ];
+ }
+
+ /**
+ * Returns the alias that corresponds to a curve
+ *
+ * @return string
+ */
+ private static function getAlias(BaseCurve $curve)
+ {
+ self::initialize_static_variables();
+
+ $reflect = new \ReflectionClass($curve);
+ $name = $reflect->getShortName();
+
+ $oid = self::$curveOIDs[$name];
+ $aliases = array_filter(self::$curveOIDs, function ($v) use ($oid) {
+ return $v == $oid;
+ });
+ $aliases = array_keys($aliases);
+
+ for ($i = 0; $i < count($aliases); $i++) {
+ if (in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) {
+ $alias = $aliases[$i];
+ break;
+ }
+ }
+
+ if (!isset($alias)) {
+ throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
+ }
+
+ return $alias;
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
+ {
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+
+ if ($curve instanceof Ed25519) {
+ $key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey));
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $key;
+ }
+
+ $key = 'ssh-ed25519 ' . base64_encode($key) . ' ' . $comment;
+ return $key;
+ }
+
+ $alias = self::getAlias($curve);
+
+ $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+ $key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points);
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $key;
+ }
+
+ $key = 'ecdsa-sha2-' . $alias . ' ' . base64_encode($key) . ' ' . $comment;
+
+ return $key;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param Ed25519 $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
+ {
+ if ($curve instanceof Ed25519) {
+ if (!isset($secret)) {
+ throw new \RuntimeException('Private Key does not have a secret set');
+ }
+ if (strlen($secret) != 32) {
+ throw new \RuntimeException('Private Key secret is not of the correct length');
+ }
+
+ $pubKey = $curve->encodePoint($publicKey);
+
+ $publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey);
+ $privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey);
+
+ return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
+ }
+
+ $alias = self::getAlias($curve);
+
+ $points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+ $publicKey = self::savePublicKey($curve, $publicKey, ['binary' => true]);
+
+ $privateKey = Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey);
+
+ return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php
new file mode 100644
index 000000000..756ffb957
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * "PKCS1" (RFC5915) Formatted EC Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by File/X509.php
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN EC PRIVATE KEY-----
+ * -----BEGIN EC PARAMETERS-----
+ *
+ * Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
+ * DSA, whose format isn't really formally described anywhere, so might as well
+ * use it to describe this, too. PKCS1 is easier to remember than RFC5915, after
+ * all. I suppose this could also be named IETF but idk
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * "PKCS1" (RFC5915) Formatted EC Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS1 extends Progenitor
+{
+ use Common;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ self::initialize_static_variables();
+
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (strpos($key, 'BEGIN EC PARAMETERS') && strpos($key, 'BEGIN EC PRIVATE KEY')) {
+ $components = [];
+
+ preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches);
+ $decoded = parent::load($matches[0], $password);
+ $decoded = ASN1::decodeBER($decoded);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $ecPrivate = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
+ if (!is_array($ecPrivate)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+
+ if (isset($ecPrivate['parameters'])) {
+ $components['curve'] = self::loadCurveByParam($ecPrivate['parameters']);
+ }
+
+ preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches);
+ $decoded = parent::load($matches[0], '');
+ $decoded = ASN1::decodeBER($decoded);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
+ if (!is_array($ecParams)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+ $ecParams = self::loadCurveByParam($ecParams);
+
+ // comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes
+ // even if the modulo is the same
+ if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) {
+ throw new \RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY');
+ }
+
+ if (!isset($components['curve'])) {
+ $components['curve'] = $ecParams;
+ }
+
+ $components['dA'] = new BigInteger($ecPrivate['privateKey'], 256);
+ $components['curve']->rangeCheck($components['dA']);
+ $components['QA'] = isset($ecPrivate['publicKey']) ?
+ self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
+ $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
+
+ return $components;
+ }
+
+ $key = parent::load($key, $password);
+
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
+ if (is_array($key)) {
+ return ['curve' => self::loadCurveByParam($key)];
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
+ if (!is_array($key)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+ if (!isset($key['parameters'])) {
+ throw new \RuntimeException('Key cannot be loaded without parameters');
+ }
+
+ $components = [];
+ $components['curve'] = self::loadCurveByParam($key['parameters']);
+ $components['dA'] = new BigInteger($key['privateKey'], 256);
+ $components['QA'] = isset($ecPrivate['publicKey']) ?
+ self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
+ $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
+
+ return $components;
+ }
+
+ /**
+ * Convert EC parameters to the appropriate format
+ *
+ * @return string
+ */
+ public static function saveParameters(BaseCurve $curve, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
+ throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
+ }
+
+ $key = self::encodeParameters($curve, false, $options);
+
+ return "-----BEGIN EC PARAMETERS-----\r\n" .
+ chunk_split(Strings::base64_encode($key), 64) .
+ "-----END EC PARAMETERS-----\r\n";
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
+ {
+ self::initialize_static_variables();
+
+ if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
+ throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
+ }
+
+ $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+
+ $key = [
+ 'version' => 'ecPrivkeyVer1',
+ 'privateKey' => $privateKey->toBytes(),
+ 'parameters' => new ASN1\Element(self::encodeParameters($curve)),
+ 'publicKey' => "\0" . $publicKey
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
+
+ return self::wrapPrivateKey($key, 'EC', $password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php
new file mode 100644
index 000000000..9fc84054e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php
@@ -0,0 +1,237 @@
+<?php
+
+/**
+ * PKCS#8 Formatted EC Key Handler
+ *
+ * PHP version 5
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
+ * is specific to private keys it's basically creating a DER-encoded wrapper
+ * for keys. This just extends that same concept to public keys (much like ssh-keygen)
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Crypt\EC\Curves\Ed448;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#8 Formatted EC Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS8 extends Progenitor
+{
+ use Common;
+
+ /**
+ * OID Name
+ *
+ * @var array
+ */
+ const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448'];
+
+ /**
+ * OID Value
+ *
+ * @var string
+ */
+ const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113'];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ // initialize_static_variables() is defined in both the trait and the parent class
+ // when it's defined in two places it's the traits one that's called
+ // the parent one is needed, as well, but the parent one is called by other methods
+ // in the parent class as needed and in the context of the parent it's the parent
+ // one that's called
+ self::initialize_static_variables();
+
+ $key = parent::load($key, $password);
+
+ $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
+
+ switch ($key[$type . 'Algorithm']['algorithm']) {
+ case 'id-Ed25519':
+ case 'id-Ed448':
+ return self::loadEdDSA($key);
+ }
+
+ $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
+ if (!$params) {
+ throw new \RuntimeException('Unable to decode the parameters using Maps\ECParameters');
+ }
+
+ $components = [];
+ $components['curve'] = self::loadCurveByParam($params);
+
+ if ($type == 'publicKey') {
+ $components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']);
+
+ return $components;
+ }
+
+ $decoded = ASN1::decodeBER($key['privateKey']);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+ $key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
+ if (isset($key['parameters']) && $params != $key['parameters']) {
+ throw new \RuntimeException('The PKCS8 parameter field does not match the private key parameter field');
+ }
+
+ $components['dA'] = new BigInteger($key['privateKey'], 256);
+ $components['curve']->rangeCheck($components['dA']);
+ $components['QA'] = isset($key['publicKey']) ?
+ self::extractPoint($key['publicKey'], $components['curve']) :
+ $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
+
+ return $components;
+ }
+
+ /**
+ * Break a public or private EdDSA key down into its constituent components
+ *
+ * @return array
+ */
+ private static function loadEdDSA(array $key)
+ {
+ $components = [];
+
+ if (isset($key['privateKey'])) {
+ $components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
+ $expected = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($components['curve']::SIZE);
+ if (substr($key['privateKey'], 0, 2) != $expected) {
+ throw new \RuntimeException(
+ 'The first two bytes of the ' .
+ $key['privateKeyAlgorithm']['algorithm'] .
+ ' private key field should be 0x' . bin2hex($expected)
+ );
+ }
+ $arr = $components['curve']->extractSecret(substr($key['privateKey'], 2));
+ $components['dA'] = $arr['dA'];
+ $components['secret'] = $arr['secret'];
+ }
+
+ if (isset($key['publicKey'])) {
+ if (!isset($components['curve'])) {
+ $components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
+ }
+
+ $components['QA'] = self::extractPoint($key['publicKey'], $components['curve']);
+ }
+
+ if (isset($key['privateKey']) && !isset($components['QA'])) {
+ $components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ if ($curve instanceof MontgomeryCurve) {
+ throw new UnsupportedCurveException('Montgomery Curves are not supported');
+ }
+
+ if ($curve instanceof TwistedEdwardsCurve) {
+ return self::wrapPublicKey(
+ $curve->encodePoint($publicKey),
+ null,
+ $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448',
+ $options
+ );
+ }
+
+ $params = new ASN1\Element(self::encodeParameters($curve, false, $options));
+
+ $key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+
+ return self::wrapPublicKey($key, $params, 'id-ecPublicKey', $options);
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = '', array $options = [])
+ {
+ self::initialize_static_variables();
+
+ if ($curve instanceof MontgomeryCurve) {
+ throw new UnsupportedCurveException('Montgomery Curves are not supported');
+ }
+
+ if ($curve instanceof TwistedEdwardsCurve) {
+ return self::wrapPrivateKey(
+ chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($curve::SIZE) . $secret,
+ [],
+ null,
+ $password,
+ $curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448'
+ );
+ }
+
+ $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+
+ $params = new ASN1\Element(self::encodeParameters($curve, false, $options));
+
+ $key = [
+ 'version' => 'ecPrivkeyVer1',
+ 'privateKey' => $privateKey->toBytes(),
+ //'parameters' => $params,
+ 'publicKey' => "\0" . $publicKey
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
+
+ return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php
new file mode 100644
index 000000000..7e1e9170f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * PuTTY Formatted EC Key Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PuTTY Formatted EC Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PuTTY extends Progenitor
+{
+ use Common;
+
+ /**
+ * Public Handler
+ *
+ * @var string
+ */
+ const PUBLIC_HANDLER = 'phpseclib3\Crypt\EC\Formats\Keys\OpenSSH';
+
+ /**
+ * Supported Key Types
+ *
+ * @var array
+ */
+ protected static $types = [
+ 'ecdsa-sha2-nistp256',
+ 'ecdsa-sha2-nistp384',
+ 'ecdsa-sha2-nistp521',
+ 'ssh-ed25519'
+ ];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $components = parent::load($key, $password);
+ if (!isset($components['private'])) {
+ return $components;
+ }
+
+ $private = $components['private'];
+
+ $temp = Strings::base64_encode(Strings::packSSH2('s', $components['type']) . $components['public']);
+ $components = OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']);
+
+ if ($components['curve'] instanceof TwistedEdwardsCurve) {
+ if (Strings::shift($private, 4) != "\0\0\0\x20") {
+ throw new \RuntimeException('Length of ssh-ed25519 key should be 32');
+ }
+ $arr = $components['curve']->extractSecret($private);
+ $components['dA'] = $arr['dA'];
+ $components['secret'] = $arr['secret'];
+ } else {
+ list($components['dA']) = Strings::unpackSSH2('i', $private);
+ $components['curve']->rangeCheck($components['dA']);
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, $secret = null, $password = false, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
+ $name = $public[0];
+ $public = Strings::base64_decode($public[1]);
+ list(, $length) = unpack('N', Strings::shift($public, 4));
+ Strings::shift($public, $length);
+
+ // PuTTY pads private keys with a null byte per the following:
+ // https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926
+ if (!$curve instanceof TwistedEdwardsCurve) {
+ $private = $privateKey->toBytes();
+ if (!(strlen($privateKey->toBits()) & 7)) {
+ $private = "\0$private";
+ }
+ }
+
+ $private = $curve instanceof TwistedEdwardsCurve ?
+ Strings::packSSH2('s', $secret) :
+ Strings::packSSH2('s', $private);
+
+ return self::wrapPrivateKey($public, $private, $name, $password, $options);
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField[] $publicKey
+ * @return string
+ */
+ public static function savePublicKey(BaseCurve $curve, array $publicKey)
+ {
+ $public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
+ $type = $public[0];
+ $public = Strings::base64_decode($public[1]);
+ list(, $length) = unpack('N', Strings::shift($public, 4));
+ Strings::shift($public, $length);
+
+ return self::wrapPublicKey($public, $type);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php
new file mode 100644
index 000000000..7f6cf6345
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/XML.php
@@ -0,0 +1,486 @@
+<?php
+
+/**
+ * XML Formatted EC Key Handler
+ *
+ * More info:
+ *
+ * https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue
+ * http://en.wikipedia.org/wiki/XML_Signature
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * XML Formatted EC Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class XML
+{
+ use Common;
+
+ /**
+ * Default namespace
+ *
+ * @var string
+ */
+ private static $namespace;
+
+ /**
+ * Flag for using RFC4050 syntax
+ *
+ * @var bool
+ */
+ private static $rfc4050 = false;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ self::initialize_static_variables();
+
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (!class_exists('DOMDocument')) {
+ throw new BadConfigurationException('The dom extension is not setup correctly on this system');
+ }
+
+ $use_errors = libxml_use_internal_errors(true);
+
+ if (substr($key, 0, 5) != '<?xml') {
+ $key = '<xml>' . $key . '</xml>';
+ }
+
+ $temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#');
+ if ($temp) {
+ $key = $temp;
+ }
+
+ $temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#');
+ if ($temp) {
+ $key = $temp;
+ }
+
+ $dom = new \DOMDocument();
+
+ if (!$dom->loadXML($key)) {
+ libxml_use_internal_errors($use_errors);
+ throw new \UnexpectedValueException('Key does not appear to contain XML');
+ }
+ $xpath = new \DOMXPath($dom);
+ libxml_use_internal_errors($use_errors);
+ $curve = self::loadCurveByParam($xpath);
+
+ $pubkey = self::query($xpath, 'publickey', 'Public Key is not present');
+
+ $QA = self::query($xpath, 'ecdsakeyvalue')->length ?
+ self::extractPointRFC4050($xpath, $curve) :
+ self::extractPoint("\0" . $pubkey, $curve);
+
+ libxml_use_internal_errors($use_errors);
+
+ return compact('curve', 'QA');
+ }
+
+ /**
+ * Case-insensitive xpath query
+ *
+ * @param \DOMXPath $xpath
+ * @param string $name
+ * @param string $error optional
+ * @param bool $decode optional
+ * @return \DOMNodeList
+ */
+ private static function query(\DOMXPath $xpath, $name, $error = null, $decode = true)
+ {
+ $query = '/';
+ $names = explode('/', $name);
+ foreach ($names as $name) {
+ $query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']";
+ }
+ $result = $xpath->query($query);
+ if (!isset($error)) {
+ return $result;
+ }
+
+ if (!$result->length) {
+ throw new \RuntimeException($error);
+ }
+ return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent;
+ }
+
+ /**
+ * Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element.
+ *
+ * @param string $xml
+ * @param string $ns
+ */
+ private static function isolateNamespace($xml, $ns)
+ {
+ $dom = new \DOMDocument();
+ if (!$dom->loadXML($xml)) {
+ return false;
+ }
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->query("//*[namespace::*[.='$ns'] and not(../namespace::*[.='$ns'])]");
+ if (!$nodes->length) {
+ return false;
+ }
+ $node = $nodes->item(0);
+ $ns_name = $node->lookupPrefix($ns);
+ if ($ns_name) {
+ $node->removeAttributeNS($ns, $ns_name);
+ }
+ return $dom->saveXML($node);
+ }
+
+ /**
+ * Decodes the value
+ *
+ * @param string $value
+ */
+ private static function decodeValue($value)
+ {
+ return Strings::base64_decode(str_replace(["\r", "\n", ' ', "\t"], '', $value));
+ }
+
+ /**
+ * Extract points from an XML document
+ *
+ * @param \DOMXPath $xpath
+ * @param BaseCurve $curve
+ * @return object[]
+ */
+ private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve $curve)
+ {
+ $x = self::query($xpath, 'publickey/x');
+ $y = self::query($xpath, 'publickey/y');
+ if (!$x->length || !$x->item(0)->hasAttribute('Value')) {
+ throw new \RuntimeException('Public Key / X coordinate not found');
+ }
+ if (!$y->length || !$y->item(0)->hasAttribute('Value')) {
+ throw new \RuntimeException('Public Key / Y coordinate not found');
+ }
+ $point = [
+ $curve->convertInteger(new BigInteger($x->item(0)->getAttribute('Value'))),
+ $curve->convertInteger(new BigInteger($y->item(0)->getAttribute('Value')))
+ ];
+ if (!$curve->verifyPoint($point)) {
+ throw new \RuntimeException('Unable to verify that point exists on curve');
+ }
+ return $point;
+ }
+
+ /**
+ * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
+ * on the curve parameters
+ *
+ * @param \DomXPath $xpath
+ * @return BaseCurve|false
+ */
+ private static function loadCurveByParam(\DOMXPath $xpath)
+ {
+ $namedCurve = self::query($xpath, 'namedcurve');
+ if ($namedCurve->length == 1) {
+ $oid = $namedCurve->item(0)->getAttribute('URN');
+ $oid = preg_replace('#[^\d.]#', '', $oid);
+ $name = array_search($oid, self::$curveOIDs);
+ if ($name === false) {
+ throw new UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported');
+ }
+
+ $curve = '\phpseclib3\Crypt\EC\Curves\\' . $name;
+ if (!class_exists($curve)) {
+ throw new UnsupportedCurveException('Named Curve of ' . $name . ' is not supported');
+ }
+ return new $curve();
+ }
+
+ $params = self::query($xpath, 'explicitparams');
+ if ($params->length) {
+ return self::loadCurveByParamRFC4050($xpath);
+ }
+
+ $params = self::query($xpath, 'ecparameters');
+ if (!$params->length) {
+ throw new \RuntimeException('No parameters are present');
+ }
+
+ $fieldTypes = [
+ 'prime-field' => ['fieldid/prime/p'],
+ 'gnb' => ['fieldid/gnb/m'],
+ 'tnb' => ['fieldid/tnb/k'],
+ 'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'],
+ 'unknown' => []
+ ];
+
+ foreach ($fieldTypes as $type => $queries) {
+ foreach ($queries as $query) {
+ $result = self::query($xpath, $query);
+ if (!$result->length) {
+ continue 2;
+ }
+ $param = preg_replace('#.*/#', '', $query);
+ $$param = self::decodeValue($result->item(0)->textContent);
+ }
+ break;
+ }
+
+ $a = self::query($xpath, 'curve/a', 'A coefficient is not present');
+ $b = self::query($xpath, 'curve/b', 'B coefficient is not present');
+ $base = self::query($xpath, 'base', 'Base point is not present');
+ $order = self::query($xpath, 'order', 'Order is not present');
+
+ switch ($type) {
+ case 'prime-field':
+ $curve = new PrimeCurve();
+ $curve->setModulo(new BigInteger($p, 256));
+ $curve->setCoefficients(
+ new BigInteger($a, 256),
+ new BigInteger($b, 256)
+ );
+ $point = self::extractPoint("\0" . $base, $curve);
+ $curve->setBasePoint(...$point);
+ $curve->setOrder(new BigInteger($order, 256));
+ return $curve;
+ case 'gnb':
+ case 'tnb':
+ case 'pnb':
+ default:
+ throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
+ }
+ }
+
+ /**
+ * Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
+ * on the curve parameters
+ *
+ * @param \DomXPath $xpath
+ * @return BaseCurve|false
+ */
+ private static function loadCurveByParamRFC4050(\DOMXPath $xpath)
+ {
+ $fieldTypes = [
+ 'prime-field' => ['primefieldparamstype/p'],
+ 'unknown' => []
+ ];
+
+ foreach ($fieldTypes as $type => $queries) {
+ foreach ($queries as $query) {
+ $result = self::query($xpath, $query);
+ if (!$result->length) {
+ continue 2;
+ }
+ $param = preg_replace('#.*/#', '', $query);
+ $$param = $result->item(0)->textContent;
+ }
+ break;
+ }
+
+ $a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', false);
+ $b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', false);
+ $x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', false);
+ $y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', false);
+ $order = self::query($xpath, 'order', 'Order is not present', false);
+
+ switch ($type) {
+ case 'prime-field':
+ $curve = new PrimeCurve();
+
+ $p = str_replace(["\r", "\n", ' ', "\t"], '', $p);
+ $curve->setModulo(new BigInteger($p));
+
+ $a = str_replace(["\r", "\n", ' ', "\t"], '', $a);
+ $b = str_replace(["\r", "\n", ' ', "\t"], '', $b);
+ $curve->setCoefficients(
+ new BigInteger($a),
+ new BigInteger($b)
+ );
+
+ $x = str_replace(["\r", "\n", ' ', "\t"], '', $x);
+ $y = str_replace(["\r", "\n", ' ', "\t"], '', $y);
+ $curve->setBasePoint(
+ new BigInteger($x),
+ new BigInteger($y)
+ );
+
+ $order = str_replace(["\r", "\n", ' ', "\t"], '', $order);
+ $curve->setOrder(new BigInteger($order));
+ return $curve;
+ default:
+ throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
+ }
+ }
+
+ /**
+ * Sets the namespace. dsig11 is the most common one.
+ *
+ * Set to null to unset. Used only for creating public keys.
+ *
+ * @param string $namespace
+ */
+ public static function setNamespace($namespace)
+ {
+ self::$namespace = $namespace;
+ }
+
+ /**
+ * Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050
+ */
+ public static function enableRFC4050Syntax()
+ {
+ self::$rfc4050 = true;
+ }
+
+ /**
+ * Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters
+ */
+ public static function disableRFC4050Syntax()
+ {
+ self::$rfc4050 = false;
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BaseCurve $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
+ throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
+ }
+
+ if (empty(static::$namespace)) {
+ $pre = $post = '';
+ } else {
+ $pre = static::$namespace . ':';
+ $post = ':' . static::$namespace;
+ }
+
+ if (self::$rfc4050) {
+ return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" .
+ self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
+ '<' . $pre . 'PublicKey>' . "\r\n" .
+ '<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" .
+ '<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" .
+ '</' . $pre . 'PublicKey>' . "\r\n" .
+ '</' . $pre . 'ECDSAKeyValue>';
+ }
+
+ $publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
+
+ return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" .
+ self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
+ '<' . $pre . 'PublicKey>' . Strings::base64_encode($publicKey) . '</' . $pre . 'PublicKey>' . "\r\n" .
+ '</' . $pre . 'ECDSAKeyValue>';
+ }
+
+ /**
+ * Encode Parameters
+ *
+ * @param BaseCurve $curve
+ * @param string $pre
+ * @param array $options optional
+ * @return string|false
+ */
+ private static function encodeXMLParameters(BaseCurve $curve, $pre, array $options = [])
+ {
+ $result = self::encodeParameters($curve, true, $options);
+
+ if (isset($result['namedCurve'])) {
+ $namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />';
+ return self::$rfc4050 ?
+ '<DomainParameters>' . str_replace('URI', 'URN', $namedCurve) . '</DomainParameters>' :
+ $namedCurve;
+ }
+
+ if (self::$rfc4050) {
+ $xml = '<' . $pre . 'ExplicitParams>' . "\r\n" .
+ '<' . $pre . 'FieldParams>' . "\r\n";
+ $temp = $result['specifiedCurve'];
+ switch ($temp['fieldID']['fieldType']) {
+ case 'prime-field':
+ $xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" .
+ '<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '</' . $pre . 'P>' . "\r\n" .
+ '</' . $pre . 'PrimeFieldParamsType>' . "\r\n";
+ $a = $curve->getA();
+ $b = $curve->getB();
+ list($x, $y) = $curve->getBasePoint();
+ break;
+ default:
+ throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
+ }
+ $xml .= '</' . $pre . 'FieldParams>' . "\r\n" .
+ '<' . $pre . 'CurveParamsType>' . "\r\n" .
+ '<' . $pre . 'A>' . $a . '</' . $pre . 'A>' . "\r\n" .
+ '<' . $pre . 'B>' . $b . '</' . $pre . 'B>' . "\r\n" .
+ '</' . $pre . 'CurveParamsType>' . "\r\n" .
+ '<' . $pre . 'BasePointParams>' . "\r\n" .
+ '<' . $pre . 'BasePoint>' . "\r\n" .
+ '<' . $pre . 'ECPointType>' . "\r\n" .
+ '<' . $pre . 'X>' . $x . '</' . $pre . 'X>' . "\r\n" .
+ '<' . $pre . 'Y>' . $y . '</' . $pre . 'Y>' . "\r\n" .
+ '</' . $pre . 'ECPointType>' . "\r\n" .
+ '</' . $pre . 'BasePoint>' . "\r\n" .
+ '<' . $pre . 'Order>' . $curve->getOrder() . '</' . $pre . 'Order>' . "\r\n" .
+ '</' . $pre . 'BasePointParams>' . "\r\n" .
+ '</' . $pre . 'ExplicitParams>' . "\r\n";
+
+ return $xml;
+ }
+
+ if (isset($result['specifiedCurve'])) {
+ $xml = '<' . $pre . 'ECParameters>' . "\r\n" .
+ '<' . $pre . 'FieldID>' . "\r\n";
+ $temp = $result['specifiedCurve'];
+ switch ($temp['fieldID']['fieldType']) {
+ case 'prime-field':
+ $xml .= '<' . $pre . 'Prime>' . "\r\n" .
+ '<' . $pre . 'P>' . Strings::base64_encode($temp['fieldID']['parameters']->toBytes()) . '</' . $pre . 'P>' . "\r\n" .
+ '</' . $pre . 'Prime>' . "\r\n" ;
+ break;
+ default:
+ throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
+ }
+ $xml .= '</' . $pre . 'FieldID>' . "\r\n" .
+ '<' . $pre . 'Curve>' . "\r\n" .
+ '<' . $pre . 'A>' . Strings::base64_encode($temp['curve']['a']) . '</' . $pre . 'A>' . "\r\n" .
+ '<' . $pre . 'B>' . Strings::base64_encode($temp['curve']['b']) . '</' . $pre . 'B>' . "\r\n" .
+ '</' . $pre . 'Curve>' . "\r\n" .
+ '<' . $pre . 'Base>' . Strings::base64_encode($temp['base']) . '</' . $pre . 'Base>' . "\r\n" .
+ '<' . $pre . 'Order>' . Strings::base64_encode($temp['order']) . '</' . $pre . 'Order>' . "\r\n" .
+ '</' . $pre . 'ECParameters>';
+ return $xml;
+ }
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php
new file mode 100644
index 000000000..cce37bab4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Keys/libsodium.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * libsodium Key Handler
+ *
+ * Different NaCl implementations store the key differently.
+ * https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates.
+ * libsodium appears to use the same format as SUPERCOP.
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Keys;
+
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * libsodium Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class libsodium
+{
+ use Common;
+
+ /**
+ * Is invisible flag
+ *
+ */
+ const IS_INVISIBLE = true;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ switch (strlen($key)) {
+ case 32:
+ $public = $key;
+ break;
+ case 64:
+ $private = substr($key, 0, 32);
+ $public = substr($key, -32);
+ break;
+ case 96:
+ $public = substr($key, -32);
+ if (substr($key, 32, 32) != $public) {
+ throw new \RuntimeException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match');
+ }
+ $private = substr($key, 0, 32);
+ break;
+ default:
+ throw new \RuntimeException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long');
+ }
+
+ $curve = new Ed25519();
+ $components = ['curve' => $curve];
+ if (isset($private)) {
+ $arr = $curve->extractSecret($private);
+ $components['dA'] = $arr['dA'];
+ $components['secret'] = $arr['secret'];
+ }
+ $components['QA'] = isset($public) ?
+ self::extractPoint($public, $curve) :
+ $curve->multiplyPoint($curve->getBasePoint(), $components['dA']);
+
+ return $components;
+ }
+
+ /**
+ * Convert an EC public key to the appropriate format
+ *
+ * @param Ed25519 $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @return string
+ */
+ public static function savePublicKey(Ed25519 $curve, array $publicKey)
+ {
+ return $curve->encodePoint($publicKey);
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $privateKey
+ * @param Ed25519 $curve
+ * @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
+ * @param string $secret optional
+ * @param string $password optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, $secret = null, $password = '')
+ {
+ if (!isset($secret)) {
+ throw new \RuntimeException('Private Key does not have a secret set');
+ }
+ if (strlen($secret) != 32) {
+ throw new \RuntimeException('Private Key secret is not of the correct length');
+ }
+ if (!empty($password) && is_string($password)) {
+ throw new UnsupportedFormatException('libsodium private keys do not support encryption');
+ }
+ return $secret . $curve->encodePoint($publicKey);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php
new file mode 100644
index 000000000..385028b3a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/ASN1.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * ASN1 Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures in the format described in
+ * https://tools.ietf.org/html/rfc3279#section-2.2.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Signature;
+
+use phpseclib3\File\ASN1 as Encoder;
+use phpseclib3\File\ASN1\Maps\EcdsaSigValue;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * ASN1 Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ASN1
+{
+ /**
+ * Loads a signature
+ *
+ * @param string $sig
+ * @return array
+ */
+ public static function load($sig)
+ {
+ if (!is_string($sig)) {
+ return false;
+ }
+
+ $decoded = Encoder::decodeBER($sig);
+ if (empty($decoded)) {
+ return false;
+ }
+ $components = Encoder::asn1map($decoded[0], EcdsaSigValue::MAP);
+
+ return $components;
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s)
+ {
+ return Encoder::encodeDER(compact('r', 's'), EcdsaSigValue::MAP);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php
new file mode 100644
index 000000000..c5e622a12
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * IEEE P1363 Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures in the format described in
+ * https://standards.ieee.org/ieee/1363/2049/ and
+ * https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign#ecdsa
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Signature;
+
+use phpseclib3\Math\BigInteger;
+
+/**
+ * ASN1 Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class IEEE
+{
+ /**
+ * Loads a signature
+ *
+ * @param string $sig
+ * @return array
+ */
+ public static function load($sig)
+ {
+ if (!is_string($sig)) {
+ return false;
+ }
+
+ $len = strlen($sig);
+ if ($len & 1) {
+ return false;
+ }
+
+ $r = new BigInteger(substr($sig, 0, $len >> 1), 256);
+ $s = new BigInteger(substr($sig, $len >> 1), 256);
+
+ return compact('r', 's');
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @param string $curve
+ * @param int $length
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s, $curve, $length)
+ {
+ $r = $r->toBytes();
+ $s = $s->toBytes();
+ $length = (int) ceil($length / 8);
+ return str_pad($r, $length, "\0", STR_PAD_LEFT) . str_pad($s, $length, "\0", STR_PAD_LEFT);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php
new file mode 100644
index 000000000..7e4b47fe6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/Raw.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Raw EC Signature Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Signature;
+
+use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor;
+
+/**
+ * Raw DSA Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Raw extends Progenitor
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php
new file mode 100644
index 000000000..698c8e4ce
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/SSH2.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * SSH2 Signature Handler
+ *
+ * PHP version 5
+ *
+ * Handles signatures in the format used by SSH2
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC\Formats\Signature;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * SSH2 Signature Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SSH2
+{
+ /**
+ * Loads a signature
+ *
+ * @param string $sig
+ * @return mixed
+ */
+ public static function load($sig)
+ {
+ if (!is_string($sig)) {
+ return false;
+ }
+
+ $result = Strings::unpackSSH2('ss', $sig);
+ if ($result === false) {
+ return false;
+ }
+ list($type, $blob) = $result;
+ switch ($type) {
+ // see https://tools.ietf.org/html/rfc5656#section-3.1.2
+ case 'ecdsa-sha2-nistp256':
+ case 'ecdsa-sha2-nistp384':
+ case 'ecdsa-sha2-nistp521':
+ break;
+ default:
+ return false;
+ }
+
+ $result = Strings::unpackSSH2('ii', $blob);
+ if ($result === false) {
+ return false;
+ }
+
+ return [
+ 'r' => $result[0],
+ 's' => $result[1]
+ ];
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @param BigInteger $r
+ * @param BigInteger $s
+ * @param string $curve
+ * @return string
+ */
+ public static function save(BigInteger $r, BigInteger $s, $curve)
+ {
+ switch ($curve) {
+ case 'secp256r1':
+ $curve = 'nistp256';
+ break;
+ case 'secp384r1':
+ $curve = 'nistp384';
+ break;
+ case 'secp521r1':
+ $curve = 'nistp521';
+ break;
+ default:
+ return false;
+ }
+
+ $blob = Strings::packSSH2('ii', $r, $s);
+
+ return Strings::packSSH2('ss', 'ecdsa-sha2-' . $curve, $blob);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php
new file mode 100644
index 000000000..c0ed64a84
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Parameters.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * EC Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC;
+
+use phpseclib3\Crypt\EC;
+
+/**
+ * EC Parameters
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class Parameters extends EC
+{
+ /**
+ * Returns the parameters
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type = 'PKCS1', array $options = [])
+ {
+ $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
+
+ return $type::saveParameters($this->curve, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php
new file mode 100644
index 000000000..9947bb7d5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PrivateKey.php
@@ -0,0 +1,283 @@
+<?php
+
+/**
+ * EC Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\EC;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Crypt\EC\Curves\Curve25519;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
+use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Exception\UnsupportedOperationException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * EC Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PrivateKey extends EC implements Common\PrivateKey
+{
+ use Common\Traits\PasswordProtected;
+
+ /**
+ * Private Key dA
+ *
+ * sign() converts this to a BigInteger so one might wonder why this is a FiniteFieldInteger instead of
+ * a BigInteger. That's because a FiniteFieldInteger, when converted to a byte string, is null padded by
+ * a certain amount whereas a BigInteger isn't.
+ *
+ * @var object
+ */
+ protected $dA;
+
+ /**
+ * @var string
+ */
+ protected $secret;
+
+ /**
+ * Multiplies an encoded point by the private key
+ *
+ * Used by ECDH
+ *
+ * @param string $coordinates
+ * @return string
+ */
+ public function multiply($coordinates)
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ if ($this->curve instanceof Curve25519 && self::$engines['libsodium']) {
+ return sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates);
+ }
+
+ $point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))];
+ $point = $this->curve->multiplyPoint($point, $this->dA);
+ return strrev($point[0]->toBytes(true));
+ }
+ if (!$this->curve instanceof TwistedEdwardsCurve) {
+ $coordinates = "\0$coordinates";
+ }
+ $point = PKCS1::extractPoint($coordinates, $this->curve);
+ $point = $this->curve->multiplyPoint($point, $this->dA);
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ return $this->curve->encodePoint($point);
+ }
+ if (empty($point)) {
+ throw new \RuntimeException('The infinity point is invalid');
+ }
+ return "\4" . $point[0]->toBytes(true) . $point[1]->toBytes(true);
+ }
+
+ /**
+ * Create a signature
+ *
+ * @see self::verify()
+ * @param string $message
+ * @return mixed
+ */
+ public function sign($message)
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
+ }
+
+ $dA = $this->dA;
+ $order = $this->curve->getOrder();
+
+ $shortFormat = $this->shortFormat;
+ $format = $this->sigFormat;
+ if ($format === false) {
+ return false;
+ }
+
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
+ $result = sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium'));
+ return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result) : $result;
+ }
+
+ // contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not.
+ // quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
+ // "The Ed25519ph and Ed448ph variants ... SHOULD NOT be used"
+ $A = $this->curve->encodePoint($this->QA);
+ $curve = $this->curve;
+ $hash = new Hash($curve::HASH);
+
+ $secret = substr($hash->hash($this->secret), $curve::SIZE);
+
+ if ($curve instanceof Ed25519) {
+ $dom = !isset($this->context) ? '' :
+ 'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
+ } else {
+ $context = isset($this->context) ? $this->context : '';
+ $dom = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
+ }
+ // SHA-512(dom2(F, C) || prefix || PH(M))
+ $r = $hash->hash($dom . $secret . $message);
+ $r = strrev($r);
+ $r = new BigInteger($r, 256);
+ list(, $r) = $r->divide($order);
+ $R = $curve->multiplyPoint($curve->getBasePoint(), $r);
+ $R = $curve->encodePoint($R);
+ $k = $hash->hash($dom . $R . $A . $message);
+ $k = strrev($k);
+ $k = new BigInteger($k, 256);
+ list(, $k) = $k->divide($order);
+ $S = $k->multiply($dA)->add($r);
+ list(, $S) = $S->divide($order);
+ $S = str_pad(strrev($S->toBytes()), $curve::SIZE, "\0");
+ return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $R . $S) : $R . $S;
+ }
+
+ if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
+ $signature = '';
+ // altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long
+ // supported signing / verification
+ // we use specified curves to avoid issues with OpenSSL possibly not supporting a given named curve;
+ // doing this may mean some curve-specific optimizations can't be used but idk if OpenSSL even
+ // has curve-specific optimizations
+ $result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());
+
+ if ($result) {
+ if ($shortFormat == 'ASN1') {
+ return $signature;
+ }
+
+ $loaded = ASN1Signature::load($signature);
+ $r = $loaded['r'];
+ $s = $loaded['s'];
+
+
+ return $this->formatSignature($r, $s);
+ }
+ }
+
+ $e = $this->hash->hash($message);
+ $e = new BigInteger($e, 256);
+
+ $Ln = $this->hash->getLength() - $order->getLength();
+ $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
+
+ while (true) {
+ $k = BigInteger::randomRange(self::$one, $order->subtract(self::$one));
+ list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
+ $x = $x->toBigInteger();
+ list(, $r) = $x->divide($order);
+ if ($r->equals(self::$zero)) {
+ continue;
+ }
+ $kinv = $k->modInverse($order);
+ $temp = $z->add($dA->multiply($r));
+ $temp = $kinv->multiply($temp);
+ list(, $s) = $temp->divide($order);
+ if (!$s->equals(self::$zero)) {
+ break;
+ }
+ }
+
+ // the following is an RFC6979 compliant implementation of deterministic ECDSA
+ // it's unused because it's mainly intended for use when a good CSPRNG isn't
+ // available. if phpseclib's CSPRNG isn't good then even key generation is
+ // suspect
+ /*
+ // if this were actually being used it'd probably be better if this lived in load() and createKey()
+ $this->q = $this->curve->getOrder();
+ $dA = $this->dA->toBigInteger();
+ $this->x = $dA;
+
+ $h1 = $this->hash->hash($message);
+ $k = $this->computek($h1);
+ list($x, $y) = $this->curve->multiplyPoint($this->curve->getBasePoint(), $k);
+ $x = $x->toBigInteger();
+ list(, $r) = $x->divide($this->q);
+ $kinv = $k->modInverse($this->q);
+ $h1 = $this->bits2int($h1);
+ $temp = $h1->add($dA->multiply($r));
+ $temp = $kinv->multiply($temp);
+ list(, $s) = $temp->divide($this->q);
+ */
+
+ return $this->formatSignature($r, $s);
+ }
+
+ /**
+ * Returns the private key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePrivateKey');
+
+ return $type::savePrivateKey($this->dA, $this->curve, $this->QA, $this->secret, $this->password, $options);
+ }
+
+ /**
+ * Returns the public key
+ *
+ * @see self::getPrivateKey()
+ * @return mixed
+ */
+ public function getPublicKey()
+ {
+ $format = 'PKCS8';
+ if ($this->curve instanceof MontgomeryCurve) {
+ $format = 'MontgomeryPublic';
+ }
+
+ $type = self::validatePlugin('Keys', $format, 'savePublicKey');
+
+ $key = $type::savePublicKey($this->curve, $this->QA);
+ $key = EC::loadFormat($format, $key);
+ if ($this->curve instanceof MontgomeryCurve) {
+ return $key;
+ }
+ $key = $key
+ ->withHash($this->hash->getHash())
+ ->withSignatureFormat($this->shortFormat);
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ $key = $key->withContext($this->context);
+ }
+ return $key;
+ }
+
+ /**
+ * Returns a signature in the appropriate format
+ *
+ * @return string
+ */
+ private function formatSignature(BigInteger $r, BigInteger $s)
+ {
+ $format = $this->sigFormat;
+
+ $temp = new \ReflectionMethod($format, 'save');
+ $paramCount = $temp->getNumberOfRequiredParameters();
+
+ // @codingStandardsIgnoreStart
+ switch ($paramCount) {
+ case 2: return $format::save($r, $s);
+ case 3: return $format::save($r, $s, $this->getCurve());
+ case 4: return $format::save($r, $s, $this->getCurve(), $this->getLength());
+ }
+ // @codingStandardsIgnoreEnd
+
+ // presumably the only way you could get to this is if you were using a custom plugin
+ throw new UnsupportedOperationException("$format::save() has $paramCount parameters - the only valid parameter counts are 2 or 3");
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php
new file mode 100644
index 000000000..d34c6c4dd
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/PublicKey.php
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * EC Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\EC;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\EC;
+use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
+use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
+use phpseclib3\Crypt\EC\Curves\Ed25519;
+use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
+use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Exception\UnsupportedOperationException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * EC Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PublicKey extends EC implements Common\PublicKey
+{
+ use Common\Traits\Fingerprint;
+
+ /**
+ * Verify a signature
+ *
+ * @see self::verify()
+ * @param string $message
+ * @param string $signature
+ * @return mixed
+ */
+ public function verify($message, $signature)
+ {
+ if ($this->curve instanceof MontgomeryCurve) {
+ throw new UnsupportedOperationException('Montgomery Curves cannot be used to create signatures');
+ }
+
+ $shortFormat = $this->shortFormat;
+ $format = $this->sigFormat;
+ if ($format === false) {
+ return false;
+ }
+
+ $order = $this->curve->getOrder();
+
+ if ($this->curve instanceof TwistedEdwardsCurve) {
+ if ($shortFormat == 'SSH2') {
+ list(, $signature) = Strings::unpackSSH2('ss', $signature);
+ }
+
+ if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
+ return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
+ }
+
+ $curve = $this->curve;
+ if (strlen($signature) != 2 * $curve::SIZE) {
+ return false;
+ }
+
+ $R = substr($signature, 0, $curve::SIZE);
+ $S = substr($signature, $curve::SIZE);
+
+ try {
+ $R = PKCS1::extractPoint($R, $curve);
+ $R = $this->curve->convertToInternal($R);
+ } catch (\Exception $e) {
+ return false;
+ }
+
+ $S = strrev($S);
+ $S = new BigInteger($S, 256);
+
+ if ($S->compare($order) >= 0) {
+ return false;
+ }
+
+ $A = $curve->encodePoint($this->QA);
+
+ if ($curve instanceof Ed25519) {
+ $dom2 = !isset($this->context) ? '' :
+ 'SigEd25519 no Ed25519 collisions' . "\0" . chr(strlen($this->context)) . $this->context;
+ } else {
+ $context = isset($this->context) ? $this->context : '';
+ $dom2 = 'SigEd448' . "\0" . chr(strlen($context)) . $context;
+ }
+
+ $hash = new Hash($curve::HASH);
+ $k = $hash->hash($dom2 . substr($signature, 0, $curve::SIZE) . $A . $message);
+ $k = strrev($k);
+ $k = new BigInteger($k, 256);
+ list(, $k) = $k->divide($order);
+
+ $qa = $curve->convertToInternal($this->QA);
+
+ $lhs = $curve->multiplyPoint($curve->getBasePoint(), $S);
+ $rhs = $curve->multiplyPoint($qa, $k);
+ $rhs = $curve->addPoint($rhs, $R);
+ $rhs = $curve->convertToAffine($rhs);
+
+ return $lhs[0]->equals($rhs[0]) && $lhs[1]->equals($rhs[1]);
+ }
+
+ $params = $format::load($signature);
+ if ($params === false || count($params) != 2) {
+ return false;
+ }
+ $r = $params['r'];
+ $s = $params['s'];
+
+ if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
+ $sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
+
+ $result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());
+
+ if ($result != -1) {
+ return (bool) $result;
+ }
+ }
+
+ $n_1 = $order->subtract(self::$one);
+ if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
+ return false;
+ }
+
+ $e = $this->hash->hash($message);
+ $e = new BigInteger($e, 256);
+
+ $Ln = $this->hash->getLength() - $order->getLength();
+ $z = $Ln > 0 ? $e->bitwise_rightShift($Ln) : $e;
+
+ $w = $s->modInverse($order);
+ list(, $u1) = $z->multiply($w)->divide($order);
+ list(, $u2) = $r->multiply($w)->divide($order);
+
+ $u1 = $this->curve->convertInteger($u1);
+ $u2 = $this->curve->convertInteger($u2);
+
+ list($x1, $y1) = $this->curve->multiplyAddPoints(
+ [$this->curve->getBasePoint(), $this->QA],
+ [$u1, $u2]
+ );
+
+ $x1 = $x1->toBigInteger();
+ list(, $x1) = $x1->divide($order);
+
+ return $x1->equals($r);
+ }
+
+ /**
+ * Returns the public key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePublicKey');
+
+ return $type::savePublicKey($this->curve, $this->QA, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php
index 5e5d13d4c..cc5b42c08 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php
@@ -1,26 +1,19 @@
<?php
/**
- * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ * Wrapper around hash() and hash_hmac() functions supporting truncated hashes
+ * such as sha256-96. Any hash algorithm returned by hash_algos() (and
+ * truncated versions thereof) are supported.
*
- * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
- *
- * md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96
- *
- * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will return the HMAC as opposed to
- * the hash. If no valid algorithm is provided, sha1 will be used.
- *
- * PHP version 5
- *
- * {@internal The variable names are the same as those in
- * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
+ * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will
+ * return the HMAC as opposed to the hash.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
- * $hash = new \phpseclib\Crypt\Hash('sha1');
+ * $hash = new \phpseclib3\Crypt\Hash('sha512');
*
* $hash->setKey('abcdefg');
*
@@ -28,148 +21,195 @@
* ?>
* </code>
*
- * @category Crypt
- * @package Hash
* @author Jim Wigginton <terrafrost@php.net>
- * @copyright 2007 Jim Wigginton
+ * @copyright 2015 Jim Wigginton
+ * @author Andreas Fischer <bantu@phpbb.com>
+ * @copyright 2015 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
-use phpseclib\Math\BigInteger;
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\InsufficientSetupException;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\PrimeField;
/**
- * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
- *
- * @package Hash
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
+ * @author Andreas Fischer <bantu@phpbb.com>
*/
class Hash
{
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\Hash::__construct()
- */
/**
- * Toggles the internal implementation
+ * Padding Types
+ *
*/
- const MODE_INTERNAL = 1;
+ const PADDING_KECCAK = 1;
+
/**
- * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
+ * Padding Types
+ *
*/
- const MODE_MHASH = 2;
+ const PADDING_SHA3 = 2;
+
/**
- * Toggles the hash() implementation, which works on PHP 5.1.2+.
+ * Padding Types
+ *
*/
- const MODE_HASH = 3;
- /**#@-*/
+ const PADDING_SHAKE = 3;
/**
- * Hash Parameter
+ * Padding Type
+ *
+ * Only used by SHA3
*
- * @see self::setHash()
* @var int
- * @access private
*/
- var $hashParam;
+ private $paddingType = 0;
/**
- * Byte-length of compression blocks / key (Internal HMAC)
+ * Hash Parameter
*
- * @see self::setAlgorithm()
+ * @see self::setHash()
* @var int
- * @access private
*/
- var $b;
+ private $hashParam;
/**
* Byte-length of hash output (Internal HMAC)
*
* @see self::setHash()
* @var int
- * @access private
*/
- var $l = false;
+ private $length;
/**
* Hash Algorithm
*
* @see self::setHash()
* @var string
- * @access private
*/
- var $hash;
+ private $algo;
/**
* Key
*
* @see self::setKey()
* @var string
- * @access private
*/
- var $key = false;
+ private $key = false;
+
+ /**
+ * Nonce
+ *
+ * @see self::setNonce()
+ * @var string
+ */
+ private $nonce = false;
+
+ /**
+ * Hash Parameters
+ *
+ * @var array
+ */
+ private $parameters = [];
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
- * @access private
*/
- var $computedKey = false;
+ private $computedKey = false;
/**
* Outer XOR (Internal HMAC)
*
- * @see self::setKey()
+ * Used only for sha512
+ *
+ * @see self::hash()
* @var string
- * @access private
*/
- var $opad;
+ private $opad;
/**
* Inner XOR (Internal HMAC)
*
- * @see self::setKey()
+ * Used only for sha512
+ *
+ * @see self::hash()
* @var string
- * @access private
*/
- var $ipad;
+ private $ipad;
/**
- * Engine
+ * Recompute AES Key
*
- * @see self::setHash()
+ * Used only for umac
+ *
+ * @see self::hash()
+ * @var boolean
+ */
+ private $recomputeAESKey;
+
+ /**
+ * umac cipher object
+ *
+ * @see self::hash()
+ * @var AES
+ */
+ private $c;
+
+ /**
+ * umac pad
+ *
+ * @see self::hash()
* @var string
- * @access private
*/
- var $engine;
+ private $pad;
+
+ /**
+ * Block Size
+ *
+ * @var int
+ */
+ private $blockSize;
+
+ /**#@+
+ * UMAC variables
+ *
+ * @var PrimeField
+ */
+ private static $factory36;
+ private static $factory64;
+ private static $factory128;
+ private static $offset64;
+ private static $offset128;
+ private static $marker64;
+ private static $marker128;
+ private static $maxwordrange64;
+ private static $maxwordrange128;
+ /**#@-*/
+
+ /**#@+
+ * AES_CMAC variables
+ *
+ * @var string
+ */
+ private $k1;
+ private $k2;
+ /**#@-*/
/**
* Default Constructor.
*
* @param string $hash
- * @return \phpseclib\Crypt\Hash
- * @access public
- */
- function __construct($hash = 'sha1')
- {
- if (!defined('CRYPT_HASH_MODE')) {
- switch (true) {
- case extension_loaded('hash'):
- define('CRYPT_HASH_MODE', self::MODE_HASH);
- break;
- case extension_loaded('mhash'):
- define('CRYPT_HASH_MODE', self::MODE_MHASH);
- break;
- default:
- define('CRYPT_HASH_MODE', self::MODE_INTERNAL);
- }
- }
-
+ */
+ public function __construct($hash = 'sha256')
+ {
$this->setHash($hash);
}
@@ -178,13 +218,33 @@ class Hash
*
* Keys can be of any length.
*
- * @access public
* @param string $key
*/
- function setKey($key = false)
+ public function setKey($key = false)
{
$this->key = $key;
- $this->_computeKey();
+ $this->computeKey();
+ $this->recomputeAESKey = true;
+ }
+
+ /**
+ * Sets the nonce for UMACs
+ *
+ * Keys can be of any length.
+ *
+ * @param string $nonce
+ */
+ public function setNonce($nonce = false)
+ {
+ switch (true) {
+ case !is_string($nonce):
+ case strlen($nonce) > 0 && strlen($nonce) <= 16:
+ $this->recomputeAESKey = true;
+ $this->nonce = $nonce;
+ return;
+ }
+
+ throw new \LengthException('The nonce length must be between 1 and 16 bytes, inclusive');
}
/**
@@ -197,30 +257,22 @@ class Hash
* when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
* every call
*
- * @access private
*/
- function _computeKey()
+ private function computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
- if (strlen($this->key) <= $this->b) {
+ if (strlen($this->key) <= $this->getBlockLengthInBytes()) {
$this->computedKey = $this->key;
return;
}
- switch ($this->engine) {
- case self::MODE_MHASH:
- $this->computedKey = mhash($this->hash, $this->key);
- break;
- case self::MODE_HASH:
- $this->computedKey = hash($this->hash, $this->key, true);
- break;
- case self::MODE_INTERNAL:
- $this->computedKey = call_user_func($this->hash, $this->key);
- }
+ $this->computedKey = is_array($this->algo) ?
+ call_user_func($this->algo, $this->key) :
+ hash($this->algo, $this->key, true);
}
/**
@@ -228,10 +280,9 @@ class Hash
*
* As set by the constructor or by the setHash() method.
*
- * @access public
* @return string
*/
- function getHash()
+ public function getHash()
{
return $this->hashParam;
}
@@ -239,418 +290,1309 @@ class Hash
/**
* Sets the hash function.
*
- * @access public
* @param string $hash
*/
- function setHash($hash)
+ public function setHash($hash)
{
+ $oldHash = $this->hashParam;
$this->hashParam = $hash = strtolower($hash);
switch ($hash) {
+ case 'umac-32':
+ case 'umac-64':
+ case 'umac-96':
+ case 'umac-128':
+ if ($oldHash != $this->hashParam) {
+ $this->recomputeAESKey = true;
+ }
+ $this->blockSize = 128;
+ $this->length = abs(substr($hash, -3)) >> 3;
+ $this->algo = 'umac';
+ return;
+ case 'aes_cmac':
+ if ($oldHash != $this->hashParam) {
+ $this->recomputeAESKey = true;
+ }
+ $this->blockSize = 128;
+ $this->length = 16;
+ $this->algo = 'aes_cmac';
+ return;
+ case 'md2-96':
case 'md5-96':
case 'sha1-96':
+ case 'sha224-96':
case 'sha256-96':
+ case 'sha384-96':
case 'sha512-96':
+ case 'sha512/224-96':
+ case 'sha512/256-96':
$hash = substr($hash, 0, -3);
- $this->l = 12; // 96 / 8 = 12
+ $this->length = 12; // 96 / 8 = 12
break;
case 'md2':
case 'md5':
- $this->l = 16;
+ $this->length = 16;
break;
case 'sha1':
- $this->l = 20;
+ $this->length = 20;
+ break;
+ case 'sha224':
+ case 'sha512/224':
+ case 'sha3-224':
+ $this->length = 28;
break;
+ case 'keccak256':
+ $this->paddingType = self::PADDING_KECCAK;
+ // fall-through
case 'sha256':
- $this->l = 32;
+ case 'sha512/256':
+ case 'sha3-256':
+ $this->length = 32;
break;
case 'sha384':
- $this->l = 48;
+ case 'sha3-384':
+ $this->length = 48;
break;
case 'sha512':
- $this->l = 64;
+ case 'sha3-512':
+ $this->length = 64;
+ break;
+ default:
+ if (preg_match('#^(shake(?:128|256))-(\d+)$#', $hash, $matches)) {
+ $this->paddingType = self::PADDING_SHAKE;
+ $hash = $matches[1];
+ $this->length = $matches[2] >> 3;
+ } else {
+ throw new UnsupportedAlgorithmException(
+ "$hash is not a supported algorithm"
+ );
+ }
}
switch ($hash) {
- case 'md2-96':
case 'md2':
- $this->b = 16;
+ case 'md2-96':
+ $this->blockSize = 128;
+ break;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
- case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
- $this->b = 64;
+ $this->blockSize = 512;
break;
- default:
- $this->b = 128;
- }
-
- switch ($hash) {
- case 'md2':
- $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
- self::MODE_HASH : self::MODE_INTERNAL;
+ case 'sha3-224':
+ $this->blockSize = 1152; // 1600 - 2*224
break;
- case 'sha384':
- case 'sha512':
- $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
+ case 'sha3-256':
+ case 'shake256':
+ case 'keccak256':
+ $this->blockSize = 1088; // 1600 - 2*256
+ break;
+ case 'sha3-384':
+ $this->blockSize = 832; // 1600 - 2*384
+ break;
+ case 'sha3-512':
+ $this->blockSize = 576; // 1600 - 2*512
+ break;
+ case 'shake128':
+ $this->blockSize = 1344; // 1600 - 2*128
break;
default:
- $this->engine = CRYPT_HASH_MODE;
- }
-
- switch ($this->engine) {
- case self::MODE_MHASH:
- switch ($hash) {
- case 'md5':
- $this->hash = MHASH_MD5;
- break;
- case 'sha256':
- $this->hash = MHASH_SHA256;
- break;
- case 'sha1':
- default:
- $this->hash = MHASH_SHA1;
+ $this->blockSize = 1024;
+ }
+
+ if (in_array(substr($hash, 0, 5), ['sha3-', 'shake', 'kecca'])) {
+ // PHP 7.1.0 introduced support for "SHA3 fixed mode algorithms":
+ // http://php.net/ChangeLog-7.php#7.1.0
+ if (version_compare(PHP_VERSION, '7.1.0') < 0 || substr($hash, 0, 5) != 'sha3-') {
+ //preg_match('#(\d+)$#', $hash, $matches);
+ //$this->parameters['capacity'] = 2 * $matches[1]; // 1600 - $this->blockSize
+ //$this->parameters['rate'] = 1600 - $this->parameters['capacity']; // == $this->blockSize
+ if (!$this->paddingType) {
+ $this->paddingType = self::PADDING_SHA3;
}
- $this->_computeKey(self::MODE_MHASH);
- return;
- case self::MODE_HASH:
- switch ($hash) {
- case 'md5':
- $this->hash = 'md5';
- return;
- case 'md2':
- case 'sha256':
- case 'sha384':
- case 'sha512':
- $this->hash = $hash;
- return;
- case 'sha1':
- default:
- $this->hash = 'sha1';
+ $this->parameters = [
+ 'capacity' => 1600 - $this->blockSize,
+ 'rate' => $this->blockSize,
+ 'length' => $this->length,
+ 'padding' => $this->paddingType
+ ];
+ $hash = ['phpseclib3\Crypt\Hash', PHP_INT_SIZE == 8 ? 'sha3_64' : 'sha3_32'];
+ }
+ }
+
+ if ($hash == 'sha512/224' || $hash == 'sha512/256') {
+ // PHP 7.1.0 introduced sha512/224 and sha512/256 support:
+ // http://php.net/ChangeLog-7.php#7.1.0
+ if (version_compare(PHP_VERSION, '7.1.0') < 0) {
+ // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24
+ $initial = $hash == 'sha512/256' ?
+ [
+ '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD',
+ '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2'
+ ] :
+ [
+ '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF',
+ '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1'
+ ];
+ for ($i = 0; $i < 8; $i++) {
+ if (PHP_INT_SIZE == 8) {
+ list(, $initial[$i]) = unpack('J', pack('H*', $initial[$i]));
+ } else {
+ $initial[$i] = new BigInteger($initial[$i], 16);
+ $initial[$i]->setPrecision(64);
+ }
}
- $this->_computeKey(self::MODE_HASH);
- return;
+
+ $this->parameters = compact('initial');
+
+ $hash = ['phpseclib3\Crypt\Hash', PHP_INT_SIZE == 8 ? 'sha512_64' : 'sha512'];
+ }
}
- switch ($hash) {
- case 'md2':
- $this->hash = array($this, '_md2');
- break;
- case 'md5':
- $this->hash = array($this, '_md5');
- break;
- case 'sha256':
- $this->hash = array($this, '_sha256');
- break;
- case 'sha384':
- case 'sha512':
- $this->hash = array($this, '_sha512');
- break;
- case 'sha1':
- default:
- $this->hash = array($this, '_sha1');
+ if (is_array($hash)) {
+ $b = $this->blockSize >> 3;
+ $this->ipad = str_repeat(chr(0x36), $b);
+ $this->opad = str_repeat(chr(0x5C), $b);
+ }
+
+ $this->algo = $hash;
+
+ $this->computeKey();
+ }
+
+ /**
+ * KDF: Key-Derivation Function
+ *
+ * The key-derivation function generates pseudorandom bits used to key the hash functions.
+ *
+ * @param int $index a non-negative integer less than 2^64
+ * @param int $numbytes a non-negative integer less than 2^64
+ * @return string string of length numbytes bytes
+ */
+ private function kdf($index, $numbytes)
+ {
+ $this->c->setIV(pack('N4', 0, $index, 0, 1));
+
+ return $this->c->encrypt(str_repeat("\0", $numbytes));
+ }
+
+ /**
+ * PDF Algorithm
+ *
+ * @return string string of length taglen bytes.
+ */
+ private function pdf()
+ {
+ $k = $this->key;
+ $nonce = $this->nonce;
+ $taglen = $this->length;
+
+ //
+ // Extract and zero low bit(s) of Nonce if needed
+ //
+ if ($taglen <= 8) {
+ $last = strlen($nonce) - 1;
+ $mask = $taglen == 4 ? "\3" : "\1";
+ $index = $nonce[$last] & $mask;
+ $nonce[$last] = $nonce[$last] ^ $index;
+ }
+
+ //
+ // Make Nonce BLOCKLEN bytes by appending zeroes if needed
+ //
+ $nonce = str_pad($nonce, 16, "\0");
+
+ //
+ // Generate subkey, encipher and extract indexed substring
+ //
+ $kp = $this->kdf(0, 16);
+ $c = new AES('ctr');
+ $c->disablePadding();
+ $c->setKey($kp);
+ $c->setIV($nonce);
+ $t = $c->encrypt("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+
+ // we could use ord() but per https://paragonie.com/blog/2016/06/constant-time-encoding-boring-cryptography-rfc-4648-and-you
+ // unpack() doesn't leak timing info
+ return $taglen <= 8 ?
+ substr($t, unpack('C', $index)[1] * $taglen, $taglen) :
+ substr($t, 0, $taglen);
+ }
+
+ /**
+ * UHASH Algorithm
+ *
+ * @param string $m string of length less than 2^67 bits.
+ * @param int $taglen the integer 4, 8, 12 or 16.
+ * @return string string of length taglen bytes.
+ */
+ private function uhash($m, $taglen)
+ {
+ //
+ // One internal iteration per 4 bytes of output
+ //
+ $iters = $taglen >> 2;
+
+ //
+ // Define total key needed for all iterations using KDF.
+ // L1Key reuses most key material between iterations.
+ //
+ //$L1Key = $this->kdf(1, 1024 + ($iters - 1) * 16);
+ $L1Key = $this->kdf(1, (1024 + ($iters - 1)) * 16);
+ $L2Key = $this->kdf(2, $iters * 24);
+ $L3Key1 = $this->kdf(3, $iters * 64);
+ $L3Key2 = $this->kdf(4, $iters * 4);
+
+ //
+ // For each iteration, extract key and do three-layer hash.
+ // If bytelength(M) <= 1024, then skip L2-HASH.
+ //
+ $y = '';
+ for ($i = 0; $i < $iters; $i++) {
+ $L1Key_i = substr($L1Key, $i * 16, 1024);
+ $L2Key_i = substr($L2Key, $i * 24, 24);
+ $L3Key1_i = substr($L3Key1, $i * 64, 64);
+ $L3Key2_i = substr($L3Key2, $i * 4, 4);
+
+ $a = self::L1Hash($L1Key_i, $m);
+ $b = strlen($m) <= 1024 ? "\0\0\0\0\0\0\0\0$a" : self::L2Hash($L2Key_i, $a);
+ $c = self::L3Hash($L3Key1_i, $L3Key2_i, $b);
+ $y .= $c;
+ }
+
+ return $y;
+ }
+
+ /**
+ * L1-HASH Algorithm
+ *
+ * The first-layer hash breaks the message into 1024-byte chunks and
+ * hashes each with a function called NH. Concatenating the results
+ * forms a string, which is up to 128 times shorter than the original.
+ *
+ * @param string $k string of length 1024 bytes.
+ * @param string $m string of length less than 2^67 bits.
+ * @return string string of length (8 * ceil(bitlength(M)/8192)) bytes.
+ */
+ private static function L1Hash($k, $m)
+ {
+ //
+ // Break M into 1024 byte chunks (final chunk may be shorter)
+ //
+ $m = str_split($m, 1024);
+
+ //
+ // For each chunk, except the last: endian-adjust, NH hash
+ // and add bit-length. Use results to build Y.
+ //
+ $length = 1024 * 8;
+ $y = '';
+
+ for ($i = 0; $i < count($m) - 1; $i++) {
+ $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP
+ $y .= PHP_INT_SIZE == 8 ?
+ static::nh64($k, $m[$i], $length) :
+ static::nh32($k, $m[$i], $length);
+ }
+
+ //
+ // For the last chunk: pad to 32-byte boundary, endian-adjust,
+ // NH hash and add bit-length. Concatenate the result to Y.
+ //
+ $length = count($m) ? strlen($m[$i]) : 0;
+ $pad = 32 - ($length % 32);
+ $pad = max(32, $length + $pad % 32);
+ $m[$i] = str_pad(isset($m[$i]) ? $m[$i] : '', $pad, "\0"); // zeropad
+ $m[$i] = pack('N*', ...unpack('V*', $m[$i])); // ENDIAN-SWAP
+
+ $y .= PHP_INT_SIZE == 8 ?
+ static::nh64($k, $m[$i], $length * 8) :
+ static::nh32($k, $m[$i], $length * 8);
+
+ return $y;
+ }
+
+ /**
+ * 32-bit safe 64-bit Multiply with 2x 32-bit ints
+ *
+ * @param int $x
+ * @param int $y
+ * @return string $x * $y
+ */
+ private static function mul32_64($x, $y)
+ {
+ // see mul64() for a more detailed explanation of how this works
+
+ $x1 = ($x >> 16) & 0xFFFF;
+ $x0 = $x & 0xFFFF;
+
+ $y1 = ($y >> 16) & 0xFFFF;
+ $y0 = $y & 0xFFFF;
+
+ // the following 3x lines will possibly yield floats
+ $z2 = $x1 * $y1;
+ $z0 = $x0 * $y0;
+ $z1 = $x1 * $y0 + $x0 * $y1;
+
+ $a = intval(fmod($z0, 65536));
+ $b = intval($z0 / 65536) + intval(fmod($z1, 65536));
+ $c = intval($z1 / 65536) + intval(fmod($z2, 65536)) + intval($b / 65536);
+ $b = intval(fmod($b, 65536));
+ $d = intval($z2 / 65536) + intval($c / 65536);
+ $c = intval(fmod($c, 65536));
+ $d = intval(fmod($d, 65536));
+
+ return pack('n4', $d, $c, $b, $a);
+ }
+
+ /**
+ * 32-bit safe 64-bit Addition with 2x 64-bit strings
+ *
+ * @param int $x
+ * @param int $y
+ * @return int $x * $y
+ */
+ private static function add32_64($x, $y)
+ {
+ list(, $x1, $x2, $x3, $x4) = unpack('n4', $x);
+ list(, $y1, $y2, $y3, $y4) = unpack('n4', $y);
+ $a = $x4 + $y4;
+ $b = $x3 + $y3 + ($a >> 16);
+ $c = $x2 + $y2 + ($b >> 16);
+ $d = $x1 + $y1 + ($c >> 16);
+ return pack('n4', $d, $c, $b, $a);
+ }
+
+ /**
+ * 32-bit safe 32-bit Addition with 2x 32-bit strings
+ *
+ * @param int $x
+ * @param int $y
+ * @return int $x * $y
+ */
+ private static function add32($x, $y)
+ {
+ // see add64() for a more detailed explanation of how this works
+
+ $x1 = $x & 0xFFFF;
+ $x2 = ($x >> 16) & 0xFFFF;
+ $y1 = $y & 0xFFFF;
+ $y2 = ($y >> 16) & 0xFFFF;
+
+ $a = $x1 + $y1;
+ $b = ($x2 + $y2 + ($a >> 16)) << 16;
+ $a &= 0xFFFF;
+
+ return $a | $b;
+ }
+
+ /**
+ * NH Algorithm / 32-bit safe
+ *
+ * @param string $k string of length 1024 bytes.
+ * @param string $m string with length divisible by 32 bytes.
+ * @return string string of length 8 bytes.
+ */
+ private static function nh32($k, $m, $length)
+ {
+ //
+ // Break M and K into 4-byte chunks
+ //
+ $k = unpack('N*', $k);
+ $m = unpack('N*', $m);
+ $t = count($m);
+
+ //
+ // Perform NH hash on the chunks, pairing words for multiplication
+ // which are 4 apart to accommodate vector-parallelism.
+ //
+ $i = 1;
+ $y = "\0\0\0\0\0\0\0\0";
+ while ($i <= $t) {
+ $temp = self::add32($m[$i], $k[$i]);
+ $temp2 = self::add32($m[$i + 4], $k[$i + 4]);
+ $y = self::add32_64($y, self::mul32_64($temp, $temp2));
+
+ $temp = self::add32($m[$i + 1], $k[$i + 1]);
+ $temp2 = self::add32($m[$i + 5], $k[$i + 5]);
+ $y = self::add32_64($y, self::mul32_64($temp, $temp2));
+
+ $temp = self::add32($m[$i + 2], $k[$i + 2]);
+ $temp2 = self::add32($m[$i + 6], $k[$i + 6]);
+ $y = self::add32_64($y, self::mul32_64($temp, $temp2));
+
+ $temp = self::add32($m[$i + 3], $k[$i + 3]);
+ $temp2 = self::add32($m[$i + 7], $k[$i + 7]);
+ $y = self::add32_64($y, self::mul32_64($temp, $temp2));
+
+ $i += 8;
+ }
+
+ return self::add32_64($y, pack('N2', 0, $length));
+ }
+
+ /**
+ * 64-bit Multiply with 2x 32-bit ints
+ *
+ * @param int $x
+ * @param int $y
+ * @return int $x * $y
+ */
+ private static function mul64($x, $y)
+ {
+ // since PHP doesn't implement unsigned integers we'll implement them with signed integers
+ // to do this we'll use karatsuba multiplication
+
+ $x1 = $x >> 16;
+ $x0 = $x & 0xFFFF;
+
+ $y1 = $y >> 16;
+ $y0 = $y & 0xFFFF;
+
+ $z2 = $x1 * $y1; // up to 32 bits long
+ $z0 = $x0 * $y0; // up to 32 bits long
+ $z1 = $x1 * $y0 + $x0 * $y1; // up to 33 bit long
+ // normally karatsuba multiplication calculates $z1 thusly:
+ //$z1 = ($x1 + $x0) * ($y0 + $y1) - $z2 - $z0;
+ // the idea being to eliminate one extra multiplication. for arbitrary precision math that makes sense
+ // but not for this purpose
+
+ // at this point karatsuba would normally return this:
+ //return ($z2 << 64) + ($z1 << 32) + $z0;
+ // the problem is that the output could be out of range for signed 64-bit ints,
+ // which would cause PHP to switch to floats, which would risk losing the lower few bits
+ // as such we'll OR 4x 16-bit blocks together like so:
+ /*
+ ........ | ........ | ........ | ........
+ upper $z2 | lower $z2 | lower $z1 | lower $z0
+ | +upper $z1 | +upper $z0 |
+ + $carry | + $carry | |
+ */
+ // technically upper $z1 is 17 bit - not 16 - but the most significant digit of that will
+ // just get added to $carry
+
+ $a = $z0 & 0xFFFF;
+ $b = ($z0 >> 16) + ($z1 & 0xFFFF);
+ $c = ($z1 >> 16) + ($z2 & 0xFFFF) + ($b >> 16);
+ $b = ($b & 0xFFFF) << 16;
+ $d = ($z2 >> 16) + ($c >> 16);
+ $c = ($c & 0xFFFF) << 32;
+ $d = ($d & 0xFFFF) << 48;
+
+ return $a | $b | $c | $d;
+ }
+
+ /**
+ * 64-bit Addition with 2x 64-bit ints
+ *
+ * @param int $x
+ * @param int $y
+ * @return int $x + $y
+ */
+ private static function add64($x, $y)
+ {
+ // doing $x + $y risks returning a result that's out of range for signed 64-bit ints
+ // in that event PHP would convert the result to a float and precision would be lost
+ // so we'll just add 2x 32-bit ints together like so:
+ /*
+ ........ | ........
+ upper $x | lower $x
+ +upper $y |+lower $y
+ + $carry |
+ */
+ $x1 = $x & 0xFFFFFFFF;
+ $x2 = ($x >> 32) & 0xFFFFFFFF;
+ $y1 = $y & 0xFFFFFFFF;
+ $y2 = ($y >> 32) & 0xFFFFFFFF;
+
+ $a = $x1 + $y1;
+ $b = ($x2 + $y2 + ($a >> 32)) << 32;
+ $a &= 0xFFFFFFFF;
+
+ return $a | $b;
+ }
+
+ /**
+ * NH Algorithm / 64-bit safe
+ *
+ * @param string $k string of length 1024 bytes.
+ * @param string $m string with length divisible by 32 bytes.
+ * @return string string of length 8 bytes.
+ */
+ private static function nh64($k, $m, $length)
+ {
+ //
+ // Break M and K into 4-byte chunks
+ //
+ $k = unpack('N*', $k);
+ $m = unpack('N*', $m);
+ $t = count($m);
+
+ //
+ // Perform NH hash on the chunks, pairing words for multiplication
+ // which are 4 apart to accommodate vector-parallelism.
+ //
+ $i = 1;
+ $y = 0;
+ while ($i <= $t) {
+ $temp = ($m[$i] + $k[$i]) & 0xFFFFFFFF;
+ $temp2 = ($m[$i + 4] + $k[$i + 4]) & 0xFFFFFFFF;
+ $y = self::add64($y, self::mul64($temp, $temp2));
+
+ $temp = ($m[$i + 1] + $k[$i + 1]) & 0xFFFFFFFF;
+ $temp2 = ($m[$i + 5] + $k[$i + 5]) & 0xFFFFFFFF;
+ $y = self::add64($y, self::mul64($temp, $temp2));
+
+ $temp = ($m[$i + 2] + $k[$i + 2]) & 0xFFFFFFFF;
+ $temp2 = ($m[$i + 6] + $k[$i + 6]) & 0xFFFFFFFF;
+ $y = self::add64($y, self::mul64($temp, $temp2));
+
+ $temp = ($m[$i + 3] + $k[$i + 3]) & 0xFFFFFFFF;
+ $temp2 = ($m[$i + 7] + $k[$i + 7]) & 0xFFFFFFFF;
+ $y = self::add64($y, self::mul64($temp, $temp2));
+
+ $i += 8;
}
- $this->ipad = str_repeat(chr(0x36), $this->b);
- $this->opad = str_repeat(chr(0x5C), $this->b);
+ return pack('J', self::add64($y, $length));
+ }
+
+ /**
+ * L2-HASH: Second-Layer Hash
+ *
+ * The second-layer rehashes the L1-HASH output using a polynomial hash
+ * called POLY. If the L1-HASH output is long, then POLY is called once
+ * on a prefix of the L1-HASH output and called using different settings
+ * on the remainder. (This two-step hashing of the L1-HASH output is
+ * needed only if the message length is greater than 16 megabytes.)
+ * Careful implementation of POLY is necessary to avoid a possible
+ * timing attack (see Section 6.6 for more information).
+ *
+ * @param string $k string of length 24 bytes.
+ * @param string $m string of length less than 2^64 bytes.
+ * @return string string of length 16 bytes.
+ */
+ private static function L2Hash($k, $m)
+ {
+ //
+ // Extract keys and restrict to special key-sets
+ //
+ $k64 = $k & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF";
+ $k64 = new BigInteger($k64, 256);
+ $k128 = substr($k, 8) & "\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF\x01\xFF\xFF\xFF";
+ $k128 = new BigInteger($k128, 256);
+
+ //
+ // If M is no more than 2^17 bytes, hash under 64-bit prime,
+ // otherwise, hash first 2^17 bytes under 64-bit prime and
+ // remainder under 128-bit prime.
+ //
+ if (strlen($m) <= 0x20000) { // 2^14 64-bit words
+ $y = self::poly(64, self::$maxwordrange64, $k64, $m);
+ } else {
+ $m_1 = substr($m, 0, 0x20000); // 1 << 17
+ $m_2 = substr($m, 0x20000) . "\x80";
+ $length = strlen($m_2);
+ $pad = 16 - ($length % 16);
+ $pad %= 16;
+ $m_2 = str_pad($m_2, $length + $pad, "\0"); // zeropad
+ $y = self::poly(64, self::$maxwordrange64, $k64, $m_1);
+ $y = str_pad($y, 16, "\0", STR_PAD_LEFT);
+ $y = self::poly(128, self::$maxwordrange128, $k128, $y . $m_2);
+ }
+
+ return str_pad($y, 16, "\0", STR_PAD_LEFT);
+ }
+
+ /**
+ * POLY Algorithm
+ *
+ * @param int $wordbits the integer 64 or 128.
+ * @param BigInteger $maxwordrange positive integer less than 2^wordbits.
+ * @param BigInteger $k integer in the range 0 ... prime(wordbits) - 1.
+ * @param string $m string with length divisible by (wordbits / 8) bytes.
+ * @return integer in the range 0 ... prime(wordbits) - 1.
+ */
+ private static function poly($wordbits, $maxwordrange, $k, $m)
+ {
+ //
+ // Define constants used for fixing out-of-range words
+ //
+ $wordbytes = $wordbits >> 3;
+ if ($wordbits == 128) {
+ $factory = self::$factory128;
+ $offset = self::$offset128;
+ $marker = self::$marker128;
+ } else {
+ $factory = self::$factory64;
+ $offset = self::$offset64;
+ $marker = self::$marker64;
+ }
+
+ $k = $factory->newInteger($k);
+
+ //
+ // Break M into chunks of length wordbytes bytes
+ //
+ $m_i = str_split($m, $wordbytes);
+
+ //
+ // Each input word m is compared with maxwordrange. If not smaller
+ // then 'marker' and (m - offset), both in range, are hashed.
+ //
+ $y = $factory->newInteger(new BigInteger(1));
+ foreach ($m_i as $m) {
+ $m = $factory->newInteger(new BigInteger($m, 256));
+ if ($m->compare($maxwordrange) >= 0) {
+ $y = $k->multiply($y)->add($marker);
+ $y = $k->multiply($y)->add($m->subtract($offset));
+ } else {
+ $y = $k->multiply($y)->add($m);
+ }
+ }
- $this->_computeKey(self::MODE_INTERNAL);
+ return $y->toBytes();
}
/**
- * Compute the HMAC.
+ * L3-HASH: Third-Layer Hash
+ *
+ * The output from L2-HASH is 16 bytes long. This final hash function
+ * hashes the 16-byte string to a fixed length of 4 bytes.
+ *
+ * @param string $k1 string of length 64 bytes.
+ * @param string $k2 string of length 4 bytes.
+ * @param string $m string of length 16 bytes.
+ * @return string string of length 4 bytes.
+ */
+ private static function L3Hash($k1, $k2, $m)
+ {
+ $factory = self::$factory36;
+
+ $y = $factory->newInteger(new BigInteger());
+ for ($i = 0; $i < 8; $i++) {
+ $m_i = $factory->newInteger(new BigInteger(substr($m, 2 * $i, 2), 256));
+ $k_i = $factory->newInteger(new BigInteger(substr($k1, 8 * $i, 8), 256));
+ $y = $y->add($m_i->multiply($k_i));
+ }
+ $y = str_pad(substr($y->toBytes(), -4), 4, "\0", STR_PAD_LEFT);
+ $y = $y ^ $k2;
+
+ return $y;
+ }
+
+ /**
+ * Compute the Hash / HMAC / UMAC.
*
- * @access public
* @param string $text
* @return string
*/
- function hash($text)
- {
- if (!empty($this->key) || is_string($this->key)) {
- switch ($this->engine) {
- case self::MODE_MHASH:
- $output = mhash($this->hash, $text, $this->computedKey);
- break;
- case self::MODE_HASH:
- $output = hash_hmac($this->hash, $text, $this->computedKey, true);
- break;
- case self::MODE_INTERNAL:
- $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1
- $temp = $this->ipad ^ $key; // step 2
- $temp .= $text; // step 3
- $temp = call_user_func($this->hash, $temp); // step 4
- $output = $this->opad ^ $key; // step 5
- $output.= $temp; // step 6
- $output = call_user_func($this->hash, $output); // step 7
+ public function hash($text)
+ {
+ $algo = $this->algo;
+ // https://www.rfc-editor.org/rfc/rfc4493.html
+ // https://en.wikipedia.org/wiki/One-key_MAC
+ if ($algo == 'aes_cmac') {
+ $constZero = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ if ($this->recomputeAESKey) {
+ if (!is_string($this->key)) {
+ throw new InsufficientSetupException('No key has been set');
+ }
+ if (strlen($this->key) != 16) {
+ throw new \LengthException('Key must be 16 bytes long');
+ }
+ // Algorithm Generate_Subkey
+ $constRb = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x87";
+ $this->c = new AES('ecb');
+ $this->c->setKey($this->key);
+ $this->c->disablePadding();
+ $l = $this->c->encrypt($constZero);
+ $msb = ($l & "\x80") == "\x80";
+ $l = new BigInteger($l, 256);
+ $l->setPrecision(128);
+ $l = $l->bitwise_leftShift(1)->toBytes();
+ // make it constant time
+ $k1 = $msb ? $l ^ $constRb : $l | $constZero;
+
+ $msb = ($k1 & "\x80") == "\x80";
+ $k2 = new BigInteger($k1, 256);
+ $k2->setPrecision(128);
+ $k2 = $k2->bitwise_leftShift(1)->toBytes();
+ // make it constant time
+ $k2 = $msb ? $k2 ^ $constRb : $k2 | $constZero;
+
+ $this->k1 = $k1;
+ $this->k2 = $k2;
}
- } else {
- switch ($this->engine) {
- case self::MODE_MHASH:
- $output = mhash($this->hash, $text);
- break;
- case self::MODE_HASH:
- $output = hash($this->hash, $text, true);
- break;
- case self::MODE_INTERNAL:
- $output = call_user_func($this->hash, $text);
+
+ $len = strlen($text);
+ $const_Bsize = 16;
+ $M = strlen($text) ? str_split($text, $const_Bsize) : [''];
+
+ // Step 2
+ $n = ceil($len / $const_Bsize);
+ // Step 3
+ if ($n == 0) {
+ $n = 1;
+ $flag = false;
+ } else {
+ $flag = $len % $const_Bsize == 0;
+ }
+ // Step 4
+ $M_last = $flag ?
+ $M[$n - 1] ^ $k1 :
+ self::OMAC_padding($M[$n - 1], $const_Bsize) ^ $k2;
+ // Step 5
+ $x = $constZero;
+ // Step 6
+ $c = &$this->c;
+ for ($i = 0; $i < $n - 1; $i++) {
+ $y = $x ^ $M[$i];
+ $x = $c->encrypt($y);
+ }
+ $y = $M_last ^ $x;
+ return $c->encrypt($y);
+ }
+ if ($algo == 'umac') {
+ if ($this->recomputeAESKey) {
+ if (!is_string($this->nonce)) {
+ throw new InsufficientSetupException('No nonce has been set');
+ }
+ if (!is_string($this->key)) {
+ throw new InsufficientSetupException('No key has been set');
+ }
+ if (strlen($this->key) != 16) {
+ throw new \LengthException('Key must be 16 bytes long');
+ }
+
+ if (!isset(self::$maxwordrange64)) {
+ $one = new BigInteger(1);
+
+ $prime36 = new BigInteger("\x00\x00\x00\x0F\xFF\xFF\xFF\xFB", 256);
+ self::$factory36 = new PrimeField($prime36);
+
+ $prime64 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC5", 256);
+ self::$factory64 = new PrimeField($prime64);
+
+ $prime128 = new BigInteger("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x61", 256);
+ self::$factory128 = new PrimeField($prime128);
+
+ self::$offset64 = new BigInteger("\1\0\0\0\0\0\0\0\0", 256);
+ self::$offset64 = self::$factory64->newInteger(self::$offset64->subtract($prime64));
+ self::$offset128 = new BigInteger("\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 256);
+ self::$offset128 = self::$factory128->newInteger(self::$offset128->subtract($prime128));
+
+ self::$marker64 = self::$factory64->newInteger($prime64->subtract($one));
+ self::$marker128 = self::$factory128->newInteger($prime128->subtract($one));
+
+ $maxwordrange64 = $one->bitwise_leftShift(64)->subtract($one->bitwise_leftShift(32));
+ self::$maxwordrange64 = self::$factory64->newInteger($maxwordrange64);
+
+ $maxwordrange128 = $one->bitwise_leftShift(128)->subtract($one->bitwise_leftShift(96));
+ self::$maxwordrange128 = self::$factory128->newInteger($maxwordrange128);
+ }
+
+ $this->c = new AES('ctr');
+ $this->c->disablePadding();
+ $this->c->setKey($this->key);
+
+ $this->pad = $this->pdf();
+
+ $this->recomputeAESKey = false;
}
+
+ $hashedmessage = $this->uhash($text, $this->length);
+ return $hashedmessage ^ $this->pad;
}
- return substr($output, 0, $this->l);
+ if (is_array($algo)) {
+ if (empty($this->key) || !is_string($this->key)) {
+ return substr($algo($text, ...array_values($this->parameters)), 0, $this->length);
+ }
+
+ // SHA3 HMACs are discussed at https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=30
+
+ $key = str_pad($this->computedKey, $b, chr(0));
+ $temp = $this->ipad ^ $key;
+ $temp .= $text;
+ $temp = substr($algo($temp, ...array_values($this->parameters)), 0, $this->length);
+ $output = $this->opad ^ $key;
+ $output .= $temp;
+ $output = $algo($output, ...array_values($this->parameters));
+
+ return substr($output, 0, $this->length);
+ }
+
+ $output = !empty($this->key) || is_string($this->key) ?
+ hash_hmac($algo, $text, $this->computedKey, true) :
+ hash($algo, $text, true);
+
+ return strlen($output) > $this->length
+ ? substr($output, 0, $this->length)
+ : $output;
}
/**
- * Returns the hash length (in bytes)
+ * Returns the hash length (in bits)
*
- * @access public
* @return int
*/
- function getLength()
+ public function getLength()
{
- return $this->l;
+ return $this->length << 3;
}
/**
- * Wrapper for MD5
+ * Returns the hash length (in bytes)
*
- * @access private
- * @param string $m
+ * @return int
*/
- function _md5($m)
+ public function getLengthInBytes()
{
- return pack('H*', md5($m));
+ return $this->length;
}
/**
- * Wrapper for SHA1
+ * Returns the block length (in bits)
*
- * @access private
- * @param string $m
+ * @return int
*/
- function _sha1($m)
+ public function getBlockLength()
{
- return pack('H*', sha1($m));
+ return $this->blockSize;
}
/**
- * Pure-PHP implementation of MD2
+ * Returns the block length (in bytes)
*
- * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
+ * @return int
+ */
+ public function getBlockLengthInBytes()
+ {
+ return $this->blockSize >> 3;
+ }
+
+ /**
+ * Pads SHA3 based on the mode
*
- * @access private
- * @param string $m
+ * @param int $padLength
+ * @param int $padType
+ * @return string
*/
- function _md2($m)
- {
- static $s = array(
- 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
- 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
- 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
- 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
- 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
- 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
- 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
- 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
- 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
- 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
- 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
- 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
- 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
- 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
- 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
- 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
- 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
- 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
- );
-
- // Step 1. Append Padding Bytes
- $pad = 16 - (strlen($m) & 0xF);
- $m.= str_repeat(chr($pad), $pad);
+ private static function sha3_pad($padLength, $padType)
+ {
+ switch ($padType) {
+ case self::PADDING_KECCAK:
+ $temp = chr(0x01) . str_repeat("\0", $padLength - 1);
+ $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80);
+ return $temp;
+ case self::PADDING_SHAKE:
+ $temp = chr(0x1F) . str_repeat("\0", $padLength - 1);
+ $temp[$padLength - 1] = $temp[$padLength - 1] | chr(0x80);
+ return $temp;
+ //case self::PADDING_SHA3:
+ default:
+ // from https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=36
+ return $padLength == 1 ? chr(0x86) : chr(0x06) . str_repeat("\0", $padLength - 2) . chr(0x80);
+ }
+ }
- $length = strlen($m);
+ /**
+ * Pure-PHP 32-bit implementation of SHA3
+ *
+ * Whereas BigInteger.php's 32-bit engine works on PHP 64-bit this 32-bit implementation
+ * of SHA3 will *not* work on PHP 64-bit. This is because this implementation
+ * employees bitwise NOTs and bitwise left shifts. And the round constants only work
+ * on 32-bit PHP. eg. dechex(-2147483648) returns 80000000 on 32-bit PHP and
+ * FFFFFFFF80000000 on 64-bit PHP. Sure, we could do bitwise ANDs but that would slow
+ * things down.
+ *
+ * SHA512 requires BigInteger to simulate 64-bit unsigned integers because SHA2 employees
+ * addition whereas SHA3 just employees bitwise operators. PHP64 only supports signed
+ * 64-bit integers, which complicates addition, whereas that limitation isn't an issue
+ * for SHA3.
+ *
+ * In https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=919061#page=16 KECCAK[C] is
+ * defined as "the KECCAK instance with KECCAK-f[1600] as the underlying permutation and
+ * capacity c". This is relevant because, altho the KECCAK standard defines a mode
+ * (KECCAK-f[800]) designed for 32-bit machines that mode is incompatible with SHA3
+ *
+ * @param string $p
+ * @param int $c
+ * @param int $r
+ * @param int $d
+ * @param int $padType
+ */
+ private static function sha3_32($p, $c, $r, $d, $padType)
+ {
+ $block_size = $r >> 3;
+ $padLength = $block_size - (strlen($p) % $block_size);
+ $num_ints = $block_size >> 2;
+
+ $p .= static::sha3_pad($padLength, $padType);
+
+ $n = strlen($p) / $r; // number of blocks
+
+ $s = [
+ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
+ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
+ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
+ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
+ [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
+ ];
+
+ $p = str_split($p, $block_size);
+
+ foreach ($p as $pi) {
+ $pi = unpack('V*', $pi);
+ $x = $y = 0;
+ for ($i = 1; $i <= $num_ints; $i += 2) {
+ $s[$x][$y][0] ^= $pi[$i + 1];
+ $s[$x][$y][1] ^= $pi[$i];
+ if (++$y == 5) {
+ $y = 0;
+ $x++;
+ }
+ }
+ static::processSHA3Block32($s);
+ }
- // Step 2. Append Checksum
- $c = str_repeat(chr(0), 16);
- $l = chr(0);
- for ($i = 0; $i < $length; $i+= 16) {
- for ($j = 0; $j < 16; $j++) {
- // RFC1319 incorrectly states that C[j] should be set to S[c xor L]
- //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
- // per <http://www.rfc-editor.org/errata_search.php?rfc=1319>, however, C[j] should be set to S[c xor L] xor C[j]
- $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j]));
- $l = $c[$j];
+ $z = '';
+ $i = $j = 0;
+ while (strlen($z) < $d) {
+ $z .= pack('V2', $s[$i][$j][1], $s[$i][$j++][0]);
+ if ($j == 5) {
+ $j = 0;
+ $i++;
+ if ($i == 5) {
+ $i = 0;
+ static::processSHA3Block32($s);
+ }
}
}
- $m.= $c;
- $length+= 16;
+ return $z;
+ }
- // Step 3. Initialize MD Buffer
- $x = str_repeat(chr(0), 48);
+ /**
+ * 32-bit block processing method for SHA3
+ *
+ * @param array $s
+ */
+ private static function processSHA3Block32(&$s)
+ {
+ static $rotationOffsets = [
+ [ 0, 1, 62, 28, 27],
+ [36, 44, 6, 55, 20],
+ [ 3, 10, 43, 25, 39],
+ [41, 45, 15, 21, 8],
+ [18, 2, 61, 56, 14]
+ ];
+
+ // the standards give these constants in hexadecimal notation. it's tempting to want to use
+ // that same notation, here, however, we can't, because 0x80000000, on PHP32, is a positive
+ // float - not the negative int that we need to be in PHP32. so we use -2147483648 instead
+ static $roundConstants = [
+ [0, 1],
+ [0, 32898],
+ [-2147483648, 32906],
+ [-2147483648, -2147450880],
+ [0, 32907],
+ [0, -2147483647],
+ [-2147483648, -2147450751],
+ [-2147483648, 32777],
+ [0, 138],
+ [0, 136],
+ [0, -2147450871],
+ [0, -2147483638],
+ [0, -2147450741],
+ [-2147483648, 139],
+ [-2147483648, 32905],
+ [-2147483648, 32771],
+ [-2147483648, 32770],
+ [-2147483648, 128],
+ [0, 32778],
+ [-2147483648, -2147483638],
+ [-2147483648, -2147450751],
+ [-2147483648, 32896],
+ [0, -2147483647],
+ [-2147483648, -2147450872]
+ ];
+
+ for ($round = 0; $round < 24; $round++) {
+ // theta step
+ $parity = $rotated = [];
+ for ($i = 0; $i < 5; $i++) {
+ $parity[] = [
+ $s[0][$i][0] ^ $s[1][$i][0] ^ $s[2][$i][0] ^ $s[3][$i][0] ^ $s[4][$i][0],
+ $s[0][$i][1] ^ $s[1][$i][1] ^ $s[2][$i][1] ^ $s[3][$i][1] ^ $s[4][$i][1]
+ ];
+ $rotated[] = static::rotateLeft32($parity[$i], 1);
+ }
- // Step 4. Process Message in 16-Byte Blocks
- for ($i = 0; $i < $length; $i+= 16) {
- for ($j = 0; $j < 16; $j++) {
- $x[$j + 16] = $m[$i + $j];
- $x[$j + 32] = $x[$j + 16] ^ $x[$j];
+ $temp = [
+ [$parity[4][0] ^ $rotated[1][0], $parity[4][1] ^ $rotated[1][1]],
+ [$parity[0][0] ^ $rotated[2][0], $parity[0][1] ^ $rotated[2][1]],
+ [$parity[1][0] ^ $rotated[3][0], $parity[1][1] ^ $rotated[3][1]],
+ [$parity[2][0] ^ $rotated[4][0], $parity[2][1] ^ $rotated[4][1]],
+ [$parity[3][0] ^ $rotated[0][0], $parity[3][1] ^ $rotated[0][1]]
+ ];
+ for ($i = 0; $i < 5; $i++) {
+ for ($j = 0; $j < 5; $j++) {
+ $s[$i][$j][0] ^= $temp[$j][0];
+ $s[$i][$j][1] ^= $temp[$j][1];
+ }
}
- $t = chr(0);
- for ($j = 0; $j < 18; $j++) {
- for ($k = 0; $k < 48; $k++) {
- $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
- //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
+
+ $st = $s;
+
+ // rho and pi steps
+ for ($i = 0; $i < 5; $i++) {
+ for ($j = 0; $j < 5; $j++) {
+ $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft32($s[$j][$i], $rotationOffsets[$j][$i]);
}
- $t = chr(ord($t) + $j);
}
- }
- // Step 5. Output
- return substr($x, 0, 16);
+ // chi step
+ for ($i = 0; $i < 5; $i++) {
+ $s[$i][0] = [
+ $st[$i][0][0] ^ (~$st[$i][1][0] & $st[$i][2][0]),
+ $st[$i][0][1] ^ (~$st[$i][1][1] & $st[$i][2][1])
+ ];
+ $s[$i][1] = [
+ $st[$i][1][0] ^ (~$st[$i][2][0] & $st[$i][3][0]),
+ $st[$i][1][1] ^ (~$st[$i][2][1] & $st[$i][3][1])
+ ];
+ $s[$i][2] = [
+ $st[$i][2][0] ^ (~$st[$i][3][0] & $st[$i][4][0]),
+ $st[$i][2][1] ^ (~$st[$i][3][1] & $st[$i][4][1])
+ ];
+ $s[$i][3] = [
+ $st[$i][3][0] ^ (~$st[$i][4][0] & $st[$i][0][0]),
+ $st[$i][3][1] ^ (~$st[$i][4][1] & $st[$i][0][1])
+ ];
+ $s[$i][4] = [
+ $st[$i][4][0] ^ (~$st[$i][0][0] & $st[$i][1][0]),
+ $st[$i][4][1] ^ (~$st[$i][0][1] & $st[$i][1][1])
+ ];
+ }
+
+ // iota step
+ $s[0][0][0] ^= $roundConstants[$round][0];
+ $s[0][0][1] ^= $roundConstants[$round][1];
+ }
}
/**
- * Pure-PHP implementation of SHA256
+ * Rotate 32-bit int
*
- * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
- *
- * @access private
- * @param string $m
+ * @param array $x
+ * @param int $shift
*/
- function _sha256($m)
- {
- if (extension_loaded('suhosin')) {
- return pack('H*', sha256($m));
- }
-
- // Initialize variables
- $hash = array(
- 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
- );
- // Initialize table of round constants
- // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
- static $k = array(
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
- );
+ private static function rotateLeft32($x, $shift)
+ {
+ if ($shift < 32) {
+ list($hi, $lo) = $x;
+ } else {
+ $shift -= 32;
+ list($lo, $hi) = $x;
+ }
- // Pre-processing
- $length = strlen($m);
- // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
- $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
- $m[$length] = chr(0x80);
- // we don't support hashing strings 512MB long
- $m.= pack('N2', 0, $length << 3);
+ $mask = -1 ^ (-1 << $shift);
+ return [
+ ($hi << $shift) | (($lo >> (32 - $shift)) & $mask),
+ ($lo << $shift) | (($hi >> (32 - $shift)) & $mask)
+ ];
+ }
- // Process the message in successive 512-bit chunks
- $chunks = str_split($m, 64);
- foreach ($chunks as $chunk) {
- $w = array();
- for ($i = 0; $i < 16; $i++) {
- extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
- $w[] = $temp;
+ /**
+ * Pure-PHP 64-bit implementation of SHA3
+ *
+ * @param string $p
+ * @param int $c
+ * @param int $r
+ * @param int $d
+ * @param int $padType
+ */
+ private static function sha3_64($p, $c, $r, $d, $padType)
+ {
+ $block_size = $r >> 3;
+ $padLength = $block_size - (strlen($p) % $block_size);
+ $num_ints = $block_size >> 2;
+
+ $p .= static::sha3_pad($padLength, $padType);
+
+ $n = strlen($p) / $r; // number of blocks
+
+ $s = [
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0]
+ ];
+
+ $p = str_split($p, $block_size);
+
+ foreach ($p as $pi) {
+ $pi = unpack('P*', $pi);
+ $x = $y = 0;
+ foreach ($pi as $subpi) {
+ $s[$x][$y++] ^= $subpi;
+ if ($y == 5) {
+ $y = 0;
+ $x++;
+ }
}
+ static::processSHA3Block64($s);
+ }
- // Extend the sixteen 32-bit words into sixty-four 32-bit words
- for ($i = 16; $i < 64; $i++) {
- // @codingStandardsIgnoreStart
- $s0 = $this->_rightRotate($w[$i - 15], 7) ^
- $this->_rightRotate($w[$i - 15], 18) ^
- $this->_rightShift( $w[$i - 15], 3);
- $s1 = $this->_rightRotate($w[$i - 2], 17) ^
- $this->_rightRotate($w[$i - 2], 19) ^
- $this->_rightShift( $w[$i - 2], 10);
- // @codingStandardsIgnoreEnd
- $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
+ $z = '';
+ $i = $j = 0;
+ while (strlen($z) < $d) {
+ $z .= pack('P', $s[$i][$j++]);
+ if ($j == 5) {
+ $j = 0;
+ $i++;
+ if ($i == 5) {
+ $i = 0;
+ static::processSHA3Block64($s);
+ }
}
+ }
- // Initialize hash value for this chunk
- list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
+ return $z;
+ }
- // Main loop
- for ($i = 0; $i < 64; $i++) {
- $s0 = $this->_rightRotate($a, 2) ^
- $this->_rightRotate($a, 13) ^
- $this->_rightRotate($a, 22);
- $maj = ($a & $b) ^
- ($a & $c) ^
- ($b & $c);
- $t2 = $this->_add($s0, $maj);
-
- $s1 = $this->_rightRotate($e, 6) ^
- $this->_rightRotate($e, 11) ^
- $this->_rightRotate($e, 25);
- $ch = ($e & $f) ^
- ($this->_not($e) & $g);
- $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
+ /**
+ * 64-bit block processing method for SHA3
+ *
+ * @param array $s
+ */
+ private static function processSHA3Block64(&$s)
+ {
+ static $rotationOffsets = [
+ [ 0, 1, 62, 28, 27],
+ [36, 44, 6, 55, 20],
+ [ 3, 10, 43, 25, 39],
+ [41, 45, 15, 21, 8],
+ [18, 2, 61, 56, 14]
+ ];
+
+ static $roundConstants = [
+ 1,
+ 32898,
+ -9223372036854742902,
+ -9223372034707259392,
+ 32907,
+ 2147483649,
+ -9223372034707259263,
+ -9223372036854743031,
+ 138,
+ 136,
+ 2147516425,
+ 2147483658,
+ 2147516555,
+ -9223372036854775669,
+ -9223372036854742903,
+ -9223372036854743037,
+ -9223372036854743038,
+ -9223372036854775680,
+ 32778,
+ -9223372034707292150,
+ -9223372034707259263,
+ -9223372036854742912,
+ 2147483649,
+ -9223372034707259384
+ ];
+
+ for ($round = 0; $round < 24; $round++) {
+ // theta step
+ $parity = [];
+ for ($i = 0; $i < 5; $i++) {
+ $parity[] = $s[0][$i] ^ $s[1][$i] ^ $s[2][$i] ^ $s[3][$i] ^ $s[4][$i];
+ }
+ $temp = [
+ $parity[4] ^ static::rotateLeft64($parity[1], 1),
+ $parity[0] ^ static::rotateLeft64($parity[2], 1),
+ $parity[1] ^ static::rotateLeft64($parity[3], 1),
+ $parity[2] ^ static::rotateLeft64($parity[4], 1),
+ $parity[3] ^ static::rotateLeft64($parity[0], 1)
+ ];
+ for ($i = 0; $i < 5; $i++) {
+ for ($j = 0; $j < 5; $j++) {
+ $s[$i][$j] ^= $temp[$j];
+ }
+ }
- $h = $g;
- $g = $f;
- $f = $e;
- $e = $this->_add($d, $t1);
- $d = $c;
- $c = $b;
- $b = $a;
- $a = $this->_add($t1, $t2);
+ $st = $s;
+
+ // rho and pi steps
+ for ($i = 0; $i < 5; $i++) {
+ for ($j = 0; $j < 5; $j++) {
+ $st[(2 * $i + 3 * $j) % 5][$j] = static::rotateLeft64($s[$j][$i], $rotationOffsets[$j][$i]);
+ }
}
- // Add this chunk's hash to result so far
- $hash = array(
- $this->_add($hash[0], $a),
- $this->_add($hash[1], $b),
- $this->_add($hash[2], $c),
- $this->_add($hash[3], $d),
- $this->_add($hash[4], $e),
- $this->_add($hash[5], $f),
- $this->_add($hash[6], $g),
- $this->_add($hash[7], $h)
- );
+ // chi step
+ for ($i = 0; $i < 5; $i++) {
+ $s[$i] = [
+ $st[$i][0] ^ (~$st[$i][1] & $st[$i][2]),
+ $st[$i][1] ^ (~$st[$i][2] & $st[$i][3]),
+ $st[$i][2] ^ (~$st[$i][3] & $st[$i][4]),
+ $st[$i][3] ^ (~$st[$i][4] & $st[$i][0]),
+ $st[$i][4] ^ (~$st[$i][0] & $st[$i][1])
+ ];
+ }
+
+ // iota step
+ $s[0][0] ^= $roundConstants[$round];
}
+ }
- // Produce the final hash value (big-endian)
- return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
+ /**
+ * Left rotate 64-bit int
+ *
+ * @param int $x
+ * @param int $shift
+ */
+ private static function rotateLeft64($x, $shift)
+ {
+ $mask = -1 ^ (-1 << $shift);
+ return ($x << $shift) | (($x >> (64 - $shift)) & $mask);
+ }
+
+ /**
+ * Right rotate 64-bit int
+ *
+ * @param int $x
+ * @param int $shift
+ */
+ private static function rotateRight64($x, $shift)
+ {
+ $mask = -1 ^ (-1 << (64 - $shift));
+ return (($x >> $shift) & $mask) | ($x << (64 - $shift));
}
/**
- * Pure-PHP implementation of SHA384 and SHA512
+ * Pure-PHP implementation of SHA512
*
- * @access private
* @param string $m
+ * @param array $hash
+ * @return string
*/
- function _sha512($m)
+ private static function sha512($m, $hash)
{
- static $init384, $init512, $k;
+ static $k;
if (!isset($k)) {
- // Initialize variables
- $init384 = array( // initial values for SHA384
- 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
- '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
- );
- $init512 = array( // initial values for SHA512
- '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
- '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
- );
-
- for ($i = 0; $i < 8; $i++) {
- $init384[$i] = new BigInteger($init384[$i], 16);
- $init384[$i]->setPrecision(64);
- $init512[$i] = new BigInteger($init512[$i], 16);
- $init512[$i]->setPrecision(64);
- }
-
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
- $k = array(
+ $k = [
'428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
@@ -671,112 +1613,110 @@ class Hash
'06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
- );
+ ];
for ($i = 0; $i < 80; $i++) {
$k[$i] = new BigInteger($k[$i], 16);
}
}
- $hash = $this->l == 48 ? $init384 : $init512;
-
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
- $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
+ $m .= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
- $m.= pack('N4', 0, 0, 0, $length << 3);
+ $m .= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
- $w = array();
+ $w = [];
for ($i = 0; $i < 16; $i++) {
- $temp = new BigInteger($this->_string_shift($chunk, 8), 256);
+ $temp = new BigInteger(Strings::shift($chunk, 8), 256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
- $temp = array(
+ $temp = [
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
- );
+ ];
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
- $temp = array(
+ $temp = [
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
- );
+ ];
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
- $w[$i] = $w[$i - 16]->copy();
+ $w[$i] = clone $w[$i - 16];
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
- $a = $hash[0]->copy();
- $b = $hash[1]->copy();
- $c = $hash[2]->copy();
- $d = $hash[3]->copy();
- $e = $hash[4]->copy();
- $f = $hash[5]->copy();
- $g = $hash[6]->copy();
- $h = $hash[7]->copy();
+ $a = clone $hash[0];
+ $b = clone $hash[1];
+ $c = clone $hash[2];
+ $d = clone $hash[3];
+ $e = clone $hash[4];
+ $f = clone $hash[5];
+ $g = clone $hash[6];
+ $h = clone $hash[7];
// Main loop
for ($i = 0; $i < 80; $i++) {
- $temp = array(
+ $temp = [
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
- );
+ ];
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
- $temp = array(
+ $temp = [
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
- );
+ ];
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
- $temp = array(
+ $temp = [
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
- );
+ ];
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
- $temp = array(
+ $temp = [
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
- );
+ ];
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
- $h = $g->copy();
- $g = $f->copy();
- $f = $e->copy();
+ $h = clone $g;
+ $g = clone $f;
+ $f = clone $e;
$e = $d->add($t1);
- $d = $c->copy();
- $c = $b->copy();
- $b = $a->copy();
+ $d = clone $c;
+ $c = clone $b;
+ $b = clone $a;
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
- $hash = array(
+ $hash = [
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
@@ -785,109 +1725,167 @@ class Hash
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
- );
+ ];
}
// Produce the final hash value (big-endian)
- // (\phpseclib\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
+ // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
- $hash[4]->toBytes() . $hash[5]->toBytes();
- if ($this->l != 48) {
- $temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
- }
+ $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes();
return $temp;
}
/**
- * Right Rotate
- *
- * @access private
- * @param int $int
- * @param int $amt
- * @see self::_sha256()
- * @return int
- */
- function _rightRotate($int, $amt)
- {
- $invamt = 32 - $amt;
- $mask = (1 << $invamt) - 1;
- return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
- }
-
- /**
- * Right Shift
+ * Pure-PHP implementation of SHA512
*
- * @access private
- * @param int $int
- * @param int $amt
- * @see self::_sha256()
- * @return int
+ * @param string $m
+ * @param array $hash
+ * @return string
*/
- function _rightShift($int, $amt)
+ private static function sha512_64($m, $hash)
{
- $mask = (1 << (32 - $amt)) - 1;
- return ($int >> $amt) & $mask;
- }
+ static $k;
- /**
- * Not
- *
- * @access private
- * @param int $int
- * @see self::_sha256()
- * @return int
- */
- function _not($int)
- {
- return ~$int & 0xFFFFFFFF;
- }
+ if (!isset($k)) {
+ // Initialize table of round constants
+ // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
+ $k = [
+ '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
+ '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
+ 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
+ '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
+ 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
+ '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
+ '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
+ 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
+ '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
+ '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
+ 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
+ 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
+ '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
+ '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
+ '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
+ '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
+ 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
+ '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
+ '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
+ '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
+ ];
- /**
- * Add
- *
- * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
- * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster.
- *
- * @return int
- * @see self::_sha256()
- * @access private
- */
- function _add()
- {
- static $mod;
- if (!isset($mod)) {
- $mod = pow(2, 32);
+ for ($i = 0; $i < 80; $i++) {
+ list(, $k[$i]) = unpack('J', pack('H*', $k[$i]));
+ }
}
- $result = 0;
- $arguments = func_get_args();
- foreach ($arguments as $argument) {
- $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
- }
+ // Pre-processing
+ $length = strlen($m);
+ // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
+ $m .= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
+ $m[$length] = chr(0x80);
+ // we don't support hashing strings 512MB long
+ $m .= pack('N4', 0, 0, 0, $length << 3);
- if (function_exists('php_uname') && is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") != 'ARM') {
- return fmod($result, $mod);
+ // Process the message in successive 1024-bit chunks
+ $chunks = str_split($m, 128);
+ foreach ($chunks as $chunk) {
+ $w = [];
+ for ($i = 0; $i < 16; $i++) {
+ list(, $w[]) = unpack('J', Strings::shift($chunk, 8));
+ }
+
+ // Extend the sixteen 32-bit words into eighty 32-bit words
+ for ($i = 16; $i < 80; $i++) {
+ $temp = [
+ self::rotateRight64($w[$i - 15], 1),
+ self::rotateRight64($w[$i - 15], 8),
+ ($w[$i - 15] >> 7) & 0x01FFFFFFFFFFFFFF,
+ ];
+ $s0 = $temp[0] ^ $temp[1] ^ $temp[2];
+ $temp = [
+ self::rotateRight64($w[$i - 2], 19),
+ self::rotateRight64($w[$i - 2], 61),
+ ($w[$i - 2] >> 6) & 0x03FFFFFFFFFFFFFF,
+ ];
+ $s1 = $temp[0] ^ $temp[1] ^ $temp[2];
+
+ $w[$i] = $w[$i - 16];
+ $w[$i] = self::add64($w[$i], $s0);
+ $w[$i] = self::add64($w[$i], $w[$i - 7]);
+ $w[$i] = self::add64($w[$i], $s1);
+ }
+
+ // Initialize hash value for this chunk
+ list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
+
+ // Main loop
+ for ($i = 0; $i < 80; $i++) {
+ $temp = [
+ self::rotateRight64($a, 28),
+ self::rotateRight64($a, 34),
+ self::rotateRight64($a, 39),
+ ];
+ $s0 = $temp[0] ^ $temp[1] ^ $temp[2];
+ $temp = [$a & $b, $a & $c, $b & $c];
+ $maj = $temp[0] ^ $temp[1] ^ $temp[2];
+ $t2 = self::add64($s0, $maj);
+
+ $temp = [
+ self::rotateRight64($e, 14),
+ self::rotateRight64($e, 18),
+ self::rotateRight64($e, 41),
+ ];
+ $s1 = $temp[0] ^ $temp[1] ^ $temp[2];
+ $ch = ($e & $f) ^ ($g & ~$e);
+ $t1 = self::add64($h, $s1);
+ $t1 = self::add64($t1, $ch);
+ $t1 = self::add64($t1, $k[$i]);
+ $t1 = self::add64($t1, $w[$i]);
+
+ $h = $g;
+ $g = $f;
+ $f = $e;
+ $e = self::add64($d, $t1);
+ $d = $c;
+ $c = $b;
+ $b = $a;
+ $a = self::add64($t1, $t2);
+ }
+
+ // Add this chunk's hash to result so far
+ $hash = [
+ self::add64($hash[0], $a),
+ self::add64($hash[1], $b),
+ self::add64($hash[2], $c),
+ self::add64($hash[3], $d),
+ self::add64($hash[4], $e),
+ self::add64($hash[5], $f),
+ self::add64($hash[6], $g),
+ self::add64($hash[7], $h),
+ ];
}
- return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
- ((fmod(floor($result / 0x80000000), 2) & 1) << 31);
+ // Produce the final hash value (big-endian)
+ // (\phpseclib3\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
+ return pack('J*', ...$hash);
}
/**
- * String Shift
+ * OMAC Padding
*
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @return string
- * @access private
+ * @link https://www.rfc-editor.org/rfc/rfc4493.html#section-2.4
+ */
+ private static function OMAC_padding($m, $length)
+ {
+ $count = $length - strlen($m) - 1;
+ return "$m\x80" . str_repeat("\0", $count);
+ }
+
+ /**
+ * __toString() magic method
*/
- function _string_shift(&$string, $index = 1)
+ public function __toString()
{
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
+ return $this->getHash();
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php
new file mode 100644
index 000000000..36264080c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/PublicKeyLoader.php
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * PublicKeyLoader
+ *
+ * Returns a PublicKey or PrivateKey object.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Exception\NoKeyLoadedException;
+use phpseclib3\File\X509;
+
+/**
+ * PublicKeyLoader
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PublicKeyLoader
+{
+ /**
+ * Loads a public or private key
+ *
+ * @return AsymmetricKey
+ * @param string|array $key
+ * @param string $password optional
+ * @throws NoKeyLoadedException if key is not valid
+ */
+ public static function load($key, $password = false)
+ {
+ try {
+ return EC::load($key, $password);
+ } catch (NoKeyLoadedException $e) {
+ }
+
+ try {
+ return RSA::load($key, $password);
+ } catch (NoKeyLoadedException $e) {
+ }
+
+ try {
+ return DSA::load($key, $password);
+ } catch (NoKeyLoadedException $e) {
+ }
+
+ try {
+ $x509 = new X509();
+ $x509->loadX509($key);
+ $key = $x509->getPublicKey();
+ if ($key) {
+ return $key;
+ }
+ } catch (\Exception $e) {
+ }
+
+ throw new NoKeyLoadedException('Unable to read key');
+ }
+
+ /**
+ * Loads a private key
+ *
+ * @return PrivateKey
+ * @param string|array $key
+ * @param string $password optional
+ */
+ public static function loadPrivateKey($key, $password = false)
+ {
+ $key = self::load($key, $password);
+ if (!$key instanceof PrivateKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a private key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads a public key
+ *
+ * @return PublicKey
+ * @param string|array $key
+ */
+ public static function loadPublicKey($key)
+ {
+ $key = self::load($key);
+ if (!$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a public key');
+ }
+ return $key;
+ }
+
+ /**
+ * Loads parameters
+ *
+ * @return AsymmetricKey
+ * @param string|array $key
+ */
+ public static function loadParameters($key)
+ {
+ $key = self::load($key);
+ if (!$key instanceof PrivateKey && !$key instanceof PublicKey) {
+ throw new NoKeyLoadedException('The key that was loaded was not a parameter');
+ }
+ return $key;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
index e0511b32f..175c52e7b 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
@@ -16,7 +16,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $rc2 = new \phpseclib\Crypt\RC2();
+ * $rc2 = new \phpseclib3\Crypt\RC2('ctr');
*
* $rc2->setKey('abcdefgh');
*
@@ -26,121 +26,105 @@
* ?>
* </code>
*
- * @category Crypt
- * @package RC2
* @author Patrick Monnerat <pm@datasphere.ch>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\BlockCipher;
+use phpseclib3\Exception\BadModeException;
/**
* Pure-PHP implementation of RC2.
*
- * @package RC2
- * @access public
*/
-class RC2 extends Base
+class RC2 extends BlockCipher
{
/**
* Block Length of the cipher
*
- * @see \phpseclib\Crypt\Base::block_size
+ * @see Common\SymmetricKey::block_size
* @var int
- * @access private
*/
- var $block_size = 8;
+ protected $block_size = 8;
/**
* The Key
*
- * @see \phpseclib\Crypt\Base::key
+ * @see Common\SymmetricKey::key
* @see self::setKey()
* @var string
- * @access private
*/
- var $key;
+ protected $key;
/**
* The Original (unpadded) Key
*
- * @see \phpseclib\Crypt\Base::key
+ * @see Common\SymmetricKey::key
* @see self::setKey()
* @see self::encrypt()
* @see self::decrypt()
* @var string
- * @access private
- */
- var $orig_key = '';
-
- /**
- * Don't truncate / null pad key
- *
- * @see \phpseclib\Crypt\Base::_clearBuffers()
- * @var bool
- * @access private
*/
- var $skip_key_adjustment = true;
+ private $orig_key;
/**
* Key Length (in bytes)
*
- * @see \phpseclib\Crypt\RC2::setKeyLength()
+ * @see \phpseclib3\Crypt\RC2::setKeyLength()
* @var int
- * @access private
*/
- var $key_length = 16; // = 128 bits
+ protected $key_length = 16; // = 128 bits
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'rc2';
+ protected $cipher_name_mcrypt = 'rc2';
/**
* Optimizing value while CFB-encrypting
*
- * @see \phpseclib\Crypt\Base::cfb_init_len
+ * @see Common\SymmetricKey::cfb_init_len
* @var int
- * @access private
*/
- var $cfb_init_len = 500;
+ protected $cfb_init_len = 500;
/**
* The key length in bits.
*
+ * {@internal Should be in range [1..1024].}
+ *
+ * {@internal Changing this value after setting the key has no effect.}
+ *
* @see self::setKeyLength()
* @see self::setKey()
* @var int
- * @access private
- * @internal Should be in range [1..1024].
- * @internal Changing this value after setting the key has no effect.
*/
- var $default_key_length = 1024;
+ private $default_key_length = 1024;
/**
* The key length in bits.
*
+ * {@internal Should be in range [1..1024].}
+ *
* @see self::isValidEnine()
* @see self::setKey()
* @var int
- * @access private
- * @internal Should be in range [1..1024].
*/
- var $current_key_length;
+ private $current_key_length;
/**
* The Key Schedule
*
- * @see self::_setupKey()
+ * @see self::setupKey()
* @var array
- * @access private
*/
- var $keys;
+ private $keys;
/**
* Key expansion randomization table.
@@ -148,9 +132,8 @@ class RC2 extends Base
*
* @see self::setKey()
* @var array
- * @access private
*/
- var $pitable = array(
+ private static $pitable = [
0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
@@ -215,16 +198,15 @@ class RC2 extends Base
0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD
- );
+ ];
/**
* Inverse key expansion randomization table.
*
* @see self::setKey()
* @var array
- * @access private
*/
- var $invpitable = array(
+ private static $invpitable = [
0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
@@ -257,36 +239,50 @@ class RC2 extends Base
0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
- );
+ ];
+
+ /**
+ * Default Constructor.
+ *
+ * @param string $mode
+ * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
+ */
+ public function __construct($mode)
+ {
+ parent::__construct($mode);
+
+ if ($this->mode == self::MODE_STREAM) {
+ throw new BadModeException('Block ciphers cannot be ran in stream mode');
+ }
+ }
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::__construct()
+ * @see Common\SymmetricKey::__construct()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
switch ($engine) {
case self::ENGINE_OPENSSL:
+ if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
+ return false;
+ }
// quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
// "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
// in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
- if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
- return false;
- }
$this->cipher_name_openssl_ecb = 'rc2-ecb';
- $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode();
+ $this->cipher_name_openssl = 'rc2-' . $this->openssl_translate_mode();
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
@@ -294,32 +290,27 @@ class RC2 extends Base
*
* Valid key lengths are 8 to 1024.
* Calling this function after setting the key has no effect until the next
- * \phpseclib\Crypt\RC2::setKey() call.
+ * \phpseclib3\Crypt\RC2::setKey() call.
*
- * @access public
* @param int $length in bits
+ * @throws \LengthException if the key length isn't supported
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
- if ($length < 8) {
- $this->default_key_length = 1;
- } elseif ($length > 1024) {
- $this->default_key_length = 128;
- } else {
- $this->default_key_length = $length;
+ if ($length < 8 || $length > 1024) {
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
}
- $this->current_key_length = $this->default_key_length;
- parent::setKeyLength($length);
+ $this->default_key_length = $this->current_key_length = $length;
+ $this->explicit_key_length = $length >> 3;
}
/**
* Returns the current key length
*
- * @access public
* @return int
*/
- function getKeyLength()
+ public function getKeyLength()
{
return $this->current_key_length;
}
@@ -332,26 +323,28 @@ class RC2 extends Base
* has more then 128 bytes in it, and set $key to a single null byte if
* it is empty.
*
- * If the key is not explicitly set, it'll be assumed to be a single
- * null byte.
- *
- * @see \phpseclib\Crypt\Base::setKey()
- * @access public
+ * @see Common\SymmetricKey::setKey()
* @param string $key
- * @param int $t1 optional Effective key length in bits.
+ * @param int|boolean $t1 optional Effective key length in bits.
+ * @throws \LengthException if the key length isn't supported
*/
- function setKey($key, $t1 = 0)
+ public function setKey($key, $t1 = false)
{
$this->orig_key = $key;
- if ($t1 <= 0) {
+ if ($t1 === false) {
$t1 = $this->default_key_length;
- } elseif ($t1 > 1024) {
- $t1 = 1024;
}
+
+ if ($t1 < 1 || $t1 > 1024) {
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
+ }
+
$this->current_key_length = $t1;
- // Key byte count should be 1..128.
- $key = strlen($key) ? substr($key, 0, 128) : "\x00";
+ if (strlen($key) < 1 || strlen($key) > 128) {
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported');
+ }
+
$t = strlen($key);
// The mcrypt RC2 implementation only supports effective key length
@@ -366,7 +359,7 @@ class RC2 extends Base
$tm = 0xFF >> (8 * $t8 - $t1);
// Expand key.
- $pitable = $this->pitable;
+ $pitable = self::$pitable;
for ($i = $t; $i < 128; $i++) {
$l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
}
@@ -377,23 +370,25 @@ class RC2 extends Base
}
// Prepare the key for mcrypt.
- $l[0] = $this->invpitable[$l[0]];
+ $l[0] = self::$invpitable[$l[0]];
array_unshift($l, 'C*');
- parent::setKey(call_user_func_array('pack', $l));
+ $this->key = pack(...$l);
+ $this->key_length = strlen($this->key);
+ $this->changed = $this->nonIVChanged = true;
+ $this->setEngine();
}
/**
* Encrypts a message.
*
- * Mostly a wrapper for \phpseclib\Crypt\Base::encrypt, with some additional OpenSSL handling code
+ * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::encrypt, with some additional OpenSSL handling code
*
* @see self::decrypt()
- * @access public
* @param string $plaintext
* @return string $ciphertext
*/
- function encrypt($plaintext)
+ public function encrypt($plaintext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
@@ -409,14 +404,13 @@ class RC2 extends Base
/**
* Decrypts a message.
*
- * Mostly a wrapper for \phpseclib\Crypt\Base::decrypt, with some additional OpenSSL handling code
+ * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::decrypt, with some additional OpenSSL handling code
*
* @see self::encrypt()
- * @access public
* @param string $ciphertext
* @return string $plaintext
*/
- function decrypt($ciphertext)
+ public function decrypt($ciphertext)
{
if ($this->engine == self::ENGINE_OPENSSL) {
$temp = $this->key;
@@ -432,18 +426,17 @@ class RC2 extends Base
/**
* Encrypts a block
*
- * @see \phpseclib\Crypt\Base::_encryptBlock()
- * @see \phpseclib\Crypt\Base::encrypt()
- * @access private
+ * @see Common\SymmetricKey::encryptBlock()
+ * @see Common\SymmetricKey::encrypt()
* @param string $in
* @return string
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 20;
- $actions = array($limit => 44, 44 => 64);
+ $actions = [$limit => 44, 44 => 64];
$j = 0;
for (;;) {
@@ -477,18 +470,17 @@ class RC2 extends Base
/**
* Decrypts a block
*
- * @see \phpseclib\Crypt\Base::_decryptBlock()
- * @see \phpseclib\Crypt\Base::decrypt()
- * @access private
+ * @see Common\SymmetricKey::decryptBlock()
+ * @see Common\SymmetricKey::decrypt()
* @param string $in
* @return string
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
$keys = $this->keys;
$limit = 44;
- $actions = array($limit => 20, 20 => 0);
+ $actions = [$limit => 20, 20 => 0];
$j = 64;
for (;;) {
@@ -520,36 +512,20 @@ class RC2 extends Base
}
/**
- * Setup the \phpseclib\Crypt\Base::ENGINE_MCRYPT $engine
- *
- * @see \phpseclib\Crypt\Base::_setupMcrypt()
- * @access private
- */
- function _setupMcrypt()
- {
- if (!isset($this->key)) {
- $this->setKey('');
- }
-
- parent::_setupMcrypt();
- }
-
- /**
* Creates the key schedule
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see Common\SymmetricKey::setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
if (!isset($this->key)) {
$this->setKey('');
}
- // Key has already been expanded in \phpseclib\Crypt\RC2::setKey():
+ // Key has already been expanded in \phpseclib3\Crypt\RC2::setKey():
// Only the first value must be altered.
$l = unpack('Ca/Cb/v*', $this->key);
- array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8));
+ array_unshift($l, self::$pitable[$l['a']] | ($l['b'] << 8));
unset($l['a']);
unset($l['b']);
$this->keys = $l;
@@ -558,137 +534,107 @@ class RC2 extends Base
/**
* Setup the performance-optimized function for de/encrypt()
*
- * @see \phpseclib\Crypt\Base::_setupInlineCrypt()
- * @access private
+ * @see Common\SymmetricKey::setupInlineCrypt()
*/
- function _setupInlineCrypt()
+ protected function setupInlineCrypt()
{
- $lambda_functions =& self::_getLambdaFunctions();
-
- // The first 10 generated $lambda_functions will use the $keys hardcoded as integers
- // for the mixing rounds, for better inline crypt performance [~20% faster].
- // But for memory reason we have to limit those ultra-optimized $lambda_functions to an amount of 10.
- // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit)
- $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
-
- // Generation of a unique hash for our generated code
- $code_hash = "Crypt_RC2, {$this->mode}";
- if ($gen_hi_opt_code) {
- $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
- }
+ // Init code for both, encrypt and decrypt.
+ $init_crypt = '$keys = $this->keys;';
- // Is there a re-usable $lambda_functions in there?
- // If not, we have to create it.
- if (!isset($lambda_functions[$code_hash])) {
- // Init code for both, encrypt and decrypt.
- $init_crypt = '$keys = $self->keys;';
-
- switch (true) {
- case $gen_hi_opt_code:
- $keys = $this->keys;
- default:
- $keys = array();
- foreach ($this->keys as $k => $v) {
- $keys[$k] = '$keys[' . $k . ']';
- }
- }
+ $keys = $this->keys;
- // $in is the current 8 bytes block which has to be en/decrypt
- $encrypt_block = $decrypt_block = '
- $in = unpack("v4", $in);
- $r0 = $in[1];
- $r1 = $in[2];
- $r2 = $in[3];
- $r3 = $in[4];
- ';
-
- // Create code for encryption.
- $limit = 20;
- $actions = array($limit => 44, 44 => 64);
- $j = 0;
-
- for (;;) {
- // Mixing round.
- $encrypt_block .= '
- $r0 = (($r0 + ' . $keys[$j++] . ' +
- ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
- $r0 |= $r0 >> 16;
- $r1 = (($r1 + ' . $keys[$j++] . ' +
- ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
- $r1 |= $r1 >> 16;
- $r2 = (($r2 + ' . $keys[$j++] . ' +
- ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
- $r2 |= $r2 >> 16;
- $r3 = (($r3 + ' . $keys[$j++] . ' +
- ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
- $r3 |= $r3 >> 16;';
-
- if ($j === $limit) {
- if ($limit === 64) {
- break;
- }
-
- // Mashing round.
- $encrypt_block .= '
- $r0 += $keys[$r3 & 0x3F];
- $r1 += $keys[$r0 & 0x3F];
- $r2 += $keys[$r1 & 0x3F];
- $r3 += $keys[$r2 & 0x3F];';
- $limit = $actions[$limit];
- }
- }
+ // $in is the current 8 bytes block which has to be en/decrypt
+ $encrypt_block = $decrypt_block = '
+ $in = unpack("v4", $in);
+ $r0 = $in[1];
+ $r1 = $in[2];
+ $r2 = $in[3];
+ $r3 = $in[4];
+ ';
- $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
+ // Create code for encryption.
+ $limit = 20;
+ $actions = [$limit => 44, 44 => 64];
+ $j = 0;
- // Create code for decryption.
- $limit = 44;
- $actions = array($limit => 20, 20 => 0);
- $j = 64;
+ for (;;) {
+ // Mixing round.
+ $encrypt_block .= '
+ $r0 = (($r0 + ' . $keys[$j++] . ' +
+ ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
+ $r0 |= $r0 >> 16;
+ $r1 = (($r1 + ' . $keys[$j++] . ' +
+ ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
+ $r1 |= $r1 >> 16;
+ $r2 = (($r2 + ' . $keys[$j++] . ' +
+ ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
+ $r2 |= $r2 >> 16;
+ $r3 = (($r3 + ' . $keys[$j++] . ' +
+ ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
+ $r3 |= $r3 >> 16;';
- for (;;) {
- // R-mixing round.
- $decrypt_block .= '
- $r3 = ($r3 | ($r3 << 16)) >> 5;
- $r3 = ($r3 - ' . $keys[--$j] . ' -
- ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
- $r2 = ($r2 | ($r2 << 16)) >> 3;
- $r2 = ($r2 - ' . $keys[--$j] . ' -
- ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
- $r1 = ($r1 | ($r1 << 16)) >> 2;
- $r1 = ($r1 - ' . $keys[--$j] . ' -
- ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
- $r0 = ($r0 | ($r0 << 16)) >> 1;
- $r0 = ($r0 - ' . $keys[--$j] . ' -
- ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';
-
- if ($j === $limit) {
- if ($limit === 0) {
- break;
- }
-
- // R-mashing round.
- $decrypt_block .= '
- $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
- $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
- $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
- $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
- $limit = $actions[$limit];
+ if ($j === $limit) {
+ if ($limit === 64) {
+ break;
}
+
+ // Mashing round.
+ $encrypt_block .= '
+ $r0 += $keys[$r3 & 0x3F];
+ $r1 += $keys[$r0 & 0x3F];
+ $r2 += $keys[$r1 & 0x3F];
+ $r3 += $keys[$r2 & 0x3F];';
+ $limit = $actions[$limit];
}
+ }
+
+ $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
+
+ // Create code for decryption.
+ $limit = 44;
+ $actions = [$limit => 20, 20 => 0];
+ $j = 64;
+
+ for (;;) {
+ // R-mixing round.
+ $decrypt_block .= '
+ $r3 = ($r3 | ($r3 << 16)) >> 5;
+ $r3 = ($r3 - ' . $keys[--$j] . ' -
+ ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
+ $r2 = ($r2 | ($r2 << 16)) >> 3;
+ $r2 = ($r2 - ' . $keys[--$j] . ' -
+ ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
+ $r1 = ($r1 | ($r1 << 16)) >> 2;
+ $r1 = ($r1 - ' . $keys[--$j] . ' -
+ ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
+ $r0 = ($r0 | ($r0 << 16)) >> 1;
+ $r0 = ($r0 - ' . $keys[--$j] . ' -
+ ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';
- $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
+ if ($j === $limit) {
+ if ($limit === 0) {
+ break;
+ }
- // Creates the inline-crypt function
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => $init_crypt,
- 'encrypt_block' => $encrypt_block,
- 'decrypt_block' => $decrypt_block
- )
- );
+ // R-mashing round.
+ $decrypt_block .= '
+ $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
+ $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
+ $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
+ $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
+ $limit = $actions[$limit];
+ }
}
- // Set the inline-crypt function as callback in: $this->inline_crypt
- $this->inline_crypt = $lambda_functions[$code_hash];
+ $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
+
+ // Creates the inline-crypt function
+ $this->inline_crypt = $this->createInlineCryptFunction(
+ [
+ 'init_crypt' => $init_crypt,
+ 'encrypt_block' => $encrypt_block,
+ 'decrypt_block' => $decrypt_block
+ ]
+ );
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php
index 2e5c05567..98cf01165 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php
@@ -20,7 +20,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $rc4 = new \phpseclib\Crypt\RC4();
+ * $rc4 = new \phpseclib3\Crypt\RC4();
*
* $rc4->setKey('abcdefgh');
*
@@ -34,166 +34,109 @@
* ?>
* </code>
*
- * @category Crypt
- * @package RC4
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\StreamCipher;
/**
* Pure-PHP implementation of RC4.
*
- * @package RC4
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class RC4 extends Base
+class RC4 extends StreamCipher
{
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\RC4::_crypt()
- */
+ /**
+ * @see \phpseclib3\Crypt\RC4::_crypt()
+ */
const ENCRYPT = 0;
- const DECRYPT = 1;
- /**#@-*/
/**
- * Block Length of the cipher
- *
- * RC4 is a stream cipher
- * so we the block_size to 0
- *
- * @see \phpseclib\Crypt\Base::block_size
- * @var int
- * @access private
+ * @see \phpseclib3\Crypt\RC4::_crypt()
*/
- var $block_size = 0;
+ const DECRYPT = 1;
/**
* Key Length (in bytes)
*
- * @see \phpseclib\Crypt\RC4::setKeyLength()
+ * @see \phpseclib3\Crypt\RC4::setKeyLength()
* @var int
- * @access private
*/
- var $key_length = 128; // = 1024 bits
+ protected $key_length = 128; // = 1024 bits
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
- */
- var $cipher_name_mcrypt = 'arcfour';
-
- /**
- * Holds whether performance-optimized $inline_crypt() can/should be used.
- *
- * @see \phpseclib\Crypt\Base::inline_crypt
- * @var mixed
- * @access private
*/
- var $use_inline_crypt = false; // currently not available
+ protected $cipher_name_mcrypt = 'arcfour';
/**
* The Key
*
* @see self::setKey()
* @var string
- * @access private
*/
- var $key;
+ protected $key;
/**
* The Key Stream for decryption and encryption
*
* @see self::setKey()
* @var array
- * @access private
*/
- var $stream;
-
- /**
- * Default Constructor.
- *
- * Determines whether or not the mcrypt extension should be used.
- *
- * @see \phpseclib\Crypt\Base::__construct()
- * @return \phpseclib\Crypt\RC4
- * @access public
- */
- function __construct()
- {
- parent::__construct(Base::MODE_STREAM);
- }
+ private $stream;
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::__construct()
+ * @see Common\SymmetricKey::__construct()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
+ if ($this->continuousBuffer) {
+ return false;
+ }
// quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
// "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
// in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
return false;
}
- if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
- $this->cipher_name_openssl = 'rc4-40';
- } else {
- switch (strlen($this->key)) {
- case 5:
- $this->cipher_name_openssl = 'rc4-40';
- break;
- case 8:
- $this->cipher_name_openssl = 'rc4-64';
- break;
- case 16:
- $this->cipher_name_openssl = 'rc4';
- break;
- default:
- return false;
- }
- }
+ $this->cipher_name_openssl = 'rc4-40';
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
- * Dummy function.
- *
- * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
- * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
- * calling setKey().
- *
- * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
- * the IV's are relatively easy to predict, an attack described by
- * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
- * can be used to quickly guess at the rest of the key. The following links elaborate:
+ * Sets the key length
*
- * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
- * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
+ * Keys can be between 1 and 256 bytes long.
*
- * @param string $iv
- * @see self::setKey()
- * @access public
+ * @param int $length
+ * @throws \LengthException if the key length is invalid
*/
- function setIV($iv)
+ public function setKeyLength($length)
{
+ if ($length < 8 || $length > 2048) {
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported');
+ }
+
+ $this->key_length = $length >> 3;
+
+ parent::setKeyLength($length);
}
/**
@@ -201,37 +144,32 @@ class RC4 extends Base
*
* Keys can be between 1 and 256 bytes long.
*
- * @access public
- * @param int $length
+ * @param string $key
*/
- function setKeyLength($length)
+ public function setKey($key)
{
- if ($length < 8) {
- $this->key_length = 1;
- } elseif ($length > 2048) {
- $this->key_length = 256;
- } else {
- $this->key_length = $length >> 3;
+ $length = strlen($key);
+ if ($length < 1 || $length > 256) {
+ throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long');
}
- parent::setKeyLength($length);
+ parent::setKey($key);
}
/**
* Encrypts a message.
*
- * @see \phpseclib\Crypt\Base::decrypt()
- * @see self::_crypt()
- * @access public
+ * @see Common\SymmetricKey::decrypt()
+ * @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
*/
- function encrypt($plaintext)
+ public function encrypt($plaintext)
{
if ($this->engine != self::ENGINE_INTERNAL) {
return parent::encrypt($plaintext);
}
- return $this->_crypt($plaintext, self::ENCRYPT);
+ return $this->crypt($plaintext, self::ENCRYPT);
}
/**
@@ -240,27 +178,25 @@ class RC4 extends Base
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
- * @see \phpseclib\Crypt\Base::encrypt()
- * @see self::_crypt()
- * @access public
+ * @see Common\SymmetricKey::encrypt()
+ * @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
*/
- function decrypt($ciphertext)
+ public function decrypt($ciphertext)
{
if ($this->engine != self::ENGINE_INTERNAL) {
return parent::decrypt($ciphertext);
}
- return $this->_crypt($ciphertext, self::DECRYPT);
+ return $this->crypt($ciphertext, self::DECRYPT);
}
/**
* Encrypts a block
*
- * @access private
* @param string $in
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
// RC4 does not utilize this method
}
@@ -268,10 +204,9 @@ class RC4 extends Base
/**
* Decrypts a block
*
- * @access private
* @param string $in
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
// RC4 does not utilize this method
}
@@ -279,10 +214,9 @@ class RC4 extends Base
/**
* Setup the key (expansion)
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see Common\SymmetricKey::_setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
$key = $this->key;
$keyLength = strlen($key);
@@ -295,12 +229,12 @@ class RC4 extends Base
$keyStream[$j] = $temp;
}
- $this->stream = array();
- $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array(
+ $this->stream = [];
+ $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [
0, // index $i
0, // index $j
$keyStream
- );
+ ];
}
/**
@@ -308,16 +242,14 @@ class RC4 extends Base
*
* @see self::encrypt()
* @see self::decrypt()
- * @access private
* @param string $text
* @param int $mode
* @return string $text
*/
- function _crypt($text, $mode)
+ private function crypt($text, $mode)
{
if ($this->changed) {
- $this->_setup();
- $this->changed = false;
+ $this->setup();
}
$stream = &$this->stream[$mode];
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
index 7f5df1d5f..0a11957b0 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
@@ -8,660 +8,362 @@
* Here's an example of how to encrypt and decrypt text with this library:
* <code>
* <?php
- * include 'vendor/autoload.php';
+ * include 'vendor/autoload.php';
*
- * $rsa = new \phpseclib\Crypt\RSA();
- * extract($rsa->createKey());
+ * $private = Crypt\RSA::createKey();
+ * $public = $private->getPublicKey();
*
- * $plaintext = 'terrafrost';
+ * $plaintext = 'terrafrost';
*
- * $rsa->loadKey($privatekey);
- * $ciphertext = $rsa->encrypt($plaintext);
+ * $ciphertext = $public->encrypt($plaintext);
*
- * $rsa->loadKey($publickey);
- * echo $rsa->decrypt($ciphertext);
+ * echo $private->decrypt($ciphertext);
* ?>
* </code>
*
* Here's an example of how to create signatures and verify signatures with this library:
* <code>
* <?php
- * include 'vendor/autoload.php';
+ * include 'vendor/autoload.php';
*
- * $rsa = new \phpseclib\Crypt\RSA();
- * extract($rsa->createKey());
+ * $private = Crypt\RSA::createKey();
+ * $public = $private->getPublicKey();
*
- * $plaintext = 'terrafrost';
+ * $plaintext = 'terrafrost';
*
- * $rsa->loadKey($privatekey);
- * $signature = $rsa->sign($plaintext);
+ * $signature = $private->sign($plaintext);
*
- * $rsa->loadKey($publickey);
- * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
+ * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
* </code>
*
- * @category Crypt
- * @package RSA
+ * One thing to consider when using this: so phpseclib uses PSS mode by default.
+ * Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So
+ * should phpseclib save to the id-RSASSA-PSS format by default or the
+ * rsaEncryption format? For stand-alone keys I figure rsaEncryption is better
+ * because SSH doesn't use PSS and idk how many SSH servers would be able to
+ * decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS
+ * format is used by default (unless you change it up to use PKCS1 instead)
+ *
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
-use phpseclib\Math\BigInteger;
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
+use phpseclib3\Crypt\RSA\PrivateKey;
+use phpseclib3\Crypt\RSA\PublicKey;
+use phpseclib3\Exception\InconsistentSetupException;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Math\BigInteger;
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
*
- * @package RSA
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class RSA
+abstract class RSA extends AsymmetricKey
{
- /**#@+
- * @access public
- * @see self::encrypt()
- * @see self::decrypt()
+ /**
+ * Algorithm Name
+ *
+ * @var string
*/
+ const ALGORITHM = 'RSA';
+
/**
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
- * Uses sha1 by default.
+ * Uses sha256 by default
*
* @see self::setHash()
* @see self::setMGFHash()
+ * @see self::encrypt()
+ * @see self::decrypt()
*/
const ENCRYPTION_OAEP = 1;
+
/**
* Use PKCS#1 padding.
*
- * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
+ * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards
* compatibility with protocols (like SSH-1) written before OAEP's introduction.
+ *
+ * @see self::encrypt()
+ * @see self::decrypt()
*/
const ENCRYPTION_PKCS1 = 2;
+
/**
* Do not use any padding
*
* Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
* stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
+ *
+ * @see self::encrypt()
+ * @see self::decrypt()
*/
- const ENCRYPTION_NONE = 3;
- /**#@-*/
+ const ENCRYPTION_NONE = 4;
- /**#@+
- * @access public
- * @see self::sign()
- * @see self::verify()
- * @see self::setHash()
- */
/**
* Use the Probabilistic Signature Scheme for signing
*
- * Uses sha1 by default.
+ * Uses sha256 and 0 as the salt length
*
* @see self::setSaltLength()
* @see self::setMGFHash()
+ * @see self::setHash()
+ * @see self::sign()
+ * @see self::verify()
+ * @see self::setHash()
*/
- const SIGNATURE_PSS = 1;
- /**
- * Use the PKCS#1 scheme by default.
- *
- * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
- * compatibility with protocols (like SSH-2) written before PSS's introduction.
- */
- const SIGNATURE_PKCS1 = 2;
- /**#@-*/
-
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\RSA::createKey()
- */
- /**
- * ASN1 Integer
- */
- const ASN1_INTEGER = 2;
- /**
- * ASN1 Bit String
- */
- const ASN1_BITSTRING = 3;
- /**
- * ASN1 Octet String
- */
- const ASN1_OCTETSTRING = 4;
- /**
- * ASN1 Object Identifier
- */
- const ASN1_OBJECT = 6;
- /**
- * ASN1 Sequence (with the constucted bit set)
- */
- const ASN1_SEQUENCE = 48;
- /**#@-*/
-
- /**#@+
- * @access private
- * @see \phpseclib\Crypt\RSA::__construct()
- */
- /**
- * To use the pure-PHP implementation
- */
- const MODE_INTERNAL = 1;
- /**
- * To use the OpenSSL library
- *
- * (if enabled; otherwise, the internal implementation will be used)
- */
- const MODE_OPENSSL = 2;
- /**#@-*/
-
- /**#@+
- * @access public
- * @see \phpseclib\Crypt\RSA::createKey()
- * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
- */
- /**
- * PKCS#1 formatted private key
- *
- * Used by OpenSSH
- */
- const PRIVATE_FORMAT_PKCS1 = 0;
- /**
- * PuTTY formatted private key
- */
- const PRIVATE_FORMAT_PUTTY = 1;
- /**
- * XML formatted private key
- */
- const PRIVATE_FORMAT_XML = 2;
- /**
- * PKCS#8 formatted private key
- */
- const PRIVATE_FORMAT_PKCS8 = 8;
- /**
- * OpenSSH formatted private key
- */
- const PRIVATE_FORMAT_OPENSSH = 9;
- /**#@-*/
-
- /**#@+
- * @access public
- * @see \phpseclib\Crypt\RSA::createKey()
- * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
- */
- /**
- * Raw public key
- *
- * An array containing two \phpseclib\Math\BigInteger objects.
- *
- * The exponent can be indexed with any of the following:
- *
- * 0, e, exponent, publicExponent
- *
- * The modulus can be indexed with any of the following:
- *
- * 1, n, modulo, modulus
- */
- const PUBLIC_FORMAT_RAW = 3;
- /**
- * PKCS#1 formatted public key (raw)
- *
- * Used by File/X509.php
- *
- * Has the following header:
- *
- * -----BEGIN RSA PUBLIC KEY-----
- *
- * Analogous to ssh-keygen's pem format (as specified by -m)
- */
- const PUBLIC_FORMAT_PKCS1 = 4;
- const PUBLIC_FORMAT_PKCS1_RAW = 4;
- /**
- * XML formatted public key
- */
- const PUBLIC_FORMAT_XML = 5;
- /**
- * OpenSSH formatted public key
- *
- * Place in $HOME/.ssh/authorized_keys
- */
- const PUBLIC_FORMAT_OPENSSH = 6;
- /**
- * PKCS#1 formatted public key (encapsulated)
- *
- * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
- *
- * Has the following header:
- *
- * -----BEGIN PUBLIC KEY-----
- *
- * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
- * is specific to private keys it's basically creating a DER-encoded wrapper
- * for keys. This just extends that same concept to public keys (much like ssh-keygen)
- */
- const PUBLIC_FORMAT_PKCS8 = 7;
- /**#@-*/
+ const SIGNATURE_PSS = 16;
/**
- * Precomputed Zero
+ * Use a relaxed version of PKCS#1 padding for signature verification
*
- * @var \phpseclib\Math\BigInteger
- * @access private
+ * @see self::sign()
+ * @see self::verify()
+ * @see self::setHash()
*/
- var $zero;
+ const SIGNATURE_RELAXED_PKCS1 = 32;
/**
- * Precomputed One
+ * Use PKCS#1 padding for signature verification
*
- * @var \phpseclib\Math\BigInteger
- * @access private
+ * @see self::sign()
+ * @see self::verify()
+ * @see self::setHash()
*/
- var $one;
+ const SIGNATURE_PKCS1 = 64;
/**
- * Private Key Format
+ * Encryption padding mode
*
* @var int
- * @access private
*/
- var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
+ protected $encryptionPadding = self::ENCRYPTION_OAEP;
/**
- * Public Key Format
+ * Signature padding mode
*
* @var int
- * @access public
*/
- var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8;
+ protected $signaturePadding = self::SIGNATURE_PSS;
/**
- * Modulus (ie. n)
- *
- * @var \phpseclib\Math\BigInteger
- * @access private
- */
- var $modulus;
-
- /**
- * Modulus length
+ * Length of hash function output
*
- * @var \phpseclib\Math\BigInteger
- * @access private
+ * @var int
*/
- var $k;
+ protected $hLen;
/**
- * Exponent (ie. e or d)
+ * Length of salt
*
- * @var \phpseclib\Math\BigInteger
- * @access private
+ * @var int
*/
- var $exponent;
+ protected $sLen;
/**
- * Primes for Chinese Remainder Theorem (ie. p and q)
+ * Label
*
- * @var array
- * @access private
+ * @var string
*/
- var $primes;
+ protected $label = '';
/**
- * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
+ * Hash function for the Mask Generation Function
*
- * @var array
- * @access private
+ * @var Hash
*/
- var $exponents;
+ protected $mgfHash;
/**
- * Coefficients for Chinese Remainder Theorem (ie. qInv)
+ * Length of MGF hash function output
*
- * @var array
- * @access private
+ * @var int
*/
- var $coefficients;
+ protected $mgfHLen;
/**
- * Hash name
+ * Modulus (ie. n)
*
- * @var string
- * @access private
+ * @var Math\BigInteger
*/
- var $hashName;
+ protected $modulus;
/**
- * Hash function
+ * Modulus length
*
- * @var \phpseclib\Crypt\Hash
- * @access private
+ * @var Math\BigInteger
*/
- var $hash;
+ protected $k;
/**
- * Length of hash function output
+ * Exponent (ie. e or d)
*
- * @var int
- * @access private
+ * @var Math\BigInteger
*/
- var $hLen;
+ protected $exponent;
/**
- * Length of salt
+ * Default public exponent
*
* @var int
- * @access private
+ * @link http://en.wikipedia.org/wiki/65537_%28number%29
*/
- var $sLen;
+ private static $defaultExponent = 65537;
/**
- * Hash function for the Mask Generation Function
+ * Enable Blinding?
*
- * @var \phpseclib\Crypt\Hash
- * @access private
+ * @var bool
*/
- var $mgfHash;
+ protected static $enableBlinding = true;
/**
- * Length of MGF hash function output
+ * OpenSSL configuration file name.
*
- * @var int
- * @access private
+ * @see self::createKey()
+ * @var ?string
*/
- var $mgfHLen;
+ protected static $configFile;
/**
- * Encryption mode
+ * Smallest Prime
*
- * @var int
- * @access private
- */
- var $encryptionMode = self::ENCRYPTION_OAEP;
-
- /**
- * Signature mode
+ * Per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
+ * than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime
+ * to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if
+ * engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is
+ * ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's
+ * a chance neither gmp nor OpenSSL are installed)
*
* @var int
- * @access private
*/
- var $signatureMode = self::SIGNATURE_PSS;
+ private static $smallestPrime = 4096;
/**
* Public Exponent
*
- * @var mixed
- * @access private
+ * @var Math\BigInteger
*/
- var $publicExponent = false;
+ protected $publicExponent;
/**
- * Password
+ * Sets the public exponent for key generation
*
- * @var string
- * @access private
- */
- var $password = false;
-
- /**
- * Components
+ * This will be 65537 unless changed.
*
- * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
- * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
- *
- * @see self::_start_element_handler()
- * @var array
- * @access private
+ * @param int $val
*/
- var $components = array();
-
- /**
- * Current String
- *
- * For use with parsing XML formatted keys.
- *
- * @see self::_character_handler()
- * @see self::_stop_element_handler()
- * @var mixed
- * @access private
- */
- var $current;
+ public static function setExponent($val)
+ {
+ self::$defaultExponent = $val;
+ }
/**
- * OpenSSL configuration file name.
+ * Sets the smallest prime number in bits. Used for key generation
*
- * Set to null to use system configuration file.
- * @see self::createKey()
- * @var mixed
- * @Access public
- */
- var $configFile;
-
- /**
- * Public key comment field.
+ * This will be 4096 unless changed.
*
- * @var string
- * @access private
+ * @param int $val
*/
- var $comment = 'phpseclib-generated-key';
+ public static function setSmallestPrime($val)
+ {
+ self::$smallestPrime = $val;
+ }
/**
- * The constructor
+ * Sets the OpenSSL config file path
*
- * If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
- * \phpseclib\Crypt\RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
- * openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
+ * Set to the empty string to use the default config file
*
- * @return \phpseclib\Crypt\RSA
- * @access public
+ * @param string $val
*/
- function __construct()
+ public static function setOpenSSLConfigPath($val)
{
- $this->configFile = dirname(__FILE__) . '/../openssl.cnf';
-
- if (!defined('CRYPT_RSA_MODE')) {
- switch (true) {
- // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular,
- // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger
- // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either.
- case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
- define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
- break;
- case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile):
- // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
- $versions = array();
-
- // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
- if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
- ob_start();
- @phpinfo();
- $content = ob_get_contents();
- ob_end_clean();
-
- preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
-
- if (!empty($matches[1])) {
- for ($i = 0; $i < count($matches[1]); $i++) {
- $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
-
- // Remove letter part in OpenSSL version
- if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
- $versions[$matches[1][$i]] = $fullVersion;
- } else {
- $versions[$matches[1][$i]] = $m[0];
- }
- }
- }
- }
-
- // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
- switch (true) {
- case !isset($versions['Header']):
- case !isset($versions['Library']):
- case $versions['Header'] == $versions['Library']:
- case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
- define('CRYPT_RSA_MODE', self::MODE_OPENSSL);
- break;
- default:
- define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
- define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
- }
- break;
- default:
- define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
- }
- }
-
- $this->zero = new BigInteger();
- $this->one = new BigInteger(1);
-
- $this->hash = new Hash('sha1');
- $this->hLen = $this->hash->getLength();
- $this->hashName = 'sha1';
- $this->mgfHash = new Hash('sha1');
- $this->mgfHLen = $this->mgfHash->getLength();
+ self::$configFile = $val;
}
/**
- * Create public / private key pair
+ * Create a private key
*
- * Returns an array with the following three elements:
- * - 'privatekey': The private key.
- * - 'publickey': The public key.
- * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
- * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
+ * The public key can be extracted from the private key
*
- * @access public
+ * @return PrivateKey
* @param int $bits
- * @param int $timeout
- * @param array $partial
*/
- function createKey($bits = 1024, $timeout = false, $partial = array())
+ public static function createKey($bits = 2048)
{
- if (!defined('CRYPT_RSA_EXPONENT')) {
- // http://en.wikipedia.org/wiki/65537_%28number%29
- define('CRYPT_RSA_EXPONENT', '65537');
+ self::initialize_static_variables();
+
+ $class = new \ReflectionClass(static::class);
+ if ($class->isFinal()) {
+ throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
}
- // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
- // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
- // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
- // CRYPT_RSA_MODE is set to self::MODE_INTERNAL. if CRYPT_RSA_MODE is set to self::MODE_OPENSSL then
- // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
- // generation when there's a chance neither gmp nor OpenSSL are installed)
- if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
- define('CRYPT_RSA_SMALLEST_PRIME', 4096);
+
+ $regSize = $bits >> 1; // divide by two to see how many bits P and Q would be
+ if ($regSize > self::$smallestPrime) {
+ $num_primes = floor($bits / self::$smallestPrime);
+ $regSize = self::$smallestPrime;
+ } else {
+ $num_primes = 2;
}
- // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
- if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
- $config = array();
- if (isset($this->configFile)) {
- $config['config'] = $this->configFile;
+ if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) {
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
}
- $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
- openssl_pkey_export($rsa, $privatekey, null, $config);
- $publickey = openssl_pkey_get_details($rsa);
- $publickey = $publickey['key'];
- $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1)));
- $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1)));
+ // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
+ if (self::$engines['OpenSSL']) {
+ $config = [];
+ if (self::$configFile) {
+ $config['config'] = self::$configFile;
+ }
+ $rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config);
+ openssl_pkey_export($rsa, $privatekeystr, null, $config);
+
+ // clear the buffer of error strings stemming from a minimalistic openssl.cnf
+ // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
+ while (openssl_error_string() !== false) {
+ }
- // clear the buffer of error strings stemming from a minimalistic openssl.cnf
- // https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
- while (openssl_error_string() !== false) {
+ return RSA::load($privatekeystr);
}
-
- return array(
- 'privatekey' => $privatekey,
- 'publickey' => $publickey,
- 'partialkey' => false
- );
}
static $e;
if (!isset($e)) {
- $e = new BigInteger(CRYPT_RSA_EXPONENT);
- }
-
- extract($this->_generateMinMax($bits));
- $absoluteMin = $min;
- $temp = $bits >> 1; // divide by two to see how many bits P and Q would be
- if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
- $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
- $temp = CRYPT_RSA_SMALLEST_PRIME;
- } else {
- $num_primes = 2;
- }
- extract($this->_generateMinMax($temp + $bits % $temp));
- $finalMax = $max;
- extract($this->_generateMinMax($temp));
-
- $generator = new BigInteger();
-
- $n = $this->one->copy();
- if (!empty($partial)) {
- extract(unserialize($partial));
- } else {
- $exponents = $coefficients = $primes = array();
- $lcm = array(
- 'top' => $this->one->copy(),
- 'bottom' => false
- );
+ $e = new BigInteger(self::$defaultExponent);
}
- $start = time();
- $i0 = count($primes) + 1;
+ $n = clone self::$one;
+ $exponents = $coefficients = $primes = [];
+ $lcm = [
+ 'top' => clone self::$one,
+ 'bottom' => false
+ ];
do {
- for ($i = $i0; $i <= $num_primes; $i++) {
- if ($timeout !== false) {
- $timeout-= time() - $start;
- $start = time();
- if ($timeout <= 0) {
- return array(
- 'privatekey' => '',
- 'publickey' => '',
- 'partialkey' => serialize(array(
- 'primes' => $primes,
- 'coefficients' => $coefficients,
- 'lcm' => $lcm,
- 'exponents' => $exponents
- ))
- );
- }
- }
-
- if ($i == $num_primes) {
- list($min, $temp) = $absoluteMin->divide($n);
- if (!$temp->equals($this->zero)) {
- $min = $min->add($this->one); // ie. ceil()
- }
- $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
+ for ($i = 1; $i <= $num_primes; $i++) {
+ if ($i != $num_primes) {
+ $primes[$i] = BigInteger::randomPrime($regSize);
} else {
- $primes[$i] = $generator->randomPrime($min, $max, $timeout);
- }
-
- if ($primes[$i] === false) { // if we've reached the timeout
- if (count($primes) > 1) {
- $partialkey = '';
- } else {
- array_pop($primes);
- $partialkey = serialize(array(
- 'primes' => $primes,
- 'coefficients' => $coefficients,
- 'lcm' => $lcm,
- 'exponents' => $exponents
- ));
- }
-
- return array(
- 'privatekey' => '',
- 'publickey' => '',
- 'partialkey' => $partialkey
- );
+ $minMax = BigInteger::minMaxBits($bits);
+ $min = $minMax['min'];
+ $max = $minMax['max'];
+ list($min) = $min->divide($n);
+ $min = $min->add(self::$one);
+ list($max) = $max->divide($n);
+ $primes[$i] = BigInteger::randomRangePrime($min, $max);
}
// the first coefficient is calculated differently from the rest
@@ -672,24 +374,27 @@ class RSA
$n = $n->multiply($primes[$i]);
- $temp = $primes[$i]->subtract($this->one);
+ $temp = $primes[$i]->subtract(self::$one);
// textbook RSA implementations use Euler's totient function instead of the least common multiple.
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] = $lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
-
- $exponents[$i] = $e->modInverse($temp);
}
list($temp) = $lcm['top']->divide($lcm['bottom']);
$gcd = $temp->gcd($e);
$i0 = 1;
- } while (!$gcd->equals($this->one));
+ } while (!$gcd->equals(self::$one));
+
+ $coefficients[2] = $primes[2]->modInverse($primes[1]);
$d = $e->modInverse($temp);
- $coefficients[2] = $primes[2]->modInverse($primes[1]);
+ foreach ($primes as $i => $prime) {
+ $temp = $prime->subtract(self::$one);
+ $exponents[$i] = $e->modInverse($temp);
+ }
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
// RSAPrivateKey ::= SEQUENCE {
@@ -704,1575 +409,99 @@ class RSA
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
+ $privatekey = new PrivateKey();
+ $privatekey->modulus = $n;
+ $privatekey->k = $bits >> 3;
+ $privatekey->publicExponent = $e;
+ $privatekey->exponent = $d;
+ $privatekey->primes = $primes;
+ $privatekey->exponents = $exponents;
+ $privatekey->coefficients = $coefficients;
- return array(
- 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
- 'publickey' => $this->_convertPublicKey($n, $e),
- 'partialkey' => false
- );
- }
-
- /**
- * Convert a private key to the appropriate format.
- *
- * @access private
- * @see self::setPrivateKeyFormat()
- * @param Math_BigInteger $n
- * @param Math_BigInteger $e
- * @param Math_BigInteger $d
- * @param array<int,Math_BigInteger> $primes
- * @param array<int,Math_BigInteger> $exponents
- * @param array<int,Math_BigInteger> $coefficients
- * @return string
- */
- function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
- {
- $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML;
- $num_primes = count($primes);
- $raw = array(
- 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
- 'modulus' => $n->toBytes($signed),
- 'publicExponent' => $e->toBytes($signed),
- 'privateExponent' => $d->toBytes($signed),
- 'prime1' => $primes[1]->toBytes($signed),
- 'prime2' => $primes[2]->toBytes($signed),
- 'exponent1' => $exponents[1]->toBytes($signed),
- 'exponent2' => $exponents[2]->toBytes($signed),
- 'coefficient' => $coefficients[2]->toBytes($signed)
- );
-
- // if the format in question does not support multi-prime rsa and multi-prime rsa was used,
- // call _convertPublicKey() instead.
- switch ($this->privateKeyFormat) {
- case self::PRIVATE_FORMAT_XML:
- if ($num_primes != 2) {
- return false;
- }
- return "<RSAKeyValue>\r\n" .
- ' <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
- ' <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
- ' <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
- ' <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
- ' <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
- ' <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
- ' <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
- ' <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
- '</RSAKeyValue>';
- break;
- case self::PRIVATE_FORMAT_PUTTY:
- if ($num_primes != 2) {
- return false;
- }
- $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
- $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
- $key.= $encryption;
- $key.= "\r\nComment: " . $this->comment . "\r\n";
- $public = pack(
- 'Na*Na*Na*',
- strlen('ssh-rsa'),
- 'ssh-rsa',
- strlen($raw['publicExponent']),
- $raw['publicExponent'],
- strlen($raw['modulus']),
- $raw['modulus']
- );
- $source = pack(
- 'Na*Na*Na*Na*',
- strlen('ssh-rsa'),
- 'ssh-rsa',
- strlen($encryption),
- $encryption,
- strlen($this->comment),
- $this->comment,
- strlen($public),
- $public
- );
- $public = base64_encode($public);
- $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n";
- $key.= chunk_split($public, 64);
- $private = pack(
- 'Na*Na*Na*Na*',
- strlen($raw['privateExponent']),
- $raw['privateExponent'],
- strlen($raw['prime1']),
- $raw['prime1'],
- strlen($raw['prime2']),
- $raw['prime2'],
- strlen($raw['coefficient']),
- $raw['coefficient']
- );
- if (empty($this->password) && !is_string($this->password)) {
- $source.= pack('Na*', strlen($private), $private);
- $hashkey = 'putty-private-key-file-mac-key';
- } else {
- $private.= Random::string(16 - (strlen($private) & 15));
- $source.= pack('Na*', strlen($private), $private);
- $sequence = 0;
- $symkey = '';
- while (strlen($symkey) < 32) {
- $temp = pack('Na*', $sequence++, $this->password);
- $symkey.= pack('H*', sha1($temp));
- }
- $symkey = substr($symkey, 0, 32);
- $crypto = new AES();
-
- $crypto->setKey($symkey);
- $crypto->disablePadding();
- $private = $crypto->encrypt($private);
- $hashkey = 'putty-private-key-file-mac-key' . $this->password;
- }
-
- $private = base64_encode($private);
- $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n";
- $key.= chunk_split($private, 64);
- $hash = new Hash('sha1');
- $hash->setKey(pack('H*', sha1($hashkey)));
- $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
-
- return $key;
- case self::PRIVATE_FORMAT_OPENSSH:
- if ($num_primes != 2) {
- return false;
- }
- $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']);
- $privateKey = pack(
- 'Na*Na*Na*Na*Na*Na*Na*',
- strlen('ssh-rsa'),
- 'ssh-rsa',
- strlen($raw['modulus']),
- $raw['modulus'],
- strlen($raw['publicExponent']),
- $raw['publicExponent'],
- strlen($raw['privateExponent']),
- $raw['privateExponent'],
- strlen($raw['coefficient']),
- $raw['coefficient'],
- strlen($raw['prime1']),
- $raw['prime1'],
- strlen($raw['prime2']),
- $raw['prime2']
- );
- $checkint = Random::string(4);
- $paddedKey = pack(
- 'a*Na*',
- $checkint . $checkint . $privateKey,
- strlen($this->comment),
- $this->comment
- );
- $paddingLength = (7 * strlen($paddedKey)) % 8;
- for ($i = 1; $i <= $paddingLength; $i++) {
- $paddedKey.= chr($i);
- }
- $key = pack(
- 'Na*Na*Na*NNa*Na*',
- strlen('none'),
- 'none',
- strlen('none'),
- 'none',
- 0,
- '',
- 1,
- strlen($publicKey),
- $publicKey,
- strlen($paddedKey),
- $paddedKey
- );
- $key = "openssh-key-v1\0$key";
-
- return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
- chunk_split(base64_encode($key), 70, "\n") .
- "-----END OPENSSH PRIVATE KEY-----\n";
- default: // eg. self::PRIVATE_FORMAT_PKCS1
- $components = array();
- foreach ($raw as $name => $value) {
- $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
- }
-
- $RSAPrivateKey = implode('', $components);
-
- if ($num_primes > 2) {
- $OtherPrimeInfos = '';
- for ($i = 3; $i <= $num_primes; $i++) {
- // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
- //
- // OtherPrimeInfo ::= SEQUENCE {
- // prime INTEGER, -- ri
- // exponent INTEGER, -- di
- // coefficient INTEGER -- ti
- // }
- $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
- $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
- $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
- $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
- }
- $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
- }
-
- $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
-
- if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) {
- $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
- $RSAPrivateKey = pack(
- 'Ca*a*Ca*a*',
- self::ASN1_INTEGER,
- "\01\00",
- $rsaOID,
- 4,
- $this->_encodeLength(strlen($RSAPrivateKey)),
- $RSAPrivateKey
- );
- $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
- if (!empty($this->password) || is_string($this->password)) {
- $salt = Random::string(8);
- $iterationCount = 2048;
-
- $crypto = new DES();
- $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
- $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey);
-
- $parameters = pack(
- 'Ca*a*Ca*N',
- self::ASN1_OCTETSTRING,
- $this->_encodeLength(strlen($salt)),
- $salt,
- self::ASN1_INTEGER,
- $this->_encodeLength(4),
- $iterationCount
- );
- $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03";
-
- $encryptionAlgorithm = pack(
- 'Ca*a*Ca*a*',
- self::ASN1_OBJECT,
- $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)),
- $pbeWithMD5AndDES_CBC,
- self::ASN1_SEQUENCE,
- $this->_encodeLength(strlen($parameters)),
- $parameters
- );
-
- $RSAPrivateKey = pack(
- 'Ca*a*Ca*a*',
- self::ASN1_SEQUENCE,
- $this->_encodeLength(strlen($encryptionAlgorithm)),
- $encryptionAlgorithm,
- self::ASN1_OCTETSTRING,
- $this->_encodeLength(strlen($RSAPrivateKey)),
- $RSAPrivateKey
- );
-
- $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
-
- $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" .
- chunk_split(base64_encode($RSAPrivateKey), 64) .
- '-----END ENCRYPTED PRIVATE KEY-----';
- } else {
- $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" .
- chunk_split(base64_encode($RSAPrivateKey), 64) .
- '-----END PRIVATE KEY-----';
- }
- return $RSAPrivateKey;
- }
-
- if (!empty($this->password) || is_string($this->password)) {
- $iv = Random::string(8);
- $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
- $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
- $des = new TripleDES();
- $des->setKey($symkey);
- $des->setIV($iv);
- $iv = strtoupper(bin2hex($iv));
- $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
- "Proc-Type: 4,ENCRYPTED\r\n" .
- "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
- "\r\n" .
- chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
- '-----END RSA PRIVATE KEY-----';
- } else {
- $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
- chunk_split(base64_encode($RSAPrivateKey), 64) .
- '-----END RSA PRIVATE KEY-----';
- }
+ /*
+ $publickey = new PublicKey;
+ $publickey->modulus = $n;
+ $publickey->k = $bits >> 3;
+ $publickey->exponent = $e;
+ $publickey->publicExponent = $e;
+ $publickey->isPublic = true;
+ */
- return $RSAPrivateKey;
- }
+ return $privatekey;
}
/**
- * Convert a public key to the appropriate format
+ * OnLoad Handler
*
- * @access private
- * @see self::setPublicKeyFormat()
- * @param Math_BigInteger $n
- * @param Math_BigInteger $e
- * @return string|array<string,Math_BigInteger>
- */
- function _convertPublicKey($n, $e)
- {
- $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
-
- $modulus = $n->toBytes($signed);
- $publicExponent = $e->toBytes($signed);
-
- switch ($this->publicKeyFormat) {
- case self::PUBLIC_FORMAT_RAW:
- return array('e' => $e->copy(), 'n' => $n->copy());
- case self::PUBLIC_FORMAT_XML:
- return "<RSAKeyValue>\r\n" .
- ' <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
- ' <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
- '</RSAKeyValue>';
- break;
- case self::PUBLIC_FORMAT_OPENSSH:
- // from <http://tools.ietf.org/html/rfc4253#page-15>:
- // string "ssh-rsa"
- // mpint e
- // mpint n
- $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
- $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
-
- return $RSAPublicKey;
- default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1
- // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
- // RSAPublicKey ::= SEQUENCE {
- // modulus INTEGER, -- n
- // publicExponent INTEGER -- e
- // }
- $components = array(
- 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
- 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
- );
-
- $RSAPublicKey = pack(
- 'Ca*a*a*',
- self::ASN1_SEQUENCE,
- $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
- $components['modulus'],
- $components['publicExponent']
- );
-
- if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) {
- $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" .
- chunk_split(base64_encode($RSAPublicKey), 64) .
- '-----END RSA PUBLIC KEY-----';
- } else {
- // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
- $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
- $RSAPublicKey = chr(0) . $RSAPublicKey;
- $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
-
- $RSAPublicKey = pack(
- 'Ca*a*',
- self::ASN1_SEQUENCE,
- $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)),
- $rsaOID . $RSAPublicKey
- );
-
- $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
- chunk_split(base64_encode($RSAPublicKey), 64) .
- '-----END PUBLIC KEY-----';
- }
-
- return $RSAPublicKey;
- }
- }
-
- /**
- * Break a public or private key down into its constituant components
- *
- * @access private
- * @see self::_convertPublicKey()
- * @see self::_convertPrivateKey()
- * @param string|array $key
- * @param int $type
- * @return array|bool
- */
- function _parseKey($key, $type)
- {
- if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
- return false;
- }
-
- switch ($type) {
- case self::PUBLIC_FORMAT_RAW:
- if (!is_array($key)) {
- return false;
- }
- $components = array();
- switch (true) {
- case isset($key['e']):
- $components['publicExponent'] = $key['e']->copy();
- break;
- case isset($key['exponent']):
- $components['publicExponent'] = $key['exponent']->copy();
- break;
- case isset($key['publicExponent']):
- $components['publicExponent'] = $key['publicExponent']->copy();
- break;
- case isset($key[0]):
- $components['publicExponent'] = $key[0]->copy();
- }
- switch (true) {
- case isset($key['n']):
- $components['modulus'] = $key['n']->copy();
- break;
- case isset($key['modulo']):
- $components['modulus'] = $key['modulo']->copy();
- break;
- case isset($key['modulus']):
- $components['modulus'] = $key['modulus']->copy();
- break;
- case isset($key[1]):
- $components['modulus'] = $key[1]->copy();
- }
- return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
- case self::PRIVATE_FORMAT_PKCS1:
- case self::PRIVATE_FORMAT_PKCS8:
- case self::PUBLIC_FORMAT_PKCS1:
- /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
- "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
- protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
- two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
-
- http://tools.ietf.org/html/rfc1421#section-4.6.1.1
- http://tools.ietf.org/html/rfc1421#section-4.6.1.3
-
- DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
- DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
- function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
- own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
- implementation are part of the standard, as well.
-
- * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
- if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
- $iv = pack('H*', trim($matches[2]));
- $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
- $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
- // remove the Proc-Type / DEK-Info sections as they're no longer needed
- $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key);
- $ciphertext = $this->_extractBER($key);
- if ($ciphertext === false) {
- $ciphertext = $key;
- }
- switch ($matches[1]) {
- case 'AES-256-CBC':
- $crypto = new AES();
- break;
- case 'AES-128-CBC':
- $symkey = substr($symkey, 0, 16);
- $crypto = new AES();
- break;
- case 'DES-EDE3-CFB':
- $crypto = new TripleDES(Base::MODE_CFB);
- break;
- case 'DES-EDE3-CBC':
- $symkey = substr($symkey, 0, 24);
- $crypto = new TripleDES();
- break;
- case 'DES-CBC':
- $crypto = new DES();
- break;
- default:
- return false;
- }
- $crypto->setKey($symkey);
- $crypto->setIV($iv);
- $decoded = $crypto->decrypt($ciphertext);
- } else {
- $decoded = $this->_extractBER($key);
- }
-
- if ($decoded !== false) {
- $key = $decoded;
- }
-
- $components = array();
-
- if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
- return false;
- }
- if ($this->_decodeLength($key) != strlen($key)) {
- return false;
- }
-
- $tag = ord($this->_string_shift($key));
- /* intended for keys for which OpenSSL's asn1parse returns the following:
-
- 0:d=0 hl=4 l= 631 cons: SEQUENCE
- 4:d=1 hl=2 l= 1 prim: INTEGER :00
- 7:d=1 hl=2 l= 13 cons: SEQUENCE
- 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
- 20:d=2 hl=2 l= 0 prim: NULL
- 22:d=1 hl=4 l= 609 prim: OCTET STRING
-
- ie. PKCS8 keys*/
-
- if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
- $this->_string_shift($key, 3);
- $tag = self::ASN1_SEQUENCE;
- }
-
- if ($tag == self::ASN1_SEQUENCE) {
- $temp = $this->_string_shift($key, $this->_decodeLength($key));
- if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) {
- return false;
- }
- $length = $this->_decodeLength($temp);
- switch ($this->_string_shift($temp, $length)) {
- case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
- case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
- break;
- case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
- /*
- PBEParameter ::= SEQUENCE {
- salt OCTET STRING (SIZE(8)),
- iterationCount INTEGER }
- */
- if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) {
- return false;
- }
- if ($this->_decodeLength($temp) != strlen($temp)) {
- return false;
- }
- $this->_string_shift($temp); // assume it's an octet string
- $salt = $this->_string_shift($temp, $this->_decodeLength($temp));
- if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) {
- return false;
- }
- $this->_decodeLength($temp);
- list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT));
- $this->_string_shift($key); // assume it's an octet string
- $length = $this->_decodeLength($key);
- if (strlen($key) != $length) {
- return false;
- }
-
- $crypto = new DES();
- $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount);
- $key = $crypto->decrypt($key);
- if ($key === false) {
- return false;
- }
- return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1);
- default:
- return false;
- }
- /* intended for keys for which OpenSSL's asn1parse returns the following:
-
- 0:d=0 hl=4 l= 290 cons: SEQUENCE
- 4:d=1 hl=2 l= 13 cons: SEQUENCE
- 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
- 17:d=2 hl=2 l= 0 prim: NULL
- 19:d=1 hl=4 l= 271 prim: BIT STRING */
- $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
- $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
- // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
- // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
- // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
- if ($tag == self::ASN1_BITSTRING) {
- $this->_string_shift($key);
- }
- if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
- return false;
- }
- if ($this->_decodeLength($key) != strlen($key)) {
- return false;
- }
- $tag = ord($this->_string_shift($key));
- }
- if ($tag != self::ASN1_INTEGER) {
- return false;
- }
-
- $length = $this->_decodeLength($key);
- $temp = $this->_string_shift($key, $length);
- if (strlen($temp) != 1 || ord($temp) > 2) {
- $components['modulus'] = new BigInteger($temp, 256);
- $this->_string_shift($key); // skip over self::ASN1_INTEGER
- $length = $this->_decodeLength($key);
- $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
-
- return $components;
- }
- if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) {
- return false;
- }
- $length = $this->_decodeLength($key);
- $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256));
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256));
-
- if (!empty($key)) {
- if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
- return false;
- }
- $this->_decodeLength($key);
- while (!empty($key)) {
- if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) {
- return false;
- }
- $this->_decodeLength($key);
- $key = substr($key, 1);
- $length = $this->_decodeLength($key);
- $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256);
- }
- }
-
- return $components;
- case self::PUBLIC_FORMAT_OPENSSH:
- $parts = explode(' ', $key, 3);
-
- $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
- if ($key === false) {
- return false;
- }
-
- $comment = isset($parts[2]) ? $parts[2] : false;
-
- $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
-
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $publicExponent = new BigInteger($this->_string_shift($key, $length), -256);
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $modulus = new BigInteger($this->_string_shift($key, $length), -256);
-
- if ($cleanup && strlen($key)) {
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $realModulus = new BigInteger($this->_string_shift($key, $length), -256);
- return strlen($key) ? false : array(
- 'modulus' => $realModulus,
- 'publicExponent' => $modulus,
- 'comment' => $comment
- );
- } else {
- return strlen($key) ? false : array(
- 'modulus' => $modulus,
- 'publicExponent' => $publicExponent,
- 'comment' => $comment
- );
- }
- // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
- // http://en.wikipedia.org/wiki/XML_Signature
- case self::PRIVATE_FORMAT_XML:
- case self::PUBLIC_FORMAT_XML:
- if (!extension_loaded('xml')) {
- return false;
- }
-
- $this->components = array();
-
- $xml = xml_parser_create('UTF-8');
- if (version_compare(PHP_VERSION, '8.4.0', '>=')) {
- xml_set_element_handler($xml, array($this, '_start_element_handler'), array($this, '_stop_element_handler'));
- xml_set_character_data_handler($xml, array($this, '_data_handler'));
- } else {
- xml_set_object($xml, $this);
- xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
- xml_set_character_data_handler($xml, '_data_handler');
- }
- // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
- if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
- xml_parser_free($xml);
- unset($xml);
- return false;
- }
-
- xml_parser_free($xml);
- unset($xml);
-
- return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
- // see PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
- case self::PRIVATE_FORMAT_PUTTY:
- $components = array();
- $key = preg_split('#\r\n|\r|\n#', $key);
- if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
- return false;
- }
- $version = (int) $this->_string_shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
- if ($version != 2 && $version != 3) {
- return false;
- }
- $type = rtrim($key[0]);
- if ($type != 'ssh-rsa') {
- return false;
- }
- $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
- $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
-
- $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
- $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
- $public = substr($public, 11);
- extract(unpack('Nlength', $this->_string_shift($public, 4)));
- $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256);
- extract(unpack('Nlength', $this->_string_shift($public, 4)));
- $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
-
- $offset = $publicLength + 4;
- switch ($encryption) {
- case 'aes256-cbc':
- $crypto = new AES();
- switch ($version) {
- case 3:
- if (!function_exists('sodium_crypto_pwhash')) {
- return false;
- }
- $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
- switch ($flavour) {
- case 'Argon2i':
- $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
- break;
- case 'Argon2id':
- $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
- break;
- default:
- return false;
- }
- $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
- $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
- $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
- $salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
-
- $length = 80; // keylen + ivlen + mac_keylen
- $temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour);
-
- $symkey = substr($temp, 0, 32);
- $symiv = substr($temp, 32, 16);
- break;
- case 2:
- $symkey = '';
- $sequence = 0;
- while (strlen($symkey) < 32) {
- $temp = pack('Na*', $sequence++, $this->password);
- $symkey.= pack('H*', sha1($temp));
- }
- $symkey = substr($symkey, 0, 32);
- $symiv = str_repeat("\0", 16);
- }
- }
-
- $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
- $private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));
-
- if ($encryption != 'none') {
- $crypto->setKey($symkey);
- $crypto->setIV($symiv);
- $crypto->disablePadding();
- $private = $crypto->decrypt($private);
- if ($private === false) {
- return false;
- }
- }
-
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256);
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256));
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256);
-
- $temp = $components['primes'][1]->subtract($this->one);
- $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
- $temp = $components['primes'][2]->subtract($this->one);
- $components['exponents'][] = $components['publicExponent']->modInverse($temp);
-
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
-
- return $components;
- case self::PRIVATE_FORMAT_OPENSSH:
- $components = array();
- $decoded = $this->_extractBER($key);
- $magic = $this->_string_shift($decoded, 15);
- if ($magic !== "openssh-key-v1\0") {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
- if (strlen($decoded) < $length) {
- return false;
- }
- $ciphername = $this->_string_shift($decoded, $length);
- extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
- if (strlen($decoded) < $length) {
- return false;
- }
- $kdfname = $this->_string_shift($decoded, $length);
- extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
- if (strlen($decoded) < $length) {
- return false;
- }
- $kdfoptions = $this->_string_shift($decoded, $length);
- extract(unpack('Nnumkeys', $this->_string_shift($decoded, 4)));
- if ($numkeys != 1 || ($ciphername != 'none' && $kdfname != 'bcrypt')) {
- return false;
- }
- switch ($ciphername) {
- case 'none':
- break;
- case 'aes256-ctr':
- extract(unpack('Nlength', $this->_string_shift($kdfoptions, 4)));
- if (strlen($kdfoptions) < $length) {
- return false;
- }
- $salt = $this->_string_shift($kdfoptions, $length);
- extract(unpack('Nrounds', $this->_string_shift($kdfoptions, 4)));
- $crypto = new AES(AES::MODE_CTR);
- $crypto->disablePadding();
- if (!$crypto->setPassword($this->password, 'bcrypt', $salt, $rounds, 32)) {
- return false;
- }
- break;
- default:
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
- if (strlen($decoded) < $length) {
- return false;
- }
- $publicKey = $this->_string_shift($decoded, $length);
- extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
- if (strlen($decoded) < $length) {
- return false;
- }
-
- if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
- return false;
- }
-
- $paddedKey = $this->_string_shift($decoded, $length);
- if (isset($crypto)) {
- $paddedKey = $crypto->decrypt($paddedKey);
- }
-
- $checkint1 = $this->_string_shift($paddedKey, 4);
- $checkint2 = $this->_string_shift($paddedKey, 4);
- if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
- return false;
- }
-
- if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
- return false;
- }
-
- $values = array(
- &$components['modulus'],
- &$components['publicExponent'],
- &$components['privateExponent'],
- &$components['coefficients'][2],
- &$components['primes'][1],
- &$components['primes'][2]
- );
-
- foreach ($values as &$value) {
- extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
- if (strlen($paddedKey) < $length) {
- return false;
- }
- $value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
- }
-
- extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
- if (strlen($paddedKey) < $length) {
- return false;
- }
- $components['comment'] = $this->_string_shift($decoded, $length);
-
- $temp = $components['primes'][1]->subtract($this->one);
- $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
- $temp = $components['primes'][2]->subtract($this->one);
- $components['exponents'][] = $components['publicExponent']->modInverse($temp);
-
- return $components;
- }
-
- return false;
- }
-
- /**
- * Returns the key size
- *
- * More specifically, this returns the size of the modulo in bits.
- *
- * @access public
- * @return int
- */
- function getSize()
- {
- return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
- }
-
- /**
- * Start Element Handler
- *
- * Called by xml_set_element_handler()
- *
- * @access private
- * @param resource $parser
- * @param string $name
- * @param array $attribs
- */
- function _start_element_handler($parser, $name, $attribs)
- {
- //$name = strtoupper($name);
- switch ($name) {
- case 'MODULUS':
- $this->current = &$this->components['modulus'];
- break;
- case 'EXPONENT':
- $this->current = &$this->components['publicExponent'];
- break;
- case 'P':
- $this->current = &$this->components['primes'][1];
- break;
- case 'Q':
- $this->current = &$this->components['primes'][2];
- break;
- case 'DP':
- $this->current = &$this->components['exponents'][1];
- break;
- case 'DQ':
- $this->current = &$this->components['exponents'][2];
- break;
- case 'INVERSEQ':
- $this->current = &$this->components['coefficients'][2];
- break;
- case 'D':
- $this->current = &$this->components['privateExponent'];
- }
- $this->current = '';
- }
-
- /**
- * Stop Element Handler
- *
- * Called by xml_set_element_handler()
- *
- * @access private
- * @param resource $parser
- * @param string $name
- */
- function _stop_element_handler($parser, $name)
- {
- if (isset($this->current)) {
- $this->current = new BigInteger(base64_decode($this->current), 256);
- unset($this->current);
- }
- }
-
- /**
- * Data Handler
- *
- * Called by xml_set_character_data_handler()
- *
- * @access private
- * @param resource $parser
- * @param string $data
- */
- function _data_handler($parser, $data)
- {
- if (!isset($this->current) || is_object($this->current)) {
- return;
- }
- $this->current.= trim($data);
- }
-
- /**
- * Loads a public or private key
- *
- * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
- *
- * @access public
- * @param string|RSA|array $key
- * @param bool|int $type optional
* @return bool
*/
- function loadKey($key, $type = false)
+ protected static function onLoad(array $components)
{
- if ($key instanceof RSA) {
- $this->privateKeyFormat = $key->privateKeyFormat;
- $this->publicKeyFormat = $key->publicKeyFormat;
- $this->k = $key->k;
- $this->hLen = $key->hLen;
- $this->sLen = $key->sLen;
- $this->mgfHLen = $key->mgfHLen;
- $this->encryptionMode = $key->encryptionMode;
- $this->signatureMode = $key->signatureMode;
- $this->password = $key->password;
- $this->configFile = $key->configFile;
- $this->comment = $key->comment;
-
- if (is_object($key->hash)) {
- $this->hash = new Hash($key->hash->getHash());
- }
- if (is_object($key->mgfHash)) {
- $this->mgfHash = new Hash($key->mgfHash->getHash());
- }
+ $key = $components['isPublicKey'] ?
+ new PublicKey() :
+ new PrivateKey();
- if (is_object($key->modulus)) {
- $this->modulus = $key->modulus->copy();
- }
- if (is_object($key->exponent)) {
- $this->exponent = $key->exponent->copy();
- }
- if (is_object($key->publicExponent)) {
- $this->publicExponent = $key->publicExponent->copy();
- }
-
- $this->primes = array();
- $this->exponents = array();
- $this->coefficients = array();
+ $key->modulus = $components['modulus'];
+ $key->publicExponent = $components['publicExponent'];
+ $key->k = $key->modulus->getLengthInBytes();
- foreach ($this->primes as $prime) {
- $this->primes[] = $prime->copy();
- }
- foreach ($this->exponents as $exponent) {
- $this->exponents[] = $exponent->copy();
- }
- foreach ($this->coefficients as $coefficient) {
- $this->coefficients[] = $coefficient->copy();
+ if ($components['isPublicKey'] || !isset($components['privateExponent'])) {
+ $key->exponent = $key->publicExponent;
+ } else {
+ $key->privateExponent = $components['privateExponent'];
+ $key->exponent = $key->privateExponent;
+ $key->primes = $components['primes'];
+ $key->exponents = $components['exponents'];
+ $key->coefficients = $components['coefficients'];
+ }
+
+ if ($components['format'] == PSS::class) {
+ // in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is
+ // explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib
+ // uses PSS padding by default. it assumes the more secure method by default and altho it provides
+ // for the less secure PKCS1 method you have to go out of your way to use it. this is consistent
+ // with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that
+ // not only does it defaults to the most secure methods - it doesn't even let you choose less
+ // secure methods
+ //$key = $key->withPadding(self::SIGNATURE_PSS);
+ if (isset($components['hash'])) {
+ $key = $key->withHash($components['hash']);
}
-
- return true;
- }
-
- if ($type === false) {
- $types = array(
- self::PUBLIC_FORMAT_RAW,
- self::PRIVATE_FORMAT_PKCS1,
- self::PRIVATE_FORMAT_XML,
- self::PRIVATE_FORMAT_PUTTY,
- self::PUBLIC_FORMAT_OPENSSH,
- self::PRIVATE_FORMAT_OPENSSH
- );
- foreach ($types as $type) {
- $components = $this->_parseKey($key, $type);
- if ($components !== false) {
- break;
- }
+ if (isset($components['MGFHash'])) {
+ $key = $key->withMGFHash($components['MGFHash']);
}
- } else {
- $components = $this->_parseKey($key, $type);
- }
-
- if ($components === false) {
- $this->comment = null;
- $this->modulus = null;
- $this->k = null;
- $this->exponent = null;
- $this->primes = null;
- $this->exponents = null;
- $this->coefficients = null;
- $this->publicExponent = null;
-
- return false;
- }
-
- if (isset($components['comment']) && $components['comment'] !== false) {
- $this->comment = $components['comment'];
- }
- $this->modulus = $components['modulus'];
- $this->k = strlen($this->modulus->toBytes());
- $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
- if (isset($components['primes'])) {
- $this->primes = $components['primes'];
- $this->exponents = $components['exponents'];
- $this->coefficients = $components['coefficients'];
- $this->publicExponent = $components['publicExponent'];
- } else {
- $this->primes = array();
- $this->exponents = array();
- $this->coefficients = array();
- $this->publicExponent = false;
- }
-
- switch ($type) {
- case self::PUBLIC_FORMAT_OPENSSH:
- case self::PUBLIC_FORMAT_RAW:
- $this->setPublicKey();
- break;
- case self::PRIVATE_FORMAT_PKCS1:
- switch (true) {
- case strpos($key, '-BEGIN PUBLIC KEY-') !== false:
- case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false:
- $this->setPublicKey();
- }
- }
-
- return true;
- }
-
- /**
- * Sets the password
- *
- * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
- * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
- *
- * @see self::createKey()
- * @see self::loadKey()
- * @access public
- * @param string $password
- */
- function setPassword($password = false)
- {
- $this->password = $password;
- }
-
- /**
- * Defines the public key
- *
- * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
- * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
- * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
- * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
- * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
- * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
- * public.
- *
- * Do note that when a new key is loaded the index will be cleared.
- *
- * Returns true on success, false on failure
- *
- * @see self::getPublicKey()
- * @access public
- * @param string $key optional
- * @param int $type optional
- * @return bool
- */
- function setPublicKey($key = false, $type = false)
- {
- // if a public key has already been loaded return false
- if (!empty($this->publicExponent)) {
- return false;
- }
-
- if ($key === false && !empty($this->modulus)) {
- $this->publicExponent = $this->exponent;
- return true;
- }
-
- if ($type === false) {
- $types = array(
- self::PUBLIC_FORMAT_RAW,
- self::PUBLIC_FORMAT_PKCS1,
- self::PUBLIC_FORMAT_XML,
- self::PUBLIC_FORMAT_OPENSSH
- );
- foreach ($types as $type) {
- $components = $this->_parseKey($key, $type);
- if ($components !== false) {
- break;
- }
+ if (isset($components['saltLength'])) {
+ $key = $key->withSaltLength($components['saltLength']);
}
- } else {
- $components = $this->_parseKey($key, $type);
- }
-
- if ($components === false) {
- return false;
- }
-
- if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
- $this->modulus = $components['modulus'];
- $this->exponent = $this->publicExponent = $components['publicExponent'];
- return true;
- }
-
- $this->publicExponent = $components['publicExponent'];
-
- return true;
- }
-
- /**
- * Defines the private key
- *
- * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
- * phpseclib to treat the key as a private key. This function will do that.
- *
- * Do note that when a new key is loaded the index will be cleared.
- *
- * Returns true on success, false on failure
- *
- * @see self::getPublicKey()
- * @access public
- * @param string $key optional
- * @param int $type optional
- * @return bool
- */
- function setPrivateKey($key = false, $type = false)
- {
- if ($key === false && !empty($this->publicExponent)) {
- $this->publicExponent = false;
- return true;
- }
-
- $rsa = new RSA();
- if (!$rsa->loadKey($key, $type)) {
- return false;
- }
- $rsa->publicExponent = false;
-
- // don't overwrite the old key if the new key is invalid
- $this->loadKey($rsa);
- return true;
- }
-
- /**
- * Returns the public key
- *
- * The public key is only returned under two circumstances - if the private key had the public key embedded within it
- * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
- * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
- *
- * @see self::getPublicKey()
- * @access public
- * @param int $type optional
- */
- function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
- {
- if (empty($this->modulus) || empty($this->publicExponent)) {
- return false;
- }
-
- $oldFormat = $this->publicKeyFormat;
- $this->publicKeyFormat = $type;
- $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
- $this->publicKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * Returns the public key's fingerprint
- *
- * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
- * no public key currently loaded, false is returned.
- * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
- *
- * @access public
- * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
- * for invalid values.
- * @return mixed
- */
- function getPublicKeyFingerprint($algorithm = 'md5')
- {
- if (empty($this->modulus) || empty($this->publicExponent)) {
- return false;
- }
-
- $modulus = $this->modulus->toBytes(true);
- $publicExponent = $this->publicExponent->toBytes(true);
-
- $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
-
- switch ($algorithm) {
- case 'sha256':
- $hash = new Hash('sha256');
- $base = base64_encode($hash->hash($RSAPublicKey));
- return substr($base, 0, strlen($base) - 1);
- case 'md5':
- return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1);
- default:
- return false;
- }
- }
-
- /**
- * Returns the private key
- *
- * The private key is only returned if the currently loaded key contains the constituent prime numbers.
- *
- * @see self::getPublicKey()
- * @access public
- * @param int $type optional
- * @return mixed
- */
- function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
- {
- if (empty($this->primes)) {
- return false;
- }
-
- $oldFormat = $this->privateKeyFormat;
- $this->privateKeyFormat = $type;
- $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
- $this->privateKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * Returns a minimalistic private key
- *
- * Returns the private key without the prime number constituants. Structurally identical to a public key that
- * hasn't been set as the public key
- *
- * @see self::getPrivateKey()
- * @access private
- * @param int $mode optional
- */
- function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
- {
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- $oldFormat = $this->publicKeyFormat;
- $this->publicKeyFormat = $mode;
- $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
- $this->publicKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * __toString() magic method
- *
- * @access public
- * @return string
- */
- function __toString()
- {
- $key = $this->getPrivateKey($this->privateKeyFormat);
- if ($key !== false) {
- return $key;
}
- $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
- return $key !== false ? $key : '';
- }
- /**
- * __clone() magic method
- *
- * @access public
- * @return Crypt_RSA
- */
- function __clone()
- {
- $key = new RSA();
- $key->loadKey($this);
return $key;
}
/**
- * Generates the smallest and largest numbers requiring $bits bits
- *
- * @access private
- * @param int $bits
- * @return array
+ * Initialize static variables
*/
- function _generateMinMax($bits)
+ protected static function initialize_static_variables()
{
- $bytes = $bits >> 3;
- $min = str_repeat(chr(0), $bytes);
- $max = str_repeat(chr(0xFF), $bytes);
- $msb = $bits & 7;
- if ($msb) {
- $min = chr(1 << ($msb - 1)) . $min;
- $max = chr((1 << $msb) - 1) . $max;
- } else {
- $min[0] = chr(0x80);
+ if (!isset(self::$configFile)) {
+ self::$configFile = dirname(__FILE__) . '/../openssl.cnf';
}
- return array(
- 'min' => new BigInteger($min, 256),
- 'max' => new BigInteger($max, 256)
- );
+ parent::initialize_static_variables();
}
/**
- * DER-decode the length
- *
- * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
- * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
+ * Constructor
*
- * @access private
- * @param string $string
- * @return int
+ * PublicKey and PrivateKey objects can only be created from abstract RSA class
*/
- function _decodeLength(&$string)
+ protected function __construct()
{
- $length = ord($this->_string_shift($string));
- if ($length & 0x80) { // definite length, long form
- $length&= 0x7F;
- $temp = $this->_string_shift($string, $length);
- list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
- }
- return $length;
- }
+ parent::__construct();
- /**
- * DER-encode the length
- *
- * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
- * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
- *
- * @access private
- * @param int $length
- * @return string
- */
- function _encodeLength($length)
- {
- if ($length <= 0x7F) {
- return chr($length);
- }
-
- $temp = ltrim(pack('N', $length), chr(0));
- return pack('Ca*', 0x80 | strlen($temp), $temp);
- }
-
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @return string
- * @access private
- */
- function _string_shift(&$string, $index = 1)
- {
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
- }
-
- /**
- * Determines the private key format
- *
- * @see self::createKey()
- * @access public
- * @param int $format
- */
- function setPrivateKeyFormat($format)
- {
- $this->privateKeyFormat = $format;
- }
-
- /**
- * Determines the public key format
- *
- * @see self::createKey()
- * @access public
- * @param int $format
- */
- function setPublicKeyFormat($format)
- {
- $this->publicKeyFormat = $format;
- }
-
- /**
- * Determines which hashing function should be used
- *
- * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
- * decryption. If $hash isn't supported, sha1 is used.
- *
- * @access public
- * @param string $hash
- */
- function setHash($hash)
- {
- // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
- switch ($hash) {
- case 'md2':
- case 'md5':
- case 'sha1':
- case 'sha256':
- case 'sha384':
- case 'sha512':
- $this->hash = new Hash($hash);
- $this->hashName = $hash;
- break;
- default:
- $this->hash = new Hash('sha1');
- $this->hashName = 'sha1';
- }
- $this->hLen = $this->hash->getLength();
- }
-
- /**
- * Determines which hashing function should be used for the mask generation function
- *
- * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
- * best if Hash and MGFHash are set to the same thing this is not a requirement.
- *
- * @access public
- * @param string $hash
- */
- function setMGFHash($hash)
- {
- // \phpseclib\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
- switch ($hash) {
- case 'md2':
- case 'md5':
- case 'sha1':
- case 'sha256':
- case 'sha384':
- case 'sha512':
- $this->mgfHash = new Hash($hash);
- break;
- default:
- $this->mgfHash = new Hash('sha1');
- }
- $this->mgfHLen = $this->mgfHash->getLength();
- }
-
- /**
- * Determines the salt length
- *
- * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
- *
- * Typical salt lengths in octets are hLen (the length of the output
- * of the hash function Hash) and 0.
- *
- * @access public
- * @param int $sLen
- */
- function setSaltLength($sLen)
- {
- $this->sLen = $sLen;
+ $this->hLen = $this->hash->getLengthInBytes();
+ $this->mgfHash = new Hash('sha256');
+ $this->mgfHLen = $this->mgfHash->getLengthInBytes();
}
/**
@@ -2280,17 +509,18 @@ class RSA
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
*
- * @access private
- * @param \phpseclib\Math\BigInteger $x
+ * @param bool|Math\BigInteger $x
* @param int $xLen
- * @return string
+ * @return bool|string
*/
- function _i2osp($x, $xLen)
+ protected function i2osp($x, $xLen)
{
+ if ($x === false) {
+ return false;
+ }
$x = $x->toBytes();
if (strlen($x) > $xLen) {
- user_error('Integer too large');
- return false;
+ throw new \OutOfRangeException('Resultant string length out of range');
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
@@ -2300,652 +530,12 @@ class RSA
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
*
- * @access private
- * @param int|string|resource $x
- * @return \phpseclib\Math\BigInteger
- */
- function _os2ip($x)
- {
- return new BigInteger($x, 256);
- }
-
- /**
- * Exponentiate with or without Chinese Remainder Theorem
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $x
- * @return \phpseclib\Math\BigInteger
- */
- function _exponentiate($x)
- {
- switch (true) {
- case empty($this->primes):
- case $this->primes[1]->equals($this->zero):
- case empty($this->coefficients):
- case $this->coefficients[2]->equals($this->zero):
- case empty($this->exponents):
- case $this->exponents[1]->equals($this->zero):
- return $x->modPow($this->exponent, $this->modulus);
- }
-
- $num_primes = count($this->primes);
-
- if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
- $m_i = array(
- 1 => $x->modPow($this->exponents[1], $this->primes[1]),
- 2 => $x->modPow($this->exponents[2], $this->primes[2])
- );
- $h = $m_i[1]->subtract($m_i[2]);
- $h = $h->multiply($this->coefficients[2]);
- list(, $h) = $h->divide($this->primes[1]);
- $m = $m_i[2]->add($h->multiply($this->primes[2]));
-
- $r = $this->primes[1];
- for ($i = 3; $i <= $num_primes; $i++) {
- $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
-
- $r = $r->multiply($this->primes[$i - 1]);
-
- $h = $m_i->subtract($m);
- $h = $h->multiply($this->coefficients[$i]);
- list(, $h) = $h->divide($this->primes[$i]);
-
- $m = $m->add($r->multiply($h));
- }
- } else {
- $smallest = $this->primes[1];
- for ($i = 2; $i <= $num_primes; $i++) {
- if ($smallest->compare($this->primes[$i]) > 0) {
- $smallest = $this->primes[$i];
- }
- }
-
- $one = new BigInteger(1);
-
- $r = $one->random($one, $smallest->subtract($one));
-
- $m_i = array(
- 1 => $this->_blind($x, $r, 1),
- 2 => $this->_blind($x, $r, 2)
- );
- $h = $m_i[1]->subtract($m_i[2]);
- $h = $h->multiply($this->coefficients[2]);
- list(, $h) = $h->divide($this->primes[1]);
- $m = $m_i[2]->add($h->multiply($this->primes[2]));
-
- $r = $this->primes[1];
- for ($i = 3; $i <= $num_primes; $i++) {
- $m_i = $this->_blind($x, $r, $i);
-
- $r = $r->multiply($this->primes[$i - 1]);
-
- $h = $m_i->subtract($m);
- $h = $h->multiply($this->coefficients[$i]);
- list(, $h) = $h->divide($this->primes[$i]);
-
- $m = $m->add($r->multiply($h));
- }
- }
-
- return $m;
- }
-
- /**
- * Performs RSA Blinding
- *
- * Protects against timing attacks by employing RSA Blinding.
- * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $x
- * @param \phpseclib\Math\BigInteger $r
- * @param int $i
- * @return \phpseclib\Math\BigInteger
- */
- function _blind($x, $r, $i)
- {
- $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
- $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
-
- $r = $r->modInverse($this->primes[$i]);
- $x = $x->multiply($r);
- list(, $x) = $x->divide($this->primes[$i]);
-
- return $x;
- }
-
- /**
- * Performs blinded RSA equality testing
- *
- * Protects against a particular type of timing attack described.
- *
- * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
- *
- * Thanks for the heads up singpolyma!
- *
- * @access private
* @param string $x
- * @param string $y
- * @return bool
- */
- function _equals($x, $y)
- {
- if (function_exists('hash_equals')) {
- return hash_equals($x, $y);
- }
-
- if (strlen($x) != strlen($y)) {
- return false;
- }
-
- $result = "\0";
- $x^= $y;
- for ($i = 0; $i < strlen($x); $i++) {
- $result|= $x[$i];
- }
-
- return $result === "\0";
- }
-
- /**
- * RSAEP
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $m
- * @return \phpseclib\Math\BigInteger
- */
- function _rsaep($m)
- {
- if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
- user_error('Message representative out of range');
- return false;
- }
- return $this->_exponentiate($m);
- }
-
- /**
- * RSADP
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $c
- * @return \phpseclib\Math\BigInteger
- */
- function _rsadp($c)
- {
- if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
- user_error('Ciphertext representative out of range');
- return false;
- }
- return $this->_exponentiate($c);
- }
-
- /**
- * RSASP1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $m
- * @return \phpseclib\Math\BigInteger
- */
- function _rsasp1($m)
- {
- if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
- user_error('Message representative out of range');
- return false;
- }
- return $this->_exponentiate($m);
- }
-
- /**
- * RSAVP1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
- *
- * @access private
- * @param \phpseclib\Math\BigInteger $s
- * @return \phpseclib\Math\BigInteger
- */
- function _rsavp1($s)
- {
- if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
- user_error('Signature representative out of range');
- return false;
- }
- return $this->_exponentiate($s);
- }
-
- /**
- * MGF1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
- *
- * @access private
- * @param string $mgfSeed
- * @param int $maskLen
- * @return string
- */
- function _mgf1($mgfSeed, $maskLen)
- {
- // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
-
- $t = '';
- $count = ceil($maskLen / $this->mgfHLen);
- for ($i = 0; $i < $count; $i++) {
- $c = pack('N', $i);
- $t.= $this->mgfHash->hash($mgfSeed . $c);
- }
-
- return substr($t, 0, $maskLen);
- }
-
- /**
- * RSAES-OAEP-ENCRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
- * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
- *
- * @access private
- * @param string $m
- * @param string $l
- * @return string
- */
- function _rsaes_oaep_encrypt($m, $l = '')
- {
- $mLen = strlen($m);
-
- // Length checking
-
- // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- if ($mLen > $this->k - 2 * $this->hLen - 2) {
- user_error('Message too long');
- return false;
- }
-
- // EME-OAEP encoding
-
- $lHash = $this->hash->hash($l);
- $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
- $db = $lHash . $ps . chr(1) . $m;
- $seed = Random::string($this->hLen);
- $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
- $maskedDB = $db ^ $dbMask;
- $seedMask = $this->_mgf1($maskedDB, $this->hLen);
- $maskedSeed = $seed ^ $seedMask;
- $em = chr(0) . $maskedSeed . $maskedDB;
-
- // RSA encryption
-
- $m = $this->_os2ip($em);
- $c = $this->_rsaep($m);
- $c = $this->_i2osp($c, $this->k);
-
- // Output the ciphertext C
-
- return $c;
- }
-
- /**
- * RSAES-OAEP-DECRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
- * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
- *
- * Note. Care must be taken to ensure that an opponent cannot
- * distinguish the different error conditions in Step 3.g, whether by
- * error message or timing, or, more generally, learn partial
- * information about the encoded message EM. Otherwise an opponent may
- * be able to obtain useful information about the decryption of the
- * ciphertext C, leading to a chosen-ciphertext attack such as the one
- * observed by Manger [36].
- *
- * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
- *
- * Both the encryption and the decryption operations of RSAES-OAEP take
- * the value of a label L as input. In this version of PKCS #1, L is
- * the empty string; other uses of the label are outside the scope of
- * this document.
- *
- * @access private
- * @param string $c
- * @param string $l
- * @return string
- */
- function _rsaes_oaep_decrypt($c, $l = '')
- {
- // Length checking
-
- // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
- user_error('Decryption error');
- return false;
- }
-
- // RSA decryption
-
- $c = $this->_os2ip($c);
- $m = $this->_rsadp($c);
- if ($m === false) {
- user_error('Decryption error');
- return false;
- }
- $em = $this->_i2osp($m, $this->k);
-
- // EME-OAEP decoding
-
- $lHash = $this->hash->hash($l);
- $y = ord($em[0]);
- $maskedSeed = substr($em, 1, $this->hLen);
- $maskedDB = substr($em, $this->hLen + 1);
- $seedMask = $this->_mgf1($maskedDB, $this->hLen);
- $seed = $maskedSeed ^ $seedMask;
- $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
- $db = $maskedDB ^ $dbMask;
- $lHash2 = substr($db, 0, $this->hLen);
- $m = substr($db, $this->hLen);
- $hashesMatch = $this->_equals($lHash, $lHash2);
- $leadingZeros = 1;
- $patternMatch = 0;
- $offset = 0;
- for ($i = 0; $i < strlen($m); $i++) {
- $patternMatch|= $leadingZeros & ($m[$i] === "\1");
- $leadingZeros&= $m[$i] === "\0";
- $offset+= $patternMatch ? 0 : 1;
- }
-
- // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
- // to protect against timing attacks
- if (!$hashesMatch | !$patternMatch) {
- user_error('Decryption error');
- return false;
- }
-
- // Output the message M
-
- return substr($m, $offset + 1);
- }
-
- /**
- * Raw Encryption / Decryption
- *
- * Doesn't use padding and is not recommended.
- *
- * @access private
- * @param string $m
- * @return string
- */
- function _raw_encrypt($m)
- {
- $temp = $this->_os2ip($m);
- $temp = $this->_rsaep($temp);
- return $this->_i2osp($temp, $this->k);
- }
-
- /**
- * RSAES-PKCS1-V1_5-ENCRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
- *
- * @access private
- * @param string $m
- * @return string
- */
- function _rsaes_pkcs1_v1_5_encrypt($m)
- {
- $mLen = strlen($m);
-
- // Length checking
-
- if ($mLen > $this->k - 11) {
- user_error('Message too long');
- return false;
- }
-
- // EME-PKCS1-v1_5 encoding
-
- $psLen = $this->k - $mLen - 3;
- $ps = '';
- while (strlen($ps) != $psLen) {
- $temp = Random::string($psLen - strlen($ps));
- $temp = str_replace("\x00", '', $temp);
- $ps.= $temp;
- }
- $type = 2;
- // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
- if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
- $type = 1;
- // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
- $ps = str_repeat("\xFF", $psLen);
- }
- $em = chr(0) . chr($type) . $ps . chr(0) . $m;
-
- // RSA encryption
- $m = $this->_os2ip($em);
- $c = $this->_rsaep($m);
- $c = $this->_i2osp($c, $this->k);
-
- // Output the ciphertext C
-
- return $c;
- }
-
- /**
- * RSAES-PKCS1-V1_5-DECRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
- *
- * For compatibility purposes, this function departs slightly from the description given in RFC3447.
- * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
- * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
- * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
- * to be 2 regardless of which key is used. For compatibility purposes, we'll just check to make sure the
- * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
- *
- * As a consequence of this, a private key encrypted ciphertext produced with \phpseclib\Crypt\RSA may not decrypt
- * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
- * not private key encrypted ciphertext's.
- *
- * @access private
- * @param string $c
- * @return string
- */
- function _rsaes_pkcs1_v1_5_decrypt($c)
- {
- // Length checking
-
- if (strlen($c) != $this->k) { // or if k < 11
- user_error('Decryption error');
- return false;
- }
-
- // RSA decryption
-
- $c = $this->_os2ip($c);
- $m = $this->_rsadp($c);
-
- if ($m === false) {
- user_error('Decryption error');
- return false;
- }
- $em = $this->_i2osp($m, $this->k);
-
- // EME-PKCS1-v1_5 decoding
-
- if (ord($em[0]) != 0 || ord($em[1]) > 2) {
- user_error('Decryption error');
- return false;
- }
-
- $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
- $m = substr($em, strlen($ps) + 3);
-
- if (strlen($ps) < 8) {
- user_error('Decryption error');
- return false;
- }
-
- // Output M
-
- return $m;
- }
-
- /**
- * EMSA-PSS-ENCODE
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
- *
- * @access private
- * @param string $m
- * @param int $emBits
- */
- function _emsa_pss_encode($m, $emBits)
- {
- // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
- $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
-
- $mHash = $this->hash->hash($m);
- if ($emLen < $this->hLen + $sLen + 2) {
- user_error('Encoding error');
- return false;
- }
-
- $salt = Random::string($sLen);
- $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
- $h = $this->hash->hash($m2);
- $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
- $db = $ps . chr(1) . $salt;
- $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
- $maskedDB = $db ^ $dbMask;
- $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
- $em = $maskedDB . $h . chr(0xBC);
-
- return $em;
- }
-
- /**
- * EMSA-PSS-VERIFY
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
- *
- * @access private
- * @param string $m
- * @param string $em
- * @param int $emBits
- * @return string
+ * @return Math\BigInteger
*/
- function _emsa_pss_verify($m, $em, $emBits)
+ protected function os2ip($x)
{
- // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
- $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
-
- $mHash = $this->hash->hash($m);
- if ($emLen < $this->hLen + $sLen + 2) {
- return false;
- }
-
- if ($em[strlen($em) - 1] != chr(0xBC)) {
- return false;
- }
-
- $maskedDB = substr($em, 0, -$this->hLen - 1);
- $h = substr($em, -$this->hLen - 1, $this->hLen);
- $temp = chr(0xFF << ($emBits & 7));
- if ((~$maskedDB[0] & $temp) != $temp) {
- return false;
- }
- $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
- $db = $maskedDB ^ $dbMask;
- $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
- $temp = $emLen - $this->hLen - $sLen - 2;
- if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
- return false;
- }
- $salt = substr($db, $temp + 1); // should be $sLen long
- $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
- $h2 = $this->hash->hash($m2);
- return $this->_equals($h, $h2);
- }
-
- /**
- * RSASSA-PSS-SIGN
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
- *
- * @access private
- * @param string $m
- * @return string
- */
- function _rsassa_pss_sign($m)
- {
- // EMSA-PSS encoding
-
- $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
-
- // RSA signature
-
- $m = $this->_os2ip($em);
- $s = $this->_rsasp1($m);
- $s = $this->_i2osp($s, $this->k);
-
- // Output the signature S
-
- return $s;
- }
-
- /**
- * RSASSA-PSS-VERIFY
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
- *
- * @access private
- * @param string $m
- * @param string $s
- * @return string
- */
- function _rsassa_pss_verify($m, $s)
- {
- // Length checking
-
- if (strlen($s) != $this->k) {
- user_error('Invalid signature');
- return false;
- }
-
- // RSA verification
-
- $modBits = strlen($this->modulus->toBits());
-
- $s2 = $this->_os2ip($s);
- $m2 = $this->_rsavp1($s2);
- if ($m2 === false) {
- user_error('Invalid signature');
- return false;
- }
- $em = $this->_i2osp($m2, $this->k);
- if ($em === false) {
- user_error('Invalid signature');
- return false;
- }
-
- // EMSA-PSS verification
-
- return $this->_emsa_pss_verify($m, $em, $modBits - 1);
+ return new BigInteger($x, 256);
}
/**
@@ -2953,44 +543,50 @@ class RSA
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
*
- * @access private
* @param string $m
* @param int $emLen
+ * @throws \LengthException if the intended encoded message length is too short
* @return string
*/
- function _emsa_pkcs1_v1_5_encode($m, $emLen)
+ protected function emsa_pkcs1_v1_5_encode($m, $emLen)
{
$h = $this->hash->hash($m);
- if ($h === false) {
- return false;
- }
// see http://tools.ietf.org/html/rfc3447#page-43
- switch ($this->hashName) {
+ switch ($this->hash->getHash()) {
case 'md2':
- $t = pack('H*', '3020300c06082a864886f70d020205000410');
+ $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10";
break;
case 'md5':
- $t = pack('H*', '3020300c06082a864886f70d020505000410');
+ $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10";
break;
case 'sha1':
- $t = pack('H*', '3021300906052b0e03021a05000414');
+ $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14";
break;
case 'sha256':
- $t = pack('H*', '3031300d060960864801650304020105000420');
+ $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
break;
case 'sha384':
- $t = pack('H*', '3041300d060960864801650304020205000430');
+ $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30";
break;
case 'sha512':
- $t = pack('H*', '3051300d060960864801650304020305000440');
+ $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40";
+ break;
+ // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
+ case 'sha224':
+ $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c";
+ break;
+ case 'sha512/224':
+ $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c";
+ break;
+ case 'sha512/256':
+ $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20";
}
- $t.= $h;
+ $t .= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
- user_error('Intended encoded message length too short');
- return false;
+ throw new \LengthException('Intended encoded message length too short');
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
@@ -3010,40 +606,46 @@ class RSA
* generally be omitted, but if present, it shall have a value of type
* NULL"
*
- * @access private
* @param string $m
* @param int $emLen
* @return string
*/
- function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
+ protected function emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
{
$h = $this->hash->hash($m);
- if ($h === false) {
- return false;
- }
- switch ($this->hashName) {
+ // see http://tools.ietf.org/html/rfc3447#page-43
+ switch ($this->hash->getHash()) {
case 'sha1':
- $t = pack('H*', '301f300706052b0e03021a0414');
+ $t = "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14";
break;
case 'sha256':
- $t = pack('H*', '302f300b06096086480165030402010420');
+ $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20";
break;
case 'sha384':
- $t = pack('H*', '303f300b06096086480165030402020430');
+ $t = "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30";
break;
case 'sha512':
- $t = pack('H*', '304f300b06096086480165030402030440');
+ $t = "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40";
+ break;
+ // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
+ case 'sha224':
+ $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c";
+ break;
+ case 'sha512/224':
+ $t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c";
+ break;
+ case 'sha512/256':
+ $t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20";
break;
default:
- return false;
+ throw new UnsupportedAlgorithmException('md2 and md5 require NULLs');
}
- $t.= $h;
+ $t .= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
- user_error('Intended encoded message length too short');
- return false;
+ throw new \LengthException('Intended encoded message length too short');
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
@@ -3054,295 +656,278 @@ class RSA
}
/**
- * RSASSA-PKCS1-V1_5-SIGN
+ * MGF1
*
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
+ * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
*
- * @access private
- * @param string $m
+ * @param string $mgfSeed
+ * @param int $maskLen
* @return string
*/
- function _rsassa_pkcs1_v1_5_sign($m)
+ protected function mgf1($mgfSeed, $maskLen)
{
- // EMSA-PKCS1-v1_5 encoding
+ // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
- $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
- if ($em === false) {
- user_error('RSA modulus too short');
- return false;
+ $t = '';
+ $count = ceil($maskLen / $this->mgfHLen);
+ for ($i = 0; $i < $count; $i++) {
+ $c = pack('N', $i);
+ $t .= $this->mgfHash->hash($mgfSeed . $c);
}
- // RSA signature
-
- $m = $this->_os2ip($em);
- $s = $this->_rsasp1($m);
- $s = $this->_i2osp($s, $this->k);
-
- // Output the signature S
-
- return $s;
+ return substr($t, 0, $maskLen);
}
/**
- * RSASSA-PKCS1-V1_5-VERIFY
+ * Returns the key size
*
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
+ * More specifically, this returns the size of the modulo in bits.
*
- * @access private
- * @param string $m
- * @param string $s
- * @return string
+ * @return int
*/
- function _rsassa_pkcs1_v1_5_verify($m, $s)
+ public function getLength()
{
- // Length checking
-
- if (strlen($s) != $this->k) {
- user_error('Invalid signature');
- return false;
- }
+ return !isset($this->modulus) ? 0 : $this->modulus->getLength();
+ }
- // RSA verification
+ /**
+ * Determines which hashing function should be used
+ *
+ * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and
+ * decryption.
+ *
+ * @param string $hash
+ */
+ public function withHash($hash)
+ {
+ $new = clone $this;
- $s = $this->_os2ip($s);
- $m2 = $this->_rsavp1($s);
- if ($m2 === false) {
- user_error('Invalid signature');
- return false;
- }
- $em = $this->_i2osp($m2, $this->k);
- if ($em === false) {
- user_error('Invalid signature');
- return false;
+ // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
+ switch (strtolower($hash)) {
+ case 'md2':
+ case 'md5':
+ case 'sha1':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ case 'sha224':
+ case 'sha512/224':
+ case 'sha512/256':
+ $new->hash = new Hash($hash);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException(
+ 'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
+ );
}
+ $new->hLen = $new->hash->getLengthInBytes();
- // EMSA-PKCS1-v1_5 encoding
+ return $new;
+ }
- $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
- $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
+ /**
+ * Determines which hashing function should be used for the mask generation function
+ *
+ * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's
+ * best if Hash and MGFHash are set to the same thing this is not a requirement.
+ *
+ * @param string $hash
+ */
+ public function withMGFHash($hash)
+ {
+ $new = clone $this;
- if ($em2 === false && $em3 === false) {
- user_error('RSA modulus too short');
- return false;
+ // Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
+ switch (strtolower($hash)) {
+ case 'md2':
+ case 'md5':
+ case 'sha1':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ case 'sha224':
+ case 'sha512/224':
+ case 'sha512/256':
+ $new->mgfHash = new Hash($hash);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException(
+ 'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
+ );
}
+ $new->mgfHLen = $new->mgfHash->getLengthInBytes();
- // Compare
-
- return ($em2 !== false && $this->_equals($em, $em2)) ||
- ($em3 !== false && $this->_equals($em, $em3));
+ return $new;
}
/**
- * Set Encryption Mode
+ * Returns the MGF hash algorithm currently being used
*
- * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
- *
- * @access public
- * @param int $mode
*/
- function setEncryptionMode($mode)
+ public function getMGFHash()
{
- $this->encryptionMode = $mode;
+ return clone $this->mgfHash;
}
/**
- * Set Signature Mode
+ * Determines the salt length
+ *
+ * Used by RSA::PADDING_PSS
+ *
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
*
- * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
+ * Typical salt lengths in octets are hLen (the length of the output
+ * of the hash function Hash) and 0.
*
- * @access public
- * @param int $mode
+ * @param int $sLen
*/
- function setSignatureMode($mode)
+ public function withSaltLength($sLen)
{
- $this->signatureMode = $mode;
+ $new = clone $this;
+ $new->sLen = $sLen;
+ return $new;
}
/**
- * Set public key comment.
+ * Returns the salt length currently being used
*
- * @access public
- * @param string $comment
*/
- function setComment($comment)
+ public function getSaltLength()
{
- $this->comment = $comment;
+ return $this->sLen !== null ? $this->sLen : $this->hLen;
}
/**
- * Get public key comment.
+ * Determines the label
*
- * @access public
- * @return string
+ * Used by RSA::PADDING_OAEP
+ *
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
+ *
+ * Both the encryption and the decryption operations of RSAES-OAEP take
+ * the value of a label L as input. In this version of PKCS #1, L is
+ * the empty string; other uses of the label are outside the scope of
+ * this document.
+ *
+ * @param string $label
*/
- function getComment()
+ public function withLabel($label)
{
- return $this->comment;
+ $new = clone $this;
+ $new->label = $label;
+ return $new;
}
/**
- * Encryption
+ * Returns the label currently being used
*
- * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
- * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
- * be concatenated together.
- *
- * @see self::decrypt()
- * @access public
- * @param string $plaintext
- * @return string
*/
- function encrypt($plaintext)
+ public function getLabel()
{
- switch ($this->encryptionMode) {
- case self::ENCRYPTION_NONE:
- $plaintext = str_split($plaintext, $this->k);
- $ciphertext = '';
- foreach ($plaintext as $m) {
- $ciphertext.= $this->_raw_encrypt($m);
- }
- return $ciphertext;
- case self::ENCRYPTION_PKCS1:
- $length = $this->k - 11;
- if ($length <= 0) {
- return false;
- }
-
- $plaintext = str_split($plaintext, $length);
- $ciphertext = '';
- foreach ($plaintext as $m) {
- $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
- }
- return $ciphertext;
- //case self::ENCRYPTION_OAEP:
- default:
- $length = $this->k - 2 * $this->hLen - 2;
- if ($length <= 0) {
- return false;
- }
-
- $plaintext = str_split($plaintext, $length);
- $ciphertext = '';
- foreach ($plaintext as $m) {
- $ciphertext.= $this->_rsaes_oaep_encrypt($m);
- }
- return $ciphertext;
- }
+ return $this->label;
}
/**
- * Decryption
+ * Determines the padding modes
*
- * @see self::encrypt()
- * @access public
- * @param string $ciphertext
- * @return string
+ * Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1);
+ *
+ * @param int $padding
*/
- function decrypt($ciphertext)
+ public function withPadding($padding)
{
- if ($this->k <= 0) {
- return false;
+ $masks = [
+ self::ENCRYPTION_OAEP,
+ self::ENCRYPTION_PKCS1,
+ self::ENCRYPTION_NONE
+ ];
+ $encryptedCount = 0;
+ $selected = 0;
+ foreach ($masks as $mask) {
+ if ($padding & $mask) {
+ $selected = $mask;
+ $encryptedCount++;
+ }
}
-
- $ciphertext = str_split($ciphertext, $this->k);
- $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
-
- $plaintext = '';
-
- switch ($this->encryptionMode) {
- case self::ENCRYPTION_NONE:
- $decrypt = '_raw_encrypt';
- break;
- case self::ENCRYPTION_PKCS1:
- $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
- break;
- //case self::ENCRYPTION_OAEP:
- default:
- $decrypt = '_rsaes_oaep_decrypt';
+ if ($encryptedCount > 1) {
+ throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected');
+ }
+ $encryptionPadding = $selected;
+
+ $masks = [
+ self::SIGNATURE_PSS,
+ self::SIGNATURE_RELAXED_PKCS1,
+ self::SIGNATURE_PKCS1
+ ];
+ $signatureCount = 0;
+ $selected = 0;
+ foreach ($masks as $mask) {
+ if ($padding & $mask) {
+ $selected = $mask;
+ $signatureCount++;
+ }
+ }
+ if ($signatureCount > 1) {
+ throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected');
}
+ $signaturePadding = $selected;
- foreach ($ciphertext as $c) {
- $temp = $this->$decrypt($c);
- if ($temp === false) {
- return false;
- }
- $plaintext.= $temp;
+ $new = clone $this;
+ if ($encryptedCount) {
+ $new->encryptionPadding = $encryptionPadding;
+ }
+ if ($signatureCount) {
+ $new->signaturePadding = $signaturePadding;
}
+ return $new;
+ }
- return $plaintext;
+ /**
+ * Returns the padding currently being used
+ *
+ */
+ public function getPadding()
+ {
+ return $this->signaturePadding | $this->encryptionPadding;
}
/**
- * Create a signature
+ * Returns the current engine being used
*
- * @see self::verify()
- * @access public
- * @param string $message
+ * OpenSSL is only used in this class (and it's subclasses) for key generation
+ * Even then it depends on the parameters you're using. It's not used for
+ * multi-prime RSA nor is it used if the key length is outside of the range
+ * supported by OpenSSL
+ *
+ * @see self::useInternalEngine()
+ * @see self::useBestEngine()
* @return string
*/
- function sign($message)
+ public function getEngine()
{
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- switch ($this->signatureMode) {
- case self::SIGNATURE_PKCS1:
- return $this->_rsassa_pkcs1_v1_5_sign($message);
- //case self::SIGNATURE_PSS:
- default:
- return $this->_rsassa_pss_sign($message);
+ if (!isset(self::$engines['PHP'])) {
+ self::useBestEngine();
}
+ return self::$engines['OpenSSL'] && self::$defaultExponent == 65537 ?
+ 'OpenSSL' :
+ 'PHP';
}
/**
- * Verifies a signature
+ * Enable RSA Blinding
*
- * @see self::sign()
- * @access public
- * @param string $message
- * @param string $signature
- * @return bool
*/
- function verify($message, $signature)
+ public static function enableBlinding()
{
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- switch ($this->signatureMode) {
- case self::SIGNATURE_PKCS1:
- return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
- //case self::SIGNATURE_PSS:
- default:
- return $this->_rsassa_pss_verify($message, $signature);
- }
+ static::$enableBlinding = true;
}
/**
- * Extract raw BER from Base64 encoding
+ * Disable RSA Blinding
*
- * @access private
- * @param string $str
- * @return string
*/
- function _extractBER($str)
+ public static function disableBlinding()
{
- /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
- * above and beyond the ceritificate.
- * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
- *
- * Bag Attributes
- * localKeyID: 01 00 00 00
- * subject=/O=organization/OU=org unit/CN=common name
- * issuer=/O=organization/CN=common name
- */
- $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
- // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
- $temp = preg_replace('#-+[^-]+-+#', '', $temp);
- // remove new lines
- $temp = str_replace(array("\r", "\n", ' '), '', $temp);
- $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
- return $temp != false ? $temp : $str;
+ static::$enableBlinding = false;
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php
new file mode 100644
index 000000000..6dcf1cb03
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/JWK.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * JSON Web Key (RFC7517) Formatted RSA Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * JWK Formatted RSA Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class JWK extends Progenitor
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ if ($key->kty != 'RSA') {
+ throw new \RuntimeException('Only RSA JWK keys are supported');
+ }
+
+ $count = $publicCount = 0;
+ $vars = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'];
+ foreach ($vars as $var) {
+ if (!isset($key->$var) || !is_string($key->$var)) {
+ continue;
+ }
+ $count++;
+ $value = new BigInteger(Strings::base64url_decode($key->$var), 256);
+ switch ($var) {
+ case 'n':
+ $publicCount++;
+ $components['modulus'] = $value;
+ break;
+ case 'e':
+ $publicCount++;
+ $components['publicExponent'] = $value;
+ break;
+ case 'd':
+ $components['privateExponent'] = $value;
+ break;
+ case 'p':
+ $components['primes'][1] = $value;
+ break;
+ case 'q':
+ $components['primes'][2] = $value;
+ break;
+ case 'dp':
+ $components['exponents'][1] = $value;
+ break;
+ case 'dq':
+ $components['exponents'][2] = $value;
+ break;
+ case 'qi':
+ $components['coefficients'][2] = $value;
+ }
+ }
+
+ if ($count == count($vars)) {
+ return $components + ['isPublicKey' => false];
+ }
+
+ if ($count == 2 && $publicCount == 2) {
+ return $components + ['isPublicKey' => true];
+ }
+
+ throw new \UnexpectedValueException('Key does not have an appropriate number of RSA parameters');
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ if (count($primes) != 2) {
+ throw new \InvalidArgumentException('JWK does not support multi-prime RSA keys');
+ }
+
+ $key = [
+ 'kty' => 'RSA',
+ 'n' => Strings::base64url_encode($n->toBytes()),
+ 'e' => Strings::base64url_encode($e->toBytes()),
+ 'd' => Strings::base64url_encode($d->toBytes()),
+ 'p' => Strings::base64url_encode($primes[1]->toBytes()),
+ 'q' => Strings::base64url_encode($primes[2]->toBytes()),
+ 'dp' => Strings::base64url_encode($exponents[1]->toBytes()),
+ 'dq' => Strings::base64url_encode($exponents[2]->toBytes()),
+ 'qi' => Strings::base64url_encode($coefficients[2]->toBytes())
+ ];
+
+ return self::wrapKey($key, $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
+ {
+ $key = [
+ 'kty' => 'RSA',
+ 'n' => Strings::base64url_encode($n->toBytes()),
+ 'e' => Strings::base64url_encode($e->toBytes())
+ ];
+
+ return self::wrapKey($key, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php
new file mode 100644
index 000000000..035fc8c38
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/MSBLOB.php
@@ -0,0 +1,224 @@
+<?php
+
+/**
+ * Miccrosoft BLOB Formatted RSA Key Handler
+ *
+ * More info:
+ *
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Microsoft BLOB Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class MSBLOB
+{
+ /**
+ * Public/Private Key Pair
+ *
+ */
+ const PRIVATEKEYBLOB = 0x7;
+ /**
+ * Public Key
+ *
+ */
+ const PUBLICKEYBLOB = 0x6;
+ /**
+ * Public Key
+ *
+ */
+ const PUBLICKEYBLOBEX = 0xA;
+ /**
+ * RSA public key exchange algorithm
+ *
+ */
+ const CALG_RSA_KEYX = 0x0000A400;
+ /**
+ * RSA public key exchange algorithm
+ *
+ */
+ const CALG_RSA_SIGN = 0x00002400;
+ /**
+ * Public Key
+ *
+ */
+ const RSA1 = 0x31415352;
+ /**
+ * Private Key
+ *
+ */
+ const RSA2 = 0x32415352;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ $key = Strings::base64_decode($key);
+
+ if (!is_string($key)) {
+ throw new \UnexpectedValueException('Base64 decoding produced an error');
+ }
+ if (strlen($key) < 20) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ // PUBLICKEYSTRUC publickeystruc
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
+ $unpacked = unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8));
+ $type = $unpacked['type'];
+ $version = $unpacked['version'];
+ $reserved = $unpacked['reserved'];
+ $algo = $unpacked['algo'];
+ switch (ord($type)) {
+ case self::PUBLICKEYBLOB:
+ case self::PUBLICKEYBLOBEX:
+ $publickey = true;
+ break;
+ case self::PRIVATEKEYBLOB:
+ $publickey = false;
+ break;
+ default:
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $components = ['isPublicKey' => $publickey];
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
+ switch ($algo) {
+ case self::CALG_RSA_KEYX:
+ case self::CALG_RSA_SIGN:
+ break;
+ default:
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ // RSAPUBKEY rsapubkey
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
+ // could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit
+ $unpacked = unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12));
+ $magic = $unpacked['magic'];
+ $bitlen = $unpacked['bitlen'];
+ $pubexp = $unpacked['pubexp'];
+ switch ($magic) {
+ case self::RSA2:
+ $components['isPublicKey'] = false;
+ // fall-through
+ case self::RSA1:
+ break;
+ default:
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $baseLength = $bitlen / 16;
+ if (strlen($key) != 2 * $baseLength && strlen($key) != 9 * $baseLength) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+
+ $components[$components['isPublicKey'] ? 'publicExponent' : 'privateExponent'] = new BigInteger(strrev($pubexp), 256);
+ // BYTE modulus[rsapubkey.bitlen/8]
+ $components['modulus'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
+
+ if ($publickey) {
+ return $components;
+ }
+
+ $components['isPublicKey'] = false;
+
+ // BYTE prime1[rsapubkey.bitlen/16]
+ $components['primes'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
+ // BYTE prime2[rsapubkey.bitlen/16]
+ $components['primes'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
+ // BYTE exponent1[rsapubkey.bitlen/16]
+ $components['exponents'] = [1 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
+ // BYTE exponent2[rsapubkey.bitlen/16]
+ $components['exponents'][] = new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256);
+ // BYTE coefficient[rsapubkey.bitlen/16]
+ $components['coefficients'] = [2 => new BigInteger(strrev(Strings::shift($key, $bitlen / 16)), 256)];
+ if (isset($components['privateExponent'])) {
+ $components['publicExponent'] = $components['privateExponent'];
+ }
+ // BYTE privateExponent[rsapubkey.bitlen/8]
+ $components['privateExponent'] = new BigInteger(strrev(Strings::shift($key, $bitlen / 8)), 256);
+
+ return $components;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '')
+ {
+ if (count($primes) != 2) {
+ throw new \InvalidArgumentException('MSBLOB does not support multi-prime RSA keys');
+ }
+
+ if (!empty($password) && is_string($password)) {
+ throw new UnsupportedFormatException('MSBLOB private keys do not support encryption');
+ }
+
+ $n = strrev($n->toBytes());
+ $e = str_pad(strrev($e->toBytes()), 4, "\0");
+ $key = pack('aavV', chr(self::PRIVATEKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
+ $key .= pack('VVa*', self::RSA2, 8 * strlen($n), $e);
+ $key .= $n;
+ $key .= strrev($primes[1]->toBytes());
+ $key .= strrev($primes[2]->toBytes());
+ $key .= strrev($exponents[1]->toBytes());
+ $key .= strrev($exponents[2]->toBytes());
+ $key .= strrev($coefficients[2]->toBytes());
+ $key .= strrev($d->toBytes());
+
+ return Strings::base64_encode($key);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e)
+ {
+ $n = strrev($n->toBytes());
+ $e = str_pad(strrev($e->toBytes()), 4, "\0");
+ $key = pack('aavV', chr(self::PUBLICKEYBLOB), chr(2), 0, self::CALG_RSA_KEYX);
+ $key .= pack('VVa*', self::RSA1, 8 * strlen($n), $e);
+ $key .= $n;
+
+ return Strings::base64_encode($key);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php
new file mode 100644
index 000000000..ca74ea481
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/OpenSSH.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * OpenSSH Formatted RSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * OpenSSH Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSH extends Progenitor
+{
+ /**
+ * Supported Key Types
+ *
+ * @var array
+ */
+ protected static $types = ['ssh-rsa'];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ $parsed = parent::load($key, $password);
+
+ if (isset($parsed['paddedKey'])) {
+ list($type) = Strings::unpackSSH2('s', $parsed['paddedKey']);
+ if ($type != $parsed['type']) {
+ throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
+ }
+
+ $primes = $coefficients = [];
+
+ list(
+ $modulus,
+ $publicExponent,
+ $privateExponent,
+ $coefficients[2],
+ $primes[1],
+ $primes[2],
+ $comment,
+ ) = Strings::unpackSSH2('i6s', $parsed['paddedKey']);
+
+ $temp = $primes[1]->subtract($one);
+ $exponents = [1 => $publicExponent->modInverse($temp)];
+ $temp = $primes[2]->subtract($one);
+ $exponents[] = $publicExponent->modInverse($temp);
+
+ $isPublicKey = false;
+
+ return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
+ }
+
+ list($publicExponent, $modulus) = Strings::unpackSSH2('ii', $parsed['publicKey']);
+
+ return [
+ 'isPublicKey' => true,
+ 'modulus' => $modulus,
+ 'publicExponent' => $publicExponent,
+ 'comment' => $parsed['comment']
+ ];
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
+ {
+ $RSAPublicKey = Strings::packSSH2('sii', 'ssh-rsa', $e, $n);
+
+ if (isset($options['binary']) ? $options['binary'] : self::$binary) {
+ return $RSAPublicKey;
+ }
+
+ $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
+ $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $comment;
+
+ return $RSAPublicKey;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ $publicKey = self::savePublicKey($n, $e, ['binary' => true]);
+ $privateKey = Strings::packSSH2('si6', 'ssh-rsa', $n, $e, $d, $coefficients[2], $primes[1], $primes[2]);
+
+ return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php
new file mode 100644
index 000000000..68d92701e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS1.php
@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * PKCS#1 Formatted RSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by File/X509.php
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * -----BEGIN RSA PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pem format (as specified by -m)
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#1 Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS1 extends Progenitor
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (strpos($key, 'PUBLIC') !== false) {
+ $components = ['isPublicKey' => true];
+ } elseif (strpos($key, 'PRIVATE') !== false) {
+ $components = ['isPublicKey' => false];
+ } else {
+ $components = [];
+ }
+
+ $key = parent::load($key, $password);
+
+ $decoded = ASN1::decodeBER($key);
+ if (!$decoded) {
+ throw new \RuntimeException('Unable to decode BER');
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP);
+ if (is_array($key)) {
+ $components += [
+ 'modulus' => $key['modulus'],
+ 'publicExponent' => $key['publicExponent'],
+ 'privateExponent' => $key['privateExponent'],
+ 'primes' => [1 => $key['prime1'], $key['prime2']],
+ 'exponents' => [1 => $key['exponent1'], $key['exponent2']],
+ 'coefficients' => [2 => $key['coefficient']]
+ ];
+ if ($key['version'] == 'multi') {
+ foreach ($key['otherPrimeInfos'] as $primeInfo) {
+ $components['primes'][] = $primeInfo['prime'];
+ $components['exponents'][] = $primeInfo['exponent'];
+ $components['coefficients'][] = $primeInfo['coefficient'];
+ }
+ }
+ if (!isset($components['isPublicKey'])) {
+ $components['isPublicKey'] = false;
+ }
+ return $components;
+ }
+
+ $key = ASN1::asn1map($decoded[0], Maps\RSAPublicKey::MAP);
+
+ if (!is_array($key)) {
+ throw new \RuntimeException('Unable to perform ASN1 mapping');
+ }
+
+ if (!isset($components['isPublicKey'])) {
+ $components['isPublicKey'] = true;
+ }
+
+ $components = $components + $key;
+ foreach ($components as &$val) {
+ if ($val instanceof BigInteger) {
+ $val = self::makePositive($val);
+ }
+ if (is_array($val)) {
+ foreach ($val as &$subval) {
+ if ($subval instanceof BigInteger) {
+ $subval = self::makePositive($subval);
+ }
+ }
+ }
+ }
+
+ return $components + $key;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ $num_primes = count($primes);
+ $key = [
+ 'version' => $num_primes == 2 ? 'two-prime' : 'multi',
+ 'modulus' => $n,
+ 'publicExponent' => $e,
+ 'privateExponent' => $d,
+ 'prime1' => $primes[1],
+ 'prime2' => $primes[2],
+ 'exponent1' => $exponents[1],
+ 'exponent2' => $exponents[2],
+ 'coefficient' => $coefficients[2]
+ ];
+ for ($i = 3; $i <= $num_primes; $i++) {
+ $key['otherPrimeInfos'][] = [
+ 'prime' => $primes[$i],
+ 'exponent' => $exponents[$i],
+ 'coefficient' => $coefficients[$i]
+ ];
+ }
+
+ $key = ASN1::encodeDER($key, Maps\RSAPrivateKey::MAP);
+
+ return self::wrapPrivateKey($key, 'RSA', $password, $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e)
+ {
+ $key = [
+ 'modulus' => $n,
+ 'publicExponent' => $e
+ ];
+
+ $key = ASN1::encodeDER($key, Maps\RSAPublicKey::MAP);
+
+ return self::wrapPublicKey($key, 'RSA');
+ }
+
+ /**
+ * Negative numbers make no sense in RSA so convert them to positive
+ *
+ * @param BigInteger $x
+ * @return string
+ */
+ private static function makePositive(BigInteger $x)
+ {
+ return $x->isNegative() ?
+ new BigInteger($x->toBytes(true), 256) :
+ $x;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php
new file mode 100644
index 000000000..30f63ff97
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PKCS8.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * PKCS#8 Formatted RSA Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
+ * is specific to private keys it's basically creating a DER-encoded wrapper
+ * for keys. This just extends that same concept to public keys (much like ssh-keygen)
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#8 Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS8 extends Progenitor
+{
+ /**
+ * OID Name
+ *
+ * @var string
+ */
+ const OID_NAME = 'rsaEncryption';
+
+ /**
+ * OID Value
+ *
+ * @var string
+ */
+ const OID_VALUE = '1.2.840.113549.1.1.1';
+
+ /**
+ * Child OIDs loaded
+ *
+ * @var bool
+ */
+ protected static $childOIDsLoaded = false;
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ $key = parent::load($key, $password);
+
+ if (isset($key['privateKey'])) {
+ $components['isPublicKey'] = false;
+ $type = 'private';
+ } else {
+ $components['isPublicKey'] = true;
+ $type = 'public';
+ }
+
+ $result = $components + PKCS1::load($key[$type . 'Key']);
+
+ if (isset($key['meta'])) {
+ $result['meta'] = $key['meta'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
+ $key = ASN1::extractBER($key);
+ return self::wrapPrivateKey($key, [], null, $password, null, '', $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
+ {
+ $key = PKCS1::savePublicKey($n, $e);
+ $key = ASN1::extractBER($key);
+ return self::wrapPublicKey($key, null, null, $options);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php
new file mode 100644
index 000000000..bf51bcf76
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PSS.php
@@ -0,0 +1,238 @@
+<?php
+
+/**
+ * PKCS#8 Formatted RSA-PSS Key Handler
+ *
+ * PHP version 5
+ *
+ * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
+ *
+ * Processes keys with the following headers:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * -----BEGIN PRIVATE KEY-----
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to "openssl genpkey -algorithm rsa-pss".
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PKCS#8 Formatted RSA-PSS Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PSS extends Progenitor
+{
+ /**
+ * OID Name
+ *
+ * @var string
+ */
+ const OID_NAME = 'id-RSASSA-PSS';
+
+ /**
+ * OID Value
+ *
+ * @var string
+ */
+ const OID_VALUE = '1.2.840.113549.1.1.10';
+
+ /**
+ * OIDs loaded
+ *
+ * @var bool
+ */
+ private static $oidsLoaded = false;
+
+ /**
+ * Child OIDs loaded
+ *
+ * @var bool
+ */
+ protected static $childOIDsLoaded = false;
+
+ /**
+ * Initialize static variables
+ */
+ private static function initialize_static_variables()
+ {
+ if (!self::$oidsLoaded) {
+ ASN1::loadOIDs([
+ 'md2' => '1.2.840.113549.2.2',
+ 'md4' => '1.2.840.113549.2.4',
+ 'md5' => '1.2.840.113549.2.5',
+ 'id-sha1' => '1.3.14.3.2.26',
+ 'id-sha256' => '2.16.840.1.101.3.4.2.1',
+ 'id-sha384' => '2.16.840.1.101.3.4.2.2',
+ 'id-sha512' => '2.16.840.1.101.3.4.2.3',
+ 'id-sha224' => '2.16.840.1.101.3.4.2.4',
+ 'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
+ 'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
+
+ 'id-mgf1' => '1.2.840.113549.1.1.8'
+ ]);
+ self::$oidsLoaded = true;
+ }
+ }
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ self::initialize_static_variables();
+
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ $components = ['isPublicKey' => strpos($key, 'PUBLIC') !== false];
+
+ $key = parent::load($key, $password);
+
+ $type = isset($key['privateKey']) ? 'private' : 'public';
+
+ $result = $components + PKCS1::load($key[$type . 'Key']);
+
+ if (isset($key[$type . 'KeyAlgorithm']['parameters'])) {
+ $decoded = ASN1::decodeBER($key[$type . 'KeyAlgorithm']['parameters']);
+ if ($decoded === false) {
+ throw new \UnexpectedValueException('Unable to decode parameters');
+ }
+ $params = ASN1::asn1map($decoded[0], Maps\RSASSA_PSS_params::MAP);
+ } else {
+ $params = [];
+ }
+
+ if (isset($params['maskGenAlgorithm']['parameters'])) {
+ $decoded = ASN1::decodeBER($params['maskGenAlgorithm']['parameters']);
+ if ($decoded === false) {
+ throw new \UnexpectedValueException('Unable to decode parameters');
+ }
+ $params['maskGenAlgorithm']['parameters'] = ASN1::asn1map($decoded[0], Maps\HashAlgorithm::MAP);
+ } else {
+ $params['maskGenAlgorithm'] = [
+ 'algorithm' => 'id-mgf1',
+ 'parameters' => ['algorithm' => 'id-sha1']
+ ];
+ }
+
+ if (!isset($params['hashAlgorithm']['algorithm'])) {
+ $params['hashAlgorithm']['algorithm'] = 'id-sha1';
+ }
+
+ $result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
+ $result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
+ if (isset($params['saltLength'])) {
+ $result['saltLength'] = (int) $params['saltLength']->toString();
+ }
+
+ if (isset($key['meta'])) {
+ $result['meta'] = $key['meta'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ self::initialize_static_variables();
+
+ $key = PKCS1::savePrivateKey($n, $e, $d, $primes, $exponents, $coefficients);
+ $key = ASN1::extractBER($key);
+ $params = self::savePSSParams($options);
+ return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e, array $options = [])
+ {
+ self::initialize_static_variables();
+
+ $key = PKCS1::savePublicKey($n, $e);
+ $key = ASN1::extractBER($key);
+ $params = self::savePSSParams($options);
+ return self::wrapPublicKey($key, $params);
+ }
+
+ /**
+ * Encodes PSS parameters
+ *
+ * @param array $options
+ * @return string
+ */
+ public static function savePSSParams(array $options)
+ {
+ /*
+ The trailerField field is an integer. It provides
+ compatibility with IEEE Std 1363a-2004 [P1363A]. The value
+ MUST be 1, which represents the trailer field with hexadecimal
+ value 0xBC. Other trailer fields, including the trailer field
+ composed of HashID concatenated with 0xCC that is specified in
+ IEEE Std 1363a, are not supported. Implementations that
+ perform signature generation MUST omit the trailerField field,
+ indicating that the default trailer field value was used.
+ Implementations that perform signature validation MUST
+ recognize both a present trailerField field with value 1 and an
+ absent trailerField field.
+
+ source: https://tools.ietf.org/html/rfc4055#page-9
+ */
+ $params = [
+ 'trailerField' => new BigInteger(1)
+ ];
+ if (isset($options['hash'])) {
+ $params['hashAlgorithm']['algorithm'] = 'id-' . $options['hash'];
+ }
+ if (isset($options['MGFHash'])) {
+ $temp = ['algorithm' => 'id-' . $options['MGFHash']];
+ $temp = ASN1::encodeDER($temp, Maps\HashAlgorithm::MAP);
+ $params['maskGenAlgorithm'] = [
+ 'algorithm' => 'id-mgf1',
+ 'parameters' => new ASN1\Element($temp)
+ ];
+ }
+ if (isset($options['saltLength'])) {
+ $params['saltLength'] = new BigInteger($options['saltLength']);
+ }
+
+ return new ASN1\Element(ASN1::encodeDER($params, Maps\RSASSA_PSS_params::MAP));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php
new file mode 100644
index 000000000..8416758c2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/PuTTY.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * PuTTY Formatted RSA Key Handler
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * PuTTY Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PuTTY extends Progenitor
+{
+ /**
+ * Public Handler
+ *
+ * @var string
+ */
+ const PUBLIC_HANDLER = 'phpseclib3\Crypt\RSA\Formats\Keys\OpenSSH';
+
+ /**
+ * Algorithm Identifier
+ *
+ * @var array
+ */
+ protected static $types = ['ssh-rsa'];
+
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ $components = parent::load($key, $password);
+ if (!isset($components['private'])) {
+ return $components;
+ }
+ $type = $components['type'];
+ $comment = $components['comment'];
+ $public = $components['public'];
+ $private = $components['private'];
+ unset($components['public'], $components['private']);
+
+ $isPublicKey = false;
+
+ $result = Strings::unpackSSH2('ii', $public);
+ if ($result === false) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+ list($publicExponent, $modulus) = $result;
+
+ $result = Strings::unpackSSH2('iiii', $private);
+ if ($result === false) {
+ throw new \UnexpectedValueException('Key appears to be malformed');
+ }
+ $primes = $coefficients = [];
+ list($privateExponent, $primes[1], $primes[2], $coefficients[2]) = $result;
+
+ $temp = $primes[1]->subtract($one);
+ $exponents = [1 => $publicExponent->modInverse($temp)];
+ $temp = $primes[2]->subtract($one);
+ $exponents[] = $publicExponent->modInverse($temp);
+
+ return compact('publicExponent', 'modulus', 'privateExponent', 'primes', 'coefficients', 'exponents', 'comment', 'isPublicKey');
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ if (count($primes) != 2) {
+ throw new \InvalidArgumentException('PuTTY does not support multi-prime RSA keys');
+ }
+
+ $public = Strings::packSSH2('ii', $e, $n);
+ $private = Strings::packSSH2('iiii', $d, $primes[1], $primes[2], $coefficients[2]);
+
+ return self::wrapPrivateKey($public, $private, 'ssh-rsa', $password, $options);
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e)
+ {
+ return self::wrapPublicKey(Strings::packSSH2('ii', $e, $n), 'ssh-rsa');
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php
new file mode 100644
index 000000000..55c7ccd7a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/Raw.php
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * Raw RSA Key Handler
+ *
+ * PHP version 5
+ *
+ * An array containing two \phpseclib3\Math\BigInteger objects.
+ *
+ * The exponent can be indexed with any of the following:
+ *
+ * 0, e, exponent, publicExponent
+ *
+ * The modulus can be indexed with any of the following:
+ *
+ * 1, n, modulo, modulus
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Raw RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Raw
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!is_array($key)) {
+ throw new \UnexpectedValueException('Key should be a array - not a ' . gettype($key));
+ }
+
+ $key = array_change_key_case($key, CASE_LOWER);
+
+ $components = ['isPublicKey' => false];
+
+ foreach (['e', 'exponent', 'publicexponent', 0, 'privateexponent', 'd'] as $index) {
+ if (isset($key[$index])) {
+ $components['publicExponent'] = $key[$index];
+ break;
+ }
+ }
+
+ foreach (['n', 'modulo', 'modulus', 1] as $index) {
+ if (isset($key[$index])) {
+ $components['modulus'] = $key[$index];
+ break;
+ }
+ }
+
+ if (!isset($components['publicExponent']) || !isset($components['modulus'])) {
+ throw new \UnexpectedValueException('Modulus / exponent not present');
+ }
+
+ if (isset($key['primes'])) {
+ $components['primes'] = $key['primes'];
+ } elseif (isset($key['p']) && isset($key['q'])) {
+ $indices = [
+ ['p', 'q'],
+ ['prime1', 'prime2']
+ ];
+ foreach ($indices as $index) {
+ list($i0, $i1) = $index;
+ if (isset($key[$i0]) && isset($key[$i1])) {
+ $components['primes'] = [1 => $key[$i0], $key[$i1]];
+ }
+ }
+ }
+
+ if (isset($key['exponents'])) {
+ $components['exponents'] = $key['exponents'];
+ } else {
+ $indices = [
+ ['dp', 'dq'],
+ ['exponent1', 'exponent2']
+ ];
+ foreach ($indices as $index) {
+ list($i0, $i1) = $index;
+ if (isset($key[$i0]) && isset($key[$i1])) {
+ $components['exponents'] = [1 => $key[$i0], $key[$i1]];
+ }
+ }
+ }
+
+ if (isset($key['coefficients'])) {
+ $components['coefficients'] = $key['coefficients'];
+ } else {
+ foreach (['inverseq', 'q\'', 'coefficient'] as $index) {
+ if (isset($key[$index])) {
+ $components['coefficients'] = [2 => $key[$index]];
+ }
+ }
+ }
+
+ if (!isset($components['primes'])) {
+ $components['isPublicKey'] = true;
+ return $components;
+ }
+
+ if (!isset($components['exponents'])) {
+ $one = new BigInteger(1);
+ $temp = $components['primes'][1]->subtract($one);
+ $exponents = [1 => $components['publicExponent']->modInverse($temp)];
+ $temp = $components['primes'][2]->subtract($one);
+ $exponents[] = $components['publicExponent']->modInverse($temp);
+ $components['exponents'] = $exponents;
+ }
+
+ if (!isset($components['coefficients'])) {
+ $components['coefficients'] = [2 => $components['primes'][2]->modInverse($components['primes'][1])];
+ }
+
+ foreach (['privateexponent', 'd'] as $index) {
+ if (isset($key[$index])) {
+ $components['privateExponent'] = $key[$index];
+ break;
+ }
+ }
+
+ return $components;
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @param array $options optional
+ * @return array
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '', array $options = [])
+ {
+ if (!empty($password) && is_string($password)) {
+ throw new UnsupportedFormatException('Raw private keys do not support encryption');
+ }
+
+ return [
+ 'e' => clone $e,
+ 'n' => clone $n,
+ 'd' => clone $d,
+ 'primes' => array_map(function ($var) {
+ return clone $var;
+ }, $primes),
+ 'exponents' => array_map(function ($var) {
+ return clone $var;
+ }, $exponents),
+ 'coefficients' => array_map(function ($var) {
+ return clone $var;
+ }, $coefficients)
+ ];
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @return array
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e)
+ {
+ return ['e' => clone $e, 'n' => clone $n];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php
new file mode 100644
index 000000000..d569dea6d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/Formats/Keys/XML.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * XML Formatted RSA Key Handler
+ *
+ * More info:
+ *
+ * http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
+ * http://www.w3.org/TR/xkms2/#XKMS_2_0_Paragraph_269
+ * http://en.wikipedia.org/wiki/XML_Signature
+ * http://en.wikipedia.org/wiki/XKMS
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA\Formats\Keys;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * XML Formatted RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class XML
+{
+ /**
+ * Break a public or private key down into its constituent components
+ *
+ * @param string $key
+ * @param string $password optional
+ * @return array
+ */
+ public static function load($key, $password = '')
+ {
+ if (!Strings::is_stringable($key)) {
+ throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key));
+ }
+
+ if (!class_exists('DOMDocument')) {
+ throw new BadConfigurationException('The dom extension is not setup correctly on this system');
+ }
+
+ $components = [
+ 'isPublicKey' => false,
+ 'primes' => [],
+ 'exponents' => [],
+ 'coefficients' => []
+ ];
+
+ $use_errors = libxml_use_internal_errors(true);
+
+ $dom = new \DOMDocument();
+ if (substr($key, 0, 5) != '<?xml') {
+ $key = '<xml>' . $key . '</xml>';
+ }
+ if (!$dom->loadXML($key)) {
+ libxml_use_internal_errors($use_errors);
+ throw new \UnexpectedValueException('Key does not appear to contain XML');
+ }
+ $xpath = new \DOMXPath($dom);
+ $keys = ['modulus', 'exponent', 'p', 'q', 'dp', 'dq', 'inverseq', 'd'];
+ foreach ($keys as $key) {
+ // $dom->getElementsByTagName($key) is case-sensitive
+ $temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
+ if (!$temp->length) {
+ continue;
+ }
+ $value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
+ switch ($key) {
+ case 'modulus':
+ $components['modulus'] = $value;
+ break;
+ case 'exponent':
+ $components['publicExponent'] = $value;
+ break;
+ case 'p':
+ $components['primes'][1] = $value;
+ break;
+ case 'q':
+ $components['primes'][2] = $value;
+ break;
+ case 'dp':
+ $components['exponents'][1] = $value;
+ break;
+ case 'dq':
+ $components['exponents'][2] = $value;
+ break;
+ case 'inverseq':
+ $components['coefficients'][2] = $value;
+ break;
+ case 'd':
+ $components['privateExponent'] = $value;
+ }
+ }
+
+ libxml_use_internal_errors($use_errors);
+
+ foreach ($components as $key => $value) {
+ if (is_array($value) && !count($value)) {
+ unset($components[$key]);
+ }
+ }
+
+ if (isset($components['modulus']) && isset($components['publicExponent'])) {
+ if (count($components) == 3) {
+ $components['isPublicKey'] = true;
+ }
+ return $components;
+ }
+
+ throw new \UnexpectedValueException('Modulus / exponent not present');
+ }
+
+ /**
+ * Convert a private key to the appropriate format.
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @param BigInteger $d
+ * @param array $primes
+ * @param array $exponents
+ * @param array $coefficients
+ * @param string $password optional
+ * @return string
+ */
+ public static function savePrivateKey(BigInteger $n, BigInteger $e, BigInteger $d, array $primes, array $exponents, array $coefficients, $password = '')
+ {
+ if (count($primes) != 2) {
+ throw new \InvalidArgumentException('XML does not support multi-prime RSA keys');
+ }
+
+ if (!empty($password) && is_string($password)) {
+ throw new UnsupportedFormatException('XML private keys do not support encryption');
+ }
+
+ return "<RSAKeyPair>\r\n" .
+ ' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
+ ' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
+ ' <P>' . Strings::base64_encode($primes[1]->toBytes()) . "</P>\r\n" .
+ ' <Q>' . Strings::base64_encode($primes[2]->toBytes()) . "</Q>\r\n" .
+ ' <DP>' . Strings::base64_encode($exponents[1]->toBytes()) . "</DP>\r\n" .
+ ' <DQ>' . Strings::base64_encode($exponents[2]->toBytes()) . "</DQ>\r\n" .
+ ' <InverseQ>' . Strings::base64_encode($coefficients[2]->toBytes()) . "</InverseQ>\r\n" .
+ ' <D>' . Strings::base64_encode($d->toBytes()) . "</D>\r\n" .
+ '</RSAKeyPair>';
+ }
+
+ /**
+ * Convert a public key to the appropriate format
+ *
+ * @param BigInteger $n
+ * @param BigInteger $e
+ * @return string
+ */
+ public static function savePublicKey(BigInteger $n, BigInteger $e)
+ {
+ return "<RSAKeyValue>\r\n" .
+ ' <Modulus>' . Strings::base64_encode($n->toBytes()) . "</Modulus>\r\n" .
+ ' <Exponent>' . Strings::base64_encode($e->toBytes()) . "</Exponent>\r\n" .
+ '</RSAKeyValue>';
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php
new file mode 100644
index 000000000..8c828b316
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PrivateKey.php
@@ -0,0 +1,530 @@
+<?php
+
+/**
+ * RSA Private Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA;
+
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Raw RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PrivateKey extends RSA implements Common\PrivateKey
+{
+ use Common\Traits\PasswordProtected;
+
+ /**
+ * Primes for Chinese Remainder Theorem (ie. p and q)
+ *
+ * @var array
+ */
+ protected $primes;
+
+ /**
+ * Exponents for Chinese Remainder Theorem (ie. dP and dQ)
+ *
+ * @var array
+ */
+ protected $exponents;
+
+ /**
+ * Coefficients for Chinese Remainder Theorem (ie. qInv)
+ *
+ * @var array
+ */
+ protected $coefficients;
+
+ /**
+ * Private Exponent
+ *
+ * @var BigInteger
+ */
+ protected $privateExponent;
+
+ /**
+ * RSADP
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
+ *
+ * @return bool|BigInteger
+ */
+ private function rsadp(BigInteger $c)
+ {
+ if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) {
+ throw new \OutOfRangeException('Ciphertext representative out of range');
+ }
+ return $this->exponentiate($c);
+ }
+
+ /**
+ * RSASP1
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
+ *
+ * @return bool|BigInteger
+ */
+ private function rsasp1(BigInteger $m)
+ {
+ if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
+ throw new \OutOfRangeException('Signature representative out of range');
+ }
+ return $this->exponentiate($m);
+ }
+
+ /**
+ * Exponentiate
+ *
+ * @param BigInteger $x
+ * @return BigInteger
+ */
+ protected function exponentiate(BigInteger $x)
+ {
+ switch (true) {
+ case empty($this->primes):
+ case $this->primes[1]->equals(self::$zero):
+ case empty($this->coefficients):
+ case $this->coefficients[2]->equals(self::$zero):
+ case empty($this->exponents):
+ case $this->exponents[1]->equals(self::$zero):
+ return $x->modPow($this->exponent, $this->modulus);
+ }
+
+ $num_primes = count($this->primes);
+
+ if (!static::$enableBlinding) {
+ $m_i = [
+ 1 => $x->modPow($this->exponents[1], $this->primes[1]),
+ 2 => $x->modPow($this->exponents[2], $this->primes[2])
+ ];
+ $h = $m_i[1]->subtract($m_i[2]);
+ $h = $h->multiply($this->coefficients[2]);
+ list(, $h) = $h->divide($this->primes[1]);
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
+
+ $r = $this->primes[1];
+ for ($i = 3; $i <= $num_primes; $i++) {
+ $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
+
+ $r = $r->multiply($this->primes[$i - 1]);
+
+ $h = $m_i->subtract($m);
+ $h = $h->multiply($this->coefficients[$i]);
+ list(, $h) = $h->divide($this->primes[$i]);
+
+ $m = $m->add($r->multiply($h));
+ }
+ } else {
+ $smallest = $this->primes[1];
+ for ($i = 2; $i <= $num_primes; $i++) {
+ if ($smallest->compare($this->primes[$i]) > 0) {
+ $smallest = $this->primes[$i];
+ }
+ }
+
+ $r = BigInteger::randomRange(self::$one, $smallest->subtract(self::$one));
+
+ $m_i = [
+ 1 => $this->blind($x, $r, 1),
+ 2 => $this->blind($x, $r, 2)
+ ];
+ $h = $m_i[1]->subtract($m_i[2]);
+ $h = $h->multiply($this->coefficients[2]);
+ list(, $h) = $h->divide($this->primes[1]);
+ $m = $m_i[2]->add($h->multiply($this->primes[2]));
+
+ $r = $this->primes[1];
+ for ($i = 3; $i <= $num_primes; $i++) {
+ $m_i = $this->blind($x, $r, $i);
+
+ $r = $r->multiply($this->primes[$i - 1]);
+
+ $h = $m_i->subtract($m);
+ $h = $h->multiply($this->coefficients[$i]);
+ list(, $h) = $h->divide($this->primes[$i]);
+
+ $m = $m->add($r->multiply($h));
+ }
+ }
+
+ return $m;
+ }
+
+ /**
+ * Performs RSA Blinding
+ *
+ * Protects against timing attacks by employing RSA Blinding.
+ * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
+ *
+ * @param BigInteger $x
+ * @param BigInteger $r
+ * @param int $i
+ * @return BigInteger
+ */
+ private function blind(BigInteger $x, BigInteger $r, $i)
+ {
+ $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
+ $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
+
+ $r = $r->modInverse($this->primes[$i]);
+ $x = $x->multiply($r);
+ list(, $x) = $x->divide($this->primes[$i]);
+
+ return $x;
+ }
+
+ /**
+ * EMSA-PSS-ENCODE
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
+ *
+ * @return string
+ * @param string $m
+ * @throws \RuntimeException on encoding error
+ * @param int $emBits
+ */
+ private function emsa_pss_encode($m, $emBits)
+ {
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
+ $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
+
+ $mHash = $this->hash->hash($m);
+ if ($emLen < $this->hLen + $sLen + 2) {
+ throw new \LengthException('RSA modulus too short');
+ }
+
+ $salt = Random::string($sLen);
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
+ $h = $this->hash->hash($m2);
+ $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
+ $db = $ps . chr(1) . $salt;
+ $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db)
+ $maskedDB = $db ^ $dbMask;
+ $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
+ $em = $maskedDB . $h . chr(0xBC);
+
+ return $em;
+ }
+
+ /**
+ * RSASSA-PSS-SIGN
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
+ *
+ * @param string $m
+ * @return bool|string
+ */
+ private function rsassa_pss_sign($m)
+ {
+ // EMSA-PSS encoding
+
+ $em = $this->emsa_pss_encode($m, 8 * $this->k - 1);
+
+ // RSA signature
+
+ $m = $this->os2ip($em);
+ $s = $this->rsasp1($m);
+ $s = $this->i2osp($s, $this->k);
+
+ // Output the signature S
+
+ return $s;
+ }
+
+ /**
+ * RSASSA-PKCS1-V1_5-SIGN
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
+ *
+ * @param string $m
+ * @throws \LengthException if the RSA modulus is too short
+ * @return bool|string
+ */
+ private function rsassa_pkcs1_v1_5_sign($m)
+ {
+ // EMSA-PKCS1-v1_5 encoding
+
+ // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
+ // too short" and stop.
+ try {
+ $em = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
+ } catch (\LengthException $e) {
+ throw new \LengthException('RSA modulus too short');
+ }
+
+ // RSA signature
+
+ $m = $this->os2ip($em);
+ $s = $this->rsasp1($m);
+ $s = $this->i2osp($s, $this->k);
+
+ // Output the signature S
+
+ return $s;
+ }
+
+ /**
+ * Create a signature
+ *
+ * @see self::verify()
+ * @param string $message
+ * @return string
+ */
+ public function sign($message)
+ {
+ switch ($this->signaturePadding) {
+ case self::SIGNATURE_PKCS1:
+ case self::SIGNATURE_RELAXED_PKCS1:
+ return $this->rsassa_pkcs1_v1_5_sign($message);
+ //case self::SIGNATURE_PSS:
+ default:
+ return $this->rsassa_pss_sign($message);
+ }
+ }
+
+ /**
+ * RSAES-PKCS1-V1_5-DECRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
+ *
+ * @param string $c
+ * @return bool|string
+ */
+ private function rsaes_pkcs1_v1_5_decrypt($c)
+ {
+ // Length checking
+
+ if (strlen($c) != $this->k) { // or if k < 11
+ throw new \LengthException('Ciphertext representative too long');
+ }
+
+ // RSA decryption
+
+ $c = $this->os2ip($c);
+ $m = $this->rsadp($c);
+ $em = $this->i2osp($m, $this->k);
+
+ // EME-PKCS1-v1_5 decoding
+
+ if (ord($em[0]) != 0 || ord($em[1]) > 2) {
+ throw new \RuntimeException('Decryption error');
+ }
+
+ $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
+ $m = substr($em, strlen($ps) + 3);
+
+ if (strlen($ps) < 8) {
+ throw new \RuntimeException('Decryption error');
+ }
+
+ // Output M
+
+ return $m;
+ }
+
+ /**
+ * RSAES-OAEP-DECRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
+ * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
+ *
+ * Note. Care must be taken to ensure that an opponent cannot
+ * distinguish the different error conditions in Step 3.g, whether by
+ * error message or timing, or, more generally, learn partial
+ * information about the encoded message EM. Otherwise an opponent may
+ * be able to obtain useful information about the decryption of the
+ * ciphertext C, leading to a chosen-ciphertext attack such as the one
+ * observed by Manger [36].
+ *
+ * @param string $c
+ * @return bool|string
+ */
+ private function rsaes_oaep_decrypt($c)
+ {
+ // Length checking
+
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
+ throw new \LengthException('Ciphertext representative too long');
+ }
+
+ // RSA decryption
+
+ $c = $this->os2ip($c);
+ $m = $this->rsadp($c);
+ $em = $this->i2osp($m, $this->k);
+
+ // EME-OAEP decoding
+
+ $lHash = $this->hash->hash($this->label);
+ $y = ord($em[0]);
+ $maskedSeed = substr($em, 1, $this->hLen);
+ $maskedDB = substr($em, $this->hLen + 1);
+ $seedMask = $this->mgf1($maskedDB, $this->hLen);
+ $seed = $maskedSeed ^ $seedMask;
+ $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
+ $db = $maskedDB ^ $dbMask;
+ $lHash2 = substr($db, 0, $this->hLen);
+ $m = substr($db, $this->hLen);
+ $hashesMatch = hash_equals($lHash, $lHash2);
+ $leadingZeros = 1;
+ $patternMatch = 0;
+ $offset = 0;
+ for ($i = 0; $i < strlen($m); $i++) {
+ $patternMatch |= $leadingZeros & ($m[$i] === "\1");
+ $leadingZeros &= $m[$i] === "\0";
+ $offset += $patternMatch ? 0 : 1;
+ }
+
+ // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
+ // to protect against timing attacks
+ if (!$hashesMatch | !$patternMatch) {
+ throw new \RuntimeException('Decryption error');
+ }
+
+ // Output the message M
+
+ return substr($m, $offset + 1);
+ }
+
+ /**
+ * Raw Encryption / Decryption
+ *
+ * Doesn't use padding and is not recommended.
+ *
+ * @param string $m
+ * @return bool|string
+ * @throws \LengthException if strlen($m) > $this->k
+ */
+ private function raw_encrypt($m)
+ {
+ if (strlen($m) > $this->k) {
+ throw new \LengthException('Ciphertext representative too long');
+ }
+
+ $temp = $this->os2ip($m);
+ $temp = $this->rsadp($temp);
+ return $this->i2osp($temp, $this->k);
+ }
+
+ /**
+ * Decryption
+ *
+ * @see self::encrypt()
+ * @param string $ciphertext
+ * @return bool|string
+ */
+ public function decrypt($ciphertext)
+ {
+ switch ($this->encryptionPadding) {
+ case self::ENCRYPTION_NONE:
+ return $this->raw_encrypt($ciphertext);
+ case self::ENCRYPTION_PKCS1:
+ return $this->rsaes_pkcs1_v1_5_decrypt($ciphertext);
+ //case self::ENCRYPTION_OAEP:
+ default:
+ return $this->rsaes_oaep_decrypt($ciphertext);
+ }
+ }
+
+ /**
+ * Returns the public key
+ *
+ * @return mixed
+ */
+ public function getPublicKey()
+ {
+ $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
+ if (empty($this->modulus) || empty($this->publicExponent)) {
+ throw new \RuntimeException('Public key components not found');
+ }
+
+ $key = $type::savePublicKey($this->modulus, $this->publicExponent);
+ return RSA::loadFormat('PKCS8', $key)
+ ->withHash($this->hash->getHash())
+ ->withMGFHash($this->mgfHash->getHash())
+ ->withSaltLength($this->sLen)
+ ->withLabel($this->label)
+ ->withPadding($this->signaturePadding | $this->encryptionPadding);
+ }
+
+ /**
+ * Returns the private key
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return string
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin(
+ 'Keys',
+ $type,
+ empty($this->primes) ? 'savePublicKey' : 'savePrivateKey'
+ );
+
+ if ($type == PSS::class) {
+ if ($this->signaturePadding == self::SIGNATURE_PSS) {
+ $options += [
+ 'hash' => $this->hash->getHash(),
+ 'MGFHash' => $this->mgfHash->getHash(),
+ 'saltLength' => $this->getSaltLength()
+ ];
+ } else {
+ throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
+ }
+ }
+
+ if (empty($this->primes)) {
+ return $type::savePublicKey($this->modulus, $this->exponent, $options);
+ }
+
+ return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
+
+ /*
+ $key = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password, $options);
+ if ($key !== false || count($this->primes) == 2) {
+ return $key;
+ }
+
+ $nSize = $this->getSize() >> 1;
+
+ $primes = [1 => clone self::$one, clone self::$one];
+ $i = 1;
+ foreach ($this->primes as $prime) {
+ $primes[$i] = $primes[$i]->multiply($prime);
+ if ($primes[$i]->getLength() >= $nSize) {
+ $i++;
+ }
+ }
+
+ $exponents = [];
+ $coefficients = [2 => $primes[2]->modInverse($primes[1])];
+
+ foreach ($primes as $i => $prime) {
+ $temp = $prime->subtract(self::$one);
+ $exponents[$i] = $this->modulus->modInverse($temp);
+ }
+
+ return $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $primes, $exponents, $coefficients, $this->password, $options);
+ */
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php
new file mode 100644
index 000000000..ff80ae79c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA/PublicKey.php
@@ -0,0 +1,513 @@
+<?php
+
+/**
+ * RSA Public Key
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt\RSA;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\File\ASN1;
+use phpseclib3\File\ASN1\Maps\DigestInfo;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Raw RSA Key Handler
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+final class PublicKey extends RSA implements Common\PublicKey
+{
+ use Common\Traits\Fingerprint;
+
+ /**
+ * Exponentiate
+ *
+ * @param BigInteger $x
+ * @return BigInteger
+ */
+ private function exponentiate(BigInteger $x)
+ {
+ return $x->modPow($this->exponent, $this->modulus);
+ }
+
+ /**
+ * RSAVP1
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
+ *
+ * @param BigInteger $s
+ * @return bool|BigInteger
+ */
+ private function rsavp1($s)
+ {
+ if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) {
+ return false;
+ }
+ return $this->exponentiate($s);
+ }
+
+ /**
+ * RSASSA-PKCS1-V1_5-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
+ *
+ * @param string $m
+ * @param string $s
+ * @throws \LengthException if the RSA modulus is too short
+ * @return bool
+ */
+ private function rsassa_pkcs1_v1_5_verify($m, $s)
+ {
+ // Length checking
+
+ if (strlen($s) != $this->k) {
+ return false;
+ }
+
+ // RSA verification
+
+ $s = $this->os2ip($s);
+ $m2 = $this->rsavp1($s);
+ if ($m2 === false) {
+ return false;
+ }
+ $em = $this->i2osp($m2, $this->k);
+ if ($em === false) {
+ return false;
+ }
+
+ // EMSA-PKCS1-v1_5 encoding
+
+ $exception = false;
+
+ // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus
+ // too short" and stop.
+ try {
+ $em2 = $this->emsa_pkcs1_v1_5_encode($m, $this->k);
+ $r1 = hash_equals($em, $em2);
+ } catch (\LengthException $e) {
+ $exception = true;
+ }
+
+ try {
+ $em3 = $this->emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
+ $r2 = hash_equals($em, $em3);
+ } catch (\LengthException $e) {
+ $exception = true;
+ } catch (UnsupportedAlgorithmException $e) {
+ $r2 = false;
+ }
+
+ if ($exception) {
+ throw new \LengthException('RSA modulus too short');
+ }
+
+ // Compare
+ return $r1 || $r2;
+ }
+
+ /**
+ * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
+ *
+ * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5
+ * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified.
+ * This means that under rare conditions you can have a perfectly valid v1.5 signature
+ * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends
+ * that if you're going to validate these types of signatures you "should indicate
+ * whether the underlying BER encoding is a DER encoding and hence whether the signature
+ * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do
+ * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of
+ * RSA::PADDING_PKCS1... that means BER encoding was used.
+ *
+ * @param string $m
+ * @param string $s
+ * @return bool
+ */
+ private function rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
+ {
+ // Length checking
+
+ if (strlen($s) != $this->k) {
+ return false;
+ }
+
+ // RSA verification
+
+ $s = $this->os2ip($s);
+ $m2 = $this->rsavp1($s);
+ if ($m2 === false) {
+ return false;
+ }
+ $em = $this->i2osp($m2, $this->k);
+ if ($em === false) {
+ return false;
+ }
+
+ if (Strings::shift($em, 2) != "\0\1") {
+ return false;
+ }
+
+ $em = ltrim($em, "\xFF");
+ if (Strings::shift($em) != "\0") {
+ return false;
+ }
+
+ $decoded = ASN1::decodeBER($em);
+ if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) {
+ return false;
+ }
+
+ static $oids;
+ if (!isset($oids)) {
+ $oids = [
+ 'md2' => '1.2.840.113549.2.2',
+ 'md4' => '1.2.840.113549.2.4', // from PKCS1 v1.5
+ 'md5' => '1.2.840.113549.2.5',
+ 'id-sha1' => '1.3.14.3.2.26',
+ 'id-sha256' => '2.16.840.1.101.3.4.2.1',
+ 'id-sha384' => '2.16.840.1.101.3.4.2.2',
+ 'id-sha512' => '2.16.840.1.101.3.4.2.3',
+ // from PKCS1 v2.2
+ 'id-sha224' => '2.16.840.1.101.3.4.2.4',
+ 'id-sha512/224' => '2.16.840.1.101.3.4.2.5',
+ 'id-sha512/256' => '2.16.840.1.101.3.4.2.6',
+ ];
+ ASN1::loadOIDs($oids);
+ }
+
+ $decoded = ASN1::asn1map($decoded[0], DigestInfo::MAP);
+ if (!isset($decoded) || $decoded === false) {
+ return false;
+ }
+
+ if (!isset($oids[$decoded['digestAlgorithm']['algorithm']])) {
+ return false;
+ }
+
+ if (isset($decoded['digestAlgorithm']['parameters']) && $decoded['digestAlgorithm']['parameters'] !== ['null' => '']) {
+ return false;
+ }
+
+ $hash = $decoded['digestAlgorithm']['algorithm'];
+ $hash = substr($hash, 0, 3) == 'id-' ?
+ substr($hash, 3) :
+ $hash;
+ $hash = new Hash($hash);
+ $em = $hash->hash($m);
+ $em2 = $decoded['digest'];
+
+ return hash_equals($em, $em2);
+ }
+
+ /**
+ * EMSA-PSS-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
+ *
+ * @param string $m
+ * @param string $em
+ * @param int $emBits
+ * @return string
+ */
+ private function emsa_pss_verify($m, $em, $emBits)
+ {
+ // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
+ $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
+
+ $mHash = $this->hash->hash($m);
+ if ($emLen < $this->hLen + $sLen + 2) {
+ return false;
+ }
+
+ if ($em[strlen($em) - 1] != chr(0xBC)) {
+ return false;
+ }
+
+ $maskedDB = substr($em, 0, -$this->hLen - 1);
+ $h = substr($em, -$this->hLen - 1, $this->hLen);
+ $temp = chr(0xFF << ($emBits & 7));
+ if ((~$maskedDB[0] & $temp) != $temp) {
+ return false;
+ }
+ $dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
+ $db = $maskedDB ^ $dbMask;
+ $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
+ $temp = $emLen - $this->hLen - $sLen - 2;
+ if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
+ return false;
+ }
+ $salt = substr($db, $temp + 1); // should be $sLen long
+ $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
+ $h2 = $this->hash->hash($m2);
+ return hash_equals($h, $h2);
+ }
+
+ /**
+ * RSASSA-PSS-VERIFY
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
+ *
+ * @param string $m
+ * @param string $s
+ * @return bool|string
+ */
+ private function rsassa_pss_verify($m, $s)
+ {
+ // Length checking
+
+ if (strlen($s) != $this->k) {
+ return false;
+ }
+
+ // RSA verification
+
+ $modBits = strlen($this->modulus->toBits());
+
+ $s2 = $this->os2ip($s);
+ $m2 = $this->rsavp1($s2);
+ $em = $this->i2osp($m2, $this->k);
+ if ($em === false) {
+ return false;
+ }
+
+ // EMSA-PSS verification
+
+ return $this->emsa_pss_verify($m, $em, $modBits - 1);
+ }
+
+ /**
+ * Verifies a signature
+ *
+ * @see self::sign()
+ * @param string $message
+ * @param string $signature
+ * @return bool
+ */
+ public function verify($message, $signature)
+ {
+ switch ($this->signaturePadding) {
+ case self::SIGNATURE_RELAXED_PKCS1:
+ return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature);
+ case self::SIGNATURE_PKCS1:
+ return $this->rsassa_pkcs1_v1_5_verify($message, $signature);
+ //case self::SIGNATURE_PSS:
+ default:
+ return $this->rsassa_pss_verify($message, $signature);
+ }
+ }
+
+ /**
+ * RSAES-PKCS1-V1_5-ENCRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
+ *
+ * @param string $m
+ * @param bool $pkcs15_compat optional
+ * @throws \LengthException if strlen($m) > $this->k - 11
+ * @return bool|string
+ */
+ private function rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false)
+ {
+ $mLen = strlen($m);
+
+ // Length checking
+
+ if ($mLen > $this->k - 11) {
+ throw new \LengthException('Message too long');
+ }
+
+ // EME-PKCS1-v1_5 encoding
+
+ $psLen = $this->k - $mLen - 3;
+ $ps = '';
+ while (strlen($ps) != $psLen) {
+ $temp = Random::string($psLen - strlen($ps));
+ $temp = str_replace("\x00", '', $temp);
+ $ps .= $temp;
+ }
+ $type = 2;
+ $em = chr(0) . chr($type) . $ps . chr(0) . $m;
+
+ // RSA encryption
+ $m = $this->os2ip($em);
+ $c = $this->rsaep($m);
+ $c = $this->i2osp($c, $this->k);
+
+ // Output the ciphertext C
+
+ return $c;
+ }
+
+ /**
+ * RSAES-OAEP-ENCRYPT
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
+ * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
+ *
+ * @param string $m
+ * @throws \LengthException if strlen($m) > $this->k - 2 * $this->hLen - 2
+ * @return string
+ */
+ private function rsaes_oaep_encrypt($m)
+ {
+ $mLen = strlen($m);
+
+ // Length checking
+
+ // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
+ // be output.
+
+ if ($mLen > $this->k - 2 * $this->hLen - 2) {
+ throw new \LengthException('Message too long');
+ }
+
+ // EME-OAEP encoding
+
+ $lHash = $this->hash->hash($this->label);
+ $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
+ $db = $lHash . $ps . chr(1) . $m;
+ $seed = Random::string($this->hLen);
+ $dbMask = $this->mgf1($seed, $this->k - $this->hLen - 1);
+ $maskedDB = $db ^ $dbMask;
+ $seedMask = $this->mgf1($maskedDB, $this->hLen);
+ $maskedSeed = $seed ^ $seedMask;
+ $em = chr(0) . $maskedSeed . $maskedDB;
+
+ // RSA encryption
+
+ $m = $this->os2ip($em);
+ $c = $this->rsaep($m);
+ $c = $this->i2osp($c, $this->k);
+
+ // Output the ciphertext C
+
+ return $c;
+ }
+
+ /**
+ * RSAEP
+ *
+ * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
+ *
+ * @param BigInteger $m
+ * @return bool|BigInteger
+ */
+ private function rsaep($m)
+ {
+ if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) {
+ throw new \OutOfRangeException('Message representative out of range');
+ }
+ return $this->exponentiate($m);
+ }
+
+ /**
+ * Raw Encryption / Decryption
+ *
+ * Doesn't use padding and is not recommended.
+ *
+ * @param string $m
+ * @return bool|string
+ * @throws \LengthException if strlen($m) > $this->k
+ */
+ private function raw_encrypt($m)
+ {
+ if (strlen($m) > $this->k) {
+ throw new \LengthException('Message too long');
+ }
+
+ $temp = $this->os2ip($m);
+ $temp = $this->rsaep($temp);
+ return $this->i2osp($temp, $this->k);
+ }
+
+ /**
+ * Encryption
+ *
+ * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be.
+ * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
+ * be concatenated together.
+ *
+ * @see self::decrypt()
+ * @param string $plaintext
+ * @return bool|string
+ * @throws \LengthException if the RSA modulus is too short
+ */
+ public function encrypt($plaintext)
+ {
+ switch ($this->encryptionPadding) {
+ case self::ENCRYPTION_NONE:
+ return $this->raw_encrypt($plaintext);
+ case self::ENCRYPTION_PKCS1:
+ return $this->rsaes_pkcs1_v1_5_encrypt($plaintext);
+ //case self::ENCRYPTION_OAEP:
+ default:
+ return $this->rsaes_oaep_encrypt($plaintext);
+ }
+ }
+
+ /**
+ * Returns the public key
+ *
+ * The public key is only returned under two circumstances - if the private key had the public key embedded within it
+ * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
+ * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
+ *
+ * @param string $type
+ * @param array $options optional
+ * @return mixed
+ */
+ public function toString($type, array $options = [])
+ {
+ $type = self::validatePlugin('Keys', $type, 'savePublicKey');
+
+ if ($type == PSS::class) {
+ if ($this->signaturePadding == self::SIGNATURE_PSS) {
+ $options += [
+ 'hash' => $this->hash->getHash(),
+ 'MGFHash' => $this->mgfHash->getHash(),
+ 'saltLength' => $this->getSaltLength()
+ ];
+ } else {
+ throw new UnsupportedFormatException('The PSS format can only be used when the signature method has been explicitly set to PSS');
+ }
+ }
+
+ return $type::savePublicKey($this->modulus, $this->publicExponent, $options);
+ }
+
+ /**
+ * Converts a public key to a private key
+ *
+ * @return RSA
+ */
+ public function asPrivateKey()
+ {
+ $new = new PrivateKey();
+ $new->exponent = $this->exponent;
+ $new->modulus = $this->modulus;
+ $new->k = $this->k;
+ $new->format = $this->format;
+ return $new
+ ->withHash($this->hash->getHash())
+ ->withMGFHash($this->mgfHash->getHash())
+ ->withSaltLength($this->sLen)
+ ->withLabel($this->label)
+ ->withPadding($this->signaturePadding | $this->encryptionPadding);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php
index e039340c5..f813a2eac 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php
@@ -10,28 +10,24 @@
* <?php
* include 'vendor/autoload.php';
*
- * echo bin2hex(\phpseclib\Crypt\Random::string(8));
+ * echo bin2hex(\phpseclib3\Crypt\Random::string(8));
* ?>
* </code>
*
- * @category Crypt
- * @package Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
/**
* Pure-PHP Random Number Generator
*
- * @package Random
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class Random
+abstract class Random
{
/**
* Generate a random string.
@@ -41,75 +37,26 @@ class Random
* eg. for RSA key generation.
*
* @param int $length
+ * @throws \RuntimeException if a symmetric cipher is needed but not loaded
* @return string
*/
- static function string($length)
+ public static function string($length)
{
if (!$length) {
return '';
}
- if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
- try {
- return \random_bytes($length);
- } catch (\Throwable $e) {
- // If a sufficient source of randomness is unavailable, random_bytes() will throw an
- // object that implements the Throwable interface (Exception, TypeError, Error).
- // We don't actually need to do anything here. The string() method should just continue
- // as normal. Note, however, that if we don't have a sufficient source of randomness for
- // random_bytes(), most of the other calls here will fail too, so we'll end up using
- // the PHP implementation.
- }
- }
-
- if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
- // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
- // ie. class_alias is a function that was introduced in PHP 5.3
- if (extension_loaded('mcrypt') && function_exists('class_alias')) {
- return @mcrypt_create_iv($length);
- }
- // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
- // to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
- // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
- // call php_win32_get_random_bytes():
- //
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
- //
- // php_win32_get_random_bytes() is defined thusly:
- //
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
- //
- // we're calling it, all the same, in the off chance that the mcrypt extension is not available
- if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
- return openssl_random_pseudo_bytes($length);
- }
- } else {
- // method 1. the fastest
- if (extension_loaded('openssl')) {
- return openssl_random_pseudo_bytes($length);
- }
- // method 2
- static $fp = true;
- if ($fp === true) {
- // warning's will be output unles the error suppression operator is used. errors such as
- // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
- $fp = @fopen('/dev/urandom', 'rb');
- }
- if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
- $temp = fread($fp, $length);
- if (strlen($temp) == $length) {
- return $temp;
- }
- }
- // method 3. pretty much does the same thing as method 2 per the following url:
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
- // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
- // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
- // restrictions or some such
- if (extension_loaded('mcrypt')) {
- return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
- }
+ try {
+ return random_bytes($length);
+ } catch (\Exception $e) {
+ // random_compat will throw an Exception, which in PHP 5 does not implement Throwable
+ } catch (\Throwable $e) {
+ // If a sufficient source of randomness is unavailable, random_bytes() will throw an
+ // object that implements the Throwable interface (Exception, TypeError, Error).
+ // We don't actually need to do anything here. The string() method should just continue
+ // as normal. Note, however, that if we don't have a sufficient source of randomness for
+ // random_bytes(), most of the other calls here will fail too, so we'll end up using
+ // the PHP implementation.
}
// at this point we have no choice but to use a pure-PHP CSPRNG
@@ -122,11 +69,11 @@ class Random
// PHP isn't low level to be able to use those as sources and on a web server there's not likely
// going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
// however, a ton of people visiting the website. obviously you don't want to base your seeding
- // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
+ // solely on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
// by the user and (2) this isn't just looking at the data sent by the current user - it's based
// on the data sent by all users. one user requests the page and a hash of their info is saved.
// another user visits the page and the serialization of their data is utilized along with the
- // server envirnment stuff and a hash of the previous http request data (which itself utilizes
+ // server environment stuff and a hash of the previous http request data (which itself utilizes
// a hash of the session data before that). certainly an attacker should be assumed to have
// full control over his own http requests. he, however, is not going to have control over
// everyone's http requests.
@@ -146,18 +93,17 @@ class Random
session_cache_limiter('');
session_start();
- $v = $seed = $_SESSION['seed'] = pack('H*', sha1(
- (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
- (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
- (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
- (isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') .
- // as of PHP 8.1 $GLOBALS can't be accessed by reference, which eliminates
- // the need for phpseclib_safe_serialize. see https://wiki.php.net/rfc/restrict_globals_usage
- // for more info
- (version_compare(PHP_VERSION, '8.1.0', '>=') ? serialize($GLOBALS) : phpseclib_safe_serialize($GLOBALS)) .
- phpseclib_safe_serialize($_SESSION) .
- phpseclib_safe_serialize($_OLD_SESSION)
- ));
+ $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') .
+ (isset($_POST) ? self::safe_serialize($_POST) : '') .
+ (isset($_GET) ? self::safe_serialize($_GET) : '') .
+ (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') .
+ // as of PHP 8.1 $GLOBALS can't be accessed by reference, which eliminates
+ // the need for phpseclib_safe_serialize. see https://wiki.php.net/rfc/restrict_globals_usage
+ // for more info
+ (version_compare(PHP_VERSION, '8.1.0', '>=') ? serialize($GLOBALS) : self::safe_serialize($GLOBALS)) .
+ self::safe_serialize($_SESSION) .
+ self::safe_serialize($_OLD_SESSION);
+ $v = $seed = $_SESSION['seed'] = sha1($v, true);
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
@@ -188,38 +134,37 @@ class Random
// http://tools.ietf.org/html/rfc4253#section-7.2
//
// see the is_string($crypto) part for an example of how to expand the keys
- $key = pack('H*', sha1($seed . 'A'));
- $iv = pack('H*', sha1($seed . 'C'));
+ $key = sha1($seed . 'A', true);
+ $iv = sha1($seed . 'C', true);
// ciphers are used as per the nist.gov link below. also, see this link:
//
// http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
switch (true) {
- case class_exists('\phpseclib\Crypt\AES'):
- $crypto = new AES(Base::MODE_CTR);
+ case class_exists('\phpseclib3\Crypt\AES'):
+ $crypto = new AES('ctr');
break;
- case class_exists('\phpseclib\Crypt\Twofish'):
- $crypto = new Twofish(Base::MODE_CTR);
+ case class_exists('\phpseclib3\Crypt\Twofish'):
+ $crypto = new Twofish('ctr');
break;
- case class_exists('\phpseclib\Crypt\Blowfish'):
- $crypto = new Blowfish(Base::MODE_CTR);
+ case class_exists('\phpseclib3\Crypt\Blowfish'):
+ $crypto = new Blowfish('ctr');
break;
- case class_exists('\phpseclib\Crypt\TripleDES'):
- $crypto = new TripleDES(Base::MODE_CTR);
+ case class_exists('\phpseclib3\Crypt\TripleDES'):
+ $crypto = new TripleDES('ctr');
break;
- case class_exists('\phpseclib\Crypt\DES'):
- $crypto = new DES(Base::MODE_CTR);
+ case class_exists('\phpseclib3\Crypt\DES'):
+ $crypto = new DES('ctr');
break;
- case class_exists('\phpseclib\Crypt\RC4'):
+ case class_exists('\phpseclib3\Crypt\RC4'):
$crypto = new RC4();
break;
default:
- user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
- return false;
+ throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded');
}
- $crypto->setKey($key);
- $crypto->setIV($iv);
+ $crypto->setKey(substr($key, 0, $crypto->getKeyLength() >> 3));
+ $crypto->setIV(substr($iv, 0, $crypto->getBlockLength() >> 3));
$crypto->enableContinuousBuffer();
}
@@ -238,23 +183,20 @@ class Random
$i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
$r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
$v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
- $result.= $r;
+ $result .= $r;
}
+
return substr($result, 0, $length);
}
-}
-if (!function_exists('phpseclib_safe_serialize')) {
/**
* Safely serialize variables
*
- * If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
- * PHP 5.3 will emit a warning.
- *
+ * If a class has a private __sleep() it'll emit a warning
+ * @return mixed
* @param mixed $arr
- * @access public
*/
- function phpseclib_safe_serialize(&$arr)
+ private static function safe_serialize(&$arr)
{
if (is_object($arr)) {
return '';
@@ -266,12 +208,12 @@ if (!function_exists('phpseclib_safe_serialize')) {
if (isset($arr['__phpseclib_marker'])) {
return '';
}
- $safearr = array();
+ $safearr = [];
$arr['__phpseclib_marker'] = true;
foreach (array_keys($arr) as $key) {
// do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
if ($key !== '__phpseclib_marker') {
- $safearr[$key] = phpseclib_safe_serialize($arr[$key]);
+ $safearr[$key] = self::safe_serialize($arr[$key]);
}
}
unset($arr['__phpseclib_marker']);
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
index 4665738e1..5ba7cf7fe 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
@@ -30,7 +30,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $rijndael = new \phpseclib\Crypt\Rijndael();
+ * $rijndael = new \phpseclib3\Crypt\Rijndael('ctr');
*
* $rijndael->setKey('abcdefghijklmnop');
*
@@ -44,135 +44,136 @@
* ?>
* </code>
*
- * @category Crypt
- * @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2008 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\BlockCipher;
+use phpseclib3\Exception\BadDecryptionException;
+use phpseclib3\Exception\BadModeException;
+use phpseclib3\Exception\InconsistentSetupException;
+use phpseclib3\Exception\InsufficientSetupException;
/**
* Pure-PHP implementation of Rijndael.
*
- * @package Rijndael
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class Rijndael extends Base
+class Rijndael extends BlockCipher
{
/**
* The mcrypt specific name of the cipher
*
* Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
- * \phpseclib\Crypt\Rijndael determines automatically whether mcrypt is useable
+ * \phpseclib3\Crypt\Rijndael determines automatically whether mcrypt is useable
* or not for the current $block_size/$key_length.
* In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
- * @see \phpseclib\Crypt\Base::engine
+ * @see Common\SymmetricKey::cipher_name_mcrypt
+ * @see Common\SymmetricKey::engine
* @see self::isValidEngine()
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'rijndael-128';
-
- /**
- * The default salt used by setPassword()
- *
- * @see \phpseclib\Crypt\Base::password_default_salt
- * @see \phpseclib\Crypt\Base::setPassword()
- * @var string
- * @access private
- */
- var $password_default_salt = 'phpseclib';
+ protected $cipher_name_mcrypt = 'rijndael-128';
/**
* The Key Schedule
*
- * @see self::_setup()
+ * @see self::setup()
* @var array
- * @access private
*/
- var $w;
+ private $w;
/**
* The Inverse Key Schedule
*
- * @see self::_setup()
+ * @see self::setup()
* @var array
- * @access private
*/
- var $dw;
+ private $dw;
/**
* The Block Length divided by 32
*
- * @see self::setBlockLength()
- * @var int
- * @access private
- * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
+ * {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
* because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
* derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
- * of that, we'll just precompute it once.
+ * of that, we'll just precompute it once.}
+ *
+ * @see self::setBlockLength()
+ * @var int
*/
- var $Nb = 4;
+ private $Nb = 4;
/**
* The Key Length (in bytes)
*
- * @see self::setKeyLength()
- * @var int
- * @access private
- * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
+ * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
* because the encryption / decryption / key schedule creation requires this number and not $key_length. We could
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
- * of that, we'll just precompute it once.
+ * of that, we'll just precompute it once.}
+ *
+ * @see self::setKeyLength()
+ * @var int
*/
- var $key_length = 16;
+ protected $key_length = 16;
/**
* The Key Length divided by 32
*
* @see self::setKeyLength()
* @var int
- * @access private
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
*/
- var $Nk = 4;
+ private $Nk = 4;
/**
* The Number of Rounds
*
+ * {@internal The max value is 14, the min value is 10.}
+ *
* @var int
- * @access private
- * @internal The max value is 14, the min value is 10.
*/
- var $Nr;
+ private $Nr;
/**
* Shift offsets
*
* @var array
- * @access private
*/
- var $c;
+ private $c;
/**
* Holds the last used key- and block_size information
*
* @var array
- * @access private
*/
- var $kl;
+ private $kl;
+
+ /**
+ * Default Constructor.
+ *
+ * @param string $mode
+ * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
+ */
+ public function __construct($mode)
+ {
+ parent::__construct($mode);
+
+ if ($this->mode == self::MODE_STREAM) {
+ throw new BadModeException('Block ciphers cannot be ran in stream mode');
+ }
+ }
/**
* Sets the key length.
*
- * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
- * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
+ * Valid key lengths are 128, 160, 192, 224, and 256.
*
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
* and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
@@ -185,73 +186,111 @@ class Rijndael extends Base
* the mcrypt php extension, even if available.
* This results then in slower encryption.
*
- * @access public
+ * @throws \LengthException if the key length is invalid
* @param int $length
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
- switch (true) {
- case $length <= 128:
- $this->key_length = 16;
- break;
- case $length <= 160:
- $this->key_length = 20;
- break;
- case $length <= 192:
- $this->key_length = 24;
- break;
- case $length <= 224:
- $this->key_length = 28;
+ switch ($length) {
+ case 128:
+ case 160:
+ case 192:
+ case 224:
+ case 256:
+ $this->key_length = $length >> 3;
break;
default:
- $this->key_length = 32;
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
}
parent::setKeyLength($length);
}
/**
+ * Sets the key.
+ *
+ * Rijndael supports five different key lengths
+ *
+ * @see setKeyLength()
+ * @param string $key
+ * @throws \LengthException if the key length isn't supported
+ */
+ public function setKey($key)
+ {
+ switch (strlen($key)) {
+ case 16:
+ case 20:
+ case 24:
+ case 28:
+ case 32:
+ break;
+ default:
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported');
+ }
+
+ parent::setKey($key);
+ }
+
+ /**
* Sets the block length
*
- * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
- * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
+ * Valid block lengths are 128, 160, 192, 224, and 256.
*
- * @access public
* @param int $length
*/
- function setBlockLength($length)
+ public function setBlockLength($length)
{
- $length >>= 5;
- if ($length > 8) {
- $length = 8;
- } elseif ($length < 4) {
- $length = 4;
- }
- $this->Nb = $length;
- $this->block_size = $length << 2;
- $this->changed = true;
- $this->_setEngine();
+ switch ($length) {
+ case 128:
+ case 160:
+ case 192:
+ case 224:
+ case 256:
+ break;
+ default:
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
+ }
+
+ $this->Nb = $length >> 5;
+ $this->block_size = $length >> 3;
+ $this->changed = $this->nonIVChanged = true;
+ $this->setEngine();
}
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::__construct()
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
switch ($engine) {
+ case self::ENGINE_LIBSODIUM:
+ return function_exists('sodium_crypto_aead_aes256gcm_is_available') &&
+ sodium_crypto_aead_aes256gcm_is_available() &&
+ $this->mode == self::MODE_GCM &&
+ $this->key_length == 32 &&
+ $this->nonce && strlen($this->nonce) == 12 &&
+ $this->block_size == 16;
+ case self::ENGINE_OPENSSL_GCM:
+ if (!extension_loaded('openssl')) {
+ return false;
+ }
+ $methods = openssl_get_cipher_methods();
+ return $this->mode == self::MODE_GCM &&
+ version_compare(PHP_VERSION, '7.1.0', '>=') &&
+ in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) &&
+ $this->block_size == 16;
case self::ENGINE_OPENSSL:
if ($this->block_size != 16) {
return false;
}
$this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
- $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode();
+ $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode();
break;
case self::ENGINE_MCRYPT:
$this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
@@ -261,21 +300,20 @@ class Rijndael extends Base
}
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
* Encrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
static $tables;
if (empty($tables)) {
- $tables = &$this->_getTables();
+ $tables = &$this->getTables();
}
$t0 = $tables[0];
$t1 = $tables[1];
@@ -283,7 +321,7 @@ class Rijndael extends Base
$t3 = $tables[3];
$sbox = $tables[4];
- $state = array();
+ $state = [];
$words = unpack('N*', $in);
$c = $this->c;
@@ -305,7 +343,7 @@ class Rijndael extends Base
// equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
- $temp = array();
+ $temp = [];
for ($round = 1; $round < $Nr; ++$round) {
$i = 0; // $c[0] == 0
$j = $c[1];
@@ -351,32 +389,20 @@ class Rijndael extends Base
$l = ($l + 1) % $Nb;
}
- switch ($Nb) {
- case 8:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
- case 7:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
- case 6:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
- case 5:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
- default:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
- }
+ return pack('N*', ...$temp);
}
/**
* Decrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
static $invtables;
if (empty($invtables)) {
- $invtables = &$this->_getInvTables();
+ $invtables = &$this->getInvTables();
}
$dt0 = $invtables[0];
$dt1 = $invtables[1];
@@ -384,7 +410,7 @@ class Rijndael extends Base
$dt3 = $invtables[3];
$isbox = $invtables[4];
- $state = array();
+ $state = [];
$words = unpack('N*', $in);
$c = $this->c;
@@ -398,7 +424,7 @@ class Rijndael extends Base
$state[] = $word ^ $dw[++$wc];
}
- $temp = array();
+ $temp = [];
for ($round = $Nr - 1; $round > 0; --$round) {
$i = 0; // $c[0] == 0
$j = $Nb - $c[1];
@@ -441,41 +467,67 @@ class Rijndael extends Base
$l = ($l + 1) % $Nb;
}
- switch ($Nb) {
- case 8:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
- case 7:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
- case 6:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
- case 5:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
- default:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
+ return pack('N*', ...$temp);
+ }
+
+ /**
+ * Setup the self::ENGINE_INTERNAL $engine
+ *
+ * (re)init, if necessary, the internal cipher $engine and flush all $buffers
+ * Used (only) if $engine == self::ENGINE_INTERNAL
+ *
+ * _setup() will be called each time if $changed === true
+ * typically this happens when using one or more of following public methods:
+ *
+ * - setKey()
+ *
+ * - setIV()
+ *
+ * - disableContinuousBuffer()
+ *
+ * - First run of encrypt() / decrypt() with no init-settings
+ *
+ * {@internal setup() is always called before en/decryption.}
+ *
+ * {@internal Could, but not must, extend by the child Crypt_* class}
+ *
+ * @see self::setKey()
+ * @see self::setIV()
+ * @see self::disableContinuousBuffer()
+ */
+ protected function setup()
+ {
+ if (!$this->changed) {
+ return;
+ }
+
+ parent::setup();
+
+ if (is_string($this->iv) && strlen($this->iv) != $this->block_size) {
+ throw new InconsistentSetupException('The IV length (' . strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')');
}
}
/**
* Setup the key (expansion)
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
static $rcon;
if (!isset($rcon)) {
- $rcon = array(0,
+ $rcon = [0,
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
- );
+ ];
$rcon = array_map('intval', $rcon);
}
@@ -483,7 +535,7 @@ class Rijndael extends Base
// already expanded
return;
}
- $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size);
+ $this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size];
$this->Nk = $this->key_length >> 2;
// see Rijndael-ammended.pdf#page=44
@@ -497,13 +549,13 @@ class Rijndael extends Base
case 4:
case 5:
case 6:
- $this->c = array(0, 1, 2, 3);
+ $this->c = [0, 1, 2, 3];
break;
case 7:
- $this->c = array(0, 1, 2, 4);
+ $this->c = [0, 1, 2, 4];
break;
case 8:
- $this->c = array(0, 1, 3, 4);
+ $this->c = [0, 1, 3, 4];
}
$w = array_values(unpack('N*words', $this->key));
@@ -517,9 +569,9 @@ class Rijndael extends Base
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
$temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord
- $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
+ $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
- $temp = $this->_subWord($temp);
+ $temp = $this->subWord($temp);
}
$w[$i] = $w[$i - $this->Nk] ^ $temp;
}
@@ -531,8 +583,8 @@ class Rijndael extends Base
// 1. Apply the Key Expansion.
// 2. Apply InvMixColumn to all Round Keys except the first and the last one."
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
- list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables();
- $temp = $this->w = $this->dw = array();
+ list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables();
+ $temp = $this->w = $this->dw = [];
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
if ($col == $this->Nb) {
if ($row == 0) {
@@ -541,7 +593,7 @@ class Rijndael extends Base
// subWord + invMixColumn + invSubWord = invMixColumn
$j = 0;
while ($j < $this->Nb) {
- $dw = $this->_subWord($this->w[$row][$j]);
+ $dw = $this->subWord($this->w[$row][$j]);
$temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
$dt1[$dw >> 16 & 0x000000FF] ^
$dt2[$dw >> 8 & 0x000000FF] ^
@@ -576,14 +628,14 @@ class Rijndael extends Base
/**
* Performs S-Box substitutions
*
- * @access private
+ * @return array
* @param int $word
*/
- function _subWord($word)
+ private function subWord($word)
{
static $sbox;
if (empty($sbox)) {
- list(, , , , $sbox) = $this->_getTables();
+ list(, , , , $sbox) = self::getTables();
}
return $sbox[$word & 0x000000FF] |
@@ -595,20 +647,19 @@ class Rijndael extends Base
/**
* Provides the mixColumns and sboxes tables
*
- * @see self::_encryptBlock()
- * @see self::_setupInlineCrypt()
- * @see self::_subWord()
- * @access private
+ * @see self::encryptBlock()
+ * @see self::setupInlineCrypt()
+ * @see self::subWord()
* @return array &$tables
*/
- function &_getTables()
+ protected function &getTables()
{
static $tables;
if (empty($tables)) {
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
// those are the names we'll use.
- $t3 = array_map('intval', array(
+ $t3 = array_map('intval', [
// with array_map('intval', ...) we ensure we have only int's and not
// some slower floats converted by php automatically on high values
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
@@ -643,7 +694,7 @@ class Rijndael extends Base
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
- ));
+ ]);
foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
@@ -651,14 +702,14 @@ class Rijndael extends Base
$t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
}
- $tables = array(
+ $tables = [
// The Precomputed mixColumns tables t0 - t3
$t0,
$t1,
$t2,
$t3,
// The SubByte S-Box
- array(
+ [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
@@ -675,8 +726,8 @@ class Rijndael extends Base
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
- )
- );
+ ]
+ ];
}
return $tables;
}
@@ -684,17 +735,16 @@ class Rijndael extends Base
/**
* Provides the inverse mixColumns and inverse sboxes tables
*
- * @see self::_decryptBlock()
- * @see self::_setupInlineCrypt()
- * @see self::_setupKey()
- * @access private
+ * @see self::decryptBlock()
+ * @see self::setupInlineCrypt()
+ * @see self::setupKey()
* @return array &$tables
*/
- function &_getInvTables()
+ protected function &getInvTables()
{
static $tables;
if (empty($tables)) {
- $dt3 = array_map('intval', array(
+ $dt3 = array_map('intval', [
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
@@ -727,7 +777,7 @@ class Rijndael extends Base
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
- ));
+ ]);
foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF);
@@ -735,14 +785,14 @@ class Rijndael extends Base
$dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
};
- $tables = array(
+ $tables = [
// The Precomputed inverse mixColumns tables dt0 - dt3
$dt0,
$dt1,
$dt2,
$dt3,
// The inverse SubByte S-Box
- array(
+ [
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
@@ -759,8 +809,8 @@ class Rijndael extends Base
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
- )
- );
+ ]
+ ];
}
return $tables;
}
@@ -768,172 +818,219 @@ class Rijndael extends Base
/**
* Setup the performance-optimized function for de/encrypt()
*
- * @see \phpseclib\Crypt\Base::_setupInlineCrypt()
- * @access private
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
*/
- function _setupInlineCrypt()
+ protected function setupInlineCrypt()
{
- // Note: _setupInlineCrypt() will be called only if $this->changed === true
- // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt().
- // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible.
-
- $lambda_functions =& self::_getLambdaFunctions();
-
- // We create max. 10 hi-optimized code for memory reason. Means: For each $key one ultra fast inline-crypt function.
- // (Currently, for Crypt_Rijndael/AES, one generated $lambda_function cost on php5.5@32bit ~80kb unfreeable mem and ~130kb on php5.5@64bit)
- // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one.
- $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
-
- // Generation of a uniqe hash for our generated code
- $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}";
- if ($gen_hi_opt_code) {
- $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
- }
-
- if (!isset($lambda_functions[$code_hash])) {
- switch (true) {
- case $gen_hi_opt_code:
- // The hi-optimized $lambda_functions will use the key-words hardcoded for better performance.
- $w = $this->w;
- $dw = $this->dw;
- $init_encrypt = '';
- $init_decrypt = '';
- break;
- default:
- for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
- $w[] = '$w[' . $i . ']';
- $dw[] = '$dw[' . $i . ']';
- }
- $init_encrypt = '$w = $self->w;';
- $init_decrypt = '$dw = $self->dw;';
- }
+ $w = $this->w;
+ $dw = $this->dw;
+ $init_encrypt = '';
+ $init_decrypt = '';
- $Nr = $this->Nr;
- $Nb = $this->Nb;
- $c = $this->c;
+ $Nr = $this->Nr;
+ $Nb = $this->Nb;
+ $c = $this->c;
- // Generating encrypt code:
- $init_encrypt.= '
- if (empty($tables)) {
- $tables = &$self->_getTables();
- }
- $t0 = $tables[0];
- $t1 = $tables[1];
- $t2 = $tables[2];
- $t3 = $tables[3];
- $sbox = $tables[4];
- ';
-
- $s = 'e';
- $e = 's';
- $wc = $Nb - 1;
-
- // Preround: addRoundKey
- $encrypt_block = '$in = unpack("N*", $in);'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
+ // Generating encrypt code:
+ $init_encrypt .= '
+ if (empty($tables)) {
+ $tables = &$this->getTables();
}
+ $t0 = $tables[0];
+ $t1 = $tables[1];
+ $t2 = $tables[2];
+ $t3 = $tables[3];
+ $sbox = $tables[4];
+ ';
+
+ $s = 'e';
+ $e = 's';
+ $wc = $Nb - 1;
- // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
- for ($round = 1; $round < $Nr; ++$round) {
- list($s, $e) = array($e, $s);
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.=
- '$'.$e.$i.' =
- $t0[($'.$s.$i .' >> 24) & 0xff] ^
- $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
- $t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^
- $t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^
- '.$w[++$wc].";\n";
- }
- }
+ // Preround: addRoundKey
+ $encrypt_block = '$in = unpack("N*", $in);' . "\n";
+ for ($i = 0; $i < $Nb; ++$i) {
+ $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n";
+ }
- // Finalround: subWord + shiftRows + addRoundKey
+ // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
+ for ($round = 1; $round < $Nr; ++$round) {
+ list($s, $e) = [$e, $s];
for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.=
- '$'.$e.$i.' =
- $sbox[ $'.$e.$i.' & 0xff] |
- ($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
- ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
- ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
+ $encrypt_block .=
+ '$' . $e . $i . ' =
+ $t0[($' . $s . $i . ' >> 24) & 0xff] ^
+ $t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^
+ $t2[($' . $s . (($i + $c[2]) % $Nb) . ' >> 8) & 0xff] ^
+ $t3[ $' . $s . (($i + $c[3]) % $Nb) . ' & 0xff] ^
+ ' . $w[++$wc] . ";\n";
}
- $encrypt_block .= '$in = pack("N*"'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.= ',
- ($'.$e.$i .' & '.((int)0xFF000000).') ^
- ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^
- ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^
- ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^
- '.$w[$i]."\n";
+ }
+
+ // Finalround: subWord + shiftRows + addRoundKey
+ for ($i = 0; $i < $Nb; ++$i) {
+ $encrypt_block .=
+ '$' . $e . $i . ' =
+ $sbox[ $' . $e . $i . ' & 0xff] |
+ ($sbox[($' . $e . $i . ' >> 8) & 0xff] << 8) |
+ ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
+ ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
+ }
+ $encrypt_block .= '$in = pack("N*"' . "\n";
+ for ($i = 0; $i < $Nb; ++$i) {
+ $encrypt_block .= ',
+ ($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
+ ($' . $e . (($i + $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
+ ($' . $e . (($i + $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
+ ($' . $e . (($i + $c[3]) % $Nb) . ' & 0x000000FF ) ^
+ ' . $w[$i] . "\n";
+ }
+ $encrypt_block .= ');';
+
+ // Generating decrypt code:
+ $init_decrypt .= '
+ if (empty($invtables)) {
+ $invtables = &$this->getInvTables();
}
- $encrypt_block .= ');';
+ $dt0 = $invtables[0];
+ $dt1 = $invtables[1];
+ $dt2 = $invtables[2];
+ $dt3 = $invtables[3];
+ $isbox = $invtables[4];
+ ';
+
+ $s = 'e';
+ $e = 's';
+ $wc = $Nb - 1;
- // Generating decrypt code:
- $init_decrypt.= '
- if (empty($invtables)) {
- $invtables = &$self->_getInvTables();
- }
- $dt0 = $invtables[0];
- $dt1 = $invtables[1];
- $dt2 = $invtables[2];
- $dt3 = $invtables[3];
- $isbox = $invtables[4];
- ';
-
- $s = 'e';
- $e = 's';
- $wc = $Nb - 1;
-
- // Preround: addRoundKey
- $decrypt_block = '$in = unpack("N*", $in);'."\n";
+ // Preround: addRoundKey
+ $decrypt_block = '$in = unpack("N*", $in);' . "\n";
+ for ($i = 0; $i < $Nb; ++$i) {
+ $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n";
+ }
+
+ // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
+ for ($round = 1; $round < $Nr; ++$round) {
+ list($s, $e) = [$e, $s];
for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
+ $decrypt_block .=
+ '$' . $e . $i . ' =
+ $dt0[($' . $s . $i . ' >> 24) & 0xff] ^
+ $dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^
+ $dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >> 8) & 0xff] ^
+ $dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . ' & 0xff] ^
+ ' . $dw[++$wc] . ";\n";
}
+ }
+
+ // Finalround: subWord + shiftRows + addRoundKey
+ for ($i = 0; $i < $Nb; ++$i) {
+ $decrypt_block .=
+ '$' . $e . $i . ' =
+ $isbox[ $' . $e . $i . ' & 0xff] |
+ ($isbox[($' . $e . $i . ' >> 8) & 0xff] << 8) |
+ ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
+ ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
+ }
+ $decrypt_block .= '$in = pack("N*"' . "\n";
+ for ($i = 0; $i < $Nb; ++$i) {
+ $decrypt_block .= ',
+ ($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
+ ($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
+ ($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
+ ($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' & 0x000000FF ) ^
+ ' . $dw[$i] . "\n";
+ }
+ $decrypt_block .= ');';
+
+ $this->inline_crypt = $this->createInlineCryptFunction(
+ [
+ 'init_crypt' => 'static $tables; static $invtables;',
+ 'init_encrypt' => $init_encrypt,
+ 'init_decrypt' => $init_decrypt,
+ 'encrypt_block' => $encrypt_block,
+ 'decrypt_block' => $decrypt_block
+ ]
+ );
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * @see self::decrypt()
+ * @see parent::encrypt()
+ * @param string $plaintext
+ * @return string
+ */
+ public function encrypt($plaintext)
+ {
+ $this->setup();
+
+ switch ($this->engine) {
+ case self::ENGINE_LIBSODIUM:
+ $this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key);
+ return Strings::shift($this->newtag, strlen($plaintext));
+ case self::ENGINE_OPENSSL_GCM:
+ return openssl_encrypt(
+ $plaintext,
+ 'aes-' . $this->getKeyLength() . '-gcm',
+ $this->key,
+ OPENSSL_RAW_DATA,
+ $this->nonce,
+ $this->newtag,
+ $this->aad
+ );
+ }
+
+ return parent::encrypt($plaintext);
+ }
- // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
- for ($round = 1; $round < $Nr; ++$round) {
- list($s, $e) = array($e, $s);
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.=
- '$'.$e.$i.' =
- $dt0[($'.$s.$i .' >> 24) & 0xff] ^
- $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
- $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^
- $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^
- '.$dw[++$wc].";\n";
+ /**
+ * Decrypts a message.
+ *
+ * @see self::encrypt()
+ * @see parent::decrypt()
+ * @param string $ciphertext
+ * @return string
+ */
+ public function decrypt($ciphertext)
+ {
+ $this->setup();
+
+ switch ($this->engine) {
+ case self::ENGINE_LIBSODIUM:
+ if ($this->oldtag === false) {
+ throw new InsufficientSetupException('Authentication Tag has not been set');
}
- }
+ if (strlen($this->oldtag) != 16) {
+ break;
+ }
+ $plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key);
+ if ($plaintext === false) {
+ $this->oldtag = false;
+ throw new BadDecryptionException('Error decrypting ciphertext with libsodium');
+ }
+ return $plaintext;
+ case self::ENGINE_OPENSSL_GCM:
+ if ($this->oldtag === false) {
+ throw new InsufficientSetupException('Authentication Tag has not been set');
+ }
+ $plaintext = openssl_decrypt(
+ $ciphertext,
+ 'aes-' . $this->getKeyLength() . '-gcm',
+ $this->key,
+ OPENSSL_RAW_DATA,
+ $this->nonce,
+ $this->oldtag,
+ $this->aad
+ );
+ if ($plaintext === false) {
+ $this->oldtag = false;
+ throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL');
+ }
+ return $plaintext;
+ }
- // Finalround: subWord + shiftRows + addRoundKey
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.=
- '$'.$e.$i.' =
- $isbox[ $'.$e.$i.' & 0xff] |
- ($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
- ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
- ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
- }
- $decrypt_block .= '$in = pack("N*"'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.= ',
- ($'.$e.$i. ' & '.((int)0xFF000000).') ^
- ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^
- ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^
- ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^
- '.$dw[$i]."\n";
- }
- $decrypt_block .= ');';
-
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => 'static $tables; static $invtables;',
- 'init_encrypt' => $init_encrypt,
- 'init_decrypt' => $init_decrypt,
- 'encrypt_block' => $encrypt_block,
- 'decrypt_block' => $decrypt_block
- )
- );
- }
- $this->inline_crypt = $lambda_functions[$code_hash];
+ return parent::decrypt($ciphertext);
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php
new file mode 100644
index 000000000..785e7aa2d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Salsa20.php
@@ -0,0 +1,528 @@
+<?php
+
+/**
+ * Pure-PHP implementation of Salsa20.
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2019 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\StreamCipher;
+use phpseclib3\Exception\BadDecryptionException;
+use phpseclib3\Exception\InsufficientSetupException;
+
+/**
+ * Pure-PHP implementation of Salsa20.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Salsa20 extends StreamCipher
+{
+ /**
+ * Part 1 of the state
+ *
+ * @var string|false
+ */
+ protected $p1 = false;
+
+ /**
+ * Part 2 of the state
+ *
+ * @var string|false
+ */
+ protected $p2 = false;
+
+ /**
+ * Key Length (in bytes)
+ *
+ * @var int
+ */
+ protected $key_length = 32; // = 256 bits
+
+ /**
+ * @see \phpseclib3\Crypt\Salsa20::crypt()
+ */
+ const ENCRYPT = 0;
+
+ /**
+ * @see \phpseclib3\Crypt\Salsa20::crypt()
+ */
+ const DECRYPT = 1;
+
+ /**
+ * Encryption buffer for continuous mode
+ *
+ * @var array
+ */
+ protected $enbuffer;
+
+ /**
+ * Decryption buffer for continuous mode
+ *
+ * @var array
+ */
+ protected $debuffer;
+
+ /**
+ * Counter
+ *
+ * @var int
+ */
+ protected $counter = 0;
+
+ /**
+ * Using Generated Poly1305 Key
+ *
+ * @var boolean
+ */
+ protected $usingGeneratedPoly1305Key = false;
+
+ /**
+ * Salsa20 uses a nonce
+ *
+ * @return bool
+ */
+ public function usesNonce()
+ {
+ return true;
+ }
+
+ /**
+ * Sets the key.
+ *
+ * @param string $key
+ * @throws \LengthException if the key length isn't supported
+ */
+ public function setKey($key)
+ {
+ switch (strlen($key)) {
+ case 16:
+ case 32:
+ break;
+ default:
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported');
+ }
+
+ parent::setKey($key);
+ }
+
+ /**
+ * Sets the nonce.
+ *
+ * @param string $nonce
+ */
+ public function setNonce($nonce)
+ {
+ if (strlen($nonce) != 8) {
+ throw new \LengthException('Nonce of size ' . strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce is supported');
+ }
+
+ $this->nonce = $nonce;
+ $this->changed = true;
+ $this->setEngine();
+ }
+
+ /**
+ * Sets the counter.
+ *
+ * @param int $counter
+ */
+ public function setCounter($counter)
+ {
+ $this->counter = $counter;
+ $this->setEngine();
+ }
+
+ /**
+ * Creates a Poly1305 key using the method discussed in RFC8439
+ *
+ * See https://tools.ietf.org/html/rfc8439#section-2.6.1
+ */
+ protected function createPoly1305Key()
+ {
+ if ($this->nonce === false) {
+ throw new InsufficientSetupException('No nonce has been defined');
+ }
+
+ if ($this->key === false) {
+ throw new InsufficientSetupException('No key has been defined');
+ }
+
+ $c = clone $this;
+ $c->setCounter(0);
+ $c->usePoly1305 = false;
+ $block = $c->encrypt(str_repeat("\0", 256));
+ $this->setPoly1305Key(substr($block, 0, 32));
+
+ if ($this->counter == 0) {
+ $this->counter++;
+ }
+ }
+
+ /**
+ * Setup the self::ENGINE_INTERNAL $engine
+ *
+ * (re)init, if necessary, the internal cipher $engine
+ *
+ * _setup() will be called each time if $changed === true
+ * typically this happens when using one or more of following public methods:
+ *
+ * - setKey()
+ *
+ * - setNonce()
+ *
+ * - First run of encrypt() / decrypt() with no init-settings
+ *
+ * @see self::setKey()
+ * @see self::setNonce()
+ * @see self::disableContinuousBuffer()
+ */
+ protected function setup()
+ {
+ if (!$this->changed) {
+ return;
+ }
+
+ $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
+
+ $this->changed = $this->nonIVChanged = false;
+
+ if ($this->nonce === false) {
+ throw new InsufficientSetupException('No nonce has been defined');
+ }
+
+ if ($this->key === false) {
+ throw new InsufficientSetupException('No key has been defined');
+ }
+
+ if ($this->usePoly1305 && !isset($this->poly1305Key)) {
+ $this->usingGeneratedPoly1305Key = true;
+ $this->createPoly1305Key();
+ }
+
+ $key = $this->key;
+ if (strlen($key) == 16) {
+ $constant = 'expand 16-byte k';
+ $key .= $key;
+ } else {
+ $constant = 'expand 32-byte k';
+ }
+
+ $this->p1 = substr($constant, 0, 4) .
+ substr($key, 0, 16) .
+ substr($constant, 4, 4) .
+ $this->nonce .
+ "\0\0\0\0";
+ $this->p2 = substr($constant, 8, 4) .
+ substr($key, 16, 16) .
+ substr($constant, 12, 4);
+ }
+
+ /**
+ * Setup the key (expansion)
+ */
+ protected function setupKey()
+ {
+ // Salsa20 does not utilize this method
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
+ * @see self::crypt()
+ * @param string $plaintext
+ * @return string $ciphertext
+ */
+ public function encrypt($plaintext)
+ {
+ $ciphertext = $this->crypt($plaintext, self::ENCRYPT);
+ if (isset($this->poly1305Key)) {
+ $this->newtag = $this->poly1305($ciphertext);
+ }
+ return $ciphertext;
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
+ * At least if the continuous buffer is disabled.
+ *
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
+ * @see self::crypt()
+ * @param string $ciphertext
+ * @return string $plaintext
+ */
+ public function decrypt($ciphertext)
+ {
+ if (isset($this->poly1305Key)) {
+ if ($this->oldtag === false) {
+ throw new InsufficientSetupException('Authentication Tag has not been set');
+ }
+ $newtag = $this->poly1305($ciphertext);
+ if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
+ $this->oldtag = false;
+ throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
+ }
+ $this->oldtag = false;
+ }
+
+ return $this->crypt($ciphertext, self::DECRYPT);
+ }
+
+ /**
+ * Encrypts a block
+ *
+ * @param string $in
+ */
+ protected function encryptBlock($in)
+ {
+ // Salsa20 does not utilize this method
+ }
+
+ /**
+ * Decrypts a block
+ *
+ * @param string $in
+ */
+ protected function decryptBlock($in)
+ {
+ // Salsa20 does not utilize this method
+ }
+
+ /**
+ * Encrypts or decrypts a message.
+ *
+ * @see self::encrypt()
+ * @see self::decrypt()
+ * @param string $text
+ * @param int $mode
+ * @return string $text
+ */
+ private function crypt($text, $mode)
+ {
+ $this->setup();
+ if (!$this->continuousBuffer) {
+ if ($this->engine == self::ENGINE_OPENSSL) {
+ $iv = pack('V', $this->counter) . $this->p2;
+ return openssl_encrypt(
+ $text,
+ $this->cipher_name_openssl,
+ $this->key,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ }
+ $i = $this->counter;
+ $blocks = str_split($text, 64);
+ foreach ($blocks as &$block) {
+ $block ^= static::salsa20($this->p1 . pack('V', $i++) . $this->p2);
+ }
+ unset($block);
+ return implode('', $blocks);
+ }
+
+ if ($mode == self::ENCRYPT) {
+ $buffer = &$this->enbuffer;
+ } else {
+ $buffer = &$this->debuffer;
+ }
+ if (!strlen($buffer['ciphertext'])) {
+ $ciphertext = '';
+ } else {
+ $ciphertext = $text ^ Strings::shift($buffer['ciphertext'], strlen($text));
+ $text = substr($text, strlen($ciphertext));
+ if (!strlen($text)) {
+ return $ciphertext;
+ }
+ }
+
+ $overflow = strlen($text) % 64; // & 0x3F
+ if ($overflow) {
+ $text2 = Strings::pop($text, $overflow);
+ if ($this->engine == self::ENGINE_OPENSSL) {
+ $iv = pack('V', $buffer['counter']) . $this->p2;
+ // at this point $text should be a multiple of 64
+ $buffer['counter'] += (strlen($text) >> 6) + 1; // ie. divide by 64
+ $encrypted = openssl_encrypt(
+ $text . str_repeat("\0", 64),
+ $this->cipher_name_openssl,
+ $this->key,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ $temp = Strings::pop($encrypted, 64);
+ } else {
+ $blocks = str_split($text, 64);
+ if (strlen($text)) {
+ foreach ($blocks as &$block) {
+ $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
+ }
+ unset($block);
+ }
+ $encrypted = implode('', $blocks);
+ $temp = static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
+ }
+ $ciphertext .= $encrypted . ($text2 ^ $temp);
+ $buffer['ciphertext'] = substr($temp, $overflow);
+ } elseif (!strlen($buffer['ciphertext'])) {
+ if ($this->engine == self::ENGINE_OPENSSL) {
+ $iv = pack('V', $buffer['counter']) . $this->p2;
+ $buffer['counter'] += (strlen($text) >> 6);
+ $ciphertext .= openssl_encrypt(
+ $text,
+ $this->cipher_name_openssl,
+ $this->key,
+ OPENSSL_RAW_DATA,
+ $iv
+ );
+ } else {
+ $blocks = str_split($text, 64);
+ foreach ($blocks as &$block) {
+ $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
+ }
+ unset($block);
+ $ciphertext .= implode('', $blocks);
+ }
+ }
+
+ return $ciphertext;
+ }
+
+ /**
+ * Left Rotate
+ *
+ * @param int $x
+ * @param int $n
+ * @return int
+ */
+ protected static function leftRotate($x, $n)
+ {
+ if (PHP_INT_SIZE == 8) {
+ $r1 = $x << $n;
+ $r1 &= 0xFFFFFFFF;
+ $r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
+ } else {
+ $x = (int) $x;
+ $r1 = $x << $n;
+ $r2 = $x >> (32 - $n);
+ $r2 &= (1 << $n) - 1;
+ }
+ return $r1 | $r2;
+ }
+
+ /**
+ * The quarterround function
+ *
+ * @param int $a
+ * @param int $b
+ * @param int $c
+ * @param int $d
+ */
+ protected static function quarterRound(&$a, &$b, &$c, &$d)
+ {
+ $b ^= self::leftRotate($a + $d, 7);
+ $c ^= self::leftRotate($b + $a, 9);
+ $d ^= self::leftRotate($c + $b, 13);
+ $a ^= self::leftRotate($d + $c, 18);
+ }
+
+ /**
+ * The doubleround function
+ *
+ * @param int $x0 (by reference)
+ * @param int $x1 (by reference)
+ * @param int $x2 (by reference)
+ * @param int $x3 (by reference)
+ * @param int $x4 (by reference)
+ * @param int $x5 (by reference)
+ * @param int $x6 (by reference)
+ * @param int $x7 (by reference)
+ * @param int $x8 (by reference)
+ * @param int $x9 (by reference)
+ * @param int $x10 (by reference)
+ * @param int $x11 (by reference)
+ * @param int $x12 (by reference)
+ * @param int $x13 (by reference)
+ * @param int $x14 (by reference)
+ * @param int $x15 (by reference)
+ */
+ protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
+ {
+ // columnRound
+ static::quarterRound($x0, $x4, $x8, $x12);
+ static::quarterRound($x5, $x9, $x13, $x1);
+ static::quarterRound($x10, $x14, $x2, $x6);
+ static::quarterRound($x15, $x3, $x7, $x11);
+ // rowRound
+ static::quarterRound($x0, $x1, $x2, $x3);
+ static::quarterRound($x5, $x6, $x7, $x4);
+ static::quarterRound($x10, $x11, $x8, $x9);
+ static::quarterRound($x15, $x12, $x13, $x14);
+ }
+
+ /**
+ * The Salsa20 hash function function
+ *
+ * @param string $x
+ */
+ protected static function salsa20($x)
+ {
+ $z = $x = unpack('V*', $x);
+ for ($i = 0; $i < 10; $i++) {
+ static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]);
+ }
+
+ for ($i = 1; $i <= 16; $i++) {
+ $x[$i] += $z[$i];
+ }
+
+ return pack('V*', ...$x);
+ }
+
+ /**
+ * Calculates Poly1305 MAC
+ *
+ * @see self::decrypt()
+ * @see self::encrypt()
+ * @param string $ciphertext
+ * @return string
+ */
+ protected function poly1305($ciphertext)
+ {
+ if (!$this->usingGeneratedPoly1305Key) {
+ return parent::poly1305($this->aad . $ciphertext);
+ } else {
+ /*
+ sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag
+ the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see
+ how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts
+ it:
+
+ $this->newtag = $this->poly1305(
+ $this->aad .
+ pack('V', strlen($this->aad)) . "\0\0\0\0" .
+ $ciphertext .
+ pack('V', strlen($ciphertext)) . "\0\0\0\0"
+ );
+
+ phpseclib opts to use the IETF construction, even when the nonce is 64-bits
+ instead of 96-bits
+ */
+ return parent::poly1305(
+ self::nullPad128($this->aad) .
+ self::nullPad128($ciphertext) .
+ pack('V', strlen($this->aad)) . "\0\0\0\0" .
+ pack('V', strlen($ciphertext)) . "\0\0\0\0"
+ );
+ }
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php
index bf2df95ed..932b7c611 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php
@@ -12,7 +12,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $des = new \phpseclib\Crypt\TripleDES();
+ * $des = new \phpseclib3\Crypt\TripleDES('ctr');
*
* $des->setKey('abcdefghijklmnopqrstuvwx');
*
@@ -26,22 +26,18 @@
* ?>
* </code>
*
- * @category Crypt
- * @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
/**
* Pure-PHP implementation of Triple DES.
*
- * @package TripleDES
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class TripleDES extends DES
{
@@ -55,161 +51,148 @@ class TripleDES extends DES
/**
* Encrypt / decrypt using outer chaining
*
- * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib\Crypt\Base::MODE_CBC.
+ * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib3\Crypt\Common\BlockCipher::MODE_CBC.
*/
const MODE_CBC3 = self::MODE_CBC;
/**
* Key Length (in bytes)
*
- * @see \phpseclib\Crypt\TripleDES::setKeyLength()
+ * @see \phpseclib3\Crypt\TripleDES::setKeyLength()
* @var int
- * @access private
*/
- var $key_length = 24;
-
- /**
- * The default salt used by setPassword()
- *
- * @see \phpseclib\Crypt\Base::password_default_salt
- * @see \phpseclib\Crypt\Base::setPassword()
- * @var string
- * @access private
- */
- var $password_default_salt = 'phpseclib';
+ protected $key_length = 24;
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\DES::cipher_name_mcrypt
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see DES::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'tripledes';
+ protected $cipher_name_mcrypt = 'tripledes';
/**
* Optimizing value while CFB-encrypting
*
- * @see \phpseclib\Crypt\Base::cfb_init_len
+ * @see Common\SymmetricKey::cfb_init_len
* @var int
- * @access private
*/
- var $cfb_init_len = 750;
+ protected $cfb_init_len = 750;
/**
* max possible size of $key
*
* @see self::setKey()
- * @see \phpseclib\Crypt\DES::setKey()
+ * @see DES::setKey()
* @var string
- * @access private
*/
- var $key_length_max = 24;
+ protected $key_length_max = 24;
/**
* Internal flag whether using self::MODE_3CBC or not
*
* @var bool
- * @access private
*/
- var $mode_3cbc;
+ private $mode_3cbc;
/**
- * The \phpseclib\Crypt\DES objects
+ * The \phpseclib3\Crypt\DES objects
*
* Used only if $mode_3cbc === true
*
* @var array
- * @access private
*/
- var $des;
+ private $des;
/**
* Default Constructor.
*
- * Determines whether or not the mcrypt extension should be used.
+ * Determines whether or not the mcrypt or OpenSSL extensions should be used.
*
* $mode could be:
*
- * - \phpseclib\Crypt\Base::MODE_ECB
+ * - ecb
*
- * - \phpseclib\Crypt\Base::MODE_CBC
+ * - cbc
*
- * - \phpseclib\Crypt\Base::MODE_CTR
+ * - ctr
*
- * - \phpseclib\Crypt\Base::MODE_CFB
+ * - cfb
*
- * - \phpseclib\Crypt\Base::MODE_OFB
+ * - ofb
*
- * - \phpseclib\Crypt\TripleDES::MODE_3CBC
+ * - 3cbc
*
- * If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
+ * - cbc3 (same as cbc)
*
- * @see \phpseclib\Crypt\DES::__construct()
- * @see \phpseclib\Crypt\Base::__construct()
- * @param int $mode
- * @access public
+ * @see Crypt\DES::__construct()
+ * @see Common\SymmetricKey::__construct()
+ * @param string $mode
*/
- function __construct($mode = self::MODE_CBC)
+ public function __construct($mode)
{
- switch ($mode) {
+ switch (strtolower($mode)) {
// In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC
// and additional flag us internally as 3CBC
- case self::MODE_3CBC:
- parent::__construct(self::MODE_CBC);
+ case '3cbc':
+ parent::__construct('cbc');
$this->mode_3cbc = true;
// This three $des'es will do the 3CBC work (if $key > 64bits)
- $this->des = array(
- new DES(self::MODE_CBC),
- new DES(self::MODE_CBC),
- new DES(self::MODE_CBC),
- );
+ $this->des = [
+ new DES('cbc'),
+ new DES('cbc'),
+ new DES('cbc'),
+ ];
- // we're going to be doing the padding, ourselves, so disable it in the \phpseclib\Crypt\DES objects
+ // we're going to be doing the padding, ourselves, so disable it in the \phpseclib3\Crypt\DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
break;
+ case 'cbc3':
+ $mode = 'cbc';
+ // fall-through
// If not 3CBC, we init as usual
default:
parent::__construct($mode);
+
+ if ($this->mode == self::MODE_STREAM) {
+ throw new BadModeException('Block ciphers cannot be ran in stream mode');
+ }
}
}
/**
* Test for engine validity
*
- * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
+ * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
- * @see \phpseclib\Crypt\Base::__construct()
+ * @see Common\SymmetricKey::__construct()
* @param int $engine
- * @access public
* @return bool
*/
- function isValidEngine($engine)
+ protected function isValidEngineHelper($engine)
{
if ($engine == self::ENGINE_OPENSSL) {
$this->cipher_name_openssl_ecb = 'des-ede3';
- $mode = $this->_openssl_translate_mode();
+ $mode = $this->openssl_translate_mode();
$this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode;
}
- return parent::isValidEngine($engine);
+ return parent::isValidEngineHelper($engine);
}
/**
- * Sets the initialization vector. (optional)
+ * Sets the initialization vector.
*
- * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. If not explicitly set, it'll be assumed
- * to be all zero's.
+ * SetIV is not required when \phpseclib3\Crypt\Common\SymmetricKey::MODE_ECB is being used.
*
- * @see \phpseclib\Crypt\Base::setIV()
- * @access public
+ * @see Common\SymmetricKey::setIV()
* @param string $iv
*/
- function setIV($iv)
+ public function setIV($iv)
{
parent::setIV($iv);
if ($this->mode_3cbc) {
@@ -222,24 +205,22 @@ class TripleDES extends DES
/**
* Sets the key length.
*
- * Valid key lengths are 64, 128 and 192
+ * Valid key lengths are 128 and 192 bits.
+ *
+ * If you want to use a 64-bit key use DES.php
*
- * @see \phpseclib\Crypt\Base:setKeyLength()
- * @access public
+ * @see Common\SymmetricKey:setKeyLength()
+ * @throws \LengthException if the key length is invalid
* @param int $length
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
- $length >>= 3;
- switch (true) {
- case $length <= 8:
- $this->key_length = 8;
- break;
- case $length <= 16:
- $this->key_length = 16;
+ switch ($length) {
+ case 128:
+ case 192:
break;
default:
- $this->key_length = 24;
+ throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported');
}
parent::setKeyLength($length);
@@ -248,38 +229,40 @@ class TripleDES extends DES
/**
* Sets the key.
*
- * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
- * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
+ * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys.
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
- * If the key is not explicitly set, it'll be assumed to be all null bytes.
- *
- * @access public
- * @see \phpseclib\Crypt\DES::setKey()
- * @see \phpseclib\Crypt\Base::setKey()
+ * @see DES::setKey()
+ * @see Common\SymmetricKey::setKey()
+ * @throws \LengthException if the key length is invalid
* @param string $key
*/
- function setKey($key)
+ public function setKey($key)
{
- $length = $this->explicit_key_length ? $this->key_length : strlen($key);
- if ($length > 8) {
- $key = str_pad(substr($key, 0, 24), 24, chr(0));
- // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
- // http://php.net/function.mcrypt-encrypt#47973
- $key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
- } else {
- $key = str_pad($key, 8, chr(0));
+ if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
+ throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
}
- parent::setKey($key);
-
- // And in case of self::MODE_3CBC:
- // if key <= 64bits we not need the 3 $des to work,
- // because we will then act as regular DES-CBC with just a <= 64bit key.
- // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des.
- if ($this->mode_3cbc && $length > 8) {
- $this->des[0]->setKey(substr($key, 0, 8));
- $this->des[1]->setKey(substr($key, 8, 8));
+
+ switch (strlen($key)) {
+ case 16:
+ $key .= substr($key, 0, 8);
+ break;
+ case 24:
+ break;
+ default:
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported');
+ }
+
+ // copied from self::setKey()
+ $this->key = $key;
+ $this->key_length = strlen($key);
+ $this->changed = $this->nonIVChanged = true;
+ $this->setEngine();
+
+ if ($this->mode_3cbc) {
+ $this->des[0]->setKey(substr($key, 0, 8));
+ $this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8));
}
}
@@ -287,12 +270,11 @@ class TripleDES extends DES
/**
* Encrypts a message.
*
- * @see \phpseclib\Crypt\Base::encrypt()
- * @access public
+ * @see Common\SymmetricKey::encrypt()
* @param string $plaintext
* @return string $cipertext
*/
- function encrypt($plaintext)
+ public function encrypt($plaintext)
{
// parent::en/decrypt() is able to do all the work for all modes and keylengths,
// except for: self::MODE_3CBC (inner chaining CBC) with a key > 64bits
@@ -302,7 +284,7 @@ class TripleDES extends DES
return $this->des[2]->encrypt(
$this->des[1]->decrypt(
$this->des[0]->encrypt(
- $this->_pad($plaintext)
+ $this->pad($plaintext)
)
)
);
@@ -314,15 +296,14 @@ class TripleDES extends DES
/**
* Decrypts a message.
*
- * @see \phpseclib\Crypt\Base::decrypt()
- * @access public
+ * @see Common\SymmetricKey::decrypt()
* @param string $ciphertext
* @return string $plaintext
*/
- function decrypt($ciphertext)
+ public function decrypt($ciphertext)
{
if ($this->mode_3cbc && strlen($this->key) > 8) {
- return $this->_unpad(
+ return $this->unpad(
$this->des[0]->decrypt(
$this->des[1]->encrypt(
$this->des[2]->decrypt(
@@ -365,16 +346,15 @@ class TripleDES extends DES
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
- * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\DES() object changes after each
+ * Put another way, when the continuous buffer is enabled, the state of the \phpseclib3\Crypt\DES() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
- * @see \phpseclib\Crypt\Base::enableContinuousBuffer()
+ * @see Common\SymmetricKey::enableContinuousBuffer()
* @see self::disableContinuousBuffer()
- * @access public
*/
- function enableContinuousBuffer()
+ public function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if ($this->mode_3cbc) {
@@ -389,11 +369,10 @@ class TripleDES extends DES
*
* The default behavior.
*
- * @see \phpseclib\Crypt\Base::disableContinuousBuffer()
+ * @see Common\SymmetricKey::disableContinuousBuffer()
* @see self::enableContinuousBuffer()
- * @access public
*/
- function disableContinuousBuffer()
+ public function disableContinuousBuffer()
{
parent::disableContinuousBuffer();
if ($this->mode_3cbc) {
@@ -406,11 +385,10 @@ class TripleDES extends DES
/**
* Creates the key schedule
*
- * @see \phpseclib\Crypt\DES::_setupKey()
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see DES::setupKey()
+ * @see Common\SymmetricKey::setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
switch (true) {
// if $key <= 64bits we configure our internal pure-php cipher engine
@@ -425,29 +403,27 @@ class TripleDES extends DES
// (only) if 3CBC is used we have, of course, to setup the $des[0-2] keys also separately.
if ($this->mode_3cbc) {
- $this->des[0]->_setupKey();
- $this->des[1]->_setupKey();
- $this->des[2]->_setupKey();
+ $this->des[0]->setupKey();
+ $this->des[1]->setupKey();
+ $this->des[2]->setupKey();
// because $des[0-2] will, now, do all the work we can return here
- // not need unnecessary stress parent::_setupKey() with our, now unused, $key.
+ // not need unnecessary stress parent::setupKey() with our, now unused, $key.
return;
}
}
// setup our key
- parent::_setupKey();
+ parent::setupKey();
}
/**
* Sets the internal crypt engine
*
- * @see \phpseclib\Crypt\Base::__construct()
- * @see \phpseclib\Crypt\Base::setPreferredEngine()
+ * @see Common\SymmetricKey::__construct()
+ * @see Common\SymmetricKey::setPreferredEngine()
* @param int $engine
- * @access public
- * @return int
*/
- function setPreferredEngine($engine)
+ public function setPreferredEngine($engine)
{
if ($this->mode_3cbc) {
$this->des[0]->setPreferredEngine($engine);
@@ -455,6 +431,6 @@ class TripleDES extends DES
$this->des[2]->setPreferredEngine($engine);
}
- return parent::setPreferredEngine($engine);
+ parent::setPreferredEngine($engine);
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
index 1c020481a..141ad0141 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
@@ -16,7 +16,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $twofish = new \phpseclib\Crypt\Twofish();
+ * $twofish = new \phpseclib3\Crypt\Twofish('ctr');
*
* $twofish->setKey('12345678901234567890123456789012');
*
@@ -26,8 +26,6 @@
* ?>
* </code>
*
- * @category Crypt
- * @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
* @copyright 2007 Jim Wigginton
@@ -35,43 +33,41 @@
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Crypt;
+namespace phpseclib3\Crypt;
+
+use phpseclib3\Crypt\Common\BlockCipher;
+use phpseclib3\Exception\BadModeException;
/**
* Pure-PHP implementation of Twofish.
*
- * @package Twofish
* @author Jim Wigginton <terrafrost@php.net>
* @author Hans-Juergen Petrich <petrich@tronic-media.com>
- * @access public
*/
-class Twofish extends Base
+class Twofish extends BlockCipher
{
/**
* The mcrypt specific name of the cipher
*
- * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
+ * @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
- * @access private
*/
- var $cipher_name_mcrypt = 'twofish';
+ protected $cipher_name_mcrypt = 'twofish';
/**
* Optimizing value while CFB-encrypting
*
- * @see \phpseclib\Crypt\Base::cfb_init_len
+ * @see Common\SymmetricKey::cfb_init_len
* @var int
- * @access private
*/
- var $cfb_init_len = 800;
+ protected $cfb_init_len = 800;
/**
* Q-Table
*
* @var array
- * @access private
*/
- var $q0 = array(
+ private static $q0 = [
0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76,
0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38,
0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C,
@@ -104,15 +100,14 @@ class Twofish extends Base
0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4,
0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00,
0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0
- );
+ ];
/**
* Q-Table
*
* @var array
- * @access private
*/
- var $q1 = array(
+ private static $q1 = [
0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8,
0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B,
0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1,
@@ -145,15 +140,14 @@ class Twofish extends Base
0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9,
0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2,
0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91
- );
+ ];
/**
* M-Table
*
* @var array
- * @access private
*/
- var $m0 = array(
+ private static $m0 = [
0xBCBC3275, 0xECEC21F3, 0x202043C6, 0xB3B3C9F4, 0xDADA03DB, 0x02028B7B, 0xE2E22BFB, 0x9E9EFAC8,
0xC9C9EC4A, 0xD4D409D3, 0x18186BE6, 0x1E1E9F6B, 0x98980E45, 0xB2B2387D, 0xA6A6D2E8, 0x2626B74B,
0x3C3C57D6, 0x93938A32, 0x8282EED8, 0x525298FD, 0x7B7BD437, 0xBBBB3771, 0x5B5B97F1, 0x474783E1,
@@ -186,15 +180,14 @@ class Twofish extends Base
0xABABA212, 0x6F6F3EA2, 0xE6E6540D, 0xDBDBF252, 0x92927BBB, 0xB7B7B602, 0x6969CA2F, 0x3939D9A9,
0xD3D30CD7, 0xA7A72361, 0xA2A2AD1E, 0xC3C399B4, 0x6C6C4450, 0x07070504, 0x04047FF6, 0x272746C2,
0xACACA716, 0xD0D07625, 0x50501386, 0xDCDCF756, 0x84841A55, 0xE1E15109, 0x7A7A25BE, 0x1313EF91
- );
+ ];
/**
* M-Table
*
* @var array
- * @access private
*/
- var $m1 = array(
+ private static $m1 = [
0xA9D93939, 0x67901717, 0xB3719C9C, 0xE8D2A6A6, 0x04050707, 0xFD985252, 0xA3658080, 0x76DFE4E4,
0x9A084545, 0x92024B4B, 0x80A0E0E0, 0x78665A5A, 0xE4DDAFAF, 0xDDB06A6A, 0xD1BF6363, 0x38362A2A,
0x0D54E6E6, 0xC6432020, 0x3562CCCC, 0x98BEF2F2, 0x181E1212, 0xF724EBEB, 0xECD7A1A1, 0x6C774141,
@@ -227,15 +220,14 @@ class Twofish extends Base
0xC8FA9E9E, 0xA882D6D6, 0x2BCF6E6E, 0x40507070, 0xDCEB8585, 0xFE750A0A, 0x328A9393, 0xA48DDFDF,
0xCA4C2929, 0x10141C1C, 0x2173D7D7, 0xF0CCB4B4, 0xD309D4D4, 0x5D108A8A, 0x0FE25151, 0x00000000,
0x6F9A1919, 0x9DE01A1A, 0x368F9494, 0x42E6C7C7, 0x4AECC9C9, 0x5EFDD2D2, 0xC1AB7F7F, 0xE0D8A8A8
- );
+ ];
/**
* M-Table
*
* @var array
- * @access private
*/
- var $m2 = array(
+ private static $m2 = [
0xBC75BC32, 0xECF3EC21, 0x20C62043, 0xB3F4B3C9, 0xDADBDA03, 0x027B028B, 0xE2FBE22B, 0x9EC89EFA,
0xC94AC9EC, 0xD4D3D409, 0x18E6186B, 0x1E6B1E9F, 0x9845980E, 0xB27DB238, 0xA6E8A6D2, 0x264B26B7,
0x3CD63C57, 0x9332938A, 0x82D882EE, 0x52FD5298, 0x7B377BD4, 0xBB71BB37, 0x5BF15B97, 0x47E14783,
@@ -268,15 +260,14 @@ class Twofish extends Base
0xAB12ABA2, 0x6FA26F3E, 0xE60DE654, 0xDB52DBF2, 0x92BB927B, 0xB702B7B6, 0x692F69CA, 0x39A939D9,
0xD3D7D30C, 0xA761A723, 0xA21EA2AD, 0xC3B4C399, 0x6C506C44, 0x07040705, 0x04F6047F, 0x27C22746,
0xAC16ACA7, 0xD025D076, 0x50865013, 0xDC56DCF7, 0x8455841A, 0xE109E151, 0x7ABE7A25, 0x139113EF
- );
+ ];
/**
* M-Table
*
* @var array
- * @access private
*/
- var $m3 = array(
+ private static $m3 = [
0xD939A9D9, 0x90176790, 0x719CB371, 0xD2A6E8D2, 0x05070405, 0x9852FD98, 0x6580A365, 0xDFE476DF,
0x08459A08, 0x024B9202, 0xA0E080A0, 0x665A7866, 0xDDAFE4DD, 0xB06ADDB0, 0xBF63D1BF, 0x362A3836,
0x54E60D54, 0x4320C643, 0x62CC3562, 0xBEF298BE, 0x1E12181E, 0x24EBF724, 0xD7A1ECD7, 0x77416C77,
@@ -309,99 +300,88 @@ class Twofish extends Base
0xFA9EC8FA, 0x82D6A882, 0xCF6E2BCF, 0x50704050, 0xEB85DCEB, 0x750AFE75, 0x8A93328A, 0x8DDFA48D,
0x4C29CA4C, 0x141C1014, 0x73D72173, 0xCCB4F0CC, 0x09D4D309, 0x108A5D10, 0xE2510FE2, 0x00000000,
0x9A196F9A, 0xE01A9DE0, 0x8F94368F, 0xE6C742E6, 0xECC94AEC, 0xFDD25EFD, 0xAB7FC1AB, 0xD8A8E0D8
- );
+ ];
/**
* The Key Schedule Array
*
* @var array
- * @access private
*/
- var $K = array();
+ private $K = [];
/**
* The Key depended S-Table 0
*
* @var array
- * @access private
*/
- var $S0 = array();
+ private $S0 = [];
/**
* The Key depended S-Table 1
*
* @var array
- * @access private
*/
- var $S1 = array();
+ private $S1 = [];
/**
* The Key depended S-Table 2
*
* @var array
- * @access private
*/
- var $S2 = array();
+ private $S2 = [];
/**
* The Key depended S-Table 3
*
* @var array
- * @access private
*/
- var $S3 = array();
+ private $S3 = [];
/**
* Holds the last used key
*
* @var array
- * @access private
*/
- var $kl;
+ private $kl;
/**
* The Key Length (in bytes)
*
* @see Crypt_Twofish::setKeyLength()
* @var int
- * @access private
*/
- var $key_length = 16;
+ protected $key_length = 16;
/**
* Default Constructor.
*
- * Determines whether or not the mcrypt extension should be used.
- *
- * $mode could be:
- *
- * - CRYPT_MODE_ECB
- *
- * - CRYPT_MODE_CBC
- *
- * - CRYPT_MODE_CTR
- *
- * - CRYPT_MODE_CFB
- *
- * - CRYPT_MODE_OFB
- *
- * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...)
- *
- * If not explicitly set, CRYPT_MODE_CBC will be used.
- *
- * @param int $mode
- * @access public
+ * @param string $mode
+ * @throws BadModeException if an invalid / unsupported mode is provided
*/
- function __construct($mode = self::MODE_CBC)
+ public function __construct($mode)
{
parent::__construct($mode);
- $this->m0 = array_map('intval', $this->m0);
- $this->m1 = array_map('intval', $this->m1);
- $this->m2 = array_map('intval', $this->m2);
- $this->m3 = array_map('intval', $this->m3);
- $this->q0 = array_map('intval', $this->q0);
- $this->q1 = array_map('intval', $this->q1);
+ if ($this->mode == self::MODE_STREAM) {
+ throw new BadModeException('Block ciphers cannot be ran in stream mode');
+ }
+ }
+
+ /**
+ * Initialize Static Variables
+ */
+ protected static function initialize_static_variables()
+ {
+ if (is_float(self::$m3[0])) {
+ self::$m0 = array_map('intval', self::$m0);
+ self::$m1 = array_map('intval', self::$m1);
+ self::$m2 = array_map('intval', self::$m2);
+ self::$m3 = array_map('intval', self::$m3);
+ self::$q0 = array_map('intval', self::$q0);
+ self::$q1 = array_map('intval', self::$q1);
+ }
+
+ parent::initialize_static_variables();
}
/**
@@ -409,56 +389,75 @@ class Twofish extends Base
*
* Valid key lengths are 128, 192 or 256 bits
*
- * @access public
* @param int $length
*/
- function setKeyLength($length)
+ public function setKeyLength($length)
{
- switch (true) {
- case $length <= 128:
- $this->key_length = 16;
- break;
- case $length <= 192:
- $this->key_length = 24;
+ switch ($length) {
+ case 128:
+ case 192:
+ case 256:
break;
default:
- $this->key_length = 32;
+ throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
}
parent::setKeyLength($length);
}
/**
+ * Sets the key.
+ *
+ * Rijndael supports five different key lengths
+ *
+ * @see setKeyLength()
+ * @param string $key
+ * @throws \LengthException if the key length isn't supported
+ */
+ public function setKey($key)
+ {
+ switch (strlen($key)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported');
+ }
+
+ parent::setKey($key);
+ }
+
+ /**
* Setup the key (expansion)
*
- * @see \phpseclib\Crypt\Base::_setupKey()
- * @access private
+ * @see Common\SymmetricKey::_setupKey()
*/
- function _setupKey()
+ protected function setupKey()
{
if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
// already expanded
return;
}
- $this->kl = array('key' => $this->key);
+ $this->kl = ['key' => $this->key];
/* Key expanding and generating the key-depended s-boxes */
$le_longs = unpack('V*', $this->key);
$key = unpack('C*', $this->key);
- $m0 = $this->m0;
- $m1 = $this->m1;
- $m2 = $this->m2;
- $m3 = $this->m3;
- $q0 = $this->q0;
- $q1 = $this->q1;
+ $m0 = self::$m0;
+ $m1 = self::$m1;
+ $m2 = self::$m2;
+ $m3 = self::$m3;
+ $q0 = self::$q0;
+ $q1 = self::$q1;
- $K = $S0 = $S1 = $S2 = $S3 = array();
+ $K = $S0 = $S1 = $S2 = $S3 = [];
switch (strlen($this->key)) {
case 16:
- list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]);
- list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]);
- for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
+ list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[1], $le_longs[2]);
+ list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[3], $le_longs[4]);
+ for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
$A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^
@@ -468,9 +467,9 @@ class Twofish extends Base
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = $A;
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
@@ -481,10 +480,10 @@ class Twofish extends Base
}
break;
case 24:
- list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]);
- list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]);
- list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]);
- for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
+ list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[1], $le_longs[2]);
+ list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[3], $le_longs[4]);
+ list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[5], $le_longs[6]);
+ for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
$A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
@@ -494,9 +493,9 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = $A;
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
@@ -507,11 +506,11 @@ class Twofish extends Base
}
break;
default: // 32
- list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]);
- list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]);
- list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]);
- list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]);
- for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) {
+ list($sf, $se, $sd, $sc) = $this->mdsrem($le_longs[1], $le_longs[2]);
+ list($sb, $sa, $s9, $s8) = $this->mdsrem($le_longs[3], $le_longs[4]);
+ list($s7, $s6, $s5, $s4) = $this->mdsrem($le_longs[5], $le_longs[6]);
+ list($s3, $s2, $s1, $s0) = $this->mdsrem($le_longs[7], $le_longs[8]);
+ for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
$A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^
$m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
$m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^
@@ -521,9 +520,9 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff);
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = $A;
- $A = $this->safe_intval($A + $B);
+ $A = self::safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
}
for ($i = 0; $i < 256; ++$i) {
@@ -544,12 +543,11 @@ class Twofish extends Base
/**
* _mdsrem function using by the twofish cipher algorithm
*
- * @access private
* @param string $A
* @param string $B
* @return array
*/
- function _mdsrem($A, $B)
+ private function mdsrem($A, $B)
{
// No gain by unrolling this loop.
for ($i = 0; $i < 8; ++$i) {
@@ -558,45 +556,44 @@ class Twofish extends Base
// Shift the others up.
$B = ($B << 8) | (0xff & ($A >> 24));
- $A<<= 8;
+ $A <<= 8;
$u = $t << 1;
// Subtract the modular polynomial on overflow.
if ($t & 0x80) {
- $u^= 0x14d;
+ $u ^= 0x14d;
}
// Remove t * (a * x^2 + 1).
$B ^= $t ^ ($u << 16);
// Form u = a*t + t/a = t*(a + 1/a).
- $u^= 0x7fffffff & ($t >> 1);
+ $u ^= 0x7fffffff & ($t >> 1);
// Add the modular polynomial on underflow.
if ($t & 0x01) {
- $u^= 0xa6 ;
+ $u ^= 0xa6 ;
}
// Remove t * (a + 1/a) * (x^3 + x).
- $B^= ($u << 24) | ($u << 8);
+ $B ^= ($u << 24) | ($u << 8);
}
- return array(
+ return [
0xff & $B >> 24,
0xff & $B >> 16,
0xff & $B >> 8,
- 0xff & $B);
+ 0xff & $B];
}
/**
* Encrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _encryptBlock($in)
+ protected function encryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
@@ -620,9 +617,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff];
- $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
+ $R2 ^= self::safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
- $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
+ $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
$t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^
@@ -632,9 +629,9 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff];
- $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
+ $R0 ^= self::safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
- $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
+ $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ self::safe_intval($t0 + ($t1 << 1) + $K[++$ki]);
}
// @codingStandardsIgnoreStart
@@ -648,11 +645,10 @@ class Twofish extends Base
/**
* Decrypts a block
*
- * @access private
* @param string $in
* @return string
*/
- function _decryptBlock($in)
+ protected function decryptBlock($in)
{
$S0 = $this->S0;
$S1 = $this->S1;
@@ -676,9 +672,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 0xff];
- $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
+ $R3 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
- $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
+ $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]);
$t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^
@@ -688,9 +684,9 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 0xff];
- $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
+ $R1 ^= self::safe_intval($t0 + ($t1 << 1) + $K[--$ki]);
$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
- $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]);
+ $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ self::safe_intval($t0 + $t1 + $K[--$ki]);
}
// @codingStandardsIgnoreStart
@@ -704,149 +700,117 @@ class Twofish extends Base
/**
* Setup the performance-optimized function for de/encrypt()
*
- * @see \phpseclib\Crypt\Base::_setupInlineCrypt()
- * @access private
+ * @see Common\SymmetricKey::_setupInlineCrypt()
*/
- function _setupInlineCrypt()
+ protected function setupInlineCrypt()
{
- $lambda_functions =& self::_getLambdaFunctions();
-
- // Max. 10 Ultra-Hi-optimized inline-crypt functions. After that, we'll (still) create very fast code, but not the ultimate fast one.
- // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit)
- $gen_hi_opt_code = (bool)(count($lambda_functions) < 10);
-
- // Generation of a unique hash for our generated code
- $code_hash = "Crypt_Twofish, {$this->mode}";
- if ($gen_hi_opt_code) {
- $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
- }
-
- $safeint = $this->safe_intval_inline();
-
- if (!isset($lambda_functions[$code_hash])) {
- switch (true) {
- case $gen_hi_opt_code:
- $K = $this->K;
- $init_crypt = '
- static $S0, $S1, $S2, $S3;
- if (!$S0) {
- for ($i = 0; $i < 256; ++$i) {
- $S0[] = (int)$self->S0[$i];
- $S1[] = (int)$self->S1[$i];
- $S2[] = (int)$self->S2[$i];
- $S3[] = (int)$self->S3[$i];
- }
- }
- ';
- break;
- default:
- $K = array();
- for ($i = 0; $i < 40; ++$i) {
- $K[] = '$K_' . $i;
- }
- $init_crypt = '
- $S0 = $self->S0;
- $S1 = $self->S1;
- $S2 = $self->S2;
- $S3 = $self->S3;
- list(' . implode(',', $K) . ') = $self->K;
- ';
+ $K = $this->K;
+ $init_crypt = '
+ static $S0, $S1, $S2, $S3;
+ if (!$S0) {
+ for ($i = 0; $i < 256; ++$i) {
+ $S0[] = (int)$this->S0[$i];
+ $S1[] = (int)$this->S1[$i];
+ $S2[] = (int)$this->S2[$i];
+ $S3[] = (int)$this->S3[$i];
+ }
}
-
- // Generating encrypt code:
- $encrypt_block = '
- $in = unpack("V4", $in);
- $R0 = '.$K[0].' ^ $in[1];
- $R1 = '.$K[1].' ^ $in[2];
- $R2 = '.$K[2].' ^ $in[3];
- $R3 = '.$K[3].' ^ $in[4];
- ';
- for ($ki = 7, $i = 0; $i < 8; ++$i) {
- $encrypt_block.= '
- $t0 = $S0[ $R0 & 0xff] ^
- $S1[($R0 >> 8) & 0xff] ^
- $S2[($R0 >> 16) & 0xff] ^
- $S3[($R0 >> 24) & 0xff];
- $t1 = $S0[($R1 >> 24) & 0xff] ^
- $S1[ $R1 & 0xff] ^
- $S2[($R1 >> 8) & 0xff] ^
- $S3[($R1 >> 16) & 0xff];
+ ';
+
+ $safeint = self::safe_intval_inline();
+
+ // Generating encrypt code:
+ $encrypt_block = '
+ $in = unpack("V4", $in);
+ $R0 = ' . $K[0] . ' ^ $in[1];
+ $R1 = ' . $K[1] . ' ^ $in[2];
+ $R2 = ' . $K[2] . ' ^ $in[3];
+ $R3 = ' . $K[3] . ' ^ $in[4];
+ ';
+ for ($ki = 7, $i = 0; $i < 8; ++$i) {
+ $encrypt_block .= '
+ $t0 = $S0[ $R0 & 0xff] ^
+ $S1[($R0 >> 8) & 0xff] ^
+ $S2[($R0 >> 16) & 0xff] ^
+ $S3[($R0 >> 24) & 0xff];
+ $t1 = $S0[($R1 >> 24) & 0xff] ^
+ $S1[ $R1 & 0xff] ^
+ $S2[($R1 >> 8) & 0xff] ^
+ $S3[($R1 >> 16) & 0xff];
$R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
- $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
- $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
-
- $t0 = $S0[ $R2 & 0xff] ^
- $S1[($R2 >> 8) & 0xff] ^
- $S2[($R2 >> 16) & 0xff] ^
- $S3[($R2 >> 24) & 0xff];
- $t1 = $S0[($R3 >> 24) & 0xff] ^
- $S1[ $R3 & 0xff] ^
- $S2[($R3 >> 8) & 0xff] ^
- $S3[($R3 >> 16) & 0xff];
- $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
- $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
- $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
- ';
- }
- $encrypt_block.= '
- $in = pack("V4", ' . $K[4] . ' ^ $R2,
- ' . $K[5] . ' ^ $R3,
- ' . $K[6] . ' ^ $R0,
- ' . $K[7] . ' ^ $R1);
+ $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
+ $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
+
+ $t0 = $S0[ $R2 & 0xff] ^
+ $S1[($R2 >> 8) & 0xff] ^
+ $S2[($R2 >> 16) & 0xff] ^
+ $S3[($R2 >> 24) & 0xff];
+ $t1 = $S0[($R3 >> 24) & 0xff] ^
+ $S1[ $R3 & 0xff] ^
+ $S2[($R3 >> 8) & 0xff] ^
+ $S3[($R3 >> 16) & 0xff];
+ $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
+ $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
+ $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . ';
';
-
- // Generating decrypt code:
- $decrypt_block = '
- $in = unpack("V4", $in);
- $R0 = '.$K[4].' ^ $in[1];
- $R1 = '.$K[5].' ^ $in[2];
- $R2 = '.$K[6].' ^ $in[3];
- $R3 = '.$K[7].' ^ $in[4];
- ';
- for ($ki = 40, $i = 0; $i < 8; ++$i) {
- $decrypt_block.= '
- $t0 = $S0[$R0 & 0xff] ^
- $S1[$R0 >> 8 & 0xff] ^
- $S2[$R0 >> 16 & 0xff] ^
- $S3[$R0 >> 24 & 0xff];
- $t1 = $S0[$R1 >> 24 & 0xff] ^
- $S1[$R1 & 0xff] ^
- $S2[$R1 >> 8 & 0xff] ^
- $S3[$R1 >> 16 & 0xff];
- $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
- $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
- $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
-
- $t0 = $S0[$R2 & 0xff] ^
- $S1[$R2 >> 8 & 0xff] ^
- $S2[$R2 >> 16 & 0xff] ^
- $S3[$R2 >> 24 & 0xff];
- $t1 = $S0[$R3 >> 24 & 0xff] ^
- $S1[$R3 & 0xff] ^
- $S2[$R3 >> 8 & 0xff] ^
- $S3[$R3 >> 16 & 0xff];
- $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
- $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
- $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . ';
- ';
- }
- $decrypt_block.= '
- $in = pack("V4", ' . $K[0] . ' ^ $R2,
- ' . $K[1] . ' ^ $R3,
- ' . $K[2] . ' ^ $R0,
- ' . $K[3] . ' ^ $R1);
+ }
+ $encrypt_block .= '
+ $in = pack("V4", ' . $K[4] . ' ^ $R2,
+ ' . $K[5] . ' ^ $R3,
+ ' . $K[6] . ' ^ $R0,
+ ' . $K[7] . ' ^ $R1);
+ ';
+
+ // Generating decrypt code:
+ $decrypt_block = '
+ $in = unpack("V4", $in);
+ $R0 = ' . $K[4] . ' ^ $in[1];
+ $R1 = ' . $K[5] . ' ^ $in[2];
+ $R2 = ' . $K[6] . ' ^ $in[3];
+ $R3 = ' . $K[7] . ' ^ $in[4];
+ ';
+ for ($ki = 40, $i = 0; $i < 8; ++$i) {
+ $decrypt_block .= '
+ $t0 = $S0[$R0 & 0xff] ^
+ $S1[$R0 >> 8 & 0xff] ^
+ $S2[$R0 >> 16 & 0xff] ^
+ $S3[$R0 >> 24 & 0xff];
+ $t1 = $S0[$R1 >> 24 & 0xff] ^
+ $S1[$R1 & 0xff] ^
+ $S2[$R1 >> 8 & 0xff] ^
+ $S3[$R1 >> 16 & 0xff];
+ $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
+ $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
+ $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . ';
+
+ $t0 = $S0[$R2 & 0xff] ^
+ $S1[$R2 >> 8 & 0xff] ^
+ $S2[$R2 >> 16 & 0xff] ^
+ $S3[$R2 >> 24 & 0xff];
+ $t1 = $S0[$R3 >> 24 & 0xff] ^
+ $S1[$R3 & 0xff] ^
+ $S2[$R3 >> 8 & 0xff] ^
+ $S3[$R3 >> 16 & 0xff];
+ $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . ';
+ $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
+ $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + ' . $K[--$ki] . ')') . ';
';
-
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => $init_crypt,
- 'init_encrypt' => '',
- 'init_decrypt' => '',
- 'encrypt_block' => $encrypt_block,
- 'decrypt_block' => $decrypt_block
- )
- );
}
- $this->inline_crypt = $lambda_functions[$code_hash];
+ $decrypt_block .= '
+ $in = pack("V4", ' . $K[0] . ' ^ $R2,
+ ' . $K[1] . ' ^ $R3,
+ ' . $K[2] . ' ^ $R0,
+ ' . $K[3] . ' ^ $R1);
+ ';
+
+ $this->inline_crypt = $this->createInlineCryptFunction(
+ [
+ 'init_crypt' => $init_crypt,
+ 'init_encrypt' => '',
+ 'init_decrypt' => '',
+ 'encrypt_block' => $encrypt_block,
+ 'decrypt_block' => $decrypt_block
+ ]
+ );
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php
new file mode 100644
index 000000000..1aabcae09
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * BadConfigurationException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * BadConfigurationException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class BadConfigurationException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php
new file mode 100644
index 000000000..88331dce0
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadDecryptionException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * BadDecryptionException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * BadDecryptionException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class BadDecryptionException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php
new file mode 100644
index 000000000..87689b224
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/BadModeException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * BadModeException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * BadModeException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class BadModeException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php
new file mode 100644
index 000000000..6aaccbad6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/ConnectionClosedException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * ConnectionClosedException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * ConnectionClosedException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class ConnectionClosedException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php
new file mode 100644
index 000000000..66e727091
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * FileNotFoundException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * FileNotFoundException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class FileNotFoundException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php
new file mode 100644
index 000000000..23c38fb02
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/InconsistentSetupException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * InconsistentSetupException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * InconsistentSetupException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class InconsistentSetupException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php
new file mode 100644
index 000000000..4f4114d70
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/InsufficientSetupException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * InsufficientSetupException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * InsufficientSetupException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class InsufficientSetupException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php
new file mode 100644
index 000000000..b96ead1e3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/InvalidPacketLengthException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace phpseclib3\Exception;
+
+/**
+ * Indicates an absent or malformed packet length header
+ */
+class InvalidPacketLengthException extends ConnectionClosedException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php
new file mode 100644
index 000000000..7ec2fe9b9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/NoKeyLoadedException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * NoKeyLoadedException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * NoKeyLoadedException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class NoKeyLoadedException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php
new file mode 100644
index 000000000..b3ea8f384
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * NoSupportedAlgorithmsException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * NoSupportedAlgorithmsException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class NoSupportedAlgorithmsException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php
new file mode 100644
index 000000000..8701f8d76
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/TimeoutException.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace phpseclib3\Exception;
+
+/**
+ * Indicates a timeout awaiting server response
+ */
+class TimeoutException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php
new file mode 100644
index 000000000..bfa005b4f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnableToConnectException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * UnableToConnectException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * UnableToConnectException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class UnableToConnectException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php
new file mode 100644
index 000000000..210a9a5ce
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * UnsupportedAlgorithmException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * UnsupportedAlgorithmException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class UnsupportedAlgorithmException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php
new file mode 100644
index 000000000..99152152c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedCurveException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * UnsupportedCurveException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * UnsupportedCurveException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class UnsupportedCurveException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php
new file mode 100644
index 000000000..e207d7e21
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedFormatException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * UnsupportedFormatException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * UnsupportedFormatException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class UnsupportedFormatException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php
new file mode 100644
index 000000000..9a1154445
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Exception/UnsupportedOperationException.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * UnsupportedOperationException
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\Exception;
+
+/**
+ * UnsupportedOperationException
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class UnsupportedOperationException extends \RuntimeException
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php b/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php
index b6874d357..41477ba5d 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php
@@ -5,27 +5,23 @@
*
* PHP version 5
*
- * If you call read() in \phpseclib\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
+ * If you call read() in \phpseclib3\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
* They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC). They tell a
* {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
- * color to display them in, etc. \phpseclib\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
+ * color to display them in, etc. \phpseclib3\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
*
- * @category File
- * @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\File;
+namespace phpseclib3\File;
/**
* Pure-PHP ANSI Decoder
*
- * @package ANSI
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class ANSI
{
@@ -33,137 +29,120 @@ class ANSI
* Max Width
*
* @var int
- * @access private
*/
- var $max_x;
+ private $max_x;
/**
* Max Height
*
* @var int
- * @access private
*/
- var $max_y;
+ private $max_y;
/**
* Max History
*
* @var int
- * @access private
*/
- var $max_history;
+ private $max_history;
/**
* History
*
* @var array
- * @access private
*/
- var $history;
+ private $history;
/**
* History Attributes
*
* @var array
- * @access private
*/
- var $history_attrs;
+ private $history_attrs;
/**
* Current Column
*
* @var int
- * @access private
*/
- var $x;
+ private $x;
/**
* Current Row
*
* @var int
- * @access private
*/
- var $y;
+ private $y;
/**
* Old Column
*
* @var int
- * @access private
*/
- var $old_x;
+ private $old_x;
/**
* Old Row
*
* @var int
- * @access private
*/
- var $old_y;
+ private $old_y;
/**
* An empty attribute cell
*
* @var object
- * @access private
*/
- var $base_attr_cell;
+ private $base_attr_cell;
/**
* The current attribute cell
*
* @var object
- * @access private
*/
- var $attr_cell;
+ private $attr_cell;
/**
* An empty attribute row
*
* @var array
- * @access private
*/
- var $attr_row;
+ private $attr_row;
/**
* The current screen text
*
- * @var array
- * @access private
+ * @var list<string>
*/
- var $screen;
+ private $screen;
/**
* The current screen attributes
*
* @var array
- * @access private
*/
- var $attrs;
+ private $attrs;
/**
* Current ANSI code
*
* @var string
- * @access private
*/
- var $ansi;
+ private $ansi;
/**
* Tokenization
*
* @var array
- * @access private
*/
- var $tokenization;
+ private $tokenization;
/**
* Default Constructor.
*
- * @return \phpseclib\File\ANSI
- * @access public
+ * @return ANSI
*/
- function __construct()
+ public function __construct()
{
$attr_cell = new \stdClass();
$attr_cell->bold = false;
@@ -186,14 +165,13 @@ class ANSI
*
* @param int $x
* @param int $y
- * @access public
*/
- function setDimensions($x, $y)
+ public function setDimensions($x, $y)
{
$this->max_x = $x - 1;
$this->max_y = $y - 1;
$this->x = $this->y = 0;
- $this->history = $this->history_attrs = array();
+ $this->history = $this->history_attrs = [];
$this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell);
$this->screen = array_fill(0, $this->max_y + 1, '');
$this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row);
@@ -204,9 +182,8 @@ class ANSI
* Set the number of lines that should be logged past the terminal height
*
* @param int $history
- * @access public
*/
- function setHistory($history)
+ public function setHistory($history)
{
$this->max_history = $history;
}
@@ -215,9 +192,8 @@ class ANSI
* Load a string
*
* @param string $source
- * @access public
*/
- function loadString($source)
+ public function loadString($source)
{
$this->setDimensions($this->max_x + 1, $this->max_y + 1);
$this->appendString($source);
@@ -227,14 +203,13 @@ class ANSI
* Appdend a string
*
* @param string $source
- * @access public
*/
- function appendString($source)
+ public function appendString($source)
{
- $this->tokenization = array('');
+ $this->tokenization = [''];
for ($i = 0; $i < strlen($source); $i++) {
if (strlen($this->ansi)) {
- $this->ansi.= $source[$i];
+ $this->ansi .= $source[$i];
$chr = ord($source[$i]);
// http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
// single character CSI's not currently supported
@@ -268,6 +243,7 @@ class ANSI
array_shift($this->history);
array_shift($this->history_attrs);
}
+ // fall-through
case "\x1B[K": // Clear screen from cursor right
$this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x);
@@ -282,28 +258,28 @@ class ANSI
case "\x1B(B": // set united states g0 character set
break;
case "\x1BE": // Move to next line
- $this->_newLine();
+ $this->newLine();
$this->x = 0;
break;
default:
switch (true) {
case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): // Move cursor down n lines
$this->old_y = $this->y;
- $this->y+= $match[1];
+ $this->y += (int) $match[1];
break;
case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): // Move cursor to screen location v,h
$this->old_x = $this->x;
$this->old_y = $this->y;
$this->x = $match[2] - 1;
- $this->y = $match[1] - 1;
+ $this->y = (int) $match[1] - 1;
break;
case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): // Move cursor right n lines
$this->old_x = $this->x;
- $this->x+= $match[1];
+ $this->x += $match[1];
break;
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x;
- $this->x-= $match[1];
+ $this->x -= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
@@ -376,13 +352,13 @@ class ANSI
continue;
}
- $this->tokenization[count($this->tokenization) - 1].= $source[$i];
+ $this->tokenization[count($this->tokenization) - 1] .= $source[$i];
switch ($source[$i]) {
case "\r":
$this->x = 0;
break;
case "\n":
- $this->_newLine();
+ $this->newLine();
break;
case "\x08": // backspace
if ($this->x) {
@@ -403,7 +379,7 @@ class ANSI
//if (!strlen($this->tokenization[count($this->tokenization) - 1])) {
// array_pop($this->tokenization);
//}
- $this->ansi.= "\x1B";
+ $this->ansi .= "\x1B";
break;
default:
$this->attrs[$this->y][$this->x] = clone $this->attr_cell;
@@ -419,7 +395,7 @@ class ANSI
if ($this->x > $this->max_x) {
$this->x = 0;
- $this->_newLine();
+ $this->newLine();
} else {
$this->x++;
}
@@ -432,19 +408,18 @@ class ANSI
*
* Also update the $this->screen and $this->history buffers
*
- * @access private
*/
- function _newLine()
+ private function newLine()
{
//if ($this->y < $this->max_y) {
// $this->y++;
//}
while ($this->y >= $this->max_y) {
- $this->history = array_merge($this->history, array(array_shift($this->screen)));
+ $this->history = array_merge($this->history, [array_shift($this->screen)]);
$this->screen[] = '';
- $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs)));
+ $this->history_attrs = array_merge($this->history_attrs, [array_shift($this->attrs)]);
$this->attrs[] = $this->attr_row;
if (count($this->history) >= $this->max_history) {
@@ -460,10 +435,12 @@ class ANSI
/**
* Returns the current coordinate without preformating
*
- * @access private
+ * @param \stdClass $last_attr
+ * @param \stdClass $cur_attr
+ * @param string $char
* @return string
*/
- function _processCoordinate($last_attr, $cur_attr, $char)
+ private function processCoordinate(\stdClass $last_attr, \stdClass $cur_attr, $char)
{
$output = '';
@@ -471,7 +448,7 @@ class ANSI
$close = $open = '';
if ($last_attr->foreground != $cur_attr->foreground) {
if ($cur_attr->foreground != 'white') {
- $open.= '<span style="color: ' . $cur_attr->foreground . '">';
+ $open .= '<span style="color: ' . $cur_attr->foreground . '">';
}
if ($last_attr->foreground != 'white') {
$close = '</span>' . $close;
@@ -479,7 +456,7 @@ class ANSI
}
if ($last_attr->background != $cur_attr->background) {
if ($cur_attr->background != 'black') {
- $open.= '<span style="background: ' . $cur_attr->background . '">';
+ $open .= '<span style="background: ' . $cur_attr->background . '">';
}
if ($last_attr->background != 'black') {
$close = '</span>' . $close;
@@ -487,29 +464,29 @@ class ANSI
}
if ($last_attr->bold != $cur_attr->bold) {
if ($cur_attr->bold) {
- $open.= '<b>';
+ $open .= '<b>';
} else {
$close = '</b>' . $close;
}
}
if ($last_attr->underline != $cur_attr->underline) {
if ($cur_attr->underline) {
- $open.= '<u>';
+ $open .= '<u>';
} else {
$close = '</u>' . $close;
}
}
if ($last_attr->blink != $cur_attr->blink) {
if ($cur_attr->blink) {
- $open.= '<blink>';
+ $open .= '<blink>';
} else {
$close = '</blink>' . $close;
}
}
- $output.= $close . $open;
+ $output .= $close . $open;
}
- $output.= htmlspecialchars($char);
+ $output .= htmlspecialchars($char);
return $output;
}
@@ -517,59 +494,56 @@ class ANSI
/**
* Returns the current screen without preformating
*
- * @access private
* @return string
*/
- function _getScreen()
+ private function getScreenHelper()
{
$output = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i <= $this->max_y; $i++) {
for ($j = 0; $j <= $this->max_x; $j++) {
$cur_attr = $this->attrs[$i][$j];
- $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
+ $output .= $this->processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : '');
$last_attr = $this->attrs[$i][$j];
}
- $output.= "\r\n";
+ $output .= "\r\n";
}
$output = substr($output, 0, -2);
// close any remaining open tags
- $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, '');
+ $output .= $this->processCoordinate($last_attr, $this->base_attr_cell, '');
return rtrim($output);
}
/**
* Returns the current screen
*
- * @access public
* @return string
*/
- function getScreen()
+ public function getScreen()
{
- return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->_getScreen() . '</pre>';
+ return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $this->getScreenHelper() . '</pre>';
}
/**
* Returns the current screen and the x previous lines
*
- * @access public
* @return string
*/
- function getHistory()
+ public function getHistory()
{
$scrollback = '';
$last_attr = $this->base_attr_cell;
for ($i = 0; $i < count($this->history); $i++) {
for ($j = 0; $j <= $this->max_x + 1; $j++) {
$cur_attr = $this->history_attrs[$i][$j];
- $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
+ $scrollback .= $this->processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : '');
$last_attr = $this->history_attrs[$i][$j];
}
- $scrollback.= "\r\n";
+ $scrollback .= "\r\n";
}
$base_attr_cell = $this->base_attr_cell;
$this->base_attr_cell = $last_attr;
- $scrollback.= $this->_getScreen();
+ $scrollback .= $this->getScreen();
$this->base_attr_cell = $base_attr_cell;
return '<pre width="' . ($this->max_x + 1) . '" style="color: white; background: black">' . $scrollback . '</span></pre>';
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
index dba99de73..2f1fb8a67 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
@@ -9,52 +9,38 @@
* utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded
* DER blobs.
*
- * \phpseclib\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
+ * \phpseclib3\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
*
* Uses the 1988 ASN.1 syntax.
*
- * @category File
- * @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\File;
+namespace phpseclib3\File;
-use phpseclib\File\ASN1\Element;
-use phpseclib\Math\BigInteger;
-use DateTime;
-use DateTimeZone;
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\File\ASN1\Element;
+use phpseclib3\Math\BigInteger;
/**
* Pure-PHP ASN.1 Parser
*
- * @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class ASN1
+abstract class ASN1
{
- /**#@+
- * Tag Classes
- *
- * @access private
- * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
- */
+ // Tag Classes
+ // http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
const CLASS_UNIVERSAL = 0;
const CLASS_APPLICATION = 1;
const CLASS_CONTEXT_SPECIFIC = 2;
const CLASS_PRIVATE = 3;
- /**#@-*/
- /**#@+
- * Tag Classes
- *
- * @access private
- * @link http://www.obj-sys.com/asn1tutorial/node124.html
- */
+ // Tag Classes
+ // http://www.obj-sys.com/asn1tutorial/node124.html
const TYPE_BOOLEAN = 1;
const TYPE_INTEGER = 2;
const TYPE_BIT_STRING = 3;
@@ -70,13 +56,9 @@ class ASN1
//const TYPE_RELATIVE_OID = 13;
const TYPE_SEQUENCE = 16; // SEQUENCE OF
const TYPE_SET = 17; // SET OF
- /**#@-*/
- /**#@+
- * More Tag Classes
- *
- * @access private
- * @link http://www.obj-sys.com/asn1tutorial/node10.html
- */
+
+ // More Tag Classes
+ // http://www.obj-sys.com/asn1tutorial/node10.html
const TYPE_NUMERIC_STRING = 18;
const TYPE_PRINTABLE_STRING = 19;
const TYPE_TELETEX_STRING = 20; // T61String
@@ -90,47 +72,34 @@ class ASN1
const TYPE_UNIVERSAL_STRING = 28;
//const TYPE_CHARACTER_STRING = 29;
const TYPE_BMP_STRING = 30;
- /**#@-*/
- /**#@+
- * Tag Aliases
- *
- * These tags are kinda place holders for other tags.
- *
- * @access private
- */
+ // Tag Aliases
+ // These tags are kinda place holders for other tags.
const TYPE_CHOICE = -1;
const TYPE_ANY = -2;
- /**#@-*/
/**
- * ASN.1 object identifier
+ * ASN.1 object identifiers
*
* @var array
- * @access private
* @link http://en.wikipedia.org/wiki/Object_identifier
*/
- var $oids = array();
+ private static $oids = [];
/**
- * Default date format
+ * ASN.1 object identifier reverse mapping
*
- * @var string
- * @access private
- * @link http://php.net/class.datetime
+ * @var array
*/
- var $format = 'D, d M Y H:i:s O';
+ private static $reverseOIDs = [];
/**
* Default date format
*
- * @var array
- * @access private
- * @see self::setTimeFormat()
- * @see self::asn1map()
+ * @var string
* @link http://php.net/class.datetime
*/
- var $encoded;
+ private static $format = 'D, d M Y H:i:s O';
/**
* Filters
@@ -138,10 +107,9 @@ class ASN1
* If the mapping type is self::TYPE_ANY what do we actually encode it as?
*
* @var array
- * @access private
- * @see self::_encode_der()
+ * @see self::encode_der()
*/
- var $filters;
+ private static $filters;
/**
* Current Location of most recent ASN.1 encode process
@@ -151,19 +119,28 @@ class ASN1
* @var array
* @see self::encode_der()
*/
- var $location;
+ private static $location;
+
+ /**
+ * DER Encoded String
+ *
+ * In case we need to create ASN1\Element object's..
+ *
+ * @var string
+ * @see self::decodeDER()
+ */
+ private static $encoded;
/**
* Type mapping table for the ANY type.
*
- * Structured or unknown types are mapped to a \phpseclib\File\ASN1\Element.
+ * Structured or unknown types are mapped to a \phpseclib3\File\ASN1\Element.
* Unambiguous types get the direct mapping (int/real/bool).
* Others are mapped as a choice, with an extra indexing level.
*
* @var array
- * @access public
*/
- var $ANYmap = array(
+ const ANY_MAP = [
self::TYPE_BOOLEAN => true,
self::TYPE_INTEGER => true,
self::TYPE_BIT_STRING => 'bitString',
@@ -186,7 +163,7 @@ class ASN1
self::TYPE_UNIVERSAL_STRING => 'universalString',
//self::TYPE_CHARACTER_STRING => 'characterString',
self::TYPE_BMP_STRING => 'bmpString'
- );
+ ];
/**
* String type to character size mapping table.
@@ -195,9 +172,8 @@ class ASN1
* size == 0 indicates variable length encoding.
*
* @var array
- * @access public
*/
- var $stringTypeSize = array(
+ const STRING_TYPE_SIZE = [
self::TYPE_UTF8_STRING => 0,
self::TYPE_BMP_STRING => 2,
self::TYPE_UNIVERSAL_STRING => 4,
@@ -205,26 +181,30 @@ class ASN1
self::TYPE_TELETEX_STRING => 1,
self::TYPE_IA5_STRING => 1,
self::TYPE_VISIBLE_STRING => 1,
- );
+ ];
/**
* Parse BER-encoding
*
* Serves a similar purpose to openssl's asn1parse
*
- * @param string $encoded
- * @return array
- * @access public
+ * @param Element|string $encoded
+ * @return ?array
*/
- function decodeBER($encoded)
+ public static function decodeBER($encoded)
{
if ($encoded instanceof Element) {
$encoded = $encoded->element;
}
- $this->encoded = $encoded;
- // encapsulate in an array for BC with the old decodeBER
- return array($this->_decode_ber($encoded));
+ self::$encoded = $encoded;
+
+ $decoded = self::decode_ber($encoded);
+ if ($decoded === false) {
+ return null;
+ }
+
+ return [$decoded];
}
/**
@@ -237,12 +217,11 @@ class ASN1
* @param string $encoded
* @param int $start
* @param int $encoded_pos
- * @return array
- * @access private
+ * @return array|bool
*/
- function _decode_ber($encoded, $start = 0, $encoded_pos = 0)
+ private static function decode_ber($encoded, $start = 0, $encoded_pos = 0)
{
- $current = array('start' => $start);
+ $current = ['start' => $start];
if (!isset($encoded[$encoded_pos])) {
return false;
@@ -273,7 +252,7 @@ class ASN1
} while ($loop);
}
- $start+= $startOffset;
+ $start += $startOffset;
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
if (!isset($encoded[$encoded_pos])) {
@@ -288,15 +267,15 @@ class ASN1
} elseif ($length & 0x80) { // definite length, long form
// technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
// support it up to four.
- $length&= 0x7F;
+ $length &= 0x7F;
$temp = substr($encoded, $encoded_pos, $length);
$encoded_pos += $length;
// tags of indefinte length don't really have a header length; this length includes the tag
- $current+= array('headerlength' => $length + 2);
- $start+= $length;
- extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
+ $current += ['headerlength' => $length + 2];
+ $start += $length;
+ $length = unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))['length'];
} else {
- $current+= array('headerlength' => 2);
+ $current += ['headerlength' => 2];
}
if ($length > (strlen($encoded) - $encoded_pos)) {
@@ -323,36 +302,36 @@ class ASN1
case self::CLASS_PRIVATE:
case self::CLASS_CONTEXT_SPECIFIC:
if (!$constructed) {
- return array(
+ return [
'type' => $class,
'constant' => $tag,
'content' => $content,
'length' => $length + $start - $current['start']
- );
+ ] + $current;
}
- $newcontent = array();
+ $newcontent = [];
$remainingLength = $length;
while ($remainingLength > 0) {
- $temp = $this->_decode_ber($content, $start, $content_pos);
+ $temp = self::decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) == "\0\0") {
- $length+= 2;
- $start+= $length;
+ $length += 2;
+ $start += $length;
$newcontent[] = $temp;
break;
}
- $start+= $length;
- $remainingLength-= $length;
+ $start += $length;
+ $remainingLength -= $length;
$newcontent[] = $temp;
$content_pos += $length;
}
- return array(
+ return [
'type' => $class,
'constant' => $tag,
// the array encapsulation is for BC with the old format
@@ -361,10 +340,10 @@ class ASN1
// the absence of $content['headerlength'] is how we know if something is indefinite or not.
// technically, it could be defined to be 2 and then another indicator could be used but whatever.
'length' => $start - $current['start']
- ) + $current;
+ ] + $current;
}
- $current+= array('type' => $tag);
+ $current += ['type' => $tag];
// decode UNIVERSAL tags
switch ($tag) {
@@ -391,18 +370,18 @@ class ASN1
if (!$constructed) {
$current['content'] = substr($content, $content_pos);
} else {
- $temp = $this->_decode_ber($content, $start, $content_pos);
+ $temp = self::decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
- $length-= (strlen($content) - $content_pos);
+ $length -= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings
if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
return false;
}
- $current['content'].= substr($temp[$i]['content'], 1);
+ $current['content'] .= substr($temp[$i]['content'], 1);
}
// all subtags should be bit strings
if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
@@ -418,7 +397,7 @@ class ASN1
$current['content'] = '';
$length = 0;
while (substr($content, $content_pos, 2) != "\0\0") {
- $temp = $this->_decode_ber($content, $length + $start, $content_pos);
+ $temp = self::decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
@@ -427,11 +406,11 @@ class ASN1
if ($temp['type'] != self::TYPE_OCTET_STRING) {
return false;
}
- $current['content'].= $temp['content'];
- $length+= $temp['length'];
+ $current['content'] .= $temp['content'];
+ $length += $temp['length'];
}
if (substr($content, $content_pos, 2) == "\0\0") {
- $length+= 2; // +2 for the EOC
+ $length += 2; // +2 for the EOC
}
}
break;
@@ -447,7 +426,7 @@ class ASN1
return false;
}
$offset = 0;
- $current['content'] = array();
+ $current['content'] = [];
$content_len = strlen($content);
while ($content_pos < $content_len) {
// if indefinite length construction was used and we have an end-of-content string next
@@ -456,20 +435,20 @@ class ASN1
$length = $offset + 2; // +2 for the EOC
break 2;
}
- $temp = $this->_decode_ber($content, $start + $offset, $content_pos);
+ $temp = self::decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
- $offset+= $temp['length'];
+ $offset += $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
if ($constructed) {
return false;
}
- $current['content'] = $this->_decodeOID(substr($content, $content_pos));
+ $current['content'] = self::decodeOID(substr($content, $content_pos));
if ($current['content'] === false) {
return false;
}
@@ -512,16 +491,16 @@ class ASN1
if ($constructed) {
return false;
}
- $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
+ $current['content'] = self::decodeTime(substr($content, $content_pos), $tag);
break;
default:
return false;
}
- $start+= $length;
+ $start += $length;
// ie. length is the length of the full TLV encoding - it's not just the length of the value
- return $current + array('length' => $start - $current['start']);
+ return $current + ['length' => $start - $current['start']];
}
/**
@@ -534,15 +513,10 @@ class ASN1
* @param array $decoded
* @param array $mapping
* @param array $special
- * @return array
- * @access public
+ * @return array|bool|Element|string|null
*/
- function asn1map($decoded, $mapping, $special = array())
+ public static function asn1map(array $decoded, $mapping, $special = [])
{
- if (!is_array($decoded)) {
- return false;
- }
-
if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}
@@ -550,12 +524,13 @@ class ASN1
switch (true) {
case $mapping['type'] == self::TYPE_ANY:
$intype = $decoded['type'];
- if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) {
- return new Element(substr($this->encoded, $decoded['start'], $decoded['length']));
+ // !isset(self::ANY_MAP[$intype]) produces a fatal error on PHP 5.6
+ if (isset($decoded['constant']) || !array_key_exists($intype, self::ANY_MAP) || (ord(self::$encoded[$decoded['start']]) & 0x20)) {
+ return new Element(substr(self::$encoded, $decoded['start'], $decoded['length']));
}
- $inmap = $this->ANYmap[$intype];
+ $inmap = self::ANY_MAP[$intype];
if (is_string($inmap)) {
- return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
+ return [$inmap => self::asn1map($decoded, ['type' => $intype] + $mapping, $special)];
}
break;
case $mapping['type'] == self::TYPE_CHOICE:
@@ -563,19 +538,19 @@ class ASN1
switch (true) {
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
case !isset($option['constant']) && $option['type'] == $decoded['type']:
- $value = $this->asn1map($decoded, $option, $special);
+ $value = self::asn1map($decoded, $option, $special);
break;
case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE:
- $v = $this->asn1map($decoded, $option, $special);
+ $v = self::asn1map($decoded, $option, $special);
if (isset($v)) {
$value = $v;
}
}
if (isset($value)) {
if (isset($special[$key])) {
- $value = call_user_func($special[$key], $value);
+ $value = $special[$key]($value);
}
- return array($key => $value);
+ return [$key => $value];
}
}
return null;
@@ -601,13 +576,13 @@ class ASN1
switch ($decoded['type']) {
case self::TYPE_SEQUENCE:
- $map = array();
+ $map = [];
// ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
- if (($map[] = $this->asn1map($content, $child, $special)) === null) {
+ if (($map[] = self::asn1map($content, $child, $special)) === null) {
return null;
}
}
@@ -643,43 +618,43 @@ class ASN1
$maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected and type matches or is generic.
- $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
+ $maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false;
}
}
}
if ($maymatch) {
// Attempt submapping.
- $candidate = $this->asn1map($temp, $child, $special);
+ $candidate = self::asn1map($temp, $child, $special);
$maymatch = $candidate !== null;
}
if ($maymatch) {
// Got the match: use it.
if (isset($special[$key])) {
- $candidate = call_user_func($special[$key], $candidate);
+ $candidate = $special[$key]($candidate);
}
$map[$key] = $candidate;
$i++;
} elseif (isset($child['default'])) {
- $map[$key] = $child['default']; // Use default.
+ $map[$key] = $child['default'];
} elseif (!isset($child['optional'])) {
return null; // Syntax error.
}
}
// Fail mapping if all input items have not been consumed.
- return $i < $n ? null: $map;
+ return $i < $n ? null : $map;
// the main diff between sets and sequences is the encapsulation of the foreach in another for loop
case self::TYPE_SET:
- $map = array();
+ $map = [];
// ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children'];
foreach ($decoded['content'] as $content) {
- if (($map[] = $this->asn1map($content, $child, $special)) === null) {
+ if (($map[] = self::asn1map($content, $child, $special)) === null) {
return null;
}
}
@@ -715,13 +690,13 @@ class ASN1
$maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected and type matches or is generic.
- $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
+ $maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false;
}
}
if ($maymatch) {
// Attempt submapping.
- $candidate = $this->asn1map($temp, $child, $special);
+ $candidate = self::asn1map($temp, $child, $special);
$maymatch = $candidate !== null;
}
@@ -731,7 +706,7 @@ class ASN1
// Got the match: use it.
if (isset($special[$key])) {
- $candidate = call_user_func($special[$key], $candidate);
+ $candidate = $special[$key]($candidate);
}
$map[$key] = $candidate;
break;
@@ -749,7 +724,7 @@ class ASN1
}
return $map;
case self::TYPE_OBJECT_IDENTIFIER:
- return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
+ return isset(self::$oids[$decoded['content']]) ? self::$oids[$decoded['content']] : $decoded['content'];
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
// for explicitly tagged optional stuff
@@ -760,9 +735,9 @@ class ASN1
// in theory, doing isset($mapping['implicit']) would work but malformed certs do exist
// in the wild that OpenSSL decodes without issue so we'll support them as well
if (!is_object($decoded['content'])) {
- $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
+ $decoded['content'] = self::decodeTime($decoded['content'], $decoded['type']);
}
- return $decoded['content'] ? $decoded['content']->format($this->format) : false;
+ return $decoded['content'] ? $decoded['content']->format(self::$format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
@@ -775,7 +750,7 @@ class ASN1
therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
0 bits."
*/
- $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
+ $bits = count($mapping['mapping']) == $size ? [] : array_fill(0, count($mapping['mapping']) - $size, false);
for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
$current = ord($decoded['content'][$i]);
for ($j = $offset; $j < 8; $j++) {
@@ -783,7 +758,7 @@ class ASN1
}
$offset = 0;
}
- $values = array();
+ $values = [];
$map = array_reverse($mapping['mapping']);
foreach ($map as $i => $value) {
if ($bits[$i]) {
@@ -792,12 +767,12 @@ class ASN1
}
return $values;
}
+ // fall-through
case self::TYPE_OCTET_STRING:
- return base64_encode($decoded['content']);
+ return $decoded['content'];
case self::TYPE_NULL:
return '';
case self::TYPE_BOOLEAN:
- return $decoded['content'];
case self::TYPE_NUMERIC_STRING:
case self::TYPE_PRINTABLE_STRING:
case self::TYPE_TELETEX_STRING:
@@ -827,6 +802,26 @@ class ASN1
}
/**
+ * DER-decode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
+ *
+ * @param string $string
+ * @return int
+ */
+ public static function decodeLength(&$string)
+ {
+ $length = ord(Strings::shift($string));
+ if ($length & 0x80) { // definite length, long form
+ $length &= 0x7F;
+ $temp = Strings::shift($string, $length);
+ list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
+ }
+ return $length;
+ }
+
+ /**
* ASN.1 Encode
*
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
@@ -834,29 +829,27 @@ class ASN1
*
* "Special" mappings can be applied via $special.
*
- * @param string $source
- * @param string $mapping
+ * @param Element|string|array $source
+ * @param array $mapping
* @param array $special
* @return string
- * @access public
*/
- function encodeDER($source, $mapping, $special = array())
+ public static function encodeDER($source, $mapping, $special = [])
{
- $this->location = array();
- return $this->_encode_der($source, $mapping, null, $special);
+ self::$location = [];
+ return self::encode_der($source, $mapping, null, $special);
}
/**
* ASN.1 Encode (Helper function)
*
- * @param string $source
- * @param string $mapping
+ * @param Element|string|array|null $source
+ * @param array $mapping
* @param int $idx
* @param array $special
* @return string
- * @access private
*/
- function _encode_der($source, $mapping, $idx = null, $special = array())
+ private static function encode_der($source, array $mapping, $idx = null, array $special = [])
{
if ($source instanceof Element) {
return $source->element;
@@ -869,9 +862,9 @@ class ASN1
if (isset($idx)) {
if (isset($special[$idx])) {
- $source = call_user_func($special[$idx], $source);
+ $source = $special[$idx]($source);
}
- $this->location[] = $idx;
+ self::$location[] = $idx;
}
$tag = $mapping['type'];
@@ -879,19 +872,19 @@ class ASN1
switch ($tag) {
case self::TYPE_SET: // Children order is not important, thus process in sequence.
case self::TYPE_SEQUENCE:
- $tag|= 0x20; // set the constructed bit
+ $tag |= 0x20; // set the constructed bit
// ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) {
- $value = array();
+ $value = [];
$child = $mapping['children'];
foreach ($source as $content) {
- $temp = $this->_encode_der($content, $child, null, $special);
+ $temp = self::encode_der($content, $child, null, $special);
if ($temp === false) {
return false;
}
- $value[]= $temp;
+ $value[] = $temp;
}
/* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared
as octet strings with the shorter components being padded at their trailing end with 0-octets.
@@ -914,7 +907,7 @@ class ASN1
continue;
}
- $temp = $this->_encode_der($source[$key], $child, $key, $special);
+ $temp = self::encode_der($source[$key], $child, $key, $special);
if ($temp === false) {
return false;
}
@@ -937,14 +930,26 @@ class ASN1
an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
*/
if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
- $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
- $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
+ if ($child['constant'] <= 30) {
+ $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
+ } else {
+ $constant = $child['constant'];
+ $subtag = '';
+ while ($constant > 0) {
+ $subtagvalue = $constant & 0x7F;
+ $subtag = (chr(0x80 | $subtagvalue)) . $subtag;
+ $constant = $constant >> 7;
+ }
+ $subtag[strlen($subtag) - 1] = $subtag[strlen($subtag) - 1] & chr(0x7F);
+ $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | 0x1f) . $subtag;
+ }
+ $temp = $subtag . self::encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
}
}
- $value.= $temp;
+ $value .= $temp;
}
break;
case self::TYPE_CHOICE:
@@ -955,7 +960,7 @@ class ASN1
continue;
}
- $temp = $this->_encode_der($source[$key], $child, $key, $special);
+ $temp = self::encode_der($source[$key], $child, $key, $special);
if ($temp === false) {
return false;
}
@@ -972,7 +977,7 @@ class ASN1
if (isset($child['constant'])) {
if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
- $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
+ $temp = $subtag . self::encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
$temp = $subtag . substr($temp, 1);
@@ -981,7 +986,7 @@ class ASN1
}
if (isset($idx)) {
- array_pop($this->location);
+ array_pop(self::$location);
}
if ($temp && isset($mapping['cast'])) {
@@ -1011,11 +1016,11 @@ class ASN1
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
- $format.= 'mdHis';
+ $format .= 'mdHis';
// if $source does _not_ include timezone information within it then assume that the timezone is GMT
- $date = new DateTime($source, new DateTimeZone('GMT'));
+ $date = new \DateTime($source, new \DateTimeZone('GMT'));
// if $source _does_ include timezone information within it then convert the time to GMT
- $date->setTimezone(new DateTimeZone('GMT'));
+ $date->setTimezone(new \DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
@@ -1045,46 +1050,47 @@ class ASN1
$bits = implode('', array_pad($bits, $size + $offset + 1, 0));
$bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
foreach ($bytes as $byte) {
- $value.= chr(bindec($byte));
+ $value .= chr(bindec($byte));
}
break;
}
+ // fall-through
case self::TYPE_OCTET_STRING:
/* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.
-- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
- $value = base64_decode($source);
+ $value = $source;
break;
case self::TYPE_OBJECT_IDENTIFIER:
- $value = $this->_encodeOID($source);
+ $value = self::encodeOID($source);
break;
case self::TYPE_ANY:
- $loc = $this->location;
+ $loc = self::$location;
if (isset($idx)) {
- array_pop($this->location);
+ array_pop(self::$location);
}
switch (true) {
case !isset($source):
- return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special);
+ return self::encode_der(null, ['type' => self::TYPE_NULL] + $mapping, null, $special);
case is_int($source):
case $source instanceof BigInteger:
- return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special);
+ return self::encode_der($source, ['type' => self::TYPE_INTEGER] + $mapping, null, $special);
case is_float($source):
- return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special);
+ return self::encode_der($source, ['type' => self::TYPE_REAL] + $mapping, null, $special);
case is_bool($source):
- return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
+ return self::encode_der($source, ['type' => self::TYPE_BOOLEAN] + $mapping, null, $special);
case is_array($source) && count($source) == 1:
$typename = implode('', array_keys($source));
- $outtype = array_search($typename, $this->ANYmap, true);
+ $outtype = array_search($typename, self::ANY_MAP, true);
if ($outtype !== false) {
- return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
+ return self::encode_der($source[$typename], ['type' => $outtype] + $mapping, null, $special);
}
}
- $filters = $this->filters;
+ $filters = self::$filters;
foreach ($loc as $part) {
if (!isset($filters[$part])) {
$filters = false;
@@ -1093,10 +1099,9 @@ class ASN1
$filters = $filters[$part];
}
if ($filters === false) {
- user_error('No filters defined for ' . implode('/', $loc));
- return false;
+ throw new \RuntimeException('No filters defined for ' . implode('/', $loc));
}
- return $this->_encode_der($source, $filters + $mapping, null, $special);
+ return self::encode_der($source, $filters + $mapping, null, $special);
case self::TYPE_NULL:
$value = '';
break;
@@ -1117,44 +1122,23 @@ class ASN1
$value = $source ? "\xFF" : "\x00";
break;
default:
- user_error('Mapping provides no type definition for ' . implode('/', $this->location));
- return false;
+ throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', self::$location));
}
if (isset($idx)) {
- array_pop($this->location);
+ array_pop(self::$location);
}
if (isset($mapping['cast'])) {
if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
- $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
+ $value = chr($tag) . self::encodeLength(strlen($value)) . $value;
$tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
} else {
$tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
}
}
- return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
- }
-
- /**
- * DER-encode the length
- *
- * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
- * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
- *
- * @access private
- * @param int $length
- * @return string
- */
- function _encodeLength($length)
- {
- if ($length <= 0x7F) {
- return chr($length);
- }
-
- $temp = ltrim(pack('N', $length), chr(0));
- return pack('Ca*', 0x80 | strlen($temp), $temp);
+ return chr($tag) . self::encodeLength(strlen($value)) . $value;
}
/**
@@ -1162,23 +1146,24 @@ class ASN1
*
* Called by _decode_ber()
*
- * @access private
* @param string $content
* @return string
*/
- function _decodeOID($content)
+ public static function decodeOID($content)
{
+ // BigInteger's are used because of OIDs like 2.25.329800735698586629295641978511506172918
+ // https://healthcaresecprivacy.blogspot.com/2011/02/creating-and-using-unique-id-uuid-oid.html elaborates.
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
- $oid = array();
+ $oid = [];
$pos = 0;
$len = strlen($content);
// see https://github.com/openjdk/jdk/blob/2deb318c9f047ec5a4b160d66a4b52f93688ec42/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java#L55
if ($len > 4096) {
- //user_error('Object Identifier size is limited to 4096 bytes');
+ //throw new \RuntimeException("Object identifier size is limited to 4096 bytes ($len bytes present)");
return false;
}
@@ -1220,11 +1205,10 @@ class ASN1
*
* Called by _encode_der()
*
- * @access private
* @param string $source
* @return string
*/
- function _encodeOID($source)
+ public static function encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
@@ -1233,11 +1217,15 @@ class ASN1
$forty = new BigInteger(40);
}
- $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
+ if (!preg_match('#(?:\d+\.)+#', $source)) {
+ $oid = isset(self::$reverseOIDs[$source]) ? self::$reverseOIDs[$source] : false;
+ } else {
+ $oid = $source;
+ }
if ($oid === false) {
- user_error('Invalid OID');
- return false;
+ throw new \RuntimeException('Invalid OID');
}
+
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);
@@ -1263,7 +1251,7 @@ class ASN1
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
- $value.= $temp;
+ $value .= $temp;
}
return $value;
@@ -1274,12 +1262,11 @@ class ASN1
*
* Called by _decode_ber() and in the case of implicit tags asn1map().
*
- * @access private
* @param string $content
* @param int $tag
- * @return string
+ * @return \DateTime|false
*/
- function _decodeTime($content, $tag)
+ private static function decodeTime($content, $tag)
{
/* UTCTime:
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
@@ -1301,7 +1288,7 @@ class ASN1
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
- $format.= '.u';
+ $format .= '.u';
}
if ($content[strlen($content) - 1] == 'Z') {
@@ -1309,12 +1296,12 @@ class ASN1
}
if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
- $format.= 'O';
+ $format .= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
- return @DateTime::createFromFormat($format, $content);
+ return @\DateTime::createFromFormat($format, $content);
}
/**
@@ -1322,55 +1309,38 @@ class ASN1
*
* Sets the time / date format for asn1map().
*
- * @access public
* @param string $format
*/
- function setTimeFormat($format)
+ public static function setTimeFormat($format)
{
- $this->format = $format;
+ self::$format = $format;
}
/**
* Load OIDs
*
* Load the relevant OIDs for a particular ASN.1 semantic mapping.
+ * Previously loaded OIDs are retained.
*
- * @access public
* @param array $oids
*/
- function loadOIDs($oids)
+ public static function loadOIDs(array $oids)
{
- $this->oids = $oids;
+ self::$reverseOIDs += $oids;
+ self::$oids = array_flip(self::$reverseOIDs);
}
/**
- * Load filters
+ * Set filters
*
- * See \phpseclib\File\X509, etc, for an example.
+ * See \phpseclib3\File\X509, etc, for an example.
+ * Previously loaded filters are not retained.
*
- * @access public
* @param array $filters
*/
- function loadFilters($filters)
- {
- $this->filters = $filters;
- }
-
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @return string
- * @access private
- */
- function _string_shift(&$string, $index = 1)
+ public static function setFilters(array $filters)
{
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
+ self::$filters = $filters;
}
/**
@@ -1383,15 +1353,15 @@ class ASN1
* @param int $from
* @param int $to
* @return string
- * @access public
*/
- function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
+ public static function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
{
- if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
+ // isset(self::STRING_TYPE_SIZE[$from] returns a fatal error on PHP 5.6
+ if (!array_key_exists($from, self::STRING_TYPE_SIZE) || !array_key_exists($to, self::STRING_TYPE_SIZE)) {
return false;
}
- $insize = $this->stringTypeSize[$from];
- $outsize = $this->stringTypeSize[$to];
+ $insize = self::STRING_TYPE_SIZE[$from];
+ $outsize = self::STRING_TYPE_SIZE[$to];
$inlength = strlen($in);
$out = '';
@@ -1406,8 +1376,10 @@ class ASN1
case $insize == 4:
$c = ($c << 8) | ord($in[$i++]);
$c = ($c << 8) | ord($in[$i++]);
+ // fall-through
case $insize == 2:
$c = ($c << 8) | ord($in[$i++]);
+ // fall-through
case $insize == 1:
break;
case ($c & 0x80) == 0x00:
@@ -1436,9 +1408,11 @@ class ASN1
$c >>= 8;
$v .= chr($c & 0xFF);
$c >>= 8;
+ // fall-through
case $outsize == 2:
$v .= chr($c & 0xFF);
$c >>= 8;
+ // fall-through
case $outsize == 1:
$v .= chr($c & 0xFF);
$c >>= 8;
@@ -1451,18 +1425,23 @@ class ASN1
case $c >= 0x04000000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x04000000;
+ // fall-through
case $c >= 0x00200000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00200000;
+ // fall-through
case $c >= 0x00010000:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00010000;
+ // fall-through
case $c >= 0x00000800:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x00000800;
+ // fall-through
case $c >= 0x00000080:
$v .= chr(0x80 | ($c & 0x3F));
$c = ($c >> 6) | 0x000000C0;
+ // fall-through
default:
$v .= chr($c);
break;
@@ -1471,4 +1450,77 @@ class ASN1
}
return $out;
}
+
+ /**
+ * Extract raw BER from Base64 encoding
+ *
+ * @param string $str
+ * @return string
+ */
+ public static function extractBER($str)
+ {
+ /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
+ * above and beyond the ceritificate.
+ * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
+ *
+ * Bag Attributes
+ * localKeyID: 01 00 00 00
+ * subject=/O=organization/OU=org unit/CN=common name
+ * issuer=/O=organization/CN=common name
+ */
+ if (strlen($str) > ini_get('pcre.backtrack_limit')) {
+ $temp = $str;
+ } else {
+ $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
+ $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
+ }
+ // remove new lines
+ $temp = str_replace(["\r", "\n", ' '], '', $temp);
+ // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
+ $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : false;
+ return $temp != false ? $temp : $str;
+ }
+
+ /**
+ * DER-encode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
+ *
+ * @param int $length
+ * @return string
+ */
+ public static function encodeLength($length)
+ {
+ if ($length <= 0x7F) {
+ return chr($length);
+ }
+
+ $temp = ltrim(pack('N', $length), chr(0));
+ return pack('Ca*', 0x80 | strlen($temp), $temp);
+ }
+
+ /**
+ * Returns the OID corresponding to a name
+ *
+ * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
+ * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
+ * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
+ * to work from version to version.
+ *
+ * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
+ * what's being passed to it already is an OID and return that instead. A few examples.
+ *
+ * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
+ * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
+ * getOID('zzz') == 'zzz'
+ *
+ * @param string $name
+ * @return string
+ */
+ public static function getOID($name)
+ {
+ return isset(self::$reverseOIDs[$name]) ? self::$reverseOIDs[$name] : $name;
+ }
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php
index 68246e2b5..ae4b764b0 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php
@@ -1,27 +1,25 @@
<?php
+
/**
- * Pure-PHP ASN.1 Parser
+ * ASN.1 Raw Element
*
* PHP version 5
*
- * @category File
- * @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\File\ASN1;
+namespace phpseclib3\File\ASN1;
/**
- * ASN.1 Element
+ * ASN.1 Raw Element
*
- * Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER()
+ * An ASN.1 ANY mapping will return an ASN1\Element object. Use of this object
+ * will also bypass the normal encoding rules in ASN1::encodeDER()
*
- * @package ASN1
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class Element
{
@@ -29,18 +27,16 @@ class Element
* Raw element value
*
* @var string
- * @access private
*/
- var $element;
+ public $element;
/**
* Constructor
*
* @param string $encoded
- * @return \phpseclib\File\ASN1\Element
- * @access public
+ * @return Element
*/
- function __construct($encoded)
+ public function __construct($encoded)
{
$this->element = $encoded;
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php
new file mode 100644
index 000000000..1cbc5a594
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AccessDescription.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * AccessDescription
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AccessDescription
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AccessDescription
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'accessMethod' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'accessLocation' => GeneralName::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php
new file mode 100644
index 000000000..04183a13b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AdministrationDomainName.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * AdministrationDomainName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AdministrationDomainName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AdministrationDomainName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or
+ // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC
+ 'class' => ASN1::CLASS_APPLICATION,
+ 'cast' => 2,
+ 'children' => [
+ 'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING],
+ 'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php
new file mode 100644
index 000000000..0da7eb102
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AlgorithmIdentifier.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * AlgorithmIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AlgorithmIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AlgorithmIdentifier
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'algorithm' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'parameters' => [
+ 'type' => ASN1::TYPE_ANY,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php
new file mode 100644
index 000000000..d96c170be
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AnotherName.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * AnotherName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AnotherName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AnotherName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'type-id' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'value' => [
+ 'type' => ASN1::TYPE_ANY,
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php
new file mode 100644
index 000000000..38a6aeefa
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attribute.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * Attribute
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Attribute
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Attribute
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'type' => AttributeType::MAP,
+ 'value' => [
+ 'type' => ASN1::TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => AttributeValue::MAP
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php
new file mode 100644
index 000000000..5cbc2bcc2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeType.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * AttributeType
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AttributeType
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AttributeType
+{
+ const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php
new file mode 100644
index 000000000..fe414f16b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeTypeAndValue.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * AttributeTypeAndValue
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AttributeTypeAndValue
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AttributeTypeAndValue
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'type' => AttributeType::MAP,
+ 'value' => AttributeValue::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php
new file mode 100644
index 000000000..3b3b6d2ed
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AttributeValue.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * AttributeValue
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AttributeValue
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AttributeValue
+{
+ const MAP = ['type' => ASN1::TYPE_ANY];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php
new file mode 100644
index 000000000..cd53ecfaf
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Attributes.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Attributes
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Attributes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Attributes
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => Attribute::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php
new file mode 100644
index 000000000..3e80a55d1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityInfoAccessSyntax.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * AuthorityInfoAccessSyntax
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AuthorityInfoAccessSyntax
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AuthorityInfoAccessSyntax
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => AccessDescription::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php
new file mode 100644
index 000000000..e7ec5b28c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/AuthorityKeyIdentifier.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * AuthorityKeyIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * AuthorityKeyIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class AuthorityKeyIdentifier
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'keyIdentifier' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + KeyIdentifier::MAP,
+ 'authorityCertIssuer' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + GeneralNames::MAP,
+ 'authorityCertSerialNumber' => [
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ] + CertificateSerialNumber::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php
new file mode 100644
index 000000000..e59668ab9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BaseDistance.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * BaseDistance
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * BaseDistance
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BaseDistance
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php
new file mode 100644
index 000000000..587ef1b0e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BasicConstraints.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * BasicConstraints
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * BasicConstraints
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BasicConstraints
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'cA' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'optional' => true,
+ 'default' => false
+ ],
+ 'pathLenConstraint' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php
new file mode 100644
index 000000000..e81bc78e8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttribute.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * BuiltInDomainDefinedAttribute
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * BuiltInDomainDefinedAttribute
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BuiltInDomainDefinedAttribute
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'type' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
+ 'value' => ['type' => ASN1::TYPE_PRINTABLE_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php
new file mode 100644
index 000000000..471e88f92
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInDomainDefinedAttributes.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * BuiltInDomainDefinedAttributes
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * BuiltInDomainDefinedAttributes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BuiltInDomainDefinedAttributes
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 4, // ub-domain-defined-attributes
+ 'children' => BuiltInDomainDefinedAttribute::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php
new file mode 100644
index 000000000..752f400da
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/BuiltInStandardAttributes.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * BuiltInStandardAttributes
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * BuiltInStandardAttributes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BuiltInStandardAttributes
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'country-name' => ['optional' => true] + CountryName::MAP,
+ 'administration-domain-name' => ['optional' => true] + AdministrationDomainName::MAP,
+ 'network-address' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + NetworkAddress::MAP,
+ 'terminal-identifier' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + TerminalIdentifier::MAP,
+ 'private-domain-name' => [
+ 'constant' => 2,
+ 'optional' => true,
+ 'explicit' => true
+ ] + PrivateDomainName::MAP,
+ 'organization-name' => [
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ] + OrganizationName::MAP,
+ 'numeric-user-identifier' => [
+ 'constant' => 4,
+ 'optional' => true,
+ 'implicit' => true
+ ] + NumericUserIdentifier::MAP,
+ 'personal-name' => [
+ 'constant' => 5,
+ 'optional' => true,
+ 'implicit' => true
+ ] + PersonalName::MAP,
+ 'organizational-unit-names' => [
+ 'constant' => 6,
+ 'optional' => true,
+ 'implicit' => true
+ ] + OrganizationalUnitNames::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php
new file mode 100644
index 000000000..56e58887e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CPSuri.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * CPSuri
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CPSuri
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CPSuri
+{
+ const MAP = ['type' => ASN1::TYPE_IA5_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php
new file mode 100644
index 000000000..79860b2fd
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLDistributionPoints.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * CRLDistributionPoints
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CRLDistributionPoints
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CRLDistributionPoints
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => DistributionPoint::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php
new file mode 100644
index 000000000..f6cb95672
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLNumber.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * CRLNumber
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CRLNumber
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CRLNumber
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php
new file mode 100644
index 000000000..d37365299
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CRLReason.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * CRLReason
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CRLReason
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CRLReason
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_ENUMERATED,
+ 'mapping' => [
+ 'unspecified',
+ 'keyCompromise',
+ 'cACompromise',
+ 'affiliationChanged',
+ 'superseded',
+ 'cessationOfOperation',
+ 'certificateHold',
+ // Value 7 is not used.
+ 8 => 'removeFromCRL',
+ 'privilegeWithdrawn',
+ 'aACompromise'
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php
new file mode 100644
index 000000000..d7e7776e8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertPolicyId.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * CertPolicyId
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertPolicyId
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertPolicyId
+{
+ const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php
new file mode 100644
index 000000000..01943a0d4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Certificate.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Certificate
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Certificate
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Certificate
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'tbsCertificate' => TBSCertificate::MAP,
+ 'signatureAlgorithm' => AlgorithmIdentifier::MAP,
+ 'signature' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php
new file mode 100644
index 000000000..ccd68dded
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateIssuer.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * CertificateIssuer
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+/**
+ * CertificateIssuer
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificateIssuer
+{
+ const MAP = GeneralNames::MAP;
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php
new file mode 100644
index 000000000..d54ed6d96
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateList.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * CertificateList
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertificateList
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificateList
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'tbsCertList' => TBSCertList::MAP,
+ 'signatureAlgorithm' => AlgorithmIdentifier::MAP,
+ 'signature' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php
new file mode 100644
index 000000000..ec0fa6b5d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificatePolicies.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * CertificatePolicies
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertificatePolicies
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificatePolicies
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => PolicyInformation::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php
new file mode 100644
index 000000000..06ec944c4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificateSerialNumber.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * CertificateSerialNumber
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertificateSerialNumber
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificateSerialNumber
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php
new file mode 100644
index 000000000..2da70ed6a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequest.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * CertificationRequest
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertificationRequest
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificationRequest
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'certificationRequestInfo' => CertificationRequestInfo::MAP,
+ 'signatureAlgorithm' => AlgorithmIdentifier::MAP,
+ 'signature' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php
new file mode 100644
index 000000000..ce6dc8800
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CertificationRequestInfo.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * CertificationRequestInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CertificationRequestInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CertificationRequestInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => ['v1']
+ ],
+ 'subject' => Name::MAP,
+ 'subjectPKInfo' => SubjectPublicKeyInfo::MAP,
+ 'attributes' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + Attributes::MAP,
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php
new file mode 100644
index 000000000..5bf59bb89
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Characteristic_two.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Characteristic_two
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Characteristic_two
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Characteristic_two
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'm' => ['type' => ASN1::TYPE_INTEGER], // field size 2**m
+ 'basis' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'parameters' => [
+ 'type' => ASN1::TYPE_ANY,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php
new file mode 100644
index 000000000..737d844d1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/CountryName.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * CountryName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * CountryName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class CountryName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ // if class isn't present it's assumed to be \phpseclib3\File\ASN1::CLASS_UNIVERSAL or
+ // (if constant is present) \phpseclib3\File\ASN1::CLASS_CONTEXT_SPECIFIC
+ 'class' => ASN1::CLASS_APPLICATION,
+ 'cast' => 1,
+ 'children' => [
+ 'x121-dcc-code' => ['type' => ASN1::TYPE_NUMERIC_STRING],
+ 'iso-3166-alpha2-code' => ['type' => ASN1::TYPE_PRINTABLE_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php
new file mode 100644
index 000000000..621f10355
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Curve.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Curve
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Curve
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Curve
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'a' => FieldElement::MAP,
+ 'b' => FieldElement::MAP,
+ 'seed' => [
+ 'type' => ASN1::TYPE_BIT_STRING,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php
new file mode 100644
index 000000000..26863dbcf
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DHParameter.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * DHParameter
+ *
+ * From: https://www.teletrust.de/fileadmin/files/oid/oid_pkcs-3v1-4.pdf#page=6
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DHParameter
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DHParameter
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'prime' => ['type' => ASN1::TYPE_INTEGER],
+ 'base' => ['type' => ASN1::TYPE_INTEGER],
+ 'privateValueLength' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php
new file mode 100644
index 000000000..7af397bb0
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAParams.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * DSAParams
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DSAParams
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DSAParams
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'p' => ['type' => ASN1::TYPE_INTEGER],
+ 'q' => ['type' => ASN1::TYPE_INTEGER],
+ 'g' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php
new file mode 100644
index 000000000..d97cd023c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPrivateKey.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * DSAPrivateKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DSAPrivateKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DSAPrivateKey
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => ['type' => ASN1::TYPE_INTEGER],
+ 'p' => ['type' => ASN1::TYPE_INTEGER],
+ 'q' => ['type' => ASN1::TYPE_INTEGER],
+ 'g' => ['type' => ASN1::TYPE_INTEGER],
+ 'y' => ['type' => ASN1::TYPE_INTEGER],
+ 'x' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php
new file mode 100644
index 000000000..f795747a2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DSAPublicKey.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * DSAPublicKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DSAPublicKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DSAPublicKey
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php
new file mode 100644
index 000000000..b38ff3c44
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DigestInfo.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * DigestInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DigestInfo
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DigestInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'digestAlgorithm' => AlgorithmIdentifier::MAP,
+ 'digest' => ['type' => ASN1::TYPE_OCTET_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php
new file mode 100644
index 000000000..45218e3e6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DirectoryString.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * DirectoryString
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DirectoryString
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DirectoryString
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'teletexString' => ['type' => ASN1::TYPE_TELETEX_STRING],
+ 'printableString' => ['type' => ASN1::TYPE_PRINTABLE_STRING],
+ 'universalString' => ['type' => ASN1::TYPE_UNIVERSAL_STRING],
+ 'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING],
+ 'bmpString' => ['type' => ASN1::TYPE_BMP_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php
new file mode 100644
index 000000000..a13e6a64e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DisplayText.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * DisplayText
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DisplayText
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DisplayText
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'ia5String' => ['type' => ASN1::TYPE_IA5_STRING],
+ 'visibleString' => ['type' => ASN1::TYPE_VISIBLE_STRING],
+ 'bmpString' => ['type' => ASN1::TYPE_BMP_STRING],
+ 'utf8String' => ['type' => ASN1::TYPE_UTF8_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php
new file mode 100644
index 000000000..4d9af6b59
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPoint.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * DistributionPoint
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DistributionPoint
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DistributionPoint
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'distributionPoint' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ] + DistributionPointName::MAP,
+ 'reasons' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + ReasonFlags::MAP,
+ 'cRLIssuer' => [
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ] + GeneralNames::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php
new file mode 100644
index 000000000..bc0cec8f7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DistributionPointName.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * DistributionPointName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DistributionPointName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DistributionPointName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'fullName' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + GeneralNames::MAP,
+ 'nameRelativeToCRLIssuer' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + RelativeDistinguishedName::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php
new file mode 100644
index 000000000..2af740883
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/DssSigValue.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * DssSigValue
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * DssSigValue
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DssSigValue
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'r' => ['type' => ASN1::TYPE_INTEGER],
+ 's' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php
new file mode 100644
index 000000000..f25f6faaa
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECParameters.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * ECParameters
+ *
+ * From: https://tools.ietf.org/html/rfc5915
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ECParameters
+ *
+ * ECParameters ::= CHOICE {
+ * namedCurve OBJECT IDENTIFIER
+ * -- implicitCurve NULL
+ * -- specifiedCurve SpecifiedECDomain
+ * }
+ * -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
+ * -- Details for SpecifiedECDomain can be found in [X9.62].
+ * -- Any future additions to this CHOICE should be coordinated
+ * -- with ANSI X9.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ECParameters
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'namedCurve' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'implicitCurve' => ['type' => ASN1::TYPE_NULL],
+ 'specifiedCurve' => SpecifiedECDomain::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php
new file mode 100644
index 000000000..fb11db83f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPoint.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * ECPoint
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ECPoint
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ECPoint
+{
+ const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php
new file mode 100644
index 000000000..7454f3874
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ECPrivateKey.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * ECPrivateKey
+ *
+ * From: https://tools.ietf.org/html/rfc5915
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ECPrivateKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ECPrivateKey
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => [1 => 'ecPrivkeyVer1']
+ ],
+ 'privateKey' => ['type' => ASN1::TYPE_OCTET_STRING],
+ 'parameters' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ] + ECParameters::MAP,
+ 'publicKey' => [
+ 'type' => ASN1::TYPE_BIT_STRING,
+ 'constant' => 1,
+ 'optional' => true,
+ 'explicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php
new file mode 100644
index 000000000..ea7dcf194
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EDIPartyName.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * EDIPartyName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * EDIPartyName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EDIPartyName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'nameAssigner' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + DirectoryString::MAP,
+ // partyName is technically required but \phpseclib3\File\ASN1 doesn't currently support non-optional constants and
+ // setting it to optional gets the job done in any event.
+ 'partyName' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + DirectoryString::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php
new file mode 100644
index 000000000..8ab9ff1eb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EcdsaSigValue.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * EcdsaSigValue
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * EcdsaSigValue
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EcdsaSigValue
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'r' => ['type' => ASN1::TYPE_INTEGER],
+ 's' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php
new file mode 100644
index 000000000..8d8739e1c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedData.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * EncryptedData
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * EncryptedData
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EncryptedData
+{
+ const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php
new file mode 100644
index 000000000..2c9356769
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/EncryptedPrivateKeyInfo.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * EncryptedPrivateKeyInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * EncryptedPrivateKeyInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EncryptedPrivateKeyInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'encryptionAlgorithm' => AlgorithmIdentifier::MAP,
+ 'encryptedData' => EncryptedData::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php
new file mode 100644
index 000000000..f9bc5deff
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtKeyUsageSyntax.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * ExtKeyUsageSyntax
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ExtKeyUsageSyntax
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ExtKeyUsageSyntax
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => KeyPurposeId::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php
new file mode 100644
index 000000000..e32668fb5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extension.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * Extension
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Extension
+ *
+ * A certificate using system MUST reject the certificate if it encounters
+ * a critical extension it does not recognize; however, a non-critical
+ * extension may be ignored if it is not recognized.
+ *
+ * http://tools.ietf.org/html/rfc5280#section-4.2
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Extension
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'extnId' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'critical' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'optional' => true,
+ 'default' => false
+ ],
+ 'extnValue' => ['type' => ASN1::TYPE_OCTET_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php
new file mode 100644
index 000000000..565b36d39
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttribute.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * ExtensionAttribute
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ExtensionAttribute
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ExtensionAttribute
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'extension-attribute-type' => [
+ 'type' => ASN1::TYPE_PRINTABLE_STRING,
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'extension-attribute-value' => [
+ 'type' => ASN1::TYPE_ANY,
+ 'constant' => 1,
+ 'optional' => true,
+ 'explicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php
new file mode 100644
index 000000000..a2e9bfaec
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ExtensionAttributes.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * ExtensionAttributes
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ExtensionAttributes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ExtensionAttributes
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SET,
+ 'min' => 1,
+ 'max' => 256, // ub-extension-attributes
+ 'children' => ExtensionAttribute::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php
new file mode 100644
index 000000000..5015c976c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Extensions.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Extensions
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Extensions
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Extensions
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ // technically, it's MAX, but we'll assume anything < 0 is MAX
+ 'max' => -1,
+ // if 'children' isn't an array then 'min' and 'max' must be defined
+ 'children' => Extension::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php
new file mode 100644
index 000000000..31734078d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldElement.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * FieldElement
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * FieldElement
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class FieldElement
+{
+ const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php
new file mode 100644
index 000000000..e32a9c03d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/FieldID.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * FieldID
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * FieldID
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class FieldID
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'fieldType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER],
+ 'parameters' => [
+ 'type' => ASN1::TYPE_ANY,
+ 'optional' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php
new file mode 100644
index 000000000..57d86da85
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralName.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * GeneralName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * GeneralName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class GeneralName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'otherName' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + AnotherName::MAP,
+ 'rfc822Name' => [
+ 'type' => ASN1::TYPE_IA5_STRING,
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'dNSName' => [
+ 'type' => ASN1::TYPE_IA5_STRING,
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'x400Address' => [
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ] + ORAddress::MAP,
+ 'directoryName' => [
+ 'constant' => 4,
+ 'optional' => true,
+ 'explicit' => true
+ ] + Name::MAP,
+ 'ediPartyName' => [
+ 'constant' => 5,
+ 'optional' => true,
+ 'implicit' => true
+ ] + EDIPartyName::MAP,
+ 'uniformResourceIdentifier' => [
+ 'type' => ASN1::TYPE_IA5_STRING,
+ 'constant' => 6,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'iPAddress' => [
+ 'type' => ASN1::TYPE_OCTET_STRING,
+ 'constant' => 7,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'registeredID' => [
+ 'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
+ 'constant' => 8,
+ 'optional' => true,
+ 'implicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php
new file mode 100644
index 000000000..5d931532d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralNames.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * GeneralNames
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * GeneralNames
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class GeneralNames
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => GeneralName::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php
new file mode 100644
index 000000000..5388db559
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtree.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * GeneralSubtree
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * GeneralSubtree
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class GeneralSubtree
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'base' => GeneralName::MAP,
+ 'minimum' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true,
+ 'default' => '0'
+ ] + BaseDistance::MAP,
+ 'maximum' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true,
+ ] + BaseDistance::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php
new file mode 100644
index 000000000..27548cfec
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/GeneralSubtrees.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * GeneralSubtrees
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * GeneralSubtrees
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class GeneralSubtrees
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => GeneralSubtree::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php
new file mode 100644
index 000000000..deb13cabe
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HashAlgorithm.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * HashAglorithm
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+/**
+ * HashAglorithm
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class HashAlgorithm
+{
+ const MAP = AlgorithmIdentifier::MAP;
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php
new file mode 100644
index 000000000..88d6ff3ea
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/HoldInstructionCode.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * HoldInstructionCode
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * HoldInstructionCode
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class HoldInstructionCode
+{
+ const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php
new file mode 100644
index 000000000..f34b5f728
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/InvalidityDate.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * InvalidityDate
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * InvalidityDate
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class InvalidityDate
+{
+ const MAP = ['type' => ASN1::TYPE_GENERALIZED_TIME];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php
new file mode 100644
index 000000000..e9d032448
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuerAltName.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * IssuerAltName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+/**
+ * IssuerAltName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class IssuerAltName
+{
+ const MAP = GeneralNames::MAP;
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php
new file mode 100644
index 000000000..415996f52
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/IssuingDistributionPoint.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * IssuingDistributionPoint
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * IssuingDistributionPoint
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class IssuingDistributionPoint
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'distributionPoint' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ] + DistributionPointName::MAP,
+ 'onlyContainsUserCerts' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'constant' => 1,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ],
+ 'onlyContainsCACerts' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'constant' => 2,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ],
+ 'onlySomeReasons' => [
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ] + ReasonFlags::MAP,
+ 'indirectCRL' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'constant' => 4,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ],
+ 'onlyContainsAttributeCerts' => [
+ 'type' => ASN1::TYPE_BOOLEAN,
+ 'constant' => 5,
+ 'optional' => true,
+ 'default' => false,
+ 'implicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php
new file mode 100644
index 000000000..82a415199
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyIdentifier.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * KeyIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * KeyIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class KeyIdentifier
+{
+ const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php
new file mode 100644
index 000000000..b8509f196
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyPurposeId.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * KeyPurposeId
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * KeyPurposeId
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class KeyPurposeId
+{
+ const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php
new file mode 100644
index 000000000..827ce0330
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/KeyUsage.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * KeyUsage
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * KeyUsage
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class KeyUsage
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_BIT_STRING,
+ 'mapping' => [
+ 'digitalSignature',
+ 'nonRepudiation',
+ 'keyEncipherment',
+ 'dataEncipherment',
+ 'keyAgreement',
+ 'keyCertSign',
+ 'cRLSign',
+ 'encipherOnly',
+ 'decipherOnly'
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php
new file mode 100644
index 000000000..ea3f998b4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/MaskGenAlgorithm.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * MaskGenAglorithm
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+/**
+ * MaskGenAglorithm
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class MaskGenAlgorithm
+{
+ const MAP = AlgorithmIdentifier::MAP;
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php
new file mode 100644
index 000000000..a6a9009dc
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Name.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Name
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Name
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Name
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'rdnSequence' => RDNSequence::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php
new file mode 100644
index 000000000..80486f94d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NameConstraints.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * NameConstraints
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * NameConstraints
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class NameConstraints
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'permittedSubtrees' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + GeneralSubtrees::MAP,
+ 'excludedSubtrees' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + GeneralSubtrees::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php
new file mode 100644
index 000000000..6c68df002
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NetworkAddress.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * NetworkAddress
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * NetworkAddress
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class NetworkAddress
+{
+ const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php
new file mode 100644
index 000000000..9eec123a9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NoticeReference.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * NoticeReference
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * NoticeReference
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class NoticeReference
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'organization' => DisplayText::MAP,
+ 'noticeNumbers' => [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 200,
+ 'children' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php
new file mode 100644
index 000000000..635a89dcb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/NumericUserIdentifier.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * NumericUserIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * NumericUserIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class NumericUserIdentifier
+{
+ const MAP = ['type' => ASN1::TYPE_NUMERIC_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php
new file mode 100644
index 000000000..b853abe82
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ORAddress.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * ORAddress
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ORAddress
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ORAddress
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'built-in-standard-attributes' => BuiltInStandardAttributes::MAP,
+ 'built-in-domain-defined-attributes' => ['optional' => true] + BuiltInDomainDefinedAttributes::MAP,
+ 'extension-attributes' => ['optional' => true] + ExtensionAttributes::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php
new file mode 100644
index 000000000..59530248c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OneAsymmetricKey.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * OneAsymmetricKey
+ *
+ * See https://tools.ietf.org/html/rfc5958
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * OneAsymmetricKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OneAsymmetricKey
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => ['v1', 'v2']
+ ],
+ 'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
+ 'privateKey' => PrivateKey::MAP,
+ 'attributes' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + Attributes::MAP,
+ 'publicKey' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + PublicKey::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php
new file mode 100644
index 000000000..b5cc9491a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationName.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * OrganizationName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * OrganizationName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OrganizationName
+{
+ const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php
new file mode 100644
index 000000000..b3e57809b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OrganizationalUnitNames.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * OrganizationalUnitNames
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * OrganizationalUnitNames
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OrganizationalUnitNames
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => 4, // ub-organizational-units
+ 'children' => ['type' => ASN1::TYPE_PRINTABLE_STRING]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php
new file mode 100644
index 000000000..5d565605e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfo.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * OtherPrimeInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * OtherPrimeInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OtherPrimeInfo
+{
+ // version must be multi if otherPrimeInfos present
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'prime' => ['type' => ASN1::TYPE_INTEGER], // ri
+ 'exponent' => ['type' => ASN1::TYPE_INTEGER], // di
+ 'coefficient' => ['type' => ASN1::TYPE_INTEGER] // ti
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php
new file mode 100644
index 000000000..9802a8089
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/OtherPrimeInfos.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * OtherPrimeInfos
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * OtherPrimeInfos
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OtherPrimeInfos
+{
+ // version must be multi if otherPrimeInfos present
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => OtherPrimeInfo::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php
new file mode 100644
index 000000000..8eb27cf62
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBEParameter.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * PBEParameter
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PBEParameter
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PBEParameter
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'salt' => ['type' => ASN1::TYPE_OCTET_STRING],
+ 'iterationCount' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php
new file mode 100644
index 000000000..bd31699ff
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBES2params.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * PBES2params
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PBES2params
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PBES2params
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'keyDerivationFunc' => AlgorithmIdentifier::MAP,
+ 'encryptionScheme' => AlgorithmIdentifier::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php
new file mode 100644
index 000000000..2dafed9ca
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBKDF2params.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * PBKDF2params
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PBKDF2params
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PBKDF2params
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ // technically, this is a CHOICE in RFC2898 but the other "choice" is, currently, more of a placeholder
+ // in the RFC
+ 'salt' => ['type' => ASN1::TYPE_OCTET_STRING],
+ 'iterationCount' => ['type' => ASN1::TYPE_INTEGER],
+ 'keyLength' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'optional' => true
+ ],
+ 'prf' => AlgorithmIdentifier::MAP + ['optional' => true]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php
new file mode 100644
index 000000000..91319f582
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PBMAC1params.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * PBMAC1params
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PBMAC1params
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PBMAC1params
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'keyDerivationFunc' => AlgorithmIdentifier::MAP,
+ 'messageAuthScheme' => AlgorithmIdentifier::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php
new file mode 100644
index 000000000..87d0862f5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PKCS9String.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * PKCS9String
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PKCS9String
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PKCS9String
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'ia5String' => ['type' => ASN1::TYPE_IA5_STRING],
+ 'directoryString' => DirectoryString::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php
new file mode 100644
index 000000000..b8c8c02fd
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Pentanomial.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Pentanomial
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Pentanomial
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Pentanomial
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'k1' => ['type' => ASN1::TYPE_INTEGER], // k1 > 0
+ 'k2' => ['type' => ASN1::TYPE_INTEGER], // k2 > k1
+ 'k3' => ['type' => ASN1::TYPE_INTEGER], // k3 > h2
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php
new file mode 100644
index 000000000..14e2860e5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PersonalName.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * PersonalName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PersonalName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PersonalName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SET,
+ 'children' => [
+ 'surname' => [
+ 'type' => ASN1::TYPE_PRINTABLE_STRING,
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'given-name' => [
+ 'type' => ASN1::TYPE_PRINTABLE_STRING,
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'initials' => [
+ 'type' => ASN1::TYPE_PRINTABLE_STRING,
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ],
+ 'generation-qualifier' => [
+ 'type' => ASN1::TYPE_PRINTABLE_STRING,
+ 'constant' => 3,
+ 'optional' => true,
+ 'implicit' => true
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php
new file mode 100644
index 000000000..1625d199a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyInformation.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * PolicyInformation
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PolicyInformation
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PolicyInformation
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'policyIdentifier' => CertPolicyId::MAP,
+ 'policyQualifiers' => [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 0,
+ 'max' => -1,
+ 'optional' => true,
+ 'children' => PolicyQualifierInfo::MAP
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php
new file mode 100644
index 000000000..d30b85235
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyMappings.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * PolicyMappings
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PolicyMappings
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PolicyMappings
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'issuerDomainPolicy' => CertPolicyId::MAP,
+ 'subjectDomainPolicy' => CertPolicyId::MAP
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php
new file mode 100644
index 000000000..7b7cd6a76
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierId.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * PolicyQualifierId
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PolicyQualifierId
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PolicyQualifierId
+{
+ const MAP = ['type' => ASN1::TYPE_OBJECT_IDENTIFIER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php
new file mode 100644
index 000000000..d227702ef
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PolicyQualifierInfo.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * PolicyQualifierInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PolicyQualifierInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PolicyQualifierInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'policyQualifierId' => PolicyQualifierId::MAP,
+ 'qualifier' => ['type' => ASN1::TYPE_ANY]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php
new file mode 100644
index 000000000..142b309e4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PostalAddress.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * PostalAddress
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PostalAddress
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PostalAddress
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'optional' => true,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => DirectoryString::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php
new file mode 100644
index 000000000..774303448
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Prime_p.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Prime_p
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Prime_p
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Prime_p
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php
new file mode 100644
index 000000000..195dcaa5e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateDomainName.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * PrivateDomainName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PrivateDomainName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PrivateDomainName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'numeric' => ['type' => ASN1::TYPE_NUMERIC_STRING],
+ 'printable' => ['type' => ASN1::TYPE_PRINTABLE_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php
new file mode 100644
index 000000000..3c8959411
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKey.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * PrivateKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PrivateKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PrivateKey
+{
+ const MAP = ['type' => ASN1::TYPE_OCTET_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php
new file mode 100644
index 000000000..b440b78df
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyInfo.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * PrivateKeyInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PrivateKeyInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PrivateKeyInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => ['v1']
+ ],
+ 'privateKeyAlgorithm' => AlgorithmIdentifier::MAP,
+ 'privateKey' => PrivateKey::MAP,
+ 'attributes' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true
+ ] + Attributes::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php
new file mode 100644
index 000000000..5b87036e6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PrivateKeyUsagePeriod.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * PrivateKeyUsagePeriod
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PrivateKeyUsagePeriod
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PrivateKeyUsagePeriod
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'notBefore' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'implicit' => true,
+ 'type' => ASN1::TYPE_GENERALIZED_TIME],
+ 'notAfter' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true,
+ 'type' => ASN1::TYPE_GENERALIZED_TIME]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php
new file mode 100644
index 000000000..484092042
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKey.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * PublicKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PublicKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PublicKey
+{
+ const MAP = ['type' => ASN1::TYPE_BIT_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php
new file mode 100644
index 000000000..432581e48
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyAndChallenge.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * PublicKeyAndChallenge
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PublicKeyAndChallenge
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PublicKeyAndChallenge
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'spki' => SubjectPublicKeyInfo::MAP,
+ 'challenge' => ['type' => ASN1::TYPE_IA5_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php
new file mode 100644
index 000000000..b39a341f0
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/PublicKeyInfo.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * PublicKeyInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * PublicKeyInfo
+ *
+ * this format is not formally defined anywhere but is none-the-less the form you
+ * get when you do "openssl rsa -in private.pem -outform PEM -pubout"
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PublicKeyInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'publicKeyAlgorithm' => AlgorithmIdentifier::MAP,
+ 'publicKey' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php
new file mode 100644
index 000000000..48649abd5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RC2CBCParameter.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * RC2CBCParameter
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RC2CBCParameter
+ *
+ * from https://tools.ietf.org/html/rfc2898#appendix-A.3
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RC2CBCParameter
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'rc2ParametersVersion' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'optional' => true
+ ],
+ 'iv' => ['type' => ASN1::TYPE_OCTET_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php
new file mode 100644
index 000000000..04b071c27
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RDNSequence.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * RDNSequence
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RDNSequence
+ *
+ * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
+ * but they can be useful at times when either there is no unique attribute in the entry or you
+ * want to ensure that the entry's DN contains some useful identifying information.
+ *
+ * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RDNSequence
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ // RDNSequence does not define a min or a max, which means it doesn't have one
+ 'min' => 0,
+ 'max' => -1,
+ 'children' => RelativeDistinguishedName::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php
new file mode 100644
index 000000000..8c19c658e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPrivateKey.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * RSAPrivateKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RSAPrivateKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RSAPrivateKey
+{
+ // version must be multi if otherPrimeInfos present
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => ['two-prime', 'multi']
+ ],
+ 'modulus' => ['type' => ASN1::TYPE_INTEGER], // n
+ 'publicExponent' => ['type' => ASN1::TYPE_INTEGER], // e
+ 'privateExponent' => ['type' => ASN1::TYPE_INTEGER], // d
+ 'prime1' => ['type' => ASN1::TYPE_INTEGER], // p
+ 'prime2' => ['type' => ASN1::TYPE_INTEGER], // q
+ 'exponent1' => ['type' => ASN1::TYPE_INTEGER], // d mod (p-1)
+ 'exponent2' => ['type' => ASN1::TYPE_INTEGER], // d mod (q-1)
+ 'coefficient' => ['type' => ASN1::TYPE_INTEGER], // (inverse of q) mod p
+ 'otherPrimeInfos' => OtherPrimeInfos::MAP + ['optional' => true]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php
new file mode 100644
index 000000000..b14c32c42
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSAPublicKey.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * RSAPublicKey
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RSAPublicKey
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RSAPublicKey
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'modulus' => ['type' => ASN1::TYPE_INTEGER],
+ 'publicExponent' => ['type' => ASN1::TYPE_INTEGER]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php
new file mode 100644
index 000000000..1a784bf4d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RSASSA_PSS_params.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * RSASSA_PSS_params
+ *
+ * As defined in https://tools.ietf.org/html/rfc4055#section-3.1
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RSASSA_PSS_params
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RSASSA_PSS_params
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'hashAlgorithm' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true,
+ //'default' => 'sha1Identifier'
+ ] + HashAlgorithm::MAP,
+ 'maskGenAlgorithm' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'explicit' => true,
+ //'default' => 'mgf1SHA1Identifier'
+ ] + MaskGenAlgorithm::MAP,
+ 'saltLength' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'constant' => 2,
+ 'optional' => true,
+ 'explicit' => true,
+ 'default' => 20
+ ],
+ 'trailerField' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'constant' => 3,
+ 'optional' => true,
+ 'explicit' => true,
+ 'default' => 1
+ ]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php
new file mode 100644
index 000000000..2e62fcdb3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/ReasonFlags.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * ReasonFlags
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * ReasonFlags
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class ReasonFlags
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_BIT_STRING,
+ 'mapping' => [
+ 'unused',
+ 'keyCompromise',
+ 'cACompromise',
+ 'affiliationChanged',
+ 'superseded',
+ 'cessationOfOperation',
+ 'certificateHold',
+ 'privilegeWithdrawn',
+ 'aACompromise'
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php
new file mode 100644
index 000000000..a0421f731
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RelativeDistinguishedName.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * RelativeDistinguishedName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RelativeDistinguishedName
+ *
+ * In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
+ * but they can be useful at times when either there is no unique attribute in the entry or you
+ * want to ensure that the entry's DN contains some useful identifying information.
+ *
+ * - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RelativeDistinguishedName
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SET,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => AttributeTypeAndValue::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php
new file mode 100644
index 000000000..ff759eb73
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/RevokedCertificate.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * RevokedCertificate
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * RevokedCertificate
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class RevokedCertificate
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'userCertificate' => CertificateSerialNumber::MAP,
+ 'revocationDate' => Time::MAP,
+ 'crlEntryExtensions' => [
+ 'optional' => true
+ ] + Extensions::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php
new file mode 100644
index 000000000..0f482a261
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SignedPublicKeyAndChallenge.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * SignedPublicKeyAndChallenge
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * SignedPublicKeyAndChallenge
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SignedPublicKeyAndChallenge
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'publicKeyAndChallenge' => PublicKeyAndChallenge::MAP,
+ 'signatureAlgorithm' => AlgorithmIdentifier::MAP,
+ 'signature' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php
new file mode 100644
index 000000000..7408a5637
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SpecifiedECDomain.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * SpecifiedECDomain
+ *
+ * From: http://www.secg.org/sec1-v2.pdf#page=109
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * SpecifiedECDomain
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SpecifiedECDomain
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => [1 => 'ecdpVer1', 'ecdpVer2', 'ecdpVer3']
+ ],
+ 'fieldID' => FieldID::MAP,
+ 'curve' => Curve::MAP,
+ 'base' => ECPoint::MAP,
+ 'order' => ['type' => ASN1::TYPE_INTEGER],
+ 'cofactor' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'optional' => true
+ ],
+ 'hash' => ['optional' => true] + HashAlgorithm::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php
new file mode 100644
index 000000000..39138a94f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectAltName.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * SubjectAltName
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+/**
+ * SubjectAltName
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SubjectAltName
+{
+ const MAP = GeneralNames::MAP;
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php
new file mode 100644
index 000000000..f2e206f6a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectDirectoryAttributes.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * SubjectDirectoryAttributes
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * SubjectDirectoryAttributes
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SubjectDirectoryAttributes
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => Attribute::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php
new file mode 100644
index 000000000..1ff241f71
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectInfoAccessSyntax.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * SubjectInfoAccessSyntax
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * SubjectInfoAccessSyntax
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SubjectInfoAccessSyntax
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => AccessDescription::MAP
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php
new file mode 100644
index 000000000..0d53d5401
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/SubjectPublicKeyInfo.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * SubjectPublicKeyInfo
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * SubjectPublicKeyInfo
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class SubjectPublicKeyInfo
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'algorithm' => AlgorithmIdentifier::MAP,
+ 'subjectPublicKey' => ['type' => ASN1::TYPE_BIT_STRING]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php
new file mode 100644
index 000000000..8e00f4d85
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertList.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * TBSCertList
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * TBSCertList
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class TBSCertList
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'mapping' => ['v1', 'v2'],
+ 'optional' => true,
+ 'default' => 'v1'
+ ],
+ 'signature' => AlgorithmIdentifier::MAP,
+ 'issuer' => Name::MAP,
+ 'thisUpdate' => Time::MAP,
+ 'nextUpdate' => [
+ 'optional' => true
+ ] + Time::MAP,
+ 'revokedCertificates' => [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'optional' => true,
+ 'min' => 0,
+ 'max' => -1,
+ 'children' => RevokedCertificate::MAP
+ ],
+ 'crlExtensions' => [
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true
+ ] + Extensions::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php
new file mode 100644
index 000000000..007360c97
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TBSCertificate.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * TBSCertificate
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * TBSCertificate
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class TBSCertificate
+{
+ // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
+ // reenforce that fact
+ 'version' => [
+ 'type' => ASN1::TYPE_INTEGER,
+ 'constant' => 0,
+ 'optional' => true,
+ 'explicit' => true,
+ 'mapping' => ['v1', 'v2', 'v3'],
+ 'default' => 'v1'
+ ],
+ 'serialNumber' => CertificateSerialNumber::MAP,
+ 'signature' => AlgorithmIdentifier::MAP,
+ 'issuer' => Name::MAP,
+ 'validity' => Validity::MAP,
+ 'subject' => Name::MAP,
+ 'subjectPublicKeyInfo' => SubjectPublicKeyInfo::MAP,
+ // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
+ 'issuerUniqueID' => [
+ 'constant' => 1,
+ 'optional' => true,
+ 'implicit' => true
+ ] + UniqueIdentifier::MAP,
+ 'subjectUniqueID' => [
+ 'constant' => 2,
+ 'optional' => true,
+ 'implicit' => true
+ ] + UniqueIdentifier::MAP,
+ // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
+ // it's not IMPLICIT, it's EXPLICIT
+ 'extensions' => [
+ 'constant' => 3,
+ 'optional' => true,
+ 'explicit' => true
+ ] + Extensions::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php
new file mode 100644
index 000000000..7f6d9d2e9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/TerminalIdentifier.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * TerminalIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * TerminalIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class TerminalIdentifier
+{
+ const MAP = ['type' => ASN1::TYPE_PRINTABLE_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php
new file mode 100644
index 000000000..744ee7049
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Time.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Time
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Time
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Time
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_CHOICE,
+ 'children' => [
+ 'utcTime' => ['type' => ASN1::TYPE_UTC_TIME],
+ 'generalTime' => ['type' => ASN1::TYPE_GENERALIZED_TIME]
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php
new file mode 100644
index 000000000..33baa91e6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Trinomial.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * Trinomial
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Trinomial
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Trinomial
+{
+ const MAP = ['type' => ASN1::TYPE_INTEGER];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php
new file mode 100644
index 000000000..f4c954bbc
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UniqueIdentifier.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * UniqueIdentifier
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * UniqueIdentifier
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class UniqueIdentifier
+{
+ const MAP = ['type' => ASN1::TYPE_BIT_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php
new file mode 100644
index 000000000..98d527b7b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/UserNotice.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * UserNotice
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * UserNotice
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class UserNotice
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'noticeRef' => [
+ 'optional' => true,
+ 'implicit' => true
+ ] + NoticeReference::MAP,
+ 'explicitText' => [
+ 'optional' => true,
+ 'implicit' => true
+ ] + DisplayText::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php
new file mode 100644
index 000000000..8ef64cf5d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/Validity.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Validity
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * Validity
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Validity
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'children' => [
+ 'notBefore' => Time::MAP,
+ 'notAfter' => Time::MAP
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php
new file mode 100644
index 000000000..2ab157287
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_ca_policy_url.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * netscape_ca_policy_url
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * netscape_ca_policy_url
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class netscape_ca_policy_url
+{
+ const MAP = ['type' => ASN1::TYPE_IA5_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php
new file mode 100644
index 000000000..49e8da4b9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_cert_type.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * netscape_cert_type
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * netscape_cert_type
+ *
+ * mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class netscape_cert_type
+{
+ const MAP = [
+ 'type' => ASN1::TYPE_BIT_STRING,
+ 'mapping' => [
+ 'SSLClient',
+ 'SSLServer',
+ 'Email',
+ 'ObjectSigning',
+ 'Reserved',
+ 'SSLCA',
+ 'EmailCA',
+ 'ObjectSigningCA'
+ ]
+ ];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php
new file mode 100644
index 000000000..d3ff4ddfb
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Maps/netscape_comment.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * netscape_comment
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2016 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\File\ASN1\Maps;
+
+use phpseclib3\File\ASN1;
+
+/**
+ * netscape_comment
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class netscape_comment
+{
+ const MAP = ['type' => ASN1::TYPE_IA5_STRING];
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php
index 64e22655c..80e0bab10 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php
@@ -16,30 +16,33 @@
* be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the
* the certificate all together unless the certificate is re-signed.
*
- * @category File
- * @package X509
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2012 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\File;
-
-use phpseclib\Crypt\Hash;
-use phpseclib\Crypt\Random;
-use phpseclib\Crypt\RSA;
-use phpseclib\File\ASN1\Element;
-use phpseclib\Math\BigInteger;
-use DateTime;
-use DateTimeZone;
+namespace phpseclib3\File;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\EC;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\File\ASN1\Element;
+use phpseclib3\File\ASN1\Maps;
+use phpseclib3\Math\BigInteger;
/**
* Pure-PHP X.509 Parser
*
- * @package X509
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class X509
{
@@ -48,59 +51,71 @@ class X509
*
* Not really used anymore but retained all the same to suppress E_NOTICEs from old installs
*
- * @access public
*/
const VALIDATE_SIGNATURE_BY_CA = 1;
- /**#@+
- * @access public
- * @see \phpseclib\File\X509::getDN()
- */
/**
* Return internal array representation
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_ARRAY = 0;
/**
* Return string
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_STRING = 1;
/**
* Return ASN.1 name string
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_ASN1 = 2;
/**
* Return OpenSSL compatible array
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_OPENSSL = 3;
/**
* Return canonical ASN.1 RDNs string
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_CANON = 4;
/**
* Return name hash for file indexing
+ *
+ * @see \phpseclib3\File\X509::getDN()
*/
const DN_HASH = 5;
- /**#@-*/
-
- /**#@+
- * @access public
- * @see \phpseclib\File\X509::saveX509()
- * @see \phpseclib\File\X509::saveCSR()
- * @see \phpseclib\File\X509::saveCRL()
- */
+
/**
* Save as PEM
*
* ie. a base64-encoded PEM with a header and a footer
+ *
+ * @see \phpseclib3\File\X509::saveX509()
+ * @see \phpseclib3\File\X509::saveCSR()
+ * @see \phpseclib3\File\X509::saveCRL()
*/
const FORMAT_PEM = 0;
/**
* Save as DER
+ *
+ * @see \phpseclib3\File\X509::saveX509()
+ * @see \phpseclib3\File\X509::saveCSR()
+ * @see \phpseclib3\File\X509::saveCRL()
*/
const FORMAT_DER = 1;
/**
* Save as a SPKAC
*
+ * @see \phpseclib3\File\X509::saveX509()
+ * @see \phpseclib3\File\X509::saveCSR()
+ * @see \phpseclib3\File\X509::saveCRL()
+ *
* Only works on CSRs. Not currently supported.
*/
const FORMAT_SPKAC = 2;
@@ -108,9 +123,12 @@ class X509
* Auto-detect the format
*
* Used only by the load*() functions
+ *
+ * @see \phpseclib3\File\X509::saveX509()
+ * @see \phpseclib3\File\X509::saveCSR()
+ * @see \phpseclib3\File\X509::saveCRL()
*/
const FORMAT_AUTO_DETECT = 3;
- /**#@-*/
/**
* Attribute value disposition.
@@ -121,163 +139,70 @@ class X509
const ATTR_REPLACE = -3; // Clear first, then add a value.
/**
- * ASN.1 syntax for X.509 certificates
- *
- * @var array
- * @access private
- */
- var $Certificate;
-
- /**#@+
- * ASN.1 syntax for various extensions
- *
- * @access private
- */
- var $DirectoryString;
- var $PKCS9String;
- var $AttributeValue;
- var $Extensions;
- var $KeyUsage;
- var $ExtKeyUsageSyntax;
- var $BasicConstraints;
- var $KeyIdentifier;
- var $CRLDistributionPoints;
- var $AuthorityKeyIdentifier;
- var $CertificatePolicies;
- var $AuthorityInfoAccessSyntax;
- var $SubjectInfoAccessSyntax;
- var $SubjectAltName;
- var $SubjectDirectoryAttributes;
- var $PrivateKeyUsagePeriod;
- var $IssuerAltName;
- var $PolicyMappings;
- var $NameConstraints;
-
- var $CPSuri;
- var $UserNotice;
-
- var $netscape_cert_type;
- var $netscape_comment;
- var $netscape_ca_policy_url;
-
- var $Name;
- var $RelativeDistinguishedName;
- var $CRLNumber;
- var $CRLReason;
- var $IssuingDistributionPoint;
- var $InvalidityDate;
- var $CertificateIssuer;
- var $HoldInstructionCode;
- var $SignedPublicKeyAndChallenge;
- /**#@-*/
-
- /**#@+
- * ASN.1 syntax for various DN attributes
- *
- * @access private
- */
- var $PostalAddress;
- /**#@-*/
-
- /**
- * ASN.1 syntax for Certificate Signing Requests (RFC2986)
- *
- * @var array
- * @access private
- */
- var $CertificationRequest;
-
- /**
- * ASN.1 syntax for Certificate Revocation Lists (RFC5280)
- *
- * @var array
- * @access private
- */
- var $CertificateList;
-
- /**
* Distinguished Name
*
* @var array
- * @access private
*/
- var $dn;
+ private $dn;
/**
* Public key
*
- * @var string
- * @access private
+ * @var string|PublicKey
*/
- var $publicKey;
+ private $publicKey;
/**
* Private key
*
- * @var string
- * @access private
- */
- var $privateKey;
-
- /**
- * Object identifiers for X.509 certificates
- *
- * @var array
- * @access private
- * @link http://en.wikipedia.org/wiki/Object_identifier
+ * @var string|PrivateKey
*/
- var $oids;
+ private $privateKey;
/**
* The certificate authorities
*
* @var array
- * @access private
*/
- var $CAs;
+ private $CAs = [];
/**
* The currently loaded certificate
*
* @var array
- * @access private
*/
- var $currentCert;
+ private $currentCert;
/**
* The signature subject
*
- * There's no guarantee \phpseclib\File\X509 is going to re-encode an X.509 cert in the same way it was originally
+ * There's no guarantee \phpseclib3\File\X509 is going to re-encode an X.509 cert in the same way it was originally
* encoded so we take save the portion of the original cert that the signature would have made for.
*
* @var string
- * @access private
*/
- var $signatureSubject;
+ private $signatureSubject;
/**
* Certificate Start Date
*
* @var string
- * @access private
*/
- var $startDate;
+ private $startDate;
/**
* Certificate End Date
*
- * @var string
- * @access private
+ * @var string|Element
*/
- var $endDate;
+ private $endDate;
/**
* Serial Number
*
* @var string
- * @access private
*/
- var $serialNumber;
+ private $serialNumber;
/**
* Key Identifier
@@ -286,1175 +211,213 @@ class X509
* {@link http://tools.ietf.org/html/rfc5280#section-4.2.1.2 RFC5280#section-4.2.1.2}.
*
* @var string
- * @access private
*/
- var $currentKeyIdentifier;
+ private $currentKeyIdentifier;
/**
* CA Flag
*
* @var bool
- * @access private
*/
- var $caFlag = false;
+ private $caFlag = false;
/**
* SPKAC Challenge
*
* @var string
- * @access private
*/
- var $challenge;
+ private $challenge;
+
+ /**
+ * @var array
+ */
+ private $extensionValues = [];
+
+ /**
+ * OIDs loaded
+ *
+ * @var bool
+ */
+ private static $oidsLoaded = false;
/**
* Recursion Limit
*
* @var int
- * @access private
*/
- static $recur_limit = 5;
+ private static $recur_limit = 5;
/**
* URL fetch flag
*
* @var bool
- * @access private
*/
- static $disable_url_fetch = false;
+ private static $disable_url_fetch = false;
+
+ /**
+ * @var array
+ */
+ private static $extensions = [];
+
+ /**
+ * @var ?array
+ */
+ private $ipAddresses = null;
+
+ /**
+ * @var ?array
+ */
+ private $domains = null;
/**
* Default Constructor.
*
- * @return \phpseclib\File\X509
- * @access public
+ * @return X509
*/
- function __construct()
+ public function __construct()
{
// Explicitly Tagged Module, 1988 Syntax
// http://tools.ietf.org/html/rfc5280#appendix-A.1
- $this->DirectoryString = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING),
- 'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING),
- 'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING),
- 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING),
- 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING)
- )
- );
-
- $this->PKCS9String = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING),
- 'directoryString' => $this->DirectoryString
- )
- );
-
- $this->AttributeValue = array('type' => ASN1::TYPE_ANY);
-
- $AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);
-
- $AttributeTypeAndValue = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'type' => $AttributeType,
- 'value'=> $this->AttributeValue
- )
- );
-
- /*
- In practice, RDNs containing multiple name-value pairs (called "multivalued RDNs") are rare,
- but they can be useful at times when either there is no unique attribute in the entry or you
- want to ensure that the entry's DN contains some useful identifying information.
-
- - https://www.opends.org/wiki/page/DefinitionRelativeDistinguishedName
- */
- $this->RelativeDistinguishedName = array(
- 'type' => ASN1::TYPE_SET,
- 'min' => 1,
- 'max' => -1,
- 'children' => $AttributeTypeAndValue
- );
-
- // http://tools.ietf.org/html/rfc5280#section-4.1.2.4
- $RDNSequence = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- // RDNSequence does not define a min or a max, which means it doesn't have one
- 'min' => 0,
- 'max' => -1,
- 'children' => $this->RelativeDistinguishedName
- );
-
- $this->Name = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'rdnSequence' => $RDNSequence
- )
- );
-
- // http://tools.ietf.org/html/rfc5280#section-4.1.1.2
- $AlgorithmIdentifier = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
- 'parameters' => array(
- 'type' => ASN1::TYPE_ANY,
- 'optional' => true
- )
- )
- );
-
- /*
- A certificate using system MUST reject the certificate if it encounters
- a critical extension it does not recognize; however, a non-critical
- extension may be ignored if it is not recognized.
-
- http://tools.ietf.org/html/rfc5280#section-4.2
- */
- $Extension = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
- 'critical' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'optional' => true,
- 'default' => false
- ),
- 'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING)
- )
- );
-
- $this->Extensions = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- // technically, it's MAX, but we'll assume anything < 0 is MAX
- 'max' => -1,
- // if 'children' isn't an array then 'min' and 'max' must be defined
- 'children' => $Extension
- );
-
- $SubjectPublicKeyInfo = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'algorithm' => $AlgorithmIdentifier,
- 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING)
- )
- );
-
- $UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING);
-
- $Time = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'utcTime' => array('type' => ASN1::TYPE_UTC_TIME),
- 'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME)
- )
- );
-
- // http://tools.ietf.org/html/rfc5280#section-4.1.2.5
- $Validity = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'notBefore' => $Time,
- 'notAfter' => $Time
- )
- );
-
- $CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER);
-
- $Version = array(
- 'type' => ASN1::TYPE_INTEGER,
- 'mapping' => array('v1', 'v2', 'v3')
- );
-
- // assert($TBSCertificate['children']['signature'] == $Certificate['children']['signatureAlgorithm'])
- $TBSCertificate = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- // technically, default implies optional, but we'll define it as being optional, none-the-less, just to
- // reenforce that fact
- 'version' => array(
- 'constant' => 0,
- 'optional' => true,
- 'explicit' => true,
- 'default' => 'v1'
- ) + $Version,
- 'serialNumber' => $CertificateSerialNumber,
- 'signature' => $AlgorithmIdentifier,
- 'issuer' => $this->Name,
- 'validity' => $Validity,
- 'subject' => $this->Name,
- 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo,
- // implicit means that the T in the TLV structure is to be rewritten, regardless of the type
- 'issuerUniqueID' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $UniqueIdentifier,
- 'subjectUniqueID' => array(
- 'constant' => 2,
- 'optional' => true,
- 'implicit' => true
- ) + $UniqueIdentifier,
- // <http://tools.ietf.org/html/rfc2459#page-74> doesn't use the EXPLICIT keyword but if
- // it's not IMPLICIT, it's EXPLICIT
- 'extensions' => array(
- 'constant' => 3,
- 'optional' => true,
- 'explicit' => true
- ) + $this->Extensions
- )
- );
-
- $this->Certificate = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'tbsCertificate' => $TBSCertificate,
- 'signatureAlgorithm' => $AlgorithmIdentifier,
- 'signature' => array('type' => ASN1::TYPE_BIT_STRING)
- )
- );
-
- $this->KeyUsage = array(
- 'type' => ASN1::TYPE_BIT_STRING,
- 'mapping' => array(
- 'digitalSignature',
- 'nonRepudiation',
- 'keyEncipherment',
- 'dataEncipherment',
- 'keyAgreement',
- 'keyCertSign',
- 'cRLSign',
- 'encipherOnly',
- 'decipherOnly'
- )
- );
-
- $this->BasicConstraints = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'cA' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'optional' => true,
- 'default' => false
- ),
- 'pathLenConstraint' => array(
- 'type' => ASN1::TYPE_INTEGER,
- 'optional' => true
- )
- )
- );
-
- $this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING);
-
- $OrganizationalUnitNames = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => 4, // ub-organizational-units
- 'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
- );
-
- $PersonalName = array(
- 'type' => ASN1::TYPE_SET,
- 'children' => array(
- 'surname' => array(
- 'type' => ASN1::TYPE_PRINTABLE_STRING,
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ),
- 'given-name' => array(
- 'type' => ASN1::TYPE_PRINTABLE_STRING,
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ),
- 'initials' => array(
- 'type' => ASN1::TYPE_PRINTABLE_STRING,
- 'constant' => 2,
- 'optional' => true,
- 'implicit' => true
- ),
- 'generation-qualifier' => array(
- 'type' => ASN1::TYPE_PRINTABLE_STRING,
- 'constant' => 3,
- 'optional' => true,
- 'implicit' => true
- )
- )
- );
-
- $NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING);
-
- $OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING);
-
- $PrivateDomainName = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING),
- 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
- )
- );
-
- $TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING);
-
- $NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING);
-
- $AdministrationDomainName = array(
- 'type' => ASN1::TYPE_CHOICE,
- // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or
- // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
- 'class' => ASN1::CLASS_APPLICATION,
- 'cast' => 2,
- 'children' => array(
- 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING),
- 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
- )
- );
-
- $CountryName = array(
- 'type' => ASN1::TYPE_CHOICE,
- // if class isn't present it's assumed to be \phpseclib\File\ASN1::CLASS_UNIVERSAL or
- // (if constant is present) \phpseclib\File\ASN1::CLASS_CONTEXT_SPECIFIC
- 'class' => ASN1::CLASS_APPLICATION,
- 'cast' => 1,
- 'children' => array(
- 'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING),
- 'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
- )
- );
-
- $AnotherName = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
- 'value' => array(
- 'type' => ASN1::TYPE_ANY,
- 'constant' => 0,
- 'optional' => true,
- 'explicit' => true
- )
- )
- );
-
- $ExtensionAttribute = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'extension-attribute-type' => array(
- 'type' => ASN1::TYPE_PRINTABLE_STRING,
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ),
- 'extension-attribute-value' => array(
- 'type' => ASN1::TYPE_ANY,
- 'constant' => 1,
- 'optional' => true,
- 'explicit' => true
- )
- )
- );
-
- $ExtensionAttributes = array(
- 'type' => ASN1::TYPE_SET,
- 'min' => 1,
- 'max' => 256, // ub-extension-attributes
- 'children' => $ExtensionAttribute
- );
-
- $BuiltInDomainDefinedAttribute = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING),
- 'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING)
- )
- );
-
- $BuiltInDomainDefinedAttributes = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => 4, // ub-domain-defined-attributes
- 'children' => $BuiltInDomainDefinedAttribute
- );
-
- $BuiltInStandardAttributes = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'country-name' => array('optional' => true) + $CountryName,
- 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName,
- 'network-address' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $NetworkAddress,
- 'terminal-identifier' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $TerminalIdentifier,
- 'private-domain-name' => array(
- 'constant' => 2,
- 'optional' => true,
- 'explicit' => true
- ) + $PrivateDomainName,
- 'organization-name' => array(
- 'constant' => 3,
- 'optional' => true,
- 'implicit' => true
- ) + $OrganizationName,
- 'numeric-user-identifier' => array(
- 'constant' => 4,
- 'optional' => true,
- 'implicit' => true
- ) + $NumericUserIdentifier,
- 'personal-name' => array(
- 'constant' => 5,
- 'optional' => true,
- 'implicit' => true
- ) + $PersonalName,
- 'organizational-unit-names' => array(
- 'constant' => 6,
- 'optional' => true,
- 'implicit' => true
- ) + $OrganizationalUnitNames
- )
- );
-
- $ORAddress = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'built-in-standard-attributes' => $BuiltInStandardAttributes,
- 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes,
- 'extension-attributes' => array('optional' => true) + $ExtensionAttributes
- )
- );
-
- $EDIPartyName = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'nameAssigner' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $this->DirectoryString,
- // partyName is technically required but \phpseclib\File\ASN1 doesn't currently support non-optional constants and
- // setting it to optional gets the job done in any event.
- 'partyName' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $this->DirectoryString
- )
- );
-
- $GeneralName = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'otherName' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $AnotherName,
- 'rfc822Name' => array(
- 'type' => ASN1::TYPE_IA5_STRING,
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ),
- 'dNSName' => array(
- 'type' => ASN1::TYPE_IA5_STRING,
- 'constant' => 2,
- 'optional' => true,
- 'implicit' => true
- ),
- 'x400Address' => array(
- 'constant' => 3,
- 'optional' => true,
- 'implicit' => true
- ) + $ORAddress,
- 'directoryName' => array(
- 'constant' => 4,
- 'optional' => true,
- 'explicit' => true
- ) + $this->Name,
- 'ediPartyName' => array(
- 'constant' => 5,
- 'optional' => true,
- 'implicit' => true
- ) + $EDIPartyName,
- 'uniformResourceIdentifier' => array(
- 'type' => ASN1::TYPE_IA5_STRING,
- 'constant' => 6,
- 'optional' => true,
- 'implicit' => true
- ),
- 'iPAddress' => array(
- 'type' => ASN1::TYPE_OCTET_STRING,
- 'constant' => 7,
- 'optional' => true,
- 'implicit' => true
- ),
- 'registeredID' => array(
- 'type' => ASN1::TYPE_OBJECT_IDENTIFIER,
- 'constant' => 8,
- 'optional' => true,
- 'implicit' => true
- )
- )
- );
-
- $GeneralNames = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $GeneralName
- );
-
- $this->IssuerAltName = $GeneralNames;
-
- $ReasonFlags = array(
- 'type' => ASN1::TYPE_BIT_STRING,
- 'mapping' => array(
- 'unused',
- 'keyCompromise',
- 'cACompromise',
- 'affiliationChanged',
- 'superseded',
- 'cessationOfOperation',
- 'certificateHold',
- 'privilegeWithdrawn',
- 'aACompromise'
- )
- );
-
- $DistributionPointName = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'fullName' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $GeneralNames,
- 'nameRelativeToCRLIssuer' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $this->RelativeDistinguishedName
- )
- );
-
- $DistributionPoint = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'distributionPoint' => array(
- 'constant' => 0,
- 'optional' => true,
- 'explicit' => true
- ) + $DistributionPointName,
- 'reasons' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $ReasonFlags,
- 'cRLIssuer' => array(
- 'constant' => 2,
- 'optional' => true,
- 'implicit' => true
- ) + $GeneralNames
- )
- );
-
- $this->CRLDistributionPoints = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $DistributionPoint
- );
-
- $this->AuthorityKeyIdentifier = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'keyIdentifier' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $this->KeyIdentifier,
- 'authorityCertIssuer' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $GeneralNames,
- 'authorityCertSerialNumber' => array(
- 'constant' => 2,
- 'optional' => true,
- 'implicit' => true
- ) + $CertificateSerialNumber
- )
- );
-
- $PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);
-
- $PolicyQualifierInfo = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'policyQualifierId' => $PolicyQualifierId,
- 'qualifier' => array('type' => ASN1::TYPE_ANY)
- )
- );
-
- $CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);
-
- $PolicyInformation = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'policyIdentifier' => $CertPolicyId,
- 'policyQualifiers' => array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 0,
- 'max' => -1,
- 'optional' => true,
- 'children' => $PolicyQualifierInfo
- )
- )
- );
-
- $this->CertificatePolicies = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $PolicyInformation
- );
-
- $this->PolicyMappings = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'issuerDomainPolicy' => $CertPolicyId,
- 'subjectDomainPolicy' => $CertPolicyId
- )
- )
- );
-
- $KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);
-
- $this->ExtKeyUsageSyntax = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $KeyPurposeId
- );
-
- $AccessDescription = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER),
- 'accessLocation' => $GeneralName
- )
- );
-
- $this->AuthorityInfoAccessSyntax = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $AccessDescription
- );
-
- $this->SubjectInfoAccessSyntax = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $AccessDescription
- );
-
- $this->SubjectAltName = $GeneralNames;
-
- $this->PrivateKeyUsagePeriod = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'notBefore' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true,
- 'type' => ASN1::TYPE_GENERALIZED_TIME),
- 'notAfter' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true,
- 'type' => ASN1::TYPE_GENERALIZED_TIME)
- )
- );
-
- $BaseDistance = array('type' => ASN1::TYPE_INTEGER);
-
- $GeneralSubtree = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'base' => $GeneralName,
- 'minimum' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true,
- 'default' => new BigInteger(0)
- ) + $BaseDistance,
- 'maximum' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true,
- ) + $BaseDistance
- )
- );
-
- $GeneralSubtrees = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $GeneralSubtree
- );
-
- $this->NameConstraints = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'permittedSubtrees' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $GeneralSubtrees,
- 'excludedSubtrees' => array(
- 'constant' => 1,
- 'optional' => true,
- 'implicit' => true
- ) + $GeneralSubtrees
- )
- );
-
- $this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING);
-
- $DisplayText = array(
- 'type' => ASN1::TYPE_CHOICE,
- 'children' => array(
- 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING),
- 'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING),
- 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING),
- 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING)
- )
- );
-
- $NoticeReference = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'organization' => $DisplayText,
- 'noticeNumbers' => array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => 200,
- 'children' => array('type' => ASN1::TYPE_INTEGER)
- )
- )
- );
-
- $this->UserNotice = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'noticeRef' => array(
- 'optional' => true,
- 'implicit' => true
- ) + $NoticeReference,
- 'explicitText' => array(
- 'optional' => true,
- 'implicit' => true
- ) + $DisplayText
- )
- );
-
- // mapping is from <http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html>
- $this->netscape_cert_type = array(
- 'type' => ASN1::TYPE_BIT_STRING,
- 'mapping' => array(
- 'SSLClient',
- 'SSLServer',
- 'Email',
- 'ObjectSigning',
- 'Reserved',
- 'SSLCA',
- 'EmailCA',
- 'ObjectSigningCA'
- )
- );
-
- $this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING);
- $this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING);
-
- // attribute is used in RFC2986 but we're using the RFC5280 definition
-
- $Attribute = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'type' => $AttributeType,
- 'value'=> array(
- 'type' => ASN1::TYPE_SET,
- 'min' => 1,
- 'max' => -1,
- 'children' => $this->AttributeValue
- )
- )
- );
-
- $this->SubjectDirectoryAttributes = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'min' => 1,
- 'max' => -1,
- 'children' => $Attribute
- );
-
- // adapted from <http://tools.ietf.org/html/rfc2986>
-
- $Attributes = array(
- 'type' => ASN1::TYPE_SET,
- 'min' => 1,
- 'max' => -1,
- 'children' => $Attribute
- );
-
- $CertificationRequestInfo = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'version' => array(
- 'type' => ASN1::TYPE_INTEGER,
- 'mapping' => array('v1')
- ),
- 'subject' => $this->Name,
- 'subjectPKInfo' => $SubjectPublicKeyInfo,
- 'attributes' => array(
- 'constant' => 0,
- 'optional' => true,
- 'implicit' => true
- ) + $Attributes,
- )
- );
-
- $this->CertificationRequest = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'certificationRequestInfo' => $CertificationRequestInfo,
- 'signatureAlgorithm' => $AlgorithmIdentifier,
- 'signature' => array('type' => ASN1::TYPE_BIT_STRING)
- )
- );
-
- $RevokedCertificate = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'userCertificate' => $CertificateSerialNumber,
- 'revocationDate' => $Time,
- 'crlEntryExtensions' => array(
- 'optional' => true
- ) + $this->Extensions
- )
- );
-
- $TBSCertList = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'version' => array(
- 'optional' => true,
- 'default' => 'v1'
- ) + $Version,
- 'signature' => $AlgorithmIdentifier,
- 'issuer' => $this->Name,
- 'thisUpdate' => $Time,
- 'nextUpdate' => array(
- 'optional' => true
- ) + $Time,
- 'revokedCertificates' => array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'optional' => true,
- 'min' => 0,
- 'max' => -1,
- 'children' => $RevokedCertificate
- ),
- 'crlExtensions' => array(
- 'constant' => 0,
- 'optional' => true,
- 'explicit' => true
- ) + $this->Extensions
- )
- );
-
- $this->CertificateList = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'tbsCertList' => $TBSCertList,
- 'signatureAlgorithm' => $AlgorithmIdentifier,
- 'signature' => array('type' => ASN1::TYPE_BIT_STRING)
- )
- );
-
- $this->CRLNumber = array('type' => ASN1::TYPE_INTEGER);
-
- $this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED,
- 'mapping' => array(
- 'unspecified',
- 'keyCompromise',
- 'cACompromise',
- 'affiliationChanged',
- 'superseded',
- 'cessationOfOperation',
- 'certificateHold',
- // Value 7 is not used.
- 8 => 'removeFromCRL',
- 'privilegeWithdrawn',
- 'aACompromise'
- )
- );
-
- $this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'distributionPoint' => array(
- 'constant' => 0,
- 'optional' => true,
- 'explicit' => true
- ) + $DistributionPointName,
- 'onlyContainsUserCerts' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'constant' => 1,
- 'optional' => true,
- 'default' => false,
- 'implicit' => true
- ),
- 'onlyContainsCACerts' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'constant' => 2,
- 'optional' => true,
- 'default' => false,
- 'implicit' => true
- ),
- 'onlySomeReasons' => array(
- 'constant' => 3,
- 'optional' => true,
- 'implicit' => true
- ) + $ReasonFlags,
- 'indirectCRL' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'constant' => 4,
- 'optional' => true,
- 'default' => false,
- 'implicit' => true
- ),
- 'onlyContainsAttributeCerts' => array(
- 'type' => ASN1::TYPE_BOOLEAN,
- 'constant' => 5,
- 'optional' => true,
- 'default' => false,
- 'implicit' => true
- )
- )
- );
-
- $this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME);
-
- $this->CertificateIssuer = $GeneralNames;
-
- $this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER);
-
- $PublicKeyAndChallenge = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'spki' => $SubjectPublicKeyInfo,
- 'challenge' => array('type' => ASN1::TYPE_IA5_STRING)
- )
- );
-
- $this->SignedPublicKeyAndChallenge = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'children' => array(
- 'publicKeyAndChallenge' => $PublicKeyAndChallenge,
- 'signatureAlgorithm' => $AlgorithmIdentifier,
- 'signature' => array('type' => ASN1::TYPE_BIT_STRING)
- )
- );
-
- $this->PostalAddress = array(
- 'type' => ASN1::TYPE_SEQUENCE,
- 'optional' => true,
- 'min' => 1,
- 'max' => -1,
- 'children' => $this->DirectoryString
- );
-
- // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
- $this->oids = array(
- '1.3.6.1.5.5.7' => 'id-pkix',
- '1.3.6.1.5.5.7.1' => 'id-pe',
- '1.3.6.1.5.5.7.2' => 'id-qt',
- '1.3.6.1.5.5.7.3' => 'id-kp',
- '1.3.6.1.5.5.7.48' => 'id-ad',
- '1.3.6.1.5.5.7.2.1' => 'id-qt-cps',
- '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice',
- '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp',
- '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers',
- '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping',
- '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository',
- '2.5.4' => 'id-at',
- '2.5.4.41' => 'id-at-name',
- '2.5.4.4' => 'id-at-surname',
- '2.5.4.42' => 'id-at-givenName',
- '2.5.4.43' => 'id-at-initials',
- '2.5.4.44' => 'id-at-generationQualifier',
- '2.5.4.3' => 'id-at-commonName',
- '2.5.4.7' => 'id-at-localityName',
- '2.5.4.8' => 'id-at-stateOrProvinceName',
- '2.5.4.10' => 'id-at-organizationName',
- '2.5.4.11' => 'id-at-organizationalUnitName',
- '2.5.4.12' => 'id-at-title',
- '2.5.4.13' => 'id-at-description',
- '2.5.4.46' => 'id-at-dnQualifier',
- '2.5.4.6' => 'id-at-countryName',
- '2.5.4.5' => 'id-at-serialNumber',
- '2.5.4.65' => 'id-at-pseudonym',
- '2.5.4.17' => 'id-at-postalCode',
- '2.5.4.9' => 'id-at-streetAddress',
- '2.5.4.45' => 'id-at-uniqueIdentifier',
- '2.5.4.72' => 'id-at-role',
- '2.5.4.16' => 'id-at-postalAddress',
- '1.3.6.1.4.1.311.60.2.1.3' => 'jurisdictionOfIncorporationCountryName',
- '1.3.6.1.4.1.311.60.2.1.2' => 'jurisdictionOfIncorporationStateOrProvinceName',
- '1.3.6.1.4.1.311.60.2.1.1' => 'jurisdictionLocalityName',
- '2.5.4.15' => 'id-at-businessCategory',
-
- '0.9.2342.19200300.100.1.25' => 'id-domainComponent',
- '1.2.840.113549.1.9' => 'pkcs-9',
- '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress',
- '2.5.29' => 'id-ce',
- '2.5.29.35' => 'id-ce-authorityKeyIdentifier',
- '2.5.29.14' => 'id-ce-subjectKeyIdentifier',
- '2.5.29.15' => 'id-ce-keyUsage',
- '2.5.29.16' => 'id-ce-privateKeyUsagePeriod',
- '2.5.29.32' => 'id-ce-certificatePolicies',
- '2.5.29.32.0' => 'anyPolicy',
-
- '2.5.29.33' => 'id-ce-policyMappings',
- '2.5.29.17' => 'id-ce-subjectAltName',
- '2.5.29.18' => 'id-ce-issuerAltName',
- '2.5.29.9' => 'id-ce-subjectDirectoryAttributes',
- '2.5.29.19' => 'id-ce-basicConstraints',
- '2.5.29.30' => 'id-ce-nameConstraints',
- '2.5.29.36' => 'id-ce-policyConstraints',
- '2.5.29.31' => 'id-ce-cRLDistributionPoints',
- '2.5.29.37' => 'id-ce-extKeyUsage',
- '2.5.29.37.0' => 'anyExtendedKeyUsage',
- '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth',
- '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth',
- '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning',
- '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection',
- '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping',
- '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning',
- '2.5.29.54' => 'id-ce-inhibitAnyPolicy',
- '2.5.29.46' => 'id-ce-freshestCRL',
- '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess',
- '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess',
- '2.5.29.20' => 'id-ce-cRLNumber',
- '2.5.29.28' => 'id-ce-issuingDistributionPoint',
- '2.5.29.27' => 'id-ce-deltaCRLIndicator',
- '2.5.29.21' => 'id-ce-cRLReasons',
- '2.5.29.29' => 'id-ce-certificateIssuer',
- '2.5.29.23' => 'id-ce-holdInstructionCode',
- '1.2.840.10040.2' => 'holdInstruction',
- '1.2.840.10040.2.1' => 'id-holdinstruction-none',
- '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer',
- '1.2.840.10040.2.3' => 'id-holdinstruction-reject',
- '2.5.29.24' => 'id-ce-invalidityDate',
-
- '1.2.840.113549.2.2' => 'md2',
- '1.2.840.113549.2.5' => 'md5',
- '1.3.14.3.2.26' => 'id-sha1',
- '1.2.840.10040.4.1' => 'id-dsa',
- '1.2.840.10040.4.3' => 'id-dsa-with-sha1',
- '1.2.840.113549.1.1' => 'pkcs-1',
- '1.2.840.113549.1.1.1' => 'rsaEncryption',
- '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
- '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption',
- '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption',
- '1.2.840.10046.2.1' => 'dhpublicnumber',
- '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm',
- '1.2.840.10045' => 'ansi-X9-62',
- '1.2.840.10045.4' => 'id-ecSigType',
- '1.2.840.10045.4.1' => 'ecdsa-with-SHA1',
- '1.2.840.10045.1' => 'id-fieldType',
- '1.2.840.10045.1.1' => 'prime-field',
- '1.2.840.10045.1.2' => 'characteristic-two-field',
- '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis',
- '1.2.840.10045.1.2.3.1' => 'gnBasis',
- '1.2.840.10045.1.2.3.2' => 'tpBasis',
- '1.2.840.10045.1.2.3.3' => 'ppBasis',
- '1.2.840.10045.2' => 'id-publicKeyType',
- '1.2.840.10045.2.1' => 'id-ecPublicKey',
- '1.2.840.10045.3' => 'ellipticCurve',
- '1.2.840.10045.3.0' => 'c-TwoCurve',
- '1.2.840.10045.3.0.1' => 'c2pnb163v1',
- '1.2.840.10045.3.0.2' => 'c2pnb163v2',
- '1.2.840.10045.3.0.3' => 'c2pnb163v3',
- '1.2.840.10045.3.0.4' => 'c2pnb176w1',
- '1.2.840.10045.3.0.5' => 'c2pnb191v1',
- '1.2.840.10045.3.0.6' => 'c2pnb191v2',
- '1.2.840.10045.3.0.7' => 'c2pnb191v3',
- '1.2.840.10045.3.0.8' => 'c2pnb191v4',
- '1.2.840.10045.3.0.9' => 'c2pnb191v5',
- '1.2.840.10045.3.0.10' => 'c2pnb208w1',
- '1.2.840.10045.3.0.11' => 'c2pnb239v1',
- '1.2.840.10045.3.0.12' => 'c2pnb239v2',
- '1.2.840.10045.3.0.13' => 'c2pnb239v3',
- '1.2.840.10045.3.0.14' => 'c2pnb239v4',
- '1.2.840.10045.3.0.15' => 'c2pnb239v5',
- '1.2.840.10045.3.0.16' => 'c2pnb272w1',
- '1.2.840.10045.3.0.17' => 'c2pnb304w1',
- '1.2.840.10045.3.0.18' => 'c2pnb359v1',
- '1.2.840.10045.3.0.19' => 'c2pnb368w1',
- '1.2.840.10045.3.0.20' => 'c2pnb431r1',
- '1.2.840.10045.3.1' => 'primeCurve',
- '1.2.840.10045.3.1.1' => 'prime192v1',
- '1.2.840.10045.3.1.2' => 'prime192v2',
- '1.2.840.10045.3.1.3' => 'prime192v3',
- '1.2.840.10045.3.1.4' => 'prime239v1',
- '1.2.840.10045.3.1.5' => 'prime239v2',
- '1.2.840.10045.3.1.6' => 'prime239v3',
- '1.2.840.10045.3.1.7' => 'prime256v1',
- '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP',
- '1.2.840.113549.1.1.9' => 'id-pSpecified',
- '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS',
- '1.2.840.113549.1.1.8' => 'id-mgf1',
- '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption',
- '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption',
- '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption',
- '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption',
- '2.16.840.1.101.3.4.2.4' => 'id-sha224',
- '2.16.840.1.101.3.4.2.1' => 'id-sha256',
- '2.16.840.1.101.3.4.2.2' => 'id-sha384',
- '2.16.840.1.101.3.4.2.3' => 'id-sha512',
- '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94',
- '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001',
- '1.2.643.2.2.20' => 'id-GostR3410-2001',
- '1.2.643.2.2.19' => 'id-GostR3410-94',
- // Netscape Object Identifiers from "Netscape Certificate Extensions"
- '2.16.840.1.113730' => 'netscape',
- '2.16.840.1.113730.1' => 'netscape-cert-extension',
- '2.16.840.1.113730.1.1' => 'netscape-cert-type',
- '2.16.840.1.113730.1.13' => 'netscape-comment',
- '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
- // the following are X.509 extensions not supported by phpseclib
- '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype',
- '1.2.840.113533.7.65.0' => 'entrustVersInfo',
- '2.16.840.1.113733.1.6.9' => 'verisignPrivate',
- // for Certificate Signing Requests
- // see http://tools.ietf.org/html/rfc2985
- '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', // PKCS #9 unstructured name
- '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', // Challenge password for certificate revocations
- '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' // Certificate extension request
- );
+ if (!self::$oidsLoaded) {
+ // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2
+ ASN1::loadOIDs([
+ //'id-pkix' => '1.3.6.1.5.5.7',
+ //'id-pe' => '1.3.6.1.5.5.7.1',
+ //'id-qt' => '1.3.6.1.5.5.7.2',
+ //'id-kp' => '1.3.6.1.5.5.7.3',
+ //'id-ad' => '1.3.6.1.5.5.7.48',
+ 'id-qt-cps' => '1.3.6.1.5.5.7.2.1',
+ 'id-qt-unotice' => '1.3.6.1.5.5.7.2.2',
+ 'id-ad-ocsp' => '1.3.6.1.5.5.7.48.1',
+ 'id-ad-caIssuers' => '1.3.6.1.5.5.7.48.2',
+ 'id-ad-timeStamping' => '1.3.6.1.5.5.7.48.3',
+ 'id-ad-caRepository' => '1.3.6.1.5.5.7.48.5',
+ //'id-at' => '2.5.4',
+ 'id-at-name' => '2.5.4.41',
+ 'id-at-surname' => '2.5.4.4',
+ 'id-at-givenName' => '2.5.4.42',
+ 'id-at-initials' => '2.5.4.43',
+ 'id-at-generationQualifier' => '2.5.4.44',
+ 'id-at-commonName' => '2.5.4.3',
+ 'id-at-localityName' => '2.5.4.7',
+ 'id-at-stateOrProvinceName' => '2.5.4.8',
+ 'id-at-organizationName' => '2.5.4.10',
+ 'id-at-organizationalUnitName' => '2.5.4.11',
+ 'id-at-title' => '2.5.4.12',
+ 'id-at-description' => '2.5.4.13',
+ 'id-at-dnQualifier' => '2.5.4.46',
+ 'id-at-countryName' => '2.5.4.6',
+ 'id-at-serialNumber' => '2.5.4.5',
+ 'id-at-pseudonym' => '2.5.4.65',
+ 'id-at-postalCode' => '2.5.4.17',
+ 'id-at-streetAddress' => '2.5.4.9',
+ 'id-at-uniqueIdentifier' => '2.5.4.45',
+ 'id-at-role' => '2.5.4.72',
+ 'id-at-postalAddress' => '2.5.4.16',
+ 'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3',
+ 'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2',
+ 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1',
+ 'id-at-businessCategory' => '2.5.4.15',
+
+ //'id-domainComponent' => '0.9.2342.19200300.100.1.25',
+ //'pkcs-9' => '1.2.840.113549.1.9',
+ 'pkcs-9-at-emailAddress' => '1.2.840.113549.1.9.1',
+ //'id-ce' => '2.5.29',
+ 'id-ce-authorityKeyIdentifier' => '2.5.29.35',
+ 'id-ce-subjectKeyIdentifier' => '2.5.29.14',
+ 'id-ce-keyUsage' => '2.5.29.15',
+ 'id-ce-privateKeyUsagePeriod' => '2.5.29.16',
+ 'id-ce-certificatePolicies' => '2.5.29.32',
+ //'anyPolicy' => '2.5.29.32.0',
+
+ 'id-ce-policyMappings' => '2.5.29.33',
+
+ 'id-ce-subjectAltName' => '2.5.29.17',
+ 'id-ce-issuerAltName' => '2.5.29.18',
+ 'id-ce-subjectDirectoryAttributes' => '2.5.29.9',
+ 'id-ce-basicConstraints' => '2.5.29.19',
+ 'id-ce-nameConstraints' => '2.5.29.30',
+ 'id-ce-policyConstraints' => '2.5.29.36',
+ 'id-ce-cRLDistributionPoints' => '2.5.29.31',
+ 'id-ce-extKeyUsage' => '2.5.29.37',
+ //'anyExtendedKeyUsage' => '2.5.29.37.0',
+ 'id-kp-serverAuth' => '1.3.6.1.5.5.7.3.1',
+ 'id-kp-clientAuth' => '1.3.6.1.5.5.7.3.2',
+ 'id-kp-codeSigning' => '1.3.6.1.5.5.7.3.3',
+ 'id-kp-emailProtection' => '1.3.6.1.5.5.7.3.4',
+ 'id-kp-timeStamping' => '1.3.6.1.5.5.7.3.8',
+ 'id-kp-OCSPSigning' => '1.3.6.1.5.5.7.3.9',
+ 'id-ce-inhibitAnyPolicy' => '2.5.29.54',
+ 'id-ce-freshestCRL' => '2.5.29.46',
+ 'id-pe-authorityInfoAccess' => '1.3.6.1.5.5.7.1.1',
+ 'id-pe-subjectInfoAccess' => '1.3.6.1.5.5.7.1.11',
+ 'id-ce-cRLNumber' => '2.5.29.20',
+ 'id-ce-issuingDistributionPoint' => '2.5.29.28',
+ 'id-ce-deltaCRLIndicator' => '2.5.29.27',
+ 'id-ce-cRLReasons' => '2.5.29.21',
+ 'id-ce-certificateIssuer' => '2.5.29.29',
+ 'id-ce-holdInstructionCode' => '2.5.29.23',
+ //'holdInstruction' => '1.2.840.10040.2',
+ 'id-holdinstruction-none' => '1.2.840.10040.2.1',
+ 'id-holdinstruction-callissuer' => '1.2.840.10040.2.2',
+ 'id-holdinstruction-reject' => '1.2.840.10040.2.3',
+ 'id-ce-invalidityDate' => '2.5.29.24',
+
+ 'rsaEncryption' => '1.2.840.113549.1.1.1',
+ 'md2WithRSAEncryption' => '1.2.840.113549.1.1.2',
+ 'md5WithRSAEncryption' => '1.2.840.113549.1.1.4',
+ 'sha1WithRSAEncryption' => '1.2.840.113549.1.1.5',
+ 'sha224WithRSAEncryption' => '1.2.840.113549.1.1.14',
+ 'sha256WithRSAEncryption' => '1.2.840.113549.1.1.11',
+ 'sha384WithRSAEncryption' => '1.2.840.113549.1.1.12',
+ 'sha512WithRSAEncryption' => '1.2.840.113549.1.1.13',
+
+ 'id-ecPublicKey' => '1.2.840.10045.2.1',
+ 'ecdsa-with-SHA1' => '1.2.840.10045.4.1',
+ // from https://tools.ietf.org/html/rfc5758#section-3.2
+ 'ecdsa-with-SHA224' => '1.2.840.10045.4.3.1',
+ 'ecdsa-with-SHA256' => '1.2.840.10045.4.3.2',
+ 'ecdsa-with-SHA384' => '1.2.840.10045.4.3.3',
+ 'ecdsa-with-SHA512' => '1.2.840.10045.4.3.4',
+
+ 'id-dsa' => '1.2.840.10040.4.1',
+ 'id-dsa-with-sha1' => '1.2.840.10040.4.3',
+ // from https://tools.ietf.org/html/rfc5758#section-3.1
+ 'id-dsa-with-sha224' => '2.16.840.1.101.3.4.3.1',
+ 'id-dsa-with-sha256' => '2.16.840.1.101.3.4.3.2',
+
+ // from https://tools.ietf.org/html/rfc8410:
+ 'id-Ed25519' => '1.3.101.112',
+ 'id-Ed448' => '1.3.101.113',
+
+ 'id-RSASSA-PSS' => '1.2.840.113549.1.1.10',
+
+ //'id-sha224' => '2.16.840.1.101.3.4.2.4',
+ //'id-sha256' => '2.16.840.1.101.3.4.2.1',
+ //'id-sha384' => '2.16.840.1.101.3.4.2.2',
+ //'id-sha512' => '2.16.840.1.101.3.4.2.3',
+ //'id-GostR3411-94-with-GostR3410-94' => '1.2.643.2.2.4',
+ //'id-GostR3411-94-with-GostR3410-2001' => '1.2.643.2.2.3',
+ //'id-GostR3410-2001' => '1.2.643.2.2.20',
+ //'id-GostR3410-94' => '1.2.643.2.2.19',
+ // Netscape Object Identifiers from "Netscape Certificate Extensions"
+ 'netscape' => '2.16.840.1.113730',
+ 'netscape-cert-extension' => '2.16.840.1.113730.1',
+ 'netscape-cert-type' => '2.16.840.1.113730.1.1',
+ 'netscape-comment' => '2.16.840.1.113730.1.13',
+ 'netscape-ca-policy-url' => '2.16.840.1.113730.1.8',
+ // the following are X.509 extensions not supported by phpseclib
+ 'id-pe-logotype' => '1.3.6.1.5.5.7.1.12',
+ 'entrustVersInfo' => '1.2.840.113533.7.65.0',
+ 'verisignPrivate' => '2.16.840.1.113733.1.6.9',
+ // for Certificate Signing Requests
+ // see http://tools.ietf.org/html/rfc2985
+ 'pkcs-9-at-unstructuredName' => '1.2.840.113549.1.9.2', // PKCS #9 unstructured name
+ 'pkcs-9-at-challengePassword' => '1.2.840.113549.1.9.7', // Challenge password for certificate revocations
+ 'pkcs-9-at-extensionRequest' => '1.2.840.113549.1.9.14' // Certificate extension request
+ ]);
+ }
}
/**
@@ -1462,12 +425,11 @@ class X509
*
* Returns an associative array describing the X.509 cert or a false if the cert failed to load
*
- * @param string $cert
+ * @param array|string $cert
* @param int $mode
- * @access public
* @return mixed
*/
- function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
+ public function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($cert) && isset($cert['tbsCertificate'])) {
unset($this->currentCert);
@@ -1486,10 +448,8 @@ class X509
return $cert;
}
- $asn1 = new ASN1();
-
if ($mode != self::FORMAT_DER) {
- $newcert = $this->_extractBER($cert);
+ $newcert = ASN1::extractBER($cert);
if ($mode == self::FORMAT_PEM && $cert == $newcert) {
return false;
}
@@ -1501,11 +461,10 @@ class X509
return false;
}
- $asn1->loadOIDs($this->oids);
- $decoded = $asn1->decodeBER($cert);
+ $decoded = ASN1::decodeBER($cert);
- if (!empty($decoded)) {
- $x509 = $asn1->asn1map($decoded[0], $this->Certificate);
+ if ($decoded) {
+ $x509 = ASN1::asn1map($decoded[0], Maps\Certificate::MAP);
}
if (!isset($x509) || $x509 === false) {
$this->currentCert = false;
@@ -1514,14 +473,18 @@ class X509
$this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
- if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) {
- $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1);
+ if ($this->isSubArrayValid($x509, 'tbsCertificate/extensions')) {
+ $this->mapInExtensions($x509, 'tbsCertificate/extensions');
}
- $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1);
- $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1);
+ $this->mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence');
+ $this->mapInDNs($x509, 'tbsCertificate/subject/rdnSequence');
- $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'];
- $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
+ $key = $x509['tbsCertificate']['subjectPublicKeyInfo'];
+ $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
+ $x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] =
+ "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(base64_encode($key), 64) .
+ "-----END PUBLIC KEY-----";
$this->currentCert = $x509;
$this->dn = $x509['tbsCertificate']['subject'];
@@ -1537,10 +500,9 @@ class X509
*
* @param array $cert
* @param int $format optional
- * @access public
* @return string
*/
- function saveX509($cert, $format = self::FORMAT_PEM)
+ public function saveX509(array $cert, $format = self::FORMAT_PEM)
{
if (!is_array($cert) || !isset($cert['tbsCertificate'])) {
return false;
@@ -1548,32 +510,17 @@ class X509
switch (true) {
// "case !$a: case !$b: break; default: whatever();" is the same thing as "if ($a && $b) whatever()"
- case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
+ case !($algorithm = $this->subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')):
case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']):
break;
default:
- switch ($algorithm) {
- case 'rsaEncryption':
- $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
- = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'])));
- /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier."
- -- https://tools.ietf.org/html/rfc3279#section-2.3.1
-
- given that and the fact that RSA keys appear ot be the only key type for which the parameters field can be blank,
- it seems like perhaps the ASN.1 description ought not say the parameters field is OPTIONAL, but whatever.
- */
- $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null;
- // https://tools.ietf.org/html/rfc3279#section-2.2.1
- $cert['signatureAlgorithm']['parameters'] = null;
- $cert['tbsCertificate']['signature']['parameters'] = null;
- }
+ $cert['tbsCertificate']['subjectPublicKeyInfo'] = new Element(
+ base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))
+ );
}
- $asn1 = new ASN1();
- $asn1->loadOIDs($this->oids);
-
- $filters = array();
- $type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING);
+ $filters = [];
+ $type_utf8_string = ['type' => ASN1::TYPE_UTF8_STRING];
$filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string;
$filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string;
$filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string;
@@ -1585,27 +532,31 @@ class X509
$filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string;
$filters['directoryName']['rdnSequence']['value'] = $type_utf8_string;
- /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib\File\ASN1::TYPE_IA5_STRING.
- \phpseclib\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
+ foreach (self::$extensions as $extension) {
+ $filters['tbsCertificate']['extensions'][] = $extension;
+ }
+
+ /* in the case of policyQualifiers/qualifier, the type has to be \phpseclib3\File\ASN1::TYPE_IA5_STRING.
+ \phpseclib3\File\ASN1::TYPE_PRINTABLE_STRING will cause OpenSSL's X.509 parser to spit out random
characters.
*/
$filters['policyQualifiers']['qualifier']
- = array('type' => ASN1::TYPE_IA5_STRING);
+ = ['type' => ASN1::TYPE_IA5_STRING];
- $asn1->loadFilters($filters);
+ ASN1::setFilters($filters);
- $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1);
- $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1);
- $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1);
+ $this->mapOutExtensions($cert, 'tbsCertificate/extensions');
+ $this->mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence');
+ $this->mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence');
- $cert = $asn1->encodeDER($cert, $this->Certificate);
+ $cert = ASN1::encodeDER($cert, Maps\Certificate::MAP);
switch ($format) {
case self::FORMAT_DER:
return $cert;
// case self::FORMAT_PEM:
default:
- return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----';
+ return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Strings::base64_encode($cert), 64) . '-----END CERTIFICATE-----';
}
}
@@ -1615,27 +566,27 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapInExtensions(&$root, $path, $asn1)
+ private function mapInExtensions(array &$root, $path)
{
- $extensions = &$this->_subArrayUnchecked($root, $path);
+ $extensions = &$this->subArrayUnchecked($root, $path);
if ($extensions) {
for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
- $value = base64_decode($value);
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */
- $map = $this->_getMapping($id);
+ $map = $this->getMapping($id);
if (!is_bool($map)) {
$decoder = $id == 'id-ce-nameConstraints' ?
- array($this, '_decodeNameConstraintIP') :
- array($this, '_decodeIP');
- $decoded = $asn1->decodeBER($value);
- $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
+ [static::class, 'decodeNameConstraintIP'] :
+ [static::class, 'decodeIP'];
+ $decoded = ASN1::decodeBER($value);
+ if (!$decoded) {
+ continue;
+ }
+ $mapped = ASN1::asn1map($decoded[0], $map, ['iPAddress' => $decoder]);
$value = $mapped === false ? $decoded[0] : $mapped;
if ($id == 'id-ce-certificatePolicies') {
@@ -1645,18 +596,19 @@ class X509
}
for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
$subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
- $map = $this->_getMapping($subid);
+ $map = $this->getMapping($subid);
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
- $decoded = $asn1->decodeBER($subvalue);
- $mapped = $asn1->asn1map($decoded[0], $map);
+ $decoded = ASN1::decodeBER($subvalue);
+ if (!$decoded) {
+ continue;
+ }
+ $mapped = ASN1::asn1map($decoded[0], $map);
$subvalue = $mapped === false ? $decoded[0] : $mapped;
}
}
}
}
- } else {
- $value = base64_encode($value);
}
}
}
@@ -1668,12 +620,30 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapOutExtensions(&$root, $path, $asn1)
- {
- $extensions = &$this->_subArray($root, $path);
+ private function mapOutExtensions(array &$root, $path)
+ {
+ $extensions = &$this->subArray($root, $path, !empty($this->extensionValues));
+
+ foreach ($this->extensionValues as $id => $data) {
+ $critical = $data['critical'];
+ $replace = $data['replace'];
+ $value = $data['value'];
+ $newext = [
+ 'extnId' => $id,
+ 'extnValue' => $value,
+ 'critical' => $critical
+ ];
+ if ($replace) {
+ foreach ($extensions as $key => $value) {
+ if ($value['extnId'] == $id) {
+ $extensions[$key] = $newext;
+ continue 2;
+ }
+ }
+ }
+ $extensions[] = $newext;
+ }
if (is_array($extensions)) {
$size = count($extensions);
@@ -1693,12 +663,12 @@ class X509
}
for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) {
$subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId'];
- $map = $this->_getMapping($subid);
+ $map = $this->getMapping($subid);
$subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier'];
if ($map !== false) {
- // by default \phpseclib\File\ASN1 will try to render qualifier as a \phpseclib\File\ASN1::TYPE_IA5_STRING since it's
- // actual type is \phpseclib\File\ASN1::TYPE_ANY
- $subvalue = new Element($asn1->encodeDER($subvalue, $map));
+ // by default \phpseclib3\File\ASN1 will try to render qualifier as a \phpseclib3\File\ASN1::TYPE_IA5_STRING since it's
+ // actual type is \phpseclib3\File\ASN1::TYPE_ANY
+ $subvalue = new Element(ASN1::encodeDER($subvalue, $map));
}
}
}
@@ -1714,15 +684,14 @@ class X509
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */
- $map = $this->_getMapping($id);
+ $map = $this->getMapping($id);
if (is_bool($map)) {
if (!$map) {
- user_error($id . ' is not a currently supported extension');
+ //user_error($id . ' is not a currently supported extension');
unset($extensions[$i]);
}
} else {
- $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP')));
- $value = base64_encode($temp);
+ $value = ASN1::encodeDER($value, $map, ['iPAddress' => [static::class, 'encodeIP']]);
}
}
}
@@ -1734,34 +703,35 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapInAttributes(&$root, $path, $asn1)
+ private function mapInAttributes(&$root, $path)
{
- $attributes = &$this->_subArray($root, $path);
+ $attributes = &$this->subArray($root, $path);
if (is_array($attributes)) {
for ($i = 0; $i < count($attributes); $i++) {
$id = $attributes[$i]['type'];
/* $value contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type */
- $map = $this->_getMapping($id);
+ $map = $this->getMapping($id);
if (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
- $value = $asn1->encodeDER($values[$j], $this->AttributeValue);
- $decoded = $asn1->decodeBER($value);
+ $value = ASN1::encodeDER($values[$j], Maps\AttributeValue::MAP);
+ $decoded = ASN1::decodeBER($value);
if (!is_bool($map)) {
- $mapped = $asn1->asn1map($decoded[0], $map);
+ if (!$decoded) {
+ continue;
+ }
+ $mapped = ASN1::asn1map($decoded[0], $map);
if ($mapped !== false) {
$values[$j] = $mapped;
}
- if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) {
- $this->_mapInExtensions($values, $j, $asn1);
+ if ($id == 'pkcs-9-at-extensionRequest' && $this->isSubArrayValid($values, $j)) {
+ $this->mapInExtensions($values, $j);
}
} elseif ($map) {
- $values[$j] = base64_encode($value);
+ $values[$j] = $value;
}
}
}
@@ -1775,12 +745,10 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapOutAttributes(&$root, $path, $asn1)
+ private function mapOutAttributes(&$root, $path)
{
- $attributes = &$this->_subArray($root, $path);
+ $attributes = &$this->subArray($root, $path);
if (is_array($attributes)) {
$size = count($attributes);
@@ -1788,23 +756,26 @@ class X509
/* [value] contains the DER encoding of an ASN.1 value
corresponding to the attribute type identified by type */
$id = $attributes[$i]['type'];
- $map = $this->_getMapping($id);
+ $map = $this->getMapping($id);
if ($map === false) {
- user_error($id . ' is not a currently supported attribute', E_USER_NOTICE);
+ //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE);
unset($attributes[$i]);
} elseif (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value'];
for ($j = 0; $j < count($values); $j++) {
switch ($id) {
case 'pkcs-9-at-extensionRequest':
- $this->_mapOutExtensions($values, $j, $asn1);
+ $this->mapOutExtensions($values, $j);
break;
}
if (!is_bool($map)) {
- $temp = $asn1->encodeDER($values[$j], $map);
- $decoded = $asn1->decodeBER($temp);
- $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue);
+ $temp = ASN1::encodeDER($values[$j], $map);
+ $decoded = ASN1::decodeBER($temp);
+ if (!$decoded) {
+ continue;
+ }
+ $values[$j] = ASN1::asn1map($decoded[0], Maps\AttributeValue::MAP);
}
}
}
@@ -1818,12 +789,10 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapInDNs(&$root, $path, $asn1)
+ private function mapInDNs(array &$root, $path)
{
- $dns = &$this->_subArray($root, $path);
+ $dns = &$this->subArray($root, $path);
if (is_array($dns)) {
for ($i = 0; $i < count($dns); $i++) {
@@ -1831,10 +800,13 @@ class X509
$type = $dns[$i][$j]['type'];
$value = &$dns[$i][$j]['value'];
if (is_object($value) && $value instanceof Element) {
- $map = $this->_getMapping($type);
+ $map = $this->getMapping($type);
if (!is_bool($map)) {
- $decoded = $asn1->decodeBER($value);
- $value = $asn1->asn1map($decoded[0], $map);
+ $decoded = ASN1::decodeBER($value);
+ if (!$decoded) {
+ continue;
+ }
+ $value = ASN1::asn1map($decoded[0], $map);
}
}
}
@@ -1848,12 +820,10 @@ class X509
*
* @param array $root (by reference)
* @param string $path
- * @param object $asn1
- * @access private
*/
- function _mapOutDNs(&$root, $path, $asn1)
+ private function mapOutDNs(array &$root, $path)
{
- $dns = &$this->_subArray($root, $path);
+ $dns = &$this->subArray($root, $path);
if (is_array($dns)) {
$size = count($dns);
@@ -1865,9 +835,9 @@ class X509
continue;
}
- $map = $this->_getMapping($type);
+ $map = $this->getMapping($type);
if (!is_bool($map)) {
- $value = new Element($asn1->encodeDER($value, $map));
+ $value = new Element(ASN1::encodeDER($value, $map));
}
}
}
@@ -1878,60 +848,61 @@ class X509
* Associate an extension ID to an extension mapping
*
* @param string $extnId
- * @access private
* @return mixed
*/
- function _getMapping($extnId)
+ private function getMapping($extnId)
{
- if (!is_string($extnId)) { // eg. if it's a \phpseclib\File\ASN1\Element object
+ if (!is_string($extnId)) { // eg. if it's a \phpseclib3\File\ASN1\Element object
return true;
}
+ if (isset(self::$extensions[$extnId])) {
+ return self::$extensions[$extnId];
+ }
+
switch ($extnId) {
case 'id-ce-keyUsage':
- return $this->KeyUsage;
+ return Maps\KeyUsage::MAP;
case 'id-ce-basicConstraints':
- return $this->BasicConstraints;
+ return Maps\BasicConstraints::MAP;
case 'id-ce-subjectKeyIdentifier':
- return $this->KeyIdentifier;
+ return Maps\KeyIdentifier::MAP;
case 'id-ce-cRLDistributionPoints':
- return $this->CRLDistributionPoints;
+ return Maps\CRLDistributionPoints::MAP;
case 'id-ce-authorityKeyIdentifier':
- return $this->AuthorityKeyIdentifier;
+ return Maps\AuthorityKeyIdentifier::MAP;
case 'id-ce-certificatePolicies':
- return $this->CertificatePolicies;
+ return Maps\CertificatePolicies::MAP;
case 'id-ce-extKeyUsage':
- return $this->ExtKeyUsageSyntax;
+ return Maps\ExtKeyUsageSyntax::MAP;
case 'id-pe-authorityInfoAccess':
- return $this->AuthorityInfoAccessSyntax;
- case 'id-pe-subjectInfoAccess':
- return $this->SubjectInfoAccessSyntax;
+ return Maps\AuthorityInfoAccessSyntax::MAP;
case 'id-ce-subjectAltName':
- return $this->SubjectAltName;
+ return Maps\SubjectAltName::MAP;
case 'id-ce-subjectDirectoryAttributes':
- return $this->SubjectDirectoryAttributes;
+ return Maps\SubjectDirectoryAttributes::MAP;
case 'id-ce-privateKeyUsagePeriod':
- return $this->PrivateKeyUsagePeriod;
+ return Maps\PrivateKeyUsagePeriod::MAP;
case 'id-ce-issuerAltName':
- return $this->IssuerAltName;
+ return Maps\IssuerAltName::MAP;
case 'id-ce-policyMappings':
- return $this->PolicyMappings;
+ return Maps\PolicyMappings::MAP;
case 'id-ce-nameConstraints':
- return $this->NameConstraints;
+ return Maps\NameConstraints::MAP;
case 'netscape-cert-type':
- return $this->netscape_cert_type;
+ return Maps\netscape_cert_type::MAP;
case 'netscape-comment':
- return $this->netscape_comment;
+ return Maps\netscape_comment::MAP;
case 'netscape-ca-policy-url':
- return $this->netscape_ca_policy_url;
+ return Maps\netscape_ca_policy_url::MAP;
// since id-qt-cps isn't a constructed type it will have already been decoded as a string by the time it gets
// back around to asn1map() and we don't want it decoded again.
//case 'id-qt-cps':
- // return $this->CPSuri;
+ // return Maps\CPSuri::MAP;
case 'id-qt-unotice':
- return $this->UserNotice;
+ return Maps\UserNotice::MAP;
// the following OIDs are unsupported but we don't want them to give notices when calling saveX509().
case 'id-pe-logotype': // http://www.ietf.org/rfc/rfc3709.txt
@@ -1952,31 +923,31 @@ class X509
// CSR attributes
case 'pkcs-9-at-unstructuredName':
- return $this->PKCS9String;
+ return Maps\PKCS9String::MAP;
case 'pkcs-9-at-challengePassword':
- return $this->DirectoryString;
+ return Maps\DirectoryString::MAP;
case 'pkcs-9-at-extensionRequest':
- return $this->Extensions;
+ return Maps\Extensions::MAP;
// CRL extensions.
case 'id-ce-cRLNumber':
- return $this->CRLNumber;
+ return Maps\CRLNumber::MAP;
case 'id-ce-deltaCRLIndicator':
- return $this->CRLNumber;
+ return Maps\CRLNumber::MAP;
case 'id-ce-issuingDistributionPoint':
- return $this->IssuingDistributionPoint;
+ return Maps\IssuingDistributionPoint::MAP;
case 'id-ce-freshestCRL':
- return $this->CRLDistributionPoints;
+ return Maps\CRLDistributionPoints::MAP;
case 'id-ce-cRLReasons':
- return $this->CRLReason;
+ return Maps\CRLReason::MAP;
case 'id-ce-invalidityDate':
- return $this->InvalidityDate;
+ return Maps\InvalidityDate::MAP;
case 'id-ce-certificateIssuer':
- return $this->CertificateIssuer;
+ return Maps\CertificateIssuer::MAP;
case 'id-ce-holdInstructionCode':
- return $this->HoldInstructionCode;
+ return Maps\HoldInstructionCode::MAP;
case 'id-at-postalAddress':
- return $this->PostalAddress;
+ return Maps\PostalAddress::MAP;
}
return false;
@@ -1986,10 +957,9 @@ class X509
* Load an X.509 certificate as a certificate authority
*
* @param string $cert
- * @access public
* @return bool
*/
- function loadCA($cert)
+ public function loadCA($cert)
{
$olddn = $this->dn;
$oldcert = $this->currentCert;
@@ -2053,10 +1023,9 @@ class X509
* not bar.foo.a.com. f*.com matches foo.com but not bar.com.
*
* @param string $url
- * @access public
* @return bool
*/
- function validateURL($url)
+ public function validateURL($url)
{
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
@@ -2101,8 +1070,8 @@ class X509
}
if ($value = $this->getDNProp('id-at-commonName')) {
- $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
- return preg_match('#^' . $value . '$#', $components['host']);
+ $value = str_replace(['.', '*'], ['\.', '[^.]*'], $value[0]);
+ return preg_match('#^' . $value . '$#', $components['host']) === 1;
}
return false;
@@ -2113,17 +1082,17 @@ class X509
*
* If $date isn't defined it is assumed to be the current date.
*
- * @param \DateTime|string $date optional
- * @access public
+ * @param \DateTimeInterface|string $date optional
+ * @return bool
*/
- function validateDate($date = null)
+ public function validateDate($date = null)
{
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
}
if (!isset($date)) {
- $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
+ $date = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
}
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@@ -2133,29 +1102,22 @@ class X509
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
if (is_string($date)) {
- $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
+ $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get()));
}
- $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
- $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));
-
- switch (true) {
- case $date < $notBefore:
- case $date > $notAfter:
- return false;
- }
+ $notBefore = new \DateTimeImmutable($notBefore, new \DateTimeZone(@date_default_timezone_get()));
+ $notAfter = new \DateTimeImmutable($notAfter, new \DateTimeZone(@date_default_timezone_get()));
- return true;
+ return $date >= $notBefore && $date <= $notAfter;
}
/**
* Fetches a URL
*
* @param string $url
- * @access private
* @return bool|string
*/
- static function _fetchURL($url)
+ private static function fetchURL($url)
{
if (self::$disable_url_fetch) {
return false;
@@ -2171,7 +1133,7 @@ class X509
}
$path = $parts['path'];
if (isset($parts['query'])) {
- $path.= '?' . $parts['query'];
+ $path .= '?' . $parts['query'];
}
fputs($fsock, "GET $path HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
@@ -2193,7 +1155,7 @@ class X509
if ($temp === false) {
return false;
}
- $data.= $temp;
+ $data .= $temp;
}
break;
@@ -2212,10 +1174,9 @@ class X509
*
* @param bool $caonly
* @param int $count
- * @access private
* @return bool
*/
- function _testForIntermediate($caonly, $count)
+ private function testForIntermediate($caonly, $count)
{
$opts = $this->getExtension('id-pe-authorityInfoAccess');
if (!is_array($opts)) {
@@ -2237,7 +1198,7 @@ class X509
return false;
}
- $cert = static::_fetchURL($url);
+ $cert = static::fetchURL($url);
if (!is_string($cert)) {
return false;
}
@@ -2257,7 +1218,7 @@ class X509
return false;
}
- if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
+ if (!$parent->validateSignatureCountable($caonly, ++$count)) {
return false;
}
@@ -2279,12 +1240,11 @@ class X509
* The behavior of this function is inspired by {@link http://php.net/openssl-verify openssl_verify}.
*
* @param bool $caonly optional
- * @access public
* @return mixed
*/
- function validateSignature($caonly = true)
+ public function validateSignature($caonly = true)
{
- return $this->_validateSignatureCountable($caonly, 0);
+ return $this->validateSignatureCountable($caonly, 0);
}
/**
@@ -2294,10 +1254,9 @@ class X509
*
* @param bool $caonly
* @param int $count
- * @access private
* @return mixed
*/
- function _validateSignatureCountable($caonly, $count)
+ private function validateSignatureCountable($caonly, $count)
{
if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
return null;
@@ -2352,32 +1311,32 @@ class X509
}
}
if (count($this->CAs) == $i && $caonly) {
- return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
+ return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
} elseif (!isset($signingCert) || $caonly) {
- return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
+ return $this->testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
- return $this->_validateSignature(
+ return $this->validateSignatureHelper(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
- substr(base64_decode($this->currentCert['signature']), 1),
+ substr($this->currentCert['signature'], 1),
$this->signatureSubject
);
case isset($this->currentCert['certificationRequestInfo']):
- return $this->_validateSignature(
+ return $this->validateSignatureHelper(
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
- substr(base64_decode($this->currentCert['signature']), 1),
+ substr($this->currentCert['signature'], 1),
$this->signatureSubject
);
case isset($this->currentCert['publicKeyAndChallenge']):
- return $this->_validateSignature(
+ return $this->validateSignatureHelper(
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
- substr(base64_decode($this->currentCert['signature']), 1),
+ substr($this->currentCert['signature'], 1),
$this->signatureSubject
);
case isset($this->currentCert['tbsCertList']):
@@ -2405,11 +1364,11 @@ class X509
if (!isset($signingCert)) {
return false;
}
- return $this->_validateSignature(
+ return $this->validateSignatureHelper(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'],
- substr(base64_decode($this->currentCert['signature']), 1),
+ substr($this->currentCert['signature'], 1),
$this->signatureSubject
);
default:
@@ -2420,24 +1379,28 @@ class X509
/**
* Validates a signature
*
- * Returns true if the signature is verified, false if it is not correct or null on error
+ * Returns true if the signature is verified and false if it is not correct.
+ * If the algorithms are unsupposed an exception is thrown.
*
* @param string $publicKeyAlgorithm
* @param string $publicKey
* @param string $signatureAlgorithm
* @param string $signature
* @param string $signatureSubject
- * @access private
- * @return int
+ * @throws UnsupportedAlgorithmException if the algorithm is unsupported
+ * @return bool
*/
- function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
+ private function validateSignatureHelper($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
{
switch ($publicKeyAlgorithm) {
+ case 'id-RSASSA-PSS':
+ $key = RSA::loadFormat('PSS', $publicKey);
+ break;
case 'rsaEncryption':
- $rsa = new RSA();
- $rsa->loadKey($publicKey);
-
+ $key = RSA::loadFormat('PKCS8', $publicKey);
switch ($signatureAlgorithm) {
+ case 'id-RSASSA-PSS':
+ break;
case 'md2WithRSAEncryption':
case 'md5WithRSAEncryption':
case 'sha1WithRSAEncryption':
@@ -2445,21 +1408,51 @@ class X509
case 'sha256WithRSAEncryption':
case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption':
- $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
- $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
- if (!@$rsa->verify($signatureSubject, $signature)) {
- return false;
- }
+ $key = $key
+ ->withHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm))
+ ->withPadding(RSA::SIGNATURE_PKCS1);
+ break;
+ default:
+ throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
+ }
+ break;
+ case 'id-Ed25519':
+ case 'id-Ed448':
+ $key = EC::loadFormat('PKCS8', $publicKey);
+ break;
+ case 'id-ecPublicKey':
+ $key = EC::loadFormat('PKCS8', $publicKey);
+ switch ($signatureAlgorithm) {
+ case 'ecdsa-with-SHA1':
+ case 'ecdsa-with-SHA224':
+ case 'ecdsa-with-SHA256':
+ case 'ecdsa-with-SHA384':
+ case 'ecdsa-with-SHA512':
+ $key = $key
+ ->withHash(preg_replace('#^ecdsa-with-#', '', strtolower($signatureAlgorithm)));
break;
default:
- return null;
+ throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
+ }
+ break;
+ case 'id-dsa':
+ $key = DSA::loadFormat('PKCS8', $publicKey);
+ switch ($signatureAlgorithm) {
+ case 'id-dsa-with-sha1':
+ case 'id-dsa-with-sha224':
+ case 'id-dsa-with-sha256':
+ $key = $key
+ ->withHash(preg_replace('#^id-dsa-with-#', '', strtolower($signatureAlgorithm)));
+ break;
+ default:
+ throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
}
break;
default:
- return null;
+ throw new UnsupportedAlgorithmException('Public key algorithm unsupported');
}
- return true;
+ return $key->verify($signatureSubject, $signature);
}
/**
@@ -2470,9 +1463,8 @@ class X509
* that we set a recursion limit. A negative number means that there is no recursion limit.
*
* @param int $count
- * @access public
*/
- static function setRecurLimit($count)
+ public static function setRecurLimit($count)
{
self::$recur_limit = $count;
}
@@ -2480,9 +1472,8 @@ class X509
/**
* Prevents URIs from being automatically retrieved
*
- * @access public
*/
- static function disableURLFetch()
+ public static function disableURLFetch()
{
self::$disable_url_fetch = true;
}
@@ -2490,51 +1481,23 @@ class X509
/**
* Allows URIs to be automatically retrieved
*
- * @access public
*/
- static function enableURLFetch()
+ public static function enableURLFetch()
{
self::$disable_url_fetch = false;
}
/**
- * Reformat public keys
- *
- * Reformats a public key to a format supported by phpseclib (if applicable)
- *
- * @param string $algorithm
- * @param string $key
- * @access private
- * @return string
- */
- function _reformatKey($algorithm, $key)
- {
- switch ($algorithm) {
- case 'rsaEncryption':
- return
- "-----BEGIN RSA PUBLIC KEY-----\r\n" .
- // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits
- // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox
- // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do.
- chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) .
- '-----END RSA PUBLIC KEY-----';
- default:
- return $key;
- }
- }
-
- /**
* Decodes an IP address
*
* Takes in a base64 encoded "blob" and returns a human readable IP address
*
* @param string $ip
- * @access private
* @return string
*/
- function _decodeIP($ip)
+ public static function decodeIP($ip)
{
- return inet_ntop(base64_decode($ip));
+ return inet_ntop($ip);
}
/**
@@ -2543,16 +1506,14 @@ class X509
* Takes in a base64 encoded "blob" and returns a human readable IP address / mask
*
* @param string $ip
- * @access private
* @return array
*/
- function _decodeNameConstraintIP($ip)
+ public static function decodeNameConstraintIP($ip)
{
- $ip = base64_decode($ip);
$size = strlen($ip) >> 1;
$mask = substr($ip, $size);
$ip = substr($ip, 0, $size);
- return array(inet_ntop($ip), inet_ntop($mask));
+ return [inet_ntop($ip), inet_ntop($mask)];
}
/**
@@ -2561,24 +1522,22 @@ class X509
* Takes a human readable IP address into a base64-encoded "blob"
*
* @param string|array $ip
- * @access private
* @return string
*/
- function _encodeIP($ip)
+ public static function encodeIP($ip)
{
return is_string($ip) ?
- base64_encode(inet_pton($ip)) :
- base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
+ inet_pton($ip) :
+ inet_pton($ip[0]) . inet_pton($ip[1]);
}
/**
* "Normalizes" a Distinguished Name property
*
* @param string $propName
- * @access private
* @return mixed
*/
- function _translateDNProp($propName)
+ private function translateDNProp($propName)
{
switch (strtolower($propName)) {
case 'jurisdictionofincorporationcountryname':
@@ -2683,29 +1642,28 @@ class X509
* @param string $propName
* @param mixed $propValue
* @param string $type optional
- * @access public
* @return bool
*/
- function setDNProp($propName, $propValue, $type = 'utf8String')
+ public function setDNProp($propName, $propValue, $type = 'utf8String')
{
if (empty($this->dn)) {
- $this->dn = array('rdnSequence' => array());
+ $this->dn = ['rdnSequence' => []];
}
- if (($propName = $this->_translateDNProp($propName)) === false) {
+ if (($propName = $this->translateDNProp($propName)) === false) {
return false;
}
foreach ((array) $propValue as $v) {
if (!is_array($v) && isset($type)) {
- $v = array($type => $v);
+ $v = [$type => $v];
}
- $this->dn['rdnSequence'][] = array(
- array(
+ $this->dn['rdnSequence'][] = [
+ [
'type' => $propName,
- 'value'=> $v
- )
- );
+ 'value' => $v
+ ]
+ ];
}
return true;
@@ -2715,15 +1673,14 @@ class X509
* Remove Distinguished Name properties
*
* @param string $propName
- * @access public
*/
- function removeDNProp($propName)
+ public function removeDNProp($propName)
{
if (empty($this->dn)) {
return;
}
- if (($propName = $this->_translateDNProp($propName)) === false) {
+ if (($propName = $this->translateDNProp($propName)) === false) {
return;
}
@@ -2749,9 +1706,8 @@ class X509
* @param array $dn optional
* @param bool $withType optional
* @return mixed
- * @access public
*/
- function getDNProp($propName, $dn = null, $withType = false)
+ public function getDNProp($propName, $dn = null, $withType = false)
{
if (!isset($dn)) {
$dn = $this->dn;
@@ -2761,27 +1717,25 @@ class X509
return false;
}
- if (($propName = $this->_translateDNProp($propName)) === false) {
+ if (($propName = $this->translateDNProp($propName)) === false) {
return false;
}
- $asn1 = new ASN1();
- $asn1->loadOIDs($this->oids);
- $filters = array();
- $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
- $asn1->loadFilters($filters);
- $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
+ $filters = [];
+ $filters['value'] = ['type' => ASN1::TYPE_UTF8_STRING];
+ ASN1::setFilters($filters);
+ $this->mapOutDNs($dn, 'rdnSequence');
$dn = $dn['rdnSequence'];
- $result = array();
+ $result = [];
for ($i = 0; $i < count($dn); $i++) {
if ($dn[$i][0]['type'] == $propName) {
$v = $dn[$i][0]['value'];
if (!$withType) {
if (is_array($v)) {
foreach ($v as $type => $s) {
- $type = array_search($type, $asn1->ANYmap, true);
- if ($type !== false && isset($asn1->stringTypeSize[$type])) {
- $s = $asn1->convert($s, $type);
+ $type = array_search($type, ASN1::ANY_MAP);
+ if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) {
+ $s = ASN1::convert($s, $type);
if ($s !== false) {
$v = $s;
break;
@@ -2792,10 +1746,13 @@ class X509
$v = array_pop($v); // Always strip data type.
}
} elseif (is_object($v) && $v instanceof Element) {
- $map = $this->_getMapping($propName);
+ $map = $this->getMapping($propName);
if (!is_bool($map)) {
- $decoded = $asn1->decodeBER($v);
- $v = $asn1->asn1map($decoded[0], $map);
+ $decoded = ASN1::decodeBER($v);
+ if (!$decoded) {
+ return false;
+ }
+ $v = ASN1::asn1map($decoded[0], $map);
}
}
}
@@ -2812,10 +1769,9 @@ class X509
* @param mixed $dn
* @param bool $merge optional
* @param string $type optional
- * @access public
* @return bool
*/
- function setDN($dn, $merge = false, $type = 'utf8String')
+ public function setDN($dn, $merge = false, $type = 'utf8String')
{
if (!$merge) {
$this->dn = null;
@@ -2838,7 +1794,7 @@ class X509
// handles everything else
$results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE);
- for ($i = 1; $i < count($results); $i+=2) {
+ for ($i = 1; $i < count($results); $i += 2) {
$prop = trim($results[$i], ', =/');
$value = $results[$i + 1];
if (!$this->setDNProp($prop, $value, $type)) {
@@ -2854,10 +1810,9 @@ class X509
*
* @param mixed $format optional
* @param array $dn optional
- * @access public
- * @return bool
+ * @return array|bool|string
*/
- function getDN($format = self::DN_ARRAY, $dn = null)
+ public function getDN($format = self::DN_ARRAY, $dn = null)
{
if (!isset($dn)) {
$dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn;
@@ -2867,32 +1822,28 @@ class X509
case self::DN_ARRAY:
return $dn;
case self::DN_ASN1:
- $asn1 = new ASN1();
- $asn1->loadOIDs($this->oids);
- $filters = array();
- $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
- $asn1->loadFilters($filters);
- $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
- return $asn1->encodeDER($dn, $this->Name);
+ $filters = [];
+ $filters['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING];
+ ASN1::setFilters($filters);
+ $this->mapOutDNs($dn, 'rdnSequence');
+ return ASN1::encodeDER($dn, Maps\Name::MAP);
case self::DN_CANON:
// No SEQUENCE around RDNs and all string values normalized as
// trimmed lowercase UTF-8 with all spacing as one blank.
// constructed RDNs will not be canonicalized
- $asn1 = new ASN1();
- $asn1->loadOIDs($this->oids);
- $filters = array();
- $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
- $asn1->loadFilters($filters);
+ $filters = [];
+ $filters['value'] = ['type' => ASN1::TYPE_UTF8_STRING];
+ ASN1::setFilters($filters);
$result = '';
- $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
+ $this->mapOutDNs($dn, 'rdnSequence');
foreach ($dn['rdnSequence'] as $rdn) {
foreach ($rdn as $i => $attr) {
$attr = &$rdn[$i];
if (is_array($attr['value'])) {
foreach ($attr['value'] as $type => $v) {
- $type = array_search($type, $asn1->ANYmap, true);
- if ($type !== false && isset($asn1->stringTypeSize[$type])) {
- $v = $asn1->convert($v, $type);
+ $type = array_search($type, ASN1::ANY_MAP, true);
+ if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) {
+ $v = ASN1::convert($v, $type);
if ($v !== false) {
$v = preg_replace('/\s+/', ' ', $v);
$attr['value'] = strtolower(trim($v));
@@ -2902,28 +1853,26 @@ class X509
}
}
}
- $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName);
+ $result .= ASN1::encodeDER($rdn, Maps\RelativeDistinguishedName::MAP);
}
return $result;
case self::DN_HASH:
$dn = $this->getDN(self::DN_CANON, $dn);
$hash = new Hash('sha1');
$hash = $hash->hash($dn);
- extract(unpack('Vhash', $hash));
- return strtolower(bin2hex(pack('N', $hash)));
+ $hash = unpack('Vhash', $hash)['hash'];
+ return strtolower(Strings::bin2hex(pack('N', $hash)));
}
// Default is to return a string.
$start = true;
$output = '';
- $result = array();
- $asn1 = new ASN1();
- $asn1->loadOIDs($this->oids);
- $filters = array();
- $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING);
- $asn1->loadFilters($filters);
- $this->_mapOutDNs($dn, 'rdnSequence', $asn1);
+ $result = [];
+ $filters = [];
+ $filters['rdnSequence']['value'] = ['type' => ASN1::TYPE_UTF8_STRING];
+ ASN1::setFilters($filters);
+ $this->mapOutDNs($dn, 'rdnSequence');
foreach ($dn['rdnSequence'] as $field) {
$prop = $field[0]['type'];
@@ -2966,13 +1915,13 @@ class X509
}
if (!$start) {
- $output.= $delim;
+ $output .= $delim;
}
if (is_array($value)) {
foreach ($value as $type => $v) {
- $type = array_search($type, $asn1->ANYmap, true);
- if ($type !== false && isset($asn1->stringTypeSize[$type])) {
- $v = $asn1->convert($v, $type);
+ $type = array_search($type, ASN1::ANY_MAP, true);
+ if ($type !== false && array_key_exists($type, ASN1::STRING_TYPE_SIZE)) {
+ $v = ASN1::convert($v, $type);
if ($v !== false) {
$value = $v;
break;
@@ -2984,13 +1933,13 @@ class X509
}
} elseif (is_object($value) && $value instanceof Element) {
$callback = function ($x) {
- return "\x" . bin2hex($x[0]);
+ return '\x' . bin2hex($x[0]);
};
$value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
}
- $output.= $desc . '=' . $value;
+ $output .= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
- array_merge((array) $result[$desc], array($value)) :
+ array_merge((array) $result[$desc], [$value]) :
$value;
$start = false;
}
@@ -3002,10 +1951,9 @@ class X509
* Get the Distinguished Name for a certificate/crl issuer
*
* @param int $format optional
- * @access public
* @return mixed
*/
- function getIssuerDN($format = self::DN_ARRAY)
+ public function getIssuerDN($format = self::DN_ARRAY)
{
switch (true) {
case !isset($this->currentCert) || !is_array($this->currentCert):
@@ -3024,10 +1972,9 @@ class X509
* Alias of getDN()
*
* @param int $format optional
- * @access public
* @return mixed
*/
- function getSubjectDN($format = self::DN_ARRAY)
+ public function getSubjectDN($format = self::DN_ARRAY)
{
switch (true) {
case !empty($this->dn):
@@ -3048,10 +1995,9 @@ class X509
*
* @param string $propName
* @param bool $withType optional
- * @access public
* @return mixed
*/
- function getIssuerDNProp($propName, $withType = false)
+ public function getIssuerDNProp($propName, $withType = false)
{
switch (true) {
case !isset($this->currentCert) || !is_array($this->currentCert):
@@ -3070,10 +2016,9 @@ class X509
*
* @param string $propName
* @param bool $withType optional
- * @access public
* @return mixed
*/
- function getSubjectDNProp($propName, $withType = false)
+ public function getSubjectDNProp($propName, $withType = false)
{
switch (true) {
case !empty($this->dn):
@@ -3092,19 +2037,15 @@ class X509
/**
* Get the certificate chain for the current cert
*
- * @access public
* @return mixed
*/
- function getChain()
+ public function getChain()
{
- $chain = array($this->currentCert);
+ $chain = [$this->currentCert];
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
return false;
}
- if (empty($this->CAs)) {
- return $chain;
- }
while (true) {
$currentCert = $chain[count($chain) - 1];
for ($i = 0; $i < count($this->CAs); $i++) {
@@ -3135,29 +2076,36 @@ class X509
}
/**
+ * Returns the current cert
+ *
+ * @return array|bool
+ */
+ public function &getCurrentCert()
+ {
+ return $this->currentCert;
+ }
+
+ /**
* Set public key
*
- * Key needs to be a \phpseclib\Crypt\RSA object
+ * Key needs to be a \phpseclib3\Crypt\RSA object
*
- * @param object $key
- * @access public
- * @return bool
+ * @param PublicKey $key
+ * @return void
*/
- function setPublicKey($key)
+ public function setPublicKey(PublicKey $key)
{
- $key->setPublicKey();
$this->publicKey = $key;
}
/**
* Set private key
*
- * Key needs to be a \phpseclib\Crypt\RSA object
+ * Key needs to be a \phpseclib3\Crypt\RSA object
*
- * @param object $key
- * @access public
+ * @param PrivateKey $key
*/
- function setPrivateKey($key)
+ public function setPrivateKey(PrivateKey $key)
{
$this->privateKey = $key;
}
@@ -3168,9 +2116,8 @@ class X509
* Used for SPKAC CSR's
*
* @param string $challenge
- * @access public
*/
- function setChallenge($challenge)
+ public function setChallenge($challenge)
{
$this->challenge = $challenge;
}
@@ -3178,20 +2125,24 @@ class X509
/**
* Gets the public key
*
- * Returns a \phpseclib\Crypt\RSA object or a false.
+ * Returns a \phpseclib3\Crypt\RSA object or a false.
*
- * @access public
* @return mixed
*/
- function getPublicKey()
+ public function getPublicKey()
{
if (isset($this->publicKey)) {
return $this->publicKey;
}
if (isset($this->currentCert) && is_array($this->currentCert)) {
- foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) {
- $keyinfo = $this->_subArray($this->currentCert, $path);
+ $paths = [
+ 'tbsCertificate/subjectPublicKeyInfo',
+ 'certificationRequestInfo/subjectPKInfo',
+ 'publicKeyAndChallenge/spki'
+ ];
+ foreach ($paths as $path) {
+ $keyinfo = $this->subArray($this->currentCert, $path);
if (!empty($keyinfo)) {
break;
}
@@ -3204,27 +2155,29 @@ class X509
$key = $keyinfo['subjectPublicKey'];
switch ($keyinfo['algorithm']['algorithm']) {
+ case 'id-RSASSA-PSS':
+ return RSA::loadFormat('PSS', $key);
case 'rsaEncryption':
- $publicKey = new RSA();
- $publicKey->loadKey($key);
- $publicKey->setPublicKey();
- break;
- default:
- return false;
+ return RSA::loadFormat('PKCS8', $key)->withPadding(RSA::SIGNATURE_PKCS1);
+ case 'id-ecPublicKey':
+ case 'id-Ed25519':
+ case 'id-Ed448':
+ return EC::loadFormat('PKCS8', $key);
+ case 'id-dsa':
+ return DSA::loadFormat('PKCS8', $key);
}
- return $publicKey;
+ return false;
}
/**
* Load a Certificate Signing Request
*
- * @param string|array $csr
+ * @param string $csr
* @param int $mode
- * @access public
* @return mixed
*/
- function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
+ public function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($csr) && isset($csr['certificationRequestInfo'])) {
unset($this->currentCert);
@@ -3241,10 +2194,8 @@ class X509
// see http://tools.ietf.org/html/rfc2986
- $asn1 = new ASN1();
-
if ($mode != self::FORMAT_DER) {
- $newcsr = $this->_extractBER($csr);
+ $newcsr = ASN1::extractBER($csr);
if ($mode == self::FORMAT_PEM && $csr == $newcsr) {
return false;
}
@@ -3257,44 +2208,39 @@ class X509
return false;
}
- $asn1->loadOIDs($this->oids);
- $decoded = $asn1->decodeBER($csr);
+ $decoded = ASN1::decodeBER($csr);
- if (empty($decoded)) {
+ if (!$decoded) {
$this->currentCert = false;
return false;
}
- $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest);
+ $csr = ASN1::asn1map($decoded[0], Maps\CertificationRequest::MAP);
if (!isset($csr) || $csr === false) {
$this->currentCert = false;
return false;
}
- $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
- $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);
+ $this->mapInAttributes($csr, 'certificationRequestInfo/attributes');
+ $this->mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence');
$this->dn = $csr['certificationRequestInfo']['subject'];
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
- $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'];
- $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'];
- $key = $this->_reformatKey($algorithm, $key);
-
- switch ($algorithm) {
- case 'rsaEncryption':
- $this->publicKey = new RSA();
- $this->publicKey->loadKey($key);
- $this->publicKey->setPublicKey();
- break;
- default:
- $this->publicKey = null;
- }
+ $key = $csr['certificationRequestInfo']['subjectPKInfo'];
+ $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
+ $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] =
+ "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(base64_encode($key), 64) .
+ "-----END PUBLIC KEY-----";
$this->currentKeyIdentifier = null;
$this->currentCert = $csr;
+ $this->publicKey = null;
+ $this->publicKey = $this->getPublicKey();
+
return $csr;
}
@@ -3303,50 +2249,40 @@ class X509
*
* @param array $csr
* @param int $format optional
- * @access public
* @return string
*/
- function saveCSR($csr, $format = self::FORMAT_PEM)
+ public function saveCSR(array $csr, $format = self::FORMAT_PEM)
{
if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) {
return false;
}
switch (true) {
- case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
+ case !($algorithm = $this->subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')):
case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']):
break;
default:
- switch ($algorithm) {
- case 'rsaEncryption':
- $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
- = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'])));
- $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null;
- $csr['signatureAlgorithm']['parameters'] = null;
- $csr['certificationRequestInfo']['signature']['parameters'] = null;
- }
+ $csr['certificationRequestInfo']['subjectPKInfo'] = new Element(
+ base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))
+ );
}
- $asn1 = new ASN1();
-
- $asn1->loadOIDs($this->oids);
-
- $filters = array();
+ $filters = [];
$filters['certificationRequestInfo']['subject']['rdnSequence']['value']
- = array('type' => ASN1::TYPE_UTF8_STRING);
+ = ['type' => ASN1::TYPE_UTF8_STRING];
- $asn1->loadFilters($filters);
+ ASN1::setFilters($filters);
- $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1);
- $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1);
- $csr = $asn1->encodeDER($csr, $this->CertificationRequest);
+ $this->mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence');
+ $this->mapOutAttributes($csr, 'certificationRequestInfo/attributes');
+ $csr = ASN1::encodeDER($csr, Maps\CertificationRequest::MAP);
switch ($format) {
case self::FORMAT_DER:
return $csr;
// case self::FORMAT_PEM:
default:
- return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
+ return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(Strings::base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----';
}
}
@@ -3357,11 +2293,10 @@ class X509
*
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
*
- * @param string|array $spkac
- * @access public
+ * @param string $spkac
* @return mixed
*/
- function loadSPKAC($spkac)
+ public function loadSPKAC($spkac)
{
if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) {
unset($this->currentCert);
@@ -3373,11 +2308,9 @@ class X509
// see http://www.w3.org/html/wg/drafts/html/master/forms.html#signedpublickeyandchallenge
- $asn1 = new ASN1();
-
// OpenSSL produces SPKAC's that are preceded by the string SPKAC=
$temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac);
- $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
+ $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Strings::base64_decode($temp) : false;
if ($temp != false) {
$spkac = $temp;
}
@@ -3388,74 +2321,63 @@ class X509
return false;
}
- $asn1->loadOIDs($this->oids);
- $decoded = $asn1->decodeBER($spkac);
+ $decoded = ASN1::decodeBER($spkac);
- if (empty($decoded)) {
+ if (!$decoded) {
$this->currentCert = false;
return false;
}
- $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge);
+ $spkac = ASN1::asn1map($decoded[0], Maps\SignedPublicKeyAndChallenge::MAP);
- if (!isset($spkac) || $spkac === false) {
+ if (!isset($spkac) || !is_array($spkac)) {
$this->currentCert = false;
return false;
}
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
- $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm'];
- $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'];
- $key = $this->_reformatKey($algorithm, $key);
-
- switch ($algorithm) {
- case 'rsaEncryption':
- $this->publicKey = new RSA();
- $this->publicKey->loadKey($key);
- $this->publicKey->setPublicKey();
- break;
- default:
- $this->publicKey = null;
- }
+ $key = $spkac['publicKeyAndChallenge']['spki'];
+ $key = ASN1::encodeDER($key, Maps\SubjectPublicKeyInfo::MAP);
+ $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] =
+ "-----BEGIN PUBLIC KEY-----\r\n" .
+ chunk_split(base64_encode($key), 64) .
+ "-----END PUBLIC KEY-----";
$this->currentKeyIdentifier = null;
$this->currentCert = $spkac;
+ $this->publicKey = null;
+ $this->publicKey = $this->getPublicKey();
+
return $spkac;
}
/**
* Save a SPKAC CSR request
*
- * @param string|array $spkac
+ * @param array $spkac
* @param int $format optional
- * @access public
* @return string
*/
- function saveSPKAC($spkac, $format = self::FORMAT_PEM)
+ public function saveSPKAC(array $spkac, $format = self::FORMAT_PEM)
{
if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) {
return false;
}
- $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm');
+ $algorithm = $this->subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm');
switch (true) {
case !$algorithm:
case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']):
break;
default:
- switch ($algorithm) {
- case 'rsaEncryption':
- $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']
- = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'])));
- }
+ $spkac['publicKeyAndChallenge']['spki'] = new Element(
+ base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))
+ );
}
- $asn1 = new ASN1();
-
- $asn1->loadOIDs($this->oids);
- $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge);
+ $spkac = ASN1::encodeDER($spkac, Maps\SignedPublicKeyAndChallenge::MAP);
switch ($format) {
case self::FORMAT_DER:
@@ -3464,7 +2386,7 @@ class X509
default:
// OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much
// no other SPKAC decoders phpseclib will use that same format
- return 'SPKAC=' . base64_encode($spkac);
+ return 'SPKAC=' . Strings::base64_encode($spkac);
}
}
@@ -3473,10 +2395,9 @@ class X509
*
* @param string $crl
* @param int $mode
- * @access public
* @return mixed
*/
- function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
+ public function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
{
if (is_array($crl) && isset($crl['tbsCertList'])) {
$this->currentCert = $crl;
@@ -3484,10 +2405,8 @@ class X509
return $crl;
}
- $asn1 = new ASN1();
-
if ($mode != self::FORMAT_DER) {
- $newcrl = $this->_extractBER($crl);
+ $newcrl = ASN1::extractBER($crl);
if ($mode == self::FORMAT_PEM && $crl == $newcrl) {
return false;
}
@@ -3500,15 +2419,14 @@ class X509
return false;
}
- $asn1->loadOIDs($this->oids);
- $decoded = $asn1->decodeBER($crl);
+ $decoded = ASN1::decodeBER($crl);
- if (empty($decoded)) {
+ if (!$decoded) {
$this->currentCert = false;
return false;
}
- $crl = $asn1->asn1map($decoded[0], $this->CertificateList);
+ $crl = ASN1::asn1map($decoded[0], Maps\CertificateList::MAP);
if (!isset($crl) || $crl === false) {
$this->currentCert = false;
return false;
@@ -3516,17 +2434,17 @@ class X509
$this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
- $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
- if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
- $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
+ $this->mapInDNs($crl, 'tbsCertList/issuer/rdnSequence');
+ if ($this->isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
+ $this->mapInExtensions($crl, 'tbsCertList/crlExtensions');
}
- if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) {
- $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
+ if ($this->isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) {
+ $rclist_ref = &$this->subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist = $crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) {
- if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) {
- $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1);
+ if ($this->isSubArrayValid($rclist, "$i/crlEntryExtensions")) {
+ $this->mapInExtensions($rclist_ref, "$i/crlEntryExtensions");
}
}
}
@@ -3543,56 +2461,51 @@ class X509
*
* @param array $crl
* @param int $format optional
- * @access public
* @return string
*/
- function saveCRL($crl, $format = self::FORMAT_PEM)
+ public function saveCRL(array $crl, $format = self::FORMAT_PEM)
{
if (!is_array($crl) || !isset($crl['tbsCertList'])) {
return false;
}
- $asn1 = new ASN1();
-
- $asn1->loadOIDs($this->oids);
-
- $filters = array();
+ $filters = [];
$filters['tbsCertList']['issuer']['rdnSequence']['value']
- = array('type' => ASN1::TYPE_UTF8_STRING);
+ = ['type' => ASN1::TYPE_UTF8_STRING];
$filters['tbsCertList']['signature']['parameters']
- = array('type' => ASN1::TYPE_UTF8_STRING);
+ = ['type' => ASN1::TYPE_UTF8_STRING];
$filters['signatureAlgorithm']['parameters']
- = array('type' => ASN1::TYPE_UTF8_STRING);
+ = ['type' => ASN1::TYPE_UTF8_STRING];
if (empty($crl['tbsCertList']['signature']['parameters'])) {
$filters['tbsCertList']['signature']['parameters']
- = array('type' => ASN1::TYPE_NULL);
+ = ['type' => ASN1::TYPE_NULL];
}
if (empty($crl['signatureAlgorithm']['parameters'])) {
$filters['signatureAlgorithm']['parameters']
- = array('type' => ASN1::TYPE_NULL);
+ = ['type' => ASN1::TYPE_NULL];
}
- $asn1->loadFilters($filters);
+ ASN1::setFilters($filters);
- $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
- $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
- $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates');
+ $this->mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence');
+ $this->mapOutExtensions($crl, 'tbsCertList/crlExtensions');
+ $rclist = &$this->subArray($crl, 'tbsCertList/revokedCertificates');
if (is_array($rclist)) {
foreach ($rclist as $i => $extension) {
- $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1);
+ $this->mapOutExtensions($rclist, "$i/crlEntryExtensions");
}
}
- $crl = $asn1->encodeDER($crl, $this->CertificateList);
+ $crl = ASN1::encodeDER($crl, Maps\CertificateList::MAP);
switch ($format) {
case self::FORMAT_DER:
return $crl;
// case self::FORMAT_PEM:
default:
- return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----';
+ return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Strings::base64_encode($crl), 64) . '-----END X509 CRL-----';
}
}
@@ -3605,20 +2518,19 @@ class X509
* by choosing utcTime iff year of date given is before 2050 and generalTime else.
*
* @param string $date in format date('D, d M Y H:i:s O')
- * @access private
- * @return array
+ * @return array|Element
*/
- function _timeField($date)
+ private function timeField($date)
{
if ($date instanceof Element) {
return $date;
}
- $dateObj = new DateTime($date, new DateTimeZone('GMT'));
+ $dateObj = new \DateTimeImmutable($date, new \DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way ASN1.php parses this
if ($year < 2050) {
- return array('utcTime' => $date);
+ return ['utcTime' => $date];
} else {
- return array('generalTime' => $date);
+ return ['generalTime' => $date];
}
}
@@ -3629,35 +2541,32 @@ class X509
* $subject can be either an existing X.509 cert (if you want to resign it),
* a CSR or something with the DN and public key explicitly set.
*
- * @param \phpseclib\File\X509 $issuer
- * @param \phpseclib\File\X509 $subject
- * @param string $signatureAlgorithm optional
- * @access public
* @return mixed
*/
- function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption')
+ public function sign(X509 $issuer, X509 $subject)
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
}
- if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) {
+ if (isset($subject->publicKey) && !($subjectPublicKey = $subject->formatSubjectPublicKey())) {
return false;
}
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
- $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
+ $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey);
if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
$this->currentCert = $subject->currentCert;
- $this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm;
- $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
+ $this->currentCert['tbsCertificate']['signature'] = $signatureAlgorithm;
+ $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm;
if (!empty($this->startDate)) {
- $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate);
+ $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->timeField($this->startDate);
}
if (!empty($this->endDate)) {
- $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate);
+ $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->timeField($this->endDate);
}
if (!empty($this->serialNumber)) {
$this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber;
@@ -3679,10 +2588,10 @@ class X509
return false;
}
- $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
+ $startDate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');
- $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
+ $endDate = new \DateTimeImmutable('+1 year', new \DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer"
@@ -3696,23 +2605,23 @@ class X509
$this->serialNumber :
new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256);
- $this->currentCert = array(
+ $this->currentCert = [
'tbsCertificate' =>
- array(
+ [
'version' => 'v3',
'serialNumber' => $serialNumber, // $this->setSerialNumber()
- 'signature' => array('algorithm' => $signatureAlgorithm),
+ 'signature' => $signatureAlgorithm,
'issuer' => false, // this is going to be overwritten later
- 'validity' => array(
- 'notBefore' => $this->_timeField($startDate), // $this->setStartDate()
- 'notAfter' => $this->_timeField($endDate) // $this->setEndDate()
- ),
+ 'validity' => [
+ 'notBefore' => $this->timeField($startDate), // $this->setStartDate()
+ 'notAfter' => $this->timeField($endDate) // $this->setEndDate()
+ ],
'subject' => $subject->dn,
'subjectPublicKeyInfo' => $subjectPublicKey
- ),
- 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ ],
+ 'signatureAlgorithm' => $signatureAlgorithm,
'signature' => false // this is going to be overwritten later
- );
+ ];
// Copy extensions from CSR.
$csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0);
@@ -3725,14 +2634,14 @@ class X509
$this->currentCert['tbsCertificate']['issuer'] = $issuer->dn;
if (isset($issuer->currentKeyIdentifier)) {
- $this->setExtension('id-ce-authorityKeyIdentifier', array(
+ $this->setExtension('id-ce-authorityKeyIdentifier', [
//'authorityCertIssuer' => array(
// array(
// 'directoryName' => $issuer->dn
// )
//),
'keyIdentifier' => $issuer->currentKeyIdentifier
- ));
+ ]);
//$extensions = &$this->currentCert['tbsCertificate']['extensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
@@ -3744,18 +2653,18 @@ class X509
$this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier);
}
- $altName = array();
+ $altName = [];
if (isset($subject->domains) && count($subject->domains)) {
- $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains);
+ $altName = array_map(['\phpseclib3\File\X509', 'dnsName'], $subject->domains);
}
if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
// should an IP address appear as the CN if no domain name is specified? idk
//$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1);
- $ipAddresses = array();
+ $ipAddresses = [];
foreach ($subject->ipAddresses as $ipAddress) {
- $encoded = $subject->_ipAddress($ipAddress);
+ $encoded = $subject->ipAddress($ipAddress);
if ($encoded !== false) {
$ipAddresses[] = $encoded;
}
@@ -3772,36 +2681,37 @@ class X509
if ($this->caFlag) {
$keyUsage = $this->getExtension('id-ce-keyUsage');
if (!$keyUsage) {
- $keyUsage = array();
+ $keyUsage = [];
}
$this->setExtension(
'id-ce-keyUsage',
- array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign'))))
+ array_values(array_unique(array_merge($keyUsage, ['cRLSign', 'keyCertSign'])))
);
$basicConstraints = $this->getExtension('id-ce-basicConstraints');
if (!$basicConstraints) {
- $basicConstraints = array();
+ $basicConstraints = [];
}
$this->setExtension(
'id-ce-basicConstraints',
- array_unique(array_merge(array('cA' => true), $basicConstraints)),
+ array_merge(['cA' => true], $basicConstraints),
true
);
if (!isset($subject->currentKeyIdentifier)) {
- $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false);
+ $this->setExtension('id-ce-subjectKeyIdentifier', $this->computeKeyIdentifier($this->currentCert), false, false);
}
}
// resync $this->signatureSubject
- // save $tbsCertificate in case there are any \phpseclib\File\ASN1\Element objects in it
+ // save $tbsCertificate in case there are any \phpseclib3\File\ASN1\Element objects in it
$tbsCertificate = $this->currentCert['tbsCertificate'];
$this->loadX509($this->saveX509($this->currentCert));
- $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
+ $result = $this->currentCert;
+ $this->currentCert['signature'] = $result['signature'] = "\0" . $issuer->privateKey->sign($this->signatureSubject);
$result['tbsCertificate'] = $tbsCertificate;
$this->currentCert = $currentCert;
@@ -3813,54 +2723,50 @@ class X509
/**
* Sign a CSR
*
- * @access public
* @return mixed
*/
- function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption')
+ public function signCSR()
{
if (!is_object($this->privateKey) || empty($this->dn)) {
return false;
}
$origPublicKey = $this->publicKey;
- $class = get_class($this->privateKey);
- $this->publicKey = new $class();
- $this->publicKey->loadKey($this->privateKey->getPublicKey());
- $this->publicKey->setPublicKey();
- if (!($publicKey = $this->_formatSubjectPublicKey())) {
- return false;
- }
+ $this->publicKey = $this->privateKey->getPublicKey();
+ $publicKey = $this->formatSubjectPublicKey();
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
- $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
+ $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey);
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) {
- $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
+ $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm;
if (!empty($this->dn)) {
$this->currentCert['certificationRequestInfo']['subject'] = $this->dn;
}
$this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey;
} else {
- $this->currentCert = array(
+ $this->currentCert = [
'certificationRequestInfo' =>
- array(
+ [
'version' => 'v1',
'subject' => $this->dn,
'subjectPKInfo' => $publicKey,
- 'attributes' => array()
- ),
- 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ 'attributes' => []
+ ],
+ 'signatureAlgorithm' => $signatureAlgorithm,
'signature' => false // this is going to be overwritten later
- );
+ ];
}
// resync $this->signatureSubject
- // save $certificationRequestInfo in case there are any \phpseclib\File\ASN1\Element objects in it
+ // save $certificationRequestInfo in case there are any \phpseclib3\File\ASN1\Element objects in it
$certificationRequestInfo = $this->currentCert['certificationRequestInfo'];
$this->loadCSR($this->saveCSR($this->currentCert));
- $result = $this->_sign($this->privateKey, $signatureAlgorithm);
+ $result = $this->currentCert;
+ $this->currentCert['signature'] = $result['signature'] = "\0" . $this->privateKey->sign($this->signatureSubject);
$result['certificationRequestInfo'] = $certificationRequestInfo;
$this->currentCert = $currentCert;
@@ -3872,41 +2778,35 @@ class X509
/**
* Sign a SPKAC
*
- * @access public
* @return mixed
*/
- function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption')
+ public function signSPKAC()
{
if (!is_object($this->privateKey)) {
return false;
}
$origPublicKey = $this->publicKey;
- $class = get_class($this->privateKey);
- $this->publicKey = new $class();
- $this->publicKey->loadKey($this->privateKey->getPublicKey());
- $this->publicKey->setPublicKey();
- $publicKey = $this->_formatSubjectPublicKey();
- if (!$publicKey) {
- return false;
- }
+ $this->publicKey = $this->privateKey->getPublicKey();
+ $publicKey = $this->formatSubjectPublicKey();
$this->publicKey = $origPublicKey;
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
- $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null;
+ $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
+ $signatureAlgorithm = self::identifySignatureAlgorithm($this->privateKey);
// re-signing a SPKAC seems silly but since everything else supports re-signing why not?
if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) {
- $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
+ $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm;
$this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey;
if (!empty($this->challenge)) {
// the bitwise AND ensures that the output is a valid IA5String
$this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge));
}
} else {
- $this->currentCert = array(
+ $this->currentCert = [
'publicKeyAndChallenge' =>
- array(
+ [
'spki' => $publicKey,
// quoting <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen>,
// "A challenge string that is submitted along with the public key. Defaults to an empty string if not specified."
@@ -3914,18 +2814,19 @@ class X509
// we could alternatively do this instead if we ignored the specs:
// Random::string(8) & str_repeat("\x7F", 8)
'challenge' => !empty($this->challenge) ? $this->challenge : ''
- ),
- 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ ],
+ 'signatureAlgorithm' => $signatureAlgorithm,
'signature' => false // this is going to be overwritten later
- );
+ ];
}
// resync $this->signatureSubject
- // save $publicKeyAndChallenge in case there are any \phpseclib\File\ASN1\Element objects in it
+ // save $publicKeyAndChallenge in case there are any \phpseclib3\File\ASN1\Element objects in it
$publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge'];
$this->loadSPKAC($this->saveSPKAC($this->currentCert));
- $result = $this->_sign($this->privateKey, $signatureAlgorithm);
+ $result = $this->currentCert;
+ $this->currentCert['signature'] = $result['signature'] = "\0" . $this->privateKey->sign($this->signatureSubject);
$result['publicKeyAndChallenge'] = $publicKeyAndChallenge;
$this->currentCert = $currentCert;
@@ -3939,13 +2840,9 @@ class X509
*
* $issuer's private key needs to be loaded.
*
- * @param \phpseclib\File\X509 $issuer
- * @param \phpseclib\File\X509 $crl
- * @param string $signatureAlgorithm optional
- * @access public
* @return mixed
*/
- function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption')
+ public function signCRL(X509 $issuer, X509 $crl)
{
if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false;
@@ -3953,34 +2850,35 @@ class X509
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
+ $signatureAlgorithm = self::identifySignatureAlgorithm($issuer->privateKey);
- $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
+ $thisUpdate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
- $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm;
- $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm;
+ $this->currentCert['tbsCertList']['signature'] = $signatureAlgorithm;
+ $this->currentCert['signatureAlgorithm'] = $signatureAlgorithm;
} else {
- $this->currentCert = array(
+ $this->currentCert = [
'tbsCertList' =>
- array(
+ [
'version' => 'v2',
- 'signature' => array('algorithm' => $signatureAlgorithm),
+ 'signature' => $signatureAlgorithm,
'issuer' => false, // this is going to be overwritten later
- 'thisUpdate' => $this->_timeField($thisUpdate) // $this->setStartDate()
- ),
- 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
+ 'thisUpdate' => $this->timeField($thisUpdate) // $this->setStartDate()
+ ],
+ 'signatureAlgorithm' => $signatureAlgorithm,
'signature' => false // this is going to be overwritten later
- );
+ ];
}
$tbsCertList = &$this->currentCert['tbsCertList'];
$tbsCertList['issuer'] = $issuer->dn;
- $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate);
+ $tbsCertList['thisUpdate'] = $this->timeField($thisUpdate);
if (!empty($this->endDate)) {
- $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); // $this->setEndDate()
+ $tbsCertList['nextUpdate'] = $this->timeField($this->endDate); // $this->setEndDate()
} else {
unset($tbsCertList['nextUpdate']);
}
@@ -4025,14 +2923,14 @@ class X509
}
if (isset($issuer->currentKeyIdentifier)) {
- $this->setExtension('id-ce-authorityKeyIdentifier', array(
+ $this->setExtension('id-ce-authorityKeyIdentifier', [
//'authorityCertIssuer' => array(
- // array(
+ // ]
// 'directoryName' => $issuer->dn
- // )
+ // ]
//),
'keyIdentifier' => $issuer->currentKeyIdentifier
- ));
+ ]);
//$extensions = &$tbsCertList['crlExtensions'];
//if (isset($issuer->serialNumber)) {
// $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber;
@@ -4054,11 +2952,12 @@ class X509
unset($tbsCertList);
// resync $this->signatureSubject
- // save $tbsCertList in case there are any \phpseclib\File\ASN1\Element objects in it
+ // save $tbsCertList in case there are any \phpseclib3\File\ASN1\Element objects in it
$tbsCertList = $this->currentCert['tbsCertList'];
$this->loadCRL($this->saveCRL($this->currentCert));
- $result = $this->_sign($issuer->privateKey, $signatureAlgorithm);
+ $result = $this->currentCert;
+ $this->currentCert['signature'] = $result['signature'] = "\0" . $issuer->privateKey->sign($this->signatureSubject);
$result['tbsCertList'] = $tbsCertList;
$this->currentCert = $currentCert;
@@ -4068,45 +2967,77 @@ class X509
}
/**
- * X.509 certificate signing helper function.
+ * Identify signature algorithm from key settings
*
- * @param \phpseclib\File\X509 $key
- * @param string $signatureAlgorithm
- * @access public
- * @return mixed
+ * @param PrivateKey $key
+ * @throws UnsupportedAlgorithmException if the algorithm is unsupported
+ * @return array
*/
- function _sign($key, $signatureAlgorithm)
+ private static function identifySignatureAlgorithm(PrivateKey $key)
{
if ($key instanceof RSA) {
- switch ($signatureAlgorithm) {
- case 'md2WithRSAEncryption':
- case 'md5WithRSAEncryption':
- case 'sha1WithRSAEncryption':
- case 'sha224WithRSAEncryption':
- case 'sha256WithRSAEncryption':
- case 'sha384WithRSAEncryption':
- case 'sha512WithRSAEncryption':
- $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
- $key->setSignatureMode(RSA::SIGNATURE_PKCS1);
-
- $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
- return $this->currentCert;
+ if ($key->getPadding() & RSA::SIGNATURE_PSS) {
+ $r = PSS::load($key->withPassword()->toString('PSS'));
+ return [
+ 'algorithm' => 'id-RSASSA-PSS',
+ 'parameters' => PSS::savePSSParams($r)
+ ];
}
+ switch ($key->getHash()) {
+ case 'md2':
+ case 'md5':
+ case 'sha1':
+ case 'sha224':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ return [
+ 'algorithm' => $key->getHash() . 'WithRSAEncryption',
+ 'parameters' => null
+ ];
+ }
+ throw new UnsupportedAlgorithmException('The only supported hash algorithms for RSA are: md2, md5, sha1, sha224, sha256, sha384, sha512');
}
- return false;
+ if ($key instanceof DSA) {
+ switch ($key->getHash()) {
+ case 'sha1':
+ case 'sha224':
+ case 'sha256':
+ return ['algorithm' => 'id-dsa-with-' . $key->getHash()];
+ }
+ throw new UnsupportedAlgorithmException('The only supported hash algorithms for DSA are: sha1, sha224, sha256');
+ }
+
+ if ($key instanceof EC) {
+ switch ($key->getCurve()) {
+ case 'Ed25519':
+ case 'Ed448':
+ return ['algorithm' => 'id-' . $key->getCurve()];
+ }
+ switch ($key->getHash()) {
+ case 'sha1':
+ case 'sha224':
+ case 'sha256':
+ case 'sha384':
+ case 'sha512':
+ return ['algorithm' => 'ecdsa-with-' . strtoupper($key->getHash())];
+ }
+ throw new UnsupportedAlgorithmException('The only supported hash algorithms for EC are: sha1, sha224, sha256, sha384, sha512');
+ }
+
+ throw new UnsupportedAlgorithmException('The only supported public key classes are: RSA, DSA, EC');
}
/**
* Set certificate start date
*
- * @param string $date
- * @access public
+ * @param \DateTimeInterface|string $date
*/
- function setStartDate($date)
+ public function setStartDate($date)
{
- if (!is_object($date) || !is_a($date, 'DateTime')) {
- $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
+ if (!is_object($date) || !($date instanceof \DateTimeInterface)) {
+ $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s O');
@@ -4115,10 +3046,9 @@ class X509
/**
* Set certificate end date
*
- * @param string $date
- * @access public
+ * @param \DateTimeInterface|string $date
*/
- function setEndDate($date)
+ public function setEndDate($date)
{
/*
To indicate that a certificate has no well-defined expiration date,
@@ -4127,14 +3057,13 @@ class X509
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.5
*/
- if (strtolower($date) == 'lifetime') {
+ if (is_string($date) && strtolower($date) === 'lifetime') {
$temp = '99991231235959Z';
- $asn1 = new ASN1();
- $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
+ $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . ASN1::encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
- if (!is_object($date) || !is_a($date, 'DateTime')) {
- $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
+ if (!is_object($date) || !($date instanceof \DateTimeInterface)) {
+ $date = new \DateTimeImmutable($date, new \DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s O');
@@ -4146,9 +3075,8 @@ class X509
*
* @param string $serial
* @param int $base optional
- * @access public
*/
- function setSerialNumber($serial, $base = -256)
+ public function setSerialNumber($serial, $base = -256)
{
$this->serialNumber = new BigInteger($serial, $base);
}
@@ -4156,9 +3084,8 @@ class X509
/**
* Turns the certificate into a certificate authority
*
- * @access public
*/
- function makeCA()
+ public function makeCA()
{
$this->caFlag = true;
}
@@ -4173,9 +3100,8 @@ class X509
* @param array $root
* @param string $path
* @return boolean
- * @access private
*/
- function _isSubArrayValid($root, $path)
+ private function isSubArrayValid(array $root, $path)
{
if (!is_array($root)) {
return false;
@@ -4209,10 +3135,9 @@ class X509
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
- * @access private
* @return array|false
*/
- function &_subArrayUnchecked(&$root, $path, $create = false)
+ private function &subArrayUnchecked(array &$root, $path, $create = false)
{
$false = false;
@@ -4222,7 +3147,7 @@ class X509
return $false;
}
- $root[$i] = array();
+ $root[$i] = [];
}
$root = &$root[$i];
@@ -4237,10 +3162,9 @@ class X509
* @param array $root
* @param string $path absolute path with / as component separator
* @param bool $create optional
- * @access private
* @return array|false
*/
- function &_subArray(&$root, $path, $create = false)
+ private function &subArray(&$root, $path, $create = false)
{
$false = false;
@@ -4258,7 +3182,7 @@ class X509
return $false;
}
- $root[$i] = array();
+ $root[$i] = [];
}
$root = &$root[$i];
@@ -4273,10 +3197,9 @@ class X509
* @param array $root
* @param string $path optional absolute path with / as component separator
* @param bool $create optional
- * @access private
* @return array|false
*/
- function &_extensions(&$root, $path = null, $create = false)
+ private function &extensions(&$root, $path = null, $create = false)
{
if (!isset($root)) {
$root = $this->currentCert;
@@ -4294,7 +3217,7 @@ class X509
break;
case isset($root['certificationRequestInfo']):
$pth = 'certificationRequestInfo/attributes';
- $attributes = &$this->_subArray($root, $pth, $create);
+ $attributes = &$this->subArray($root, $pth, $create);
if (is_array($attributes)) {
foreach ($attributes as $key => $value) {
@@ -4305,14 +3228,14 @@ class X509
}
if ($create) {
$key = count($attributes);
- $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array());
+ $attributes[] = ['type' => 'pkcs-9-at-extensionRequest', 'value' => []];
$path = "$pth/$key/value/0";
}
}
break;
}
- $extensions = &$this->_subArray($root, $path, $create);
+ $extensions = &$this->subArray($root, $path, $create);
if (!is_array($extensions)) {
$false = false;
@@ -4327,12 +3250,11 @@ class X509
*
* @param string $id
* @param string $path optional
- * @access private
* @return bool
*/
- function _removeExtension($id, $path = null)
+ private function removeExtensionHelper($id, $path = null)
{
- $extensions = &$this->_extensions($this->currentCert, $path);
+ $extensions = &$this->extensions($this->currentCert, $path);
if (!is_array($extensions)) {
return false;
@@ -4362,12 +3284,11 @@ class X509
* @param string $id
* @param array $cert optional
* @param string $path optional
- * @access private
* @return mixed
*/
- function _getExtension($id, $cert = null, $path = null)
+ private function getExtensionHelper($id, $cert = null, $path = null)
{
- $extensions = $this->_extensions($cert, $path);
+ $extensions = $this->extensions($cert, $path);
if (!is_array($extensions)) {
return false;
@@ -4387,13 +3308,12 @@ class X509
*
* @param array $cert optional
* @param string $path optional
- * @access private
* @return array
*/
- function _getExtensions($cert = null, $path = null)
+ private function getExtensionsHelper($cert = null, $path = null)
{
- $exts = $this->_extensions($cert, $path);
- $extensions = array();
+ $exts = $this->extensions($cert, $path);
+ $extensions = [];
if (is_array($exts)) {
foreach ($exts as $extension) {
@@ -4412,18 +3332,17 @@ class X509
* @param bool $critical optional
* @param bool $replace optional
* @param string $path optional
- * @access private
* @return bool
*/
- function _setExtension($id, $value, $critical = false, $replace = true, $path = null)
+ private function setExtensionHelper($id, $value, $critical = false, $replace = true, $path = null)
{
- $extensions = &$this->_extensions($this->currentCert, $path, true);
+ $extensions = &$this->extensions($this->currentCert, $path, true);
if (!is_array($extensions)) {
return false;
}
- $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value);
+ $newext = ['extnId' => $id, 'critical' => $critical, 'extnValue' => $value];
foreach ($extensions as $key => $value) {
if ($value['extnId'] == $id) {
@@ -4444,12 +3363,11 @@ class X509
* Remove a certificate, CSR or CRL Extension
*
* @param string $id
- * @access public
* @return bool
*/
- function removeExtension($id)
+ public function removeExtension($id)
{
- return $this->_removeExtension($id);
+ return $this->removeExtensionHelper($id);
}
/**
@@ -4459,24 +3377,24 @@ class X509
*
* @param string $id
* @param array $cert optional
- * @access public
+ * @param string $path
* @return mixed
*/
- function getExtension($id, $cert = null)
+ public function getExtension($id, $cert = null, $path = null)
{
- return $this->_getExtension($id, $cert);
+ return $this->getExtensionHelper($id, $cert, $path);
}
/**
* Returns a list of all extensions in use in certificate, CSR or CRL
*
* @param array $cert optional
- * @access public
+ * @param string $path optional
* @return array
*/
- function getExtensions($cert = null)
+ public function getExtensions($cert = null, $path = null)
{
- return $this->_getExtensions($cert);
+ return $this->getExtensionsHelper($cert, $path);
}
/**
@@ -4486,12 +3404,11 @@ class X509
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
- * @access public
* @return bool
*/
- function setExtension($id, $value, $critical = false, $replace = true)
+ public function setExtension($id, $value, $critical = false, $replace = true)
{
- return $this->_setExtension($id, $value, $critical, $replace);
+ return $this->setExtensionHelper($id, $value, $critical, $replace);
}
/**
@@ -4499,12 +3416,11 @@ class X509
*
* @param string $id
* @param int $disposition optional
- * @access public
* @return bool
*/
- function removeAttribute($id, $disposition = self::ATTR_ALL)
+ public function removeAttribute($id, $disposition = self::ATTR_ALL)
{
- $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes');
+ $attributes = &$this->subArray($this->currentCert, 'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
@@ -4550,16 +3466,15 @@ class X509
* @param string $id
* @param int $disposition optional
* @param array $csr optional
- * @access public
* @return mixed
*/
- function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
+ public function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
- $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
+ $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes');
if (!is_array($attributes)) {
return false;
@@ -4587,20 +3502,41 @@ class X509
}
/**
+ * Get all requested CSR extensions
+ *
+ * Returns the list of extensions if there are any and false if not
+ *
+ * @param array $csr optional
+ * @return mixed
+ */
+ public function getRequestedCertificateExtensions($csr = null)
+ {
+ if (empty($csr)) {
+ $csr = $this->currentCert;
+ }
+
+ $requestedExtensions = $this->getAttribute('pkcs-9-at-extensionRequest');
+ if ($requestedExtensions === false) {
+ return false;
+ }
+
+ return $this->getAttribute('pkcs-9-at-extensionRequest')[0];
+ }
+
+ /**
* Returns a list of all CSR attributes in use
*
* @param array $csr optional
- * @access public
* @return array
*/
- function getAttributes($csr = null)
+ public function getAttributes($csr = null)
{
if (empty($csr)) {
$csr = $this->currentCert;
}
- $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes');
- $attrs = array();
+ $attributes = $this->subArray($csr, 'certificationRequestInfo/attributes');
+ $attrs = [];
if (is_array($attributes)) {
foreach ($attributes as $attribute) {
@@ -4616,13 +3552,12 @@ class X509
*
* @param string $id
* @param mixed $value
- * @param bool $disposition optional
- * @access public
+ * @param int $disposition optional
* @return bool
*/
- function setAttribute($id, $value, $disposition = self::ATTR_ALL)
+ public function setAttribute($id, $value, $disposition = self::ATTR_ALL)
{
- $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true);
+ $attributes = &$this->subArray($this->currentCert, 'certificationRequestInfo/attributes', true);
if (!is_array($attributes)) {
return false;
@@ -4631,6 +3566,7 @@ class X509
switch ($disposition) {
case self::ATTR_REPLACE:
$disposition = self::ATTR_APPEND;
+ // fall-through
case self::ATTR_ALL:
$this->removeAttribute($id);
break;
@@ -4660,7 +3596,7 @@ class X509
$attributes[$last]['value'][] = $value;
break;
default:
- $attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value));
+ $attributes[] = ['type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value : [$value]];
break;
}
@@ -4673,14 +3609,13 @@ class X509
* This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
*
* @param string $value
- * @access public
*/
- function setKeyIdentifier($value)
+ public function setKeyIdentifier($value)
{
if (empty($value)) {
unset($this->currentKeyIdentifier);
} else {
- $this->currentKeyIdentifier = base64_encode($value);
+ $this->currentKeyIdentifier = $value;
}
}
@@ -4692,17 +3627,16 @@ class X509
* recommended methods (4.2.1.2 RFC 3280).
* Highly polymorphic: try to accept all possible forms of key:
* - Key object
- * - \phpseclib\File\X509 object with public or private key defined
+ * - \phpseclib3\File\X509 object with public or private key defined
* - Certificate or CSR array
- * - \phpseclib\File\ASN1\Element object
+ * - \phpseclib3\File\ASN1\Element object
* - PEM or DER string
*
* @param mixed $key optional
* @param int $method optional
- * @access public
* @return string binary key identifier
*/
- function computeKeyIdentifier($key = null, $method = 1)
+ public function computeKeyIdentifier($key = null, $method = 1)
{
if (is_null($key)) {
$key = $this;
@@ -4719,25 +3653,20 @@ class X509
return false;
case $key instanceof Element:
// Assume the element is a bitstring-packed key.
- $asn1 = new ASN1();
- $decoded = $asn1->decodeBER($key->element);
- if (empty($decoded)) {
+ $decoded = ASN1::decodeBER($key->element);
+ if (!$decoded) {
return false;
}
- $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING));
+ $raw = ASN1::asn1map($decoded[0], ['type' => ASN1::TYPE_BIT_STRING]);
if (empty($raw)) {
return false;
}
- $raw = base64_decode($raw);
// If the key is private, compute identifier from its corresponding public key.
- $key = new RSA();
- if (!$key->loadKey($raw)) {
- return false; // Not an unencrypted RSA key.
- }
- if ($key->getPrivateKey() !== false) { // If private.
+ $key = PublicKeyLoader::load($raw);
+ if ($key instanceof PrivateKey) { // If private.
return $this->computeKeyIdentifier($key, $method);
}
- $key = $raw; // Is a public key.
+ $key = $raw; // Is a public key.
break;
case $key instanceof X509:
if (isset($key->publicKey)) {
@@ -4750,13 +3679,13 @@ class X509
return $this->computeKeyIdentifier($key->currentCert, $method);
}
return false;
- default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA).
- $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
+ default: // Should be a key object (i.e.: \phpseclib3\Crypt\RSA).
+ $key = $key->getPublicKey();
break;
}
// If in PEM format, convert to binary.
- $key = $this->_extractBER($key);
+ $key = ASN1::extractBER($key);
// Now we have the key string: compute its sha-1 sum.
$hash = new Hash('sha1');
@@ -4773,33 +3702,39 @@ class X509
/**
* Format a public key as appropriate
*
- * @access private
- * @return array
+ * @return array|false
*/
- function _formatSubjectPublicKey()
+ private function formatSubjectPublicKey()
{
- if ($this->publicKey instanceof RSA) {
- // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason.
- // the former is a good example of how to do fuzzing on the public key
- //return new Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey())));
- return array(
- 'algorithm' => array('algorithm' => 'rsaEncryption'),
- 'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1)
- );
+ $format = $this->publicKey instanceof RSA && ($this->publicKey->getPadding() & RSA::SIGNATURE_PSS) ?
+ 'PSS' :
+ 'PKCS8';
+
+ $publicKey = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->toString($format)));
+
+ $decoded = ASN1::decodeBER($publicKey);
+ if (!$decoded) {
+ return false;
+ }
+ $mapped = ASN1::asn1map($decoded[0], Maps\SubjectPublicKeyInfo::MAP);
+ if (!is_array($mapped)) {
+ return false;
}
- return false;
+ $mapped['subjectPublicKey'] = $this->publicKey->toString($format);
+
+ return $mapped;
}
/**
* Set the domain name's which the cert is to be valid for
*
- * @access public
- * @return array
+ * @param mixed ...$domains
+ * @return void
*/
- function setDomain()
+ public function setDomain(...$domains)
{
- $this->domains = func_get_args();
+ $this->domains = $domains;
$this->removeDNProp('id-at-commonName');
$this->setDNProp('id-at-commonName', $this->domains[0]);
}
@@ -4807,11 +3742,11 @@ class X509
/**
* Set the IP Addresses's which the cert is to be valid for
*
- * @access public
+ * @param mixed[] ...$ipAddresses
*/
- function setIPAddress()
+ public function setIPAddress(...$ipAddresses)
{
- $this->ipAddresses = func_get_args();
+ $this->ipAddresses = $ipAddresses;
/*
if (!isset($this->domains)) {
$this->removeDNProp('id-at-commonName');
@@ -4823,13 +3758,12 @@ class X509
/**
* Helper function to build domain array
*
- * @access private
* @param string $domain
* @return array
*/
- function _dnsName($domain)
+ private static function dnsName($domain)
{
- return array('dNSName' => $domain);
+ return ['dNSName' => $domain];
}
/**
@@ -4837,13 +3771,12 @@ class X509
*
* (IPv6 is not currently supported)
*
- * @access private
* @param string $address
* @return array
*/
- function _iPAddress($address)
+ private function iPAddress($address)
{
- return array('iPAddress' => $address);
+ return ['iPAddress' => $address];
}
/**
@@ -4852,10 +3785,9 @@ class X509
* @param array $rclist
* @param string $serial
* @param bool $create optional
- * @access private
* @return int|false
*/
- function _revokedCertificate(&$rclist, $serial, $create = false)
+ private function revokedCertificate(array &$rclist, $serial, $create = false)
{
$serial = new BigInteger($serial);
@@ -4870,9 +3802,9 @@ class X509
}
$i = count($rclist);
- $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
- $rclist[] = array('userCertificate' => $serial,
- 'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O')));
+ $revocationDate = new \DateTimeImmutable('now', new \DateTimeZone(@date_default_timezone_get()));
+ $rclist[] = ['userCertificate' => $serial,
+ 'revocationDate' => $this->timeField($revocationDate->format('D, d M Y H:i:s O'))];
return $i;
}
@@ -4881,17 +3813,16 @@ class X509
*
* @param string $serial
* @param string $date optional
- * @access public
* @return bool
*/
- function revoke($serial, $date = null)
+ public function revoke($serial, $date = null)
{
if (isset($this->currentCert['tbsCertList'])) {
- if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
- if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked
- if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
+ if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
+ if ($this->revokedCertificate($rclist, $serial) === false) { // If not yet revoked
+ if (($i = $this->revokedCertificate($rclist, $serial, true)) !== false) {
if (!empty($date)) {
- $rclist[$i]['revocationDate'] = $this->_timeField($date);
+ $rclist[$i]['revocationDate'] = $this->timeField($date);
}
return true;
@@ -4907,13 +3838,12 @@ class X509
* Unrevoke a certificate.
*
* @param string $serial
- * @access public
* @return bool
*/
- function unrevoke($serial)
+ public function unrevoke($serial)
{
- if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
- if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->revokedCertificate($rclist, $serial)) !== false) {
unset($rclist[$i]);
$rclist = array_values($rclist);
return true;
@@ -4927,13 +3857,12 @@ class X509
* Get a revoked certificate.
*
* @param string $serial
- * @access public
* @return mixed
*/
- function getRevoked($serial)
+ public function getRevoked($serial)
{
- if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
- if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
+ if (is_array($rclist = $this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->revokedCertificate($rclist, $serial)) !== false) {
return $rclist[$i];
}
}
@@ -4945,10 +3874,9 @@ class X509
* List revoked certificates
*
* @param array $crl optional
- * @access public
- * @return array
+ * @return array|bool
*/
- function listRevoked($crl = null)
+ public function listRevoked($crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
@@ -4958,9 +3886,9 @@ class X509
return false;
}
- $result = array();
+ $result = [];
- if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) {
foreach ($rclist as $rc) {
$result[] = $rc['userCertificate']->toString();
}
@@ -4974,14 +3902,13 @@ class X509
*
* @param string $serial
* @param string $id
- * @access public
* @return bool
*/
- function removeRevokedCertificateExtension($serial, $id)
+ public function removeRevokedCertificateExtension($serial, $id)
{
- if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
- if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
- return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->revokedCertificate($rclist, $serial)) !== false) {
+ return $this->removeExtensionHelper($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
@@ -4996,18 +3923,17 @@ class X509
* @param string $serial
* @param string $id
* @param array $crl optional
- * @access public
* @return mixed
*/
- function getRevokedCertificateExtension($serial, $id, $crl = null)
+ public function getRevokedCertificateExtension($serial, $id, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
- if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
- if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
- return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->revokedCertificate($rclist, $serial)) !== false) {
+ return $this->getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
@@ -5019,18 +3945,17 @@ class X509
*
* @param string $serial
* @param array $crl optional
- * @access public
- * @return array
+ * @return array|bool
*/
- function getRevokedCertificateExtensions($serial, $crl = null)
+ public function getRevokedCertificateExtensions($serial, $crl = null)
{
if (!isset($crl)) {
$crl = $this->currentCert;
}
- if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) {
- if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) {
- return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ if (is_array($rclist = $this->subArray($crl, 'tbsCertList/revokedCertificates'))) {
+ if (($i = $this->revokedCertificate($rclist, $serial)) !== false) {
+ return $this->getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
@@ -5045,15 +3970,14 @@ class X509
* @param mixed $value
* @param bool $critical optional
* @param bool $replace optional
- * @access public
* @return bool
*/
- function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
+ public function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
{
if (isset($this->currentCert['tbsCertList'])) {
- if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
- if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) {
- return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
+ if (is_array($rclist = &$this->subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) {
+ if (($i = $this->revokedCertificate($rclist, $serial, true)) !== false) {
+ return $this->setExtensionHelper($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions");
}
}
}
@@ -5062,61 +3986,44 @@ class X509
}
/**
- * Extract raw BER from Base64 encoding
+ * Register the mapping for a custom/unsupported extension.
*
- * @access private
- * @param string $str
- * @return string
+ * @param string $id
+ * @param array $mapping
*/
- function _extractBER($str)
+ public static function registerExtension($id, array $mapping)
{
- /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them
- * above and beyond the ceritificate.
- * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line:
- *
- * Bag Attributes
- * localKeyID: 01 00 00 00
- * subject=/O=organization/OU=org unit/CN=common name
- * issuer=/O=organization/CN=common name
- */
- if (strlen($str) > ini_get('pcre.backtrack_limit')) {
- $temp = $str;
- } else {
- $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
- $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
- }
- // remove new lines
- $temp = str_replace(array("\r", "\n", ' '), '', $temp);
- // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
- $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
- $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
- return $temp != false ? $temp : $str;
+ if (isset(self::$extensions[$id]) && self::$extensions[$id] !== $mapping) {
+ throw new \RuntimeException(
+ 'Extension ' . $id . ' has already been defined with a different mapping.'
+ );
+ }
+
+ self::$extensions[$id] = $mapping;
}
/**
- * Returns the OID corresponding to a name
+ * Register the mapping for a custom/unsupported extension.
*
- * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
- * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
- * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
- * to work from version to version.
- *
- * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
- * what's being passed to it already is an OID and return that instead. A few examples.
+ * @param string $id
*
- * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
- * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
- * getOID('zzz') == 'zzz'
+ * @return array|null
+ */
+ public static function getRegisteredExtension($id)
+ {
+ return isset(self::$extensions[$id]) ? self::$extensions[$id] : null;
+ }
+
+ /**
+ * Register the mapping for a custom/unsupported extension.
*
- * @access public
- * @return string
+ * @param string $id
+ * @param mixed $value
+ * @param bool $critical
+ * @param bool $replace
*/
- function getOID($name)
+ public function setExtensionValue($id, $value, $critical = false, $replace = false)
{
- static $reverseMap;
- if (!isset($reverseMap)) {
- $reverseMap = array_flip($this->oids);
- }
- return isset($reverseMap[$name]) ? $reverseMap[$name] : $name;
+ $this->extensionValues[$id] = compact('critical', 'replace', 'value');
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
index 5f2283678..965d7ff08 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
@@ -6,33 +6,13 @@
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
* and an internal implementation, otherwise.
*
- * PHP version 5
- *
- * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
- * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
- *
- * BigInteger uses base-2**26 to perform operations such as multiplication and division and
- * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
- * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
- * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
- * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
- * which only supports integers. Although this fact will slow this library down, the fact that such a high
- * base is being used should more than compensate.
- *
- * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
- * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
- *
- * Useful resources are as follows:
- *
- * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
- * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
- * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
+ * PHP version 5 and 7
*
* Here's an example of how to use this library:
* <code>
* <?php
- * $a = new \phpseclib\Math\BigInteger(2);
- * $b = new \phpseclib\Math\BigInteger(3);
+ * $a = new \phpseclib3\Math\BigInteger(2);
+ * $b = new \phpseclib3\Math\BigInteger(3);
*
* $c = $a->add($b);
*
@@ -40,1392 +20,265 @@
* ?>
* </code>
*
- * @category Math
- * @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
- * @copyright 2006 Jim Wigginton
+ * @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
-namespace phpseclib\Math;
+namespace phpseclib3\Math;
-use phpseclib\Crypt\Random;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Math\BigInteger\Engines\Engine;
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
* numbers.
*
- * @package BigInteger
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
-class BigInteger
+class BigInteger implements \JsonSerializable
{
- /**#@+
- * Reduction constants
- *
- * @access private
- * @see BigInteger::_reduce()
- */
- /**
- * @see BigInteger::_montgomery()
- * @see BigInteger::_prepMontgomery()
- */
- const MONTGOMERY = 0;
- /**
- * @see BigInteger::_barrett()
- */
- const BARRETT = 1;
- /**
- * @see BigInteger::_mod2()
- */
- const POWEROF2 = 2;
- /**
- * @see BigInteger::_remainder()
- */
- const CLASSIC = 3;
- /**
- * @see BigInteger::__clone()
- */
- const NONE = 4;
- /**#@-*/
-
- /**#@+
- * Array constants
- *
- * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
- * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
- *
- * @access private
- */
- /**
- * $result[self::VALUE] contains the value.
- */
- const VALUE = 0;
- /**
- * $result[self::SIGN] contains the sign.
- */
- const SIGN = 1;
- /**#@-*/
-
- /**#@+
- * @access private
- * @see BigInteger::_montgomery()
- * @see BigInteger::_barrett()
- */
- /**
- * Cache constants
- *
- * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
- */
- const VARIABLE = 0;
- /**
- * $cache[self::DATA] contains the cached data.
- */
- const DATA = 1;
- /**#@-*/
-
- /**#@+
- * Mode constants.
- *
- * @access private
- * @see BigInteger::__construct()
- */
- /**
- * To use the pure-PHP implementation
- */
- const MODE_INTERNAL = 1;
- /**
- * To use the BCMath library
- *
- * (if enabled; otherwise, the internal implementation will be used)
- */
- const MODE_BCMATH = 2;
- /**
- * To use the GMP library
- *
- * (if present; otherwise, either the BCMath or the internal implementation will be used)
- */
- const MODE_GMP = 3;
- /**#@-*/
-
/**
- * Karatsuba Cutoff
- *
- * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
- *
- * @access private
- */
- const KARATSUBA_CUTOFF = 25;
-
- /**#@+
- * Static properties used by the pure-PHP implementation.
+ * Main Engine
*
- * @see __construct()
+ * @var class-string<Engine>
*/
- static $base;
- static $baseFull;
- static $maxDigit;
- static $msb;
+ private static $mainEngine;
/**
- * $max10 in greatest $max10Len satisfying
- * $max10 = 10**$max10Len <= 2**$base.
- */
- static $max10;
-
- /**
- * $max10Len in greatest $max10Len satisfying
- * $max10 = 10**$max10Len <= 2**$base.
- */
- static $max10Len;
- static $maxDigit2;
- /**#@-*/
-
- /**
- * Holds the BigInteger's value.
+ * Selected Engines
*
- * @var array
- * @access private
+ * @var list<string>
*/
- var $value;
+ private static $engines;
/**
- * Holds the BigInteger's magnitude.
+ * The actual BigInteger object
*
- * @var bool
- * @access private
+ * @var object
*/
- var $is_negative = false;
+ private $value;
/**
- * Precision
- *
- * @see self::setPrecision()
- * @access private
- */
- var $precision = -1;
-
- /**
- * Precision Bitmask
+ * Mode independent value used for serialization.
*
- * @see self::setPrecision()
- * @access private
+ * @see self::__sleep()
+ * @see self::__wakeup()
+ * @var string
*/
- var $bitmask = false;
+ private $hex;
/**
- * Mode independent value used for serialization.
- *
- * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
- * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
- * however, $this->hex is only calculated when $this->__sleep() is called.
+ * Precision (used only for serialization)
*
* @see self::__sleep()
* @see self::__wakeup()
- * @var string
- * @access private
+ * @var int
*/
- var $hex;
+ private $precision;
/**
- * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
+ * Sets engine type.
*
- * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
- * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50 in base-16
- *
- * echo $a->toString(); // outputs 50
- * ?>
- * </code>
+ * Throws an exception if the type is invalid
*
- * @param int|string|resource $x base-10 number or base-$base number if $base set.
- * @param int $base
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @param string $main
+ * @param list<string> $modexps optional
+ * @return void
*/
- function __construct($x = 0, $base = 10)
+ public static function setEngine($main, array $modexps = ['DefaultEngine'])
{
- if (!defined('MATH_BIGINTEGER_MODE')) {
-
- // https://github.com/php/php-src/commit/e0a0e216a909dc4ee4ea7c113a5f41d49525f02e broke GMP
- // https://github.com/php/php-src/commit/424ba0f2ff9677d16b4e339e90885bd4bc49fcf1 fixed it
- // see https://github.com/php/php-src/issues/16870 for more info
- if (version_compare(PHP_VERSION, '8.2.26', '<')) {
- $gmpOK = true;
- } else {
- $gmpOK = !in_array(PHP_VERSION_ID, array(80226, 80314, 80400, 80401));
- }
- switch (true) {
- case extension_loaded('gmp') && $gmpOK:
- define('MATH_BIGINTEGER_MODE', self::MODE_GMP);
- break;
- case extension_loaded('bcmath'):
- define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH);
- break;
- default:
- define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL);
- }
- }
-
- if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
- $versions = array();
-
- // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
- if (function_exists('phpinfo')) {
- ob_start();
- @phpinfo();
- $content = ob_get_contents();
- ob_end_clean();
-
- preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
-
- if (!empty($matches[1])) {
- for ($i = 0; $i < count($matches[1]); $i++) {
- $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
-
- // Remove letter part in OpenSSL version
- if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) {
- $versions[$matches[1][$i]] = $fullVersion;
- } else {
- $versions[$matches[1][$i]] = $m[0];
- }
- }
- }
- }
-
- // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
- switch (true) {
- case !isset($versions['Header']):
- case !isset($versions['Library']):
- case $versions['Header'] == $versions['Library']:
- case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0:
- define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
- break;
- default:
- define('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
- }
- }
+ self::$engines = [];
- if (!defined('PHP_INT_SIZE')) {
- define('PHP_INT_SIZE', 4);
+ $fqmain = 'phpseclib3\\Math\\BigInteger\\Engines\\' . $main;
+ if (!class_exists($fqmain) || !method_exists($fqmain, 'isValidEngine')) {
+ throw new \InvalidArgumentException("$main is not a valid engine");
}
-
- if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) {
- switch (PHP_INT_SIZE) {
- case 8: // use 64-bit integers if int size is 8 bytes
- self::$base = 31;
- self::$baseFull = 0x80000000;
- self::$maxDigit = 0x7FFFFFFF;
- self::$msb = 0x40000000;
- self::$max10 = 1000000000;
- self::$max10Len = 9;
- self::$maxDigit2 = pow(2, 62);
- break;
- //case 4: // use 64-bit floats if int size is 4 bytes
- default:
- self::$base = 26;
- self::$baseFull = 0x4000000;
- self::$maxDigit = 0x3FFFFFF;
- self::$msb = 0x2000000;
- self::$max10 = 10000000;
- self::$max10Len = 7;
- self::$maxDigit2 = pow(2, 52); // pow() prevents truncation
- }
+ if (!$fqmain::isValidEngine()) {
+ throw new BadConfigurationException("$main is not setup correctly on this system");
}
+ /** @var class-string<Engine> $fqmain */
+ self::$mainEngine = $fqmain;
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- switch (true) {
- case is_resource($x) && get_resource_type($x) == 'GMP integer':
- // PHP 5.6 switched GMP from using resources to objects
- case $x instanceof \GMP:
- $this->value = $x;
- return;
- }
- $this->value = gmp_init(0);
+ $found = false;
+ foreach ($modexps as $modexp) {
+ try {
+ $fqmain::setModExpEngine($modexp);
+ $found = true;
break;
- case self::MODE_BCMATH:
- $this->value = '0';
- break;
- default:
- $this->value = array();
+ } catch (\Exception $e) {
+ }
}
- // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
- // '0' is the only value like this per http://php.net/empty
- if (empty($x) && (abs($base) != 256 || $x !== '0')) {
- return;
+ if (!$found) {
+ throw new BadConfigurationException("No valid modular exponentiation engine found for $main");
}
- switch ($base) {
- case -256:
- if (ord($x[0]) & 0x80) {
- $x = ~$x;
- $this->is_negative = true;
- }
- case 256:
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $this->value = function_exists('gmp_import') ?
- gmp_import($x) :
- gmp_init('0x' . bin2hex($x));
- if ($this->is_negative) {
- $this->value = gmp_neg($this->value);
- }
- break;
- case self::MODE_BCMATH:
- // round $len to the nearest 4 (thanks, DavidMJ!)
- $len = (strlen($x) + 3) & ~3;
-
- $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
-
- for ($i = 0; $i < $len; $i+= 4) {
- $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
- $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
- }
-
- if ($this->is_negative) {
- $this->value = '-' . $this->value;
- }
-
- break;
- // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
- default:
- while (strlen($x)) {
- $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base));
- }
- }
-
- if ($this->is_negative) {
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- $this->is_negative = false;
- }
- $temp = $this->add(new static('-1'));
- $this->value = $temp->value;
- }
- break;
- case 16:
- case -16:
- if ($base > 0 && $x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x);
-
- $is_negative = false;
- if ($base < 0 && hexdec($x[0]) >= 8) {
- $this->is_negative = $is_negative = true;
- $x = bin2hex(~pack('H*', $x));
- }
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
- $this->value = gmp_init($temp);
- $this->is_negative = false;
- break;
- case self::MODE_BCMATH:
- $x = (strlen($x) & 1) ? '0' . $x : $x;
- $temp = new static(pack('H*', $x), 256);
- $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
- $this->is_negative = false;
- break;
- default:
- $x = (strlen($x) & 1) ? '0' . $x : $x;
- $temp = new static(pack('H*', $x), 256);
- $this->value = $temp->value;
- }
-
- if ($is_negative) {
- $temp = $this->add(new static('-1'));
- $this->value = $temp->value;
- }
- break;
- case 10:
- case -10:
- // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
- // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
- // [^-0-9].*: find any non-numeric characters and then any characters that follow that
- $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s', '', $x);
- if (!strlen($x) || $x == '-') {
- $x = '0';
- }
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $this->value = gmp_init($x);
- break;
- case self::MODE_BCMATH:
- // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
- // results then doing it on '-1' does (modInverse does $x[0])
- $this->value = $x === '-' ? '0' : (string) $x;
- break;
- default:
- $temp = new static();
-
- $multiplier = new static();
- $multiplier->value = array(self::$max10);
-
- if ($x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
- while (strlen($x)) {
- $temp = $temp->multiply($multiplier);
- $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
- $x = substr($x, self::$max10Len);
- }
-
- $this->value = $temp->value;
- }
- break;
- case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
- case -2:
- if ($base > 0 && $x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = preg_replace('#^([01]*).*#s', '$1', $x);
- $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
-
- $str = '0x';
- while (strlen($x)) {
- $part = substr($x, 0, 4);
- $str.= dechex(bindec($part));
- $x = substr($x, 4);
- }
-
- if ($this->is_negative) {
- $str = '-' . $str;
- }
-
- $temp = new static($str, 8 * $base); // ie. either -16 or +16
- $this->value = $temp->value;
- $this->is_negative = $temp->is_negative;
-
- break;
- default:
- // base not supported, so we'll let $this == 0
- }
+ self::$engines = [$main, $modexp];
}
/**
- * Converts a BigInteger to a byte string (eg. base-256).
+ * Returns the engine type
*
- * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
- * saved as two's compliment.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('65');
- *
- * echo $a->toBytes(); // outputs chr(65)
- * ?>
- * </code>
- *
- * @param bool $twos_compliment
- * @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**8
+ * @return string[]
*/
- function toBytes($twos_compliment = false)
+ public static function getEngine()
{
- if ($twos_compliment) {
- $comparison = $this->compare(new static());
- if ($comparison == 0) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
+ self::initialize_static_variables();
- $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy();
- $bytes = $temp->toBytes();
-
- if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
- $bytes = chr(0);
- }
-
- if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) {
- $bytes = chr(0) . $bytes;
- }
-
- return $comparison < 0 ? ~$bytes : $bytes;
- }
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- if (gmp_cmp($this->value, gmp_init(0)) == 0) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
-
- if (function_exists('gmp_export')) {
- $temp = gmp_export($this->value);
- } else {
- $temp = gmp_strval(gmp_abs($this->value), 16);
- $temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
- $temp = pack('H*', $temp);
- }
-
- return $this->precision > 0 ?
- substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
- ltrim($temp, chr(0));
- case self::MODE_BCMATH:
- if ($this->value === '0') {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
-
- $value = '';
- $current = $this->value;
-
- if ($current[0] == '-') {
- $current = substr($current, 1);
- }
-
- while (bccomp($current, '0', 0) > 0) {
- $temp = bcmod($current, '16777216');
- $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
- $current = bcdiv($current, '16777216', 0);
- }
-
- return $this->precision > 0 ?
- substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
- ltrim($value, chr(0));
- }
-
- if (!count($this->value)) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
- $result = $this->_int2bytes($this->value[count($this->value) - 1]);
-
- $temp = $this->copy();
-
- for ($i = count($temp->value) - 2; $i >= 0; --$i) {
- $temp->_base256_lshift($result, self::$base);
- $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
- }
-
- return $this->precision > 0 ?
- str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
- $result;
+ return self::$engines;
}
/**
- * Converts a BigInteger to a hex string (eg. base-16)).
- *
- * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
- * saved as two's compliment.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('65');
- *
- * echo $a->toHex(); // outputs '41'
- * ?>
- * </code>
- *
- * @param bool $twos_compliment
- * @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**8
+ * Initialize static variables
*/
- function toHex($twos_compliment = false)
+ private static function initialize_static_variables()
{
- return bin2hex($this->toBytes($twos_compliment));
- }
+ if (!isset(self::$mainEngine)) {
+ $engines = [
+ ['GMP', ['DefaultEngine']],
+ ['PHP64', ['OpenSSL']],
+ ['BCMath', ['OpenSSL']],
+ ['PHP32', ['OpenSSL']],
+ ['PHP64', ['DefaultEngine']],
+ ['PHP32', ['DefaultEngine']]
+ ];
+ // per https://phpseclib.com/docs/speed PHP 8.4.0+ _significantly_ sped up BCMath
+ if (version_compare(PHP_VERSION, '8.4.0') >= 0) {
+ $engines[1][0] = 'BCMath';
+ $engines[2][0] = 'PHP64';
+ }
- /**
- * Converts a BigInteger to a bit string (eg. base-2).
- *
- * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
- * saved as two's compliment.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('65');
- *
- * echo $a->toBits(); // outputs '1000001'
- * ?>
- * </code>
- *
- * @param bool $twos_compliment
- * @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**2
- */
- function toBits($twos_compliment = false)
- {
- $hex = $this->toHex($twos_compliment);
- $bits = '';
- for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) {
- $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits;
- }
- if ($start) { // hexdec('') == 0
- $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits;
- }
- $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
+ foreach ($engines as $engine) {
+ try {
+ self::setEngine($engine[0], $engine[1]);
+ return;
+ } catch (\Exception $e) {
+ }
+ }
- if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
- return '0' . $result;
+ throw new \UnexpectedValueException('No valid BigInteger found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available so either disable JIT or install GMP / BCMath');
}
-
- return $result;
}
/**
- * Converts a BigInteger to a base-10 number.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('50');
+ * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
*
- * echo $a->toString(); // outputs 50
- * ?>
- * </code>
+ * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
+ * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
*
- * @return string
- * @access public
- * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
+ * @param string|int|Engine $x Base-10 number or base-$base number if $base set.
+ * @param int $base
*/
- function toString()
+ public function __construct($x = 0, $base = 10)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_strval($this->value);
- case self::MODE_BCMATH:
- if ($this->value === '0') {
- return '0';
- }
-
- return ltrim($this->value, '0');
- }
+ self::initialize_static_variables();
- if (!count($this->value)) {
- return '0';
- }
-
- $temp = $this->copy();
- $temp->bitmask = false;
- $temp->is_negative = false;
-
- $divisor = new static();
- $divisor->value = array(self::$max10);
- $result = '';
- while (count($temp->value)) {
- list($temp, $mod) = $temp->divide($divisor);
- $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
- }
- $result = ltrim($result, '0');
- if (empty($result)) {
- $result = '0';
- }
-
- if ($this->is_negative) {
- $result = '-' . $result;
- }
-
- return $result;
- }
-
- /**
- * Return the size of a BigInteger in bits
- *
- * @return int
- */
- function getLength()
- {
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- return strlen($this->toBits());
+ if ($x instanceof self::$mainEngine) {
+ $this->value = clone $x;
+ } elseif ($x instanceof Engine) {
+ $this->value = new static("$x");
+ $this->value->setPrecision($x->getPrecision());
+ } else {
+ $this->value = new self::$mainEngine($x, $base);
}
-
- $max = count($this->value) - 1;
- return $max != -1 ?
- $max * self::$base + intval(ceil(log($this->value[$max] + 1, 2))) :
- 0;
}
/**
- * Return the size of a BigInteger in bytes
- *
- * @return int
- */
- function getLengthInBytes()
- {
- return (int) ceil($this->getLength() / 8);
- }
-
- /**
- * Copy an object
- *
- * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
- * that all objects are passed by value, when appropriate. More information can be found here:
- *
- * {@link http://php.net/language.oop5.basic#51624}
+ * Converts a BigInteger to a base-10 number.
*
- * @access public
- * @see self::__clone()
- * @return \phpseclib\Math\BigInteger
+ * @return string
*/
- function copy()
+ public function toString()
{
- $temp = new static();
- $temp->value = $this->value;
- $temp->is_negative = $this->is_negative;
- $temp->precision = $this->precision;
- $temp->bitmask = $this->bitmask;
- return $temp;
+ return $this->value->toString();
}
/**
* __toString() magic method
- *
- * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
- * toString().
- *
- * @access public
- * @internal Implemented per a suggestion by Techie-Michael - thanks!
*/
- function __toString()
+ public function __toString()
{
- return $this->toString();
+ return (string)$this->value;
}
/**
- * __clone() magic method
- *
- * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly
- * in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
- * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and
- * PHP5, call BigInteger::copy(), instead.
+ * __debugInfo() magic method
*
- * @access public
- * @see self::copy()
- * @return \phpseclib\Math\BigInteger
+ * Will be called, automatically, when print_r() or var_dump() are called
*/
- function __clone()
+ public function __debugInfo()
{
- return $this->copy();
+ return $this->value->__debugInfo();
}
/**
- * __sleep() magic method
- *
- * Will be called, automatically, when serialize() is called on a BigInteger object.
+ * Converts a BigInteger to a byte string (eg. base-256).
*
- * @see self::__wakeup()
- * @access public
+ * @param bool $twos_compliment
+ * @return string
*/
- function __sleep()
+ public function toBytes($twos_compliment = false)
{
- $this->hex = $this->toHex(true);
- $vars = array('hex');
- if ($this->precision > 0) {
- $vars[] = 'precision';
- }
- return $vars;
+ return $this->value->toBytes($twos_compliment);
}
/**
- * __wakeup() magic method
+ * Converts a BigInteger to a hex string (eg. base-16).
*
- * Will be called, automatically, when unserialize() is called on a BigInteger object.
- *
- * @see self::__sleep()
- * @access public
+ * @param bool $twos_compliment
+ * @return string
*/
- function __wakeup()
+ public function toHex($twos_compliment = false)
{
- $temp = new static($this->hex, -16);
- $this->value = $temp->value;
- $this->is_negative = $temp->is_negative;
- if ($this->precision > 0) {
- // recalculate $this->bitmask
- $this->setPrecision($this->precision);
- }
+ return $this->value->toHex($twos_compliment);
}
/**
- * __debugInfo() magic method
+ * Converts a BigInteger to a bit string (eg. base-2).
*
- * Will be called, automatically, when print_r() or var_dump() are called
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
*
- * @access public
+ * @param bool $twos_compliment
+ * @return string
*/
- function __debugInfo()
+ public function toBits($twos_compliment = false)
{
- $opts = array();
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $engine = 'gmp';
- break;
- case self::MODE_BCMATH:
- $engine = 'bcmath';
- break;
- case self::MODE_INTERNAL:
- $engine = 'internal';
- $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
- }
- if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- $opts[] = 'OpenSSL';
- }
- if (!empty($opts)) {
- $engine.= ' (' . implode('.', $opts) . ')';
- }
- return array(
- 'value' => '0x' . $this->toHex(true),
- 'engine' => $engine
- );
+ return $this->value->toBits($twos_compliment);
}
/**
* Adds two BigIntegers.
*
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('10');
- * $b = new \phpseclib\Math\BigInteger('20');
- *
- * $c = $a->add($b);
- *
- * echo $c->toString(); // outputs 30
- * ?>
- * </code>
- *
- * @param \phpseclib\Math\BigInteger $y
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Performs base-2**52 addition
- */
- function add($y)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_add($this->value, $y->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcadd($this->value, $y->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
-
- $result = new static();
- $result->value = $temp[self::VALUE];
- $result->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($result);
- }
-
- /**
- * Performs addition.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
+ * @param BigInteger $y
+ * @return BigInteger
*/
- function _add($x_value, $x_negative, $y_value, $y_negative)
+ public function add(BigInteger $y)
{
- $x_size = count($x_value);
- $y_size = count($y_value);
-
- if ($x_size == 0) {
- return array(
- self::VALUE => $y_value,
- self::SIGN => $y_negative
- );
- } elseif ($y_size == 0) {
- return array(
- self::VALUE => $x_value,
- self::SIGN => $x_negative
- );
- }
-
- // subtract, if appropriate
- if ($x_negative != $y_negative) {
- if ($x_value == $y_value) {
- return array(
- self::VALUE => array(),
- self::SIGN => false
- );
- }
-
- $temp = $this->_subtract($x_value, false, $y_value, false);
- $temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
- $x_negative : $y_negative;
-
- return $temp;
- }
-
- if ($x_size < $y_size) {
- $size = $x_size;
- $value = $y_value;
- } else {
- $size = $y_size;
- $value = $x_value;
- }
-
- $value[count($value)] = 0; // just in case the carry adds an extra digit
-
- $carry = 0;
- for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
- $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
- $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
- $sum = $carry ? $sum - self::$maxDigit2 : $sum;
-
- $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
-
- $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
- $value[$j] = $temp;
- }
-
- if ($j == $size) { // ie. if $y_size is odd
- $sum = $x_value[$i] + $y_value[$i] + $carry;
- $carry = $sum >= self::$baseFull;
- $value[$i] = $carry ? $sum - self::$baseFull : $sum;
- ++$i; // ie. let $i = $j since we've just done $value[$i]
- }
-
- if ($carry) {
- for (; $value[$i] == self::$maxDigit; ++$i) {
- $value[$i] = 0;
- }
- ++$value[$i];
- }
-
- return array(
- self::VALUE => $this->_trim($value),
- self::SIGN => $x_negative
- );
+ return new static($this->value->add($y->value));
}
/**
* Subtracts two BigIntegers.
*
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('10');
- * $b = new \phpseclib\Math\BigInteger('20');
- *
- * $c = $a->subtract($b);
- *
- * echo $c->toString(); // outputs -10
- * ?>
- * </code>
- *
- * @param \phpseclib\Math\BigInteger $y
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Performs base-2**52 subtraction
+ * @param BigInteger $y
+ * @return BigInteger
*/
- function subtract($y)
+ public function subtract(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_sub($this->value, $y->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcsub($this->value, $y->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
-
- $result = new static();
- $result->value = $temp[self::VALUE];
- $result->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($result);
- }
-
- /**
- * Performs subtraction.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
- */
- function _subtract($x_value, $x_negative, $y_value, $y_negative)
- {
- $x_size = count($x_value);
- $y_size = count($y_value);
-
- if ($x_size == 0) {
- return array(
- self::VALUE => $y_value,
- self::SIGN => !$y_negative
- );
- } elseif ($y_size == 0) {
- return array(
- self::VALUE => $x_value,
- self::SIGN => $x_negative
- );
- }
-
- // add, if appropriate (ie. -$x - +$y or +$x - -$y)
- if ($x_negative != $y_negative) {
- $temp = $this->_add($x_value, false, $y_value, false);
- $temp[self::SIGN] = $x_negative;
-
- return $temp;
- }
-
- $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
-
- if (!$diff) {
- return array(
- self::VALUE => array(),
- self::SIGN => false
- );
- }
-
- // switch $x and $y around, if appropriate.
- if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
- $temp = $x_value;
- $x_value = $y_value;
- $y_value = $temp;
-
- $x_negative = !$x_negative;
-
- $x_size = count($x_value);
- $y_size = count($y_value);
- }
-
- // at this point, $x_value should be at least as big as - if not bigger than - $y_value
-
- $carry = 0;
- for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
- $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
- $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
- $sum = $carry ? $sum + self::$maxDigit2 : $sum;
-
- $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
-
- $x_value[$i] = (int) ($sum - self::$baseFull * $temp);
- $x_value[$j] = $temp;
- }
-
- if ($j == $y_size) { // ie. if $y_size is odd
- $sum = $x_value[$i] - $y_value[$i] - $carry;
- $carry = $sum < 0;
- $x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
- ++$i;
- }
-
- if ($carry) {
- for (; !$x_value[$i]; ++$i) {
- $x_value[$i] = self::$maxDigit;
- }
- --$x_value[$i];
- }
-
- return array(
- self::VALUE => $this->_trim($x_value),
- self::SIGN => $x_negative
- );
+ return new static($this->value->subtract($y->value));
}
/**
* Multiplies two BigIntegers
*
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('10');
- * $b = new \phpseclib\Math\BigInteger('20');
- *
- * $c = $a->multiply($b);
- *
- * echo $c->toString(); // outputs 200
- * ?>
- * </code>
- *
- * @param \phpseclib\Math\BigInteger $x
- * @return \phpseclib\Math\BigInteger
- * @access public
- */
- function multiply($x)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_mul($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcmul($this->value, $x->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
-
- $product = new static();
- $product->value = $temp[self::VALUE];
- $product->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($product);
- }
-
- /**
- * Performs multiplication.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
- */
- function _multiply($x_value, $x_negative, $y_value, $y_negative)
- {
- //if ( $x_value == $y_value ) {
- // return array(
- // self::VALUE => $this->_square($x_value),
- // self::SIGN => $x_sign != $y_value
- // );
- //}
-
- $x_length = count($x_value);
- $y_length = count($y_value);
-
- if (!$x_length || !$y_length) { // a 0 is being multiplied
- return array(
- self::VALUE => array(),
- self::SIGN => false
- );
- }
-
- return array(
- self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
- $this->_trim($this->_regularMultiply($x_value, $y_value)) :
- $this->_trim($this->_karatsuba($x_value, $y_value)),
- self::SIGN => $x_negative != $y_negative
- );
- }
-
- /**
- * Performs long multiplication on two BigIntegers
- *
- * Modeled after 'multiply' in MutableBigInteger.java.
- *
- * @param array $x_value
- * @param array $y_value
- * @return array
- * @access private
+ * @param BigInteger $x
+ * @return BigInteger
*/
- function _regularMultiply($x_value, $y_value)
+ public function multiply(BigInteger $x)
{
- $x_length = count($x_value);
- $y_length = count($y_value);
-
- if (!$x_length || !$y_length) { // a 0 is being multiplied
- return array();
- }
-
- if ($x_length < $y_length) {
- $temp = $x_value;
- $x_value = $y_value;
- $y_value = $temp;
-
- $x_length = count($x_value);
- $y_length = count($y_value);
- }
-
- $product_value = $this->_array_repeat(0, $x_length + $y_length);
-
- // the following for loop could be removed if the for loop following it
- // (the one with nested for loops) initially set $i to 0, but
- // doing so would also make the result in one set of unnecessary adds,
- // since on the outermost loops first pass, $product->value[$k] is going
- // to always be 0
-
- $carry = 0;
-
- for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
- $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$j] = (int) ($temp - self::$baseFull * $carry);
- }
-
- $product_value[$j] = $carry;
-
- // the above for loop is what the previous comment was talking about. the
- // following for loop is the "one with nested for loops"
- for ($i = 1; $i < $y_length; ++$i) {
- $carry = 0;
-
- for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
- $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- $product_value[$k] = $carry;
- }
-
- return $product_value;
- }
-
- /**
- * Performs Karatsuba multiplication on two BigIntegers
- *
- * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
- *
- * @param array $x_value
- * @param array $y_value
- * @return array
- * @access private
- */
- function _karatsuba($x_value, $y_value)
- {
- $m = min(count($x_value) >> 1, count($y_value) >> 1);
-
- if ($m < self::KARATSUBA_CUTOFF) {
- return $this->_regularMultiply($x_value, $y_value);
- }
-
- $x1 = array_slice($x_value, $m);
- $x0 = array_slice($x_value, 0, $m);
- $y1 = array_slice($y_value, $m);
- $y0 = array_slice($y_value, 0, $m);
-
- $z2 = $this->_karatsuba($x1, $y1);
- $z0 = $this->_karatsuba($x0, $y0);
-
- $z1 = $this->_add($x1, false, $x0, false);
- $temp = $this->_add($y1, false, $y0, false);
- $z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
- $temp = $this->_add($z2, false, $z0, false);
- $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
-
- $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
- $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
-
- $xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
- $xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false);
-
- return $xy[self::VALUE];
- }
-
- /**
- * Performs squaring
- *
- * @param array $x
- * @return array
- * @access private
- */
- function _square($x = false)
- {
- return count($x) < 2 * self::KARATSUBA_CUTOFF ?
- $this->_trim($this->_baseSquare($x)) :
- $this->_trim($this->_karatsubaSquare($x));
- }
-
- /**
- * Performs traditional squaring on two BigIntegers
- *
- * Squaring can be done faster than multiplying a number by itself can be. See
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
- *
- * @param array $value
- * @return array
- * @access private
- */
- function _baseSquare($value)
- {
- if (empty($value)) {
- return array();
- }
- $square_value = $this->_array_repeat(0, 2 * count($value));
-
- for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
- $i2 = $i << 1;
-
- $temp = $square_value[$i2] + $value[$i] * $value[$i];
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
-
- // note how we start from $i+1 instead of 0 as we do in multiplication.
- for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
- $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $square_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- // the following line can yield values larger 2**15. at this point, PHP should switch
- // over to floats.
- $square_value[$i + $max_index + 1] = $carry;
- }
-
- return $square_value;
- }
-
- /**
- * Performs Karatsuba "squaring" on two BigIntegers
- *
- * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
- *
- * @param array $value
- * @return array
- * @access private
- */
- function _karatsubaSquare($value)
- {
- $m = count($value) >> 1;
-
- if ($m < self::KARATSUBA_CUTOFF) {
- return $this->_baseSquare($value);
- }
-
- $x1 = array_slice($value, $m);
- $x0 = array_slice($value, 0, $m);
-
- $z2 = $this->_karatsubaSquare($x1);
- $z0 = $this->_karatsubaSquare($x0);
-
- $z1 = $this->_add($x1, false, $x0, false);
- $z1 = $this->_karatsubaSquare($z1[self::VALUE]);
- $temp = $this->_add($z2, false, $z0, false);
- $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false);
-
- $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
- $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
-
- $xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
- $xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false);
-
- return $xx[self::VALUE];
+ return new static($this->value->multiply($x->value));
}
/**
@@ -1439,8 +292,8 @@ class BigInteger
* Here's an example:
* <code>
* <?php
- * $a = new \phpseclib\Math\BigInteger('10');
- * $b = new \phpseclib\Math\BigInteger('20');
+ * $a = new \phpseclib3\Math\BigInteger('10');
+ * $b = new \phpseclib3\Math\BigInteger('20');
*
* list($quotient, $remainder) = $a->divide($b);
*
@@ -1450,1272 +303,186 @@ class BigInteger
* ?>
* </code>
*
- * @param \phpseclib\Math\BigInteger $y
- * @return array
- * @access public
- * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
+ * @param BigInteger $y
+ * @return BigInteger[]
*/
- function divide($y)
+ public function divide(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $quotient = new static();
- $remainder = new static();
-
- list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
-
- if (gmp_sign($remainder->value) < 0) {
- $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
- }
-
- return array($this->_normalize($quotient), $this->_normalize($remainder));
- case self::MODE_BCMATH:
- $quotient = new static();
- $remainder = new static();
-
- $quotient->value = bcdiv($this->value, $y->value, 0);
- $remainder->value = bcmod($this->value, $y->value);
-
- if ($remainder->value[0] == '-') {
- $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
- }
-
- return array($this->_normalize($quotient), $this->_normalize($remainder));
- }
-
- if (count($y->value) == 1) {
- list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
- $quotient = new static();
- $remainder = new static();
- $quotient->value = $q;
- $remainder->value = array($r);
- $quotient->is_negative = $this->is_negative != $y->is_negative;
- return array($this->_normalize($quotient), $this->_normalize($remainder));
- }
-
- static $zero;
- if (!isset($zero)) {
- $zero = new static();
- }
-
- $x = $this->copy();
- $y = $y->copy();
-
- $x_sign = $x->is_negative;
- $y_sign = $y->is_negative;
-
- $x->is_negative = $y->is_negative = false;
-
- $diff = $x->compare($y);
-
- if (!$diff) {
- $temp = new static();
- $temp->value = array(1);
- $temp->is_negative = $x_sign != $y_sign;
- return array($this->_normalize($temp), $this->_normalize(new static()));
- }
-
- if ($diff < 0) {
- // if $x is negative, "add" $y.
- if ($x_sign) {
- $x = $y->subtract($x);
- }
- return array($this->_normalize(new static()), $this->_normalize($x));
- }
-
- // normalize $x and $y as described in HAC 14.23 / 14.24
- $msb = $y->value[count($y->value) - 1];
- for ($shift = 0; !($msb & self::$msb); ++$shift) {
- $msb <<= 1;
- }
- $x->_lshift($shift);
- $y->_lshift($shift);
- $y_value = &$y->value;
-
- $x_max = count($x->value) - 1;
- $y_max = count($y->value) - 1;
-
- $quotient = new static();
- $quotient_value = &$quotient->value;
- $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
-
- static $temp, $lhs, $rhs;
- if (!isset($temp)) {
- $temp = new static();
- $lhs = new static();
- $rhs = new static();
- }
- $temp_value = &$temp->value;
- $rhs_value = &$rhs->value;
-
- // $temp = $y << ($x_max - $y_max-1) in base 2**26
- $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
-
- while ($x->compare($temp) >= 0) {
- // calculate the "common residue"
- ++$quotient_value[$x_max - $y_max];
- $x = $x->subtract($temp);
- $x_max = count($x->value) - 1;
- }
-
- for ($i = $x_max; $i >= $y_max + 1; --$i) {
- $x_value = &$x->value;
- $x_window = array(
- isset($x_value[$i]) ? $x_value[$i] : 0,
- isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
- isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
- );
- $y_window = array(
- $y_value[$y_max],
- ($y_max > 0) ? $y_value[$y_max - 1] : 0
- );
-
- $q_index = $i - $y_max - 1;
- if ($x_window[0] == $y_window[0]) {
- $quotient_value[$q_index] = self::$maxDigit;
- } else {
- $quotient_value[$q_index] = $this->_safe_divide(
- $x_window[0] * self::$baseFull + $x_window[1],
- $y_window[0]
- );
- }
-
- $temp_value = array($y_window[1], $y_window[0]);
-
- $lhs->value = array($quotient_value[$q_index]);
- $lhs = $lhs->multiply($temp);
-
- $rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
-
- while ($lhs->compare($rhs) > 0) {
- --$quotient_value[$q_index];
-
- $lhs->value = array($quotient_value[$q_index]);
- $lhs = $lhs->multiply($temp);
- }
-
- $adjust = $this->_array_repeat(0, $q_index);
- $temp_value = array($quotient_value[$q_index]);
- $temp = $temp->multiply($y);
- $temp_value = &$temp->value;
- if (count($temp_value)) {
- $temp_value = array_merge($adjust, $temp_value);
- }
-
- $x = $x->subtract($temp);
-
- if ($x->compare($zero) < 0) {
- $temp_value = array_merge($adjust, $y_value);
- $x = $x->add($temp);
-
- --$quotient_value[$q_index];
- }
-
- $x_max = count($x_value) - 1;
- }
-
- // unnormalize the remainder
- $x->_rshift($shift);
-
- $quotient->is_negative = $x_sign != $y_sign;
-
- // calculate the "common residue", if appropriate
- if ($x_sign) {
- $y->_rshift($shift);
- $x = $y->subtract($x);
- }
-
- return array($this->_normalize($quotient), $this->_normalize($x));
+ list($q, $r) = $this->value->divide($y->value);
+ return [
+ new static($q),
+ new static($r)
+ ];
}
/**
- * Divides a BigInteger by a regular integer
+ * Calculates modular inverses.
*
- * abc / x = a00 / x + b0 / x + c / x
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
*
- * @param array $dividend
- * @param array $divisor
- * @return array
- * @access private
+ * @param BigInteger $n
+ * @return BigInteger
*/
- function _divide_digit($dividend, $divisor)
+ public function modInverse(BigInteger $n)
{
- $carry = 0;
- $result = array();
-
- for ($i = count($dividend) - 1; $i >= 0; --$i) {
- $temp = self::$baseFull * $carry + $dividend[$i];
- $result[$i] = $this->_safe_divide($temp, $divisor);
- $carry = (int) ($temp - $divisor * $result[$i]);
- }
-
- return array($result, $carry);
+ return new static($this->value->modInverse($n->value));
}
/**
- * Performs modular exponentiation.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger('10');
- * $b = new \phpseclib\Math\BigInteger('20');
- * $c = new \phpseclib\Math\BigInteger('30');
- *
- * $c = $a->modPow($b, $c);
+ * Calculates modular inverses.
*
- * echo $c->toString(); // outputs 10
- * ?>
- * </code>
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
*
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
- * and although the approach involving repeated squaring does vastly better, it, too, is impractical
- * for our purposes. The reason being that division - by far the most complicated and time-consuming
- * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
- *
- * Modular reductions resolve this issue. Although an individual modular reduction takes more time
- * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
- *
- * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
- * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
- * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
- * the product of two odd numbers is odd), but what about when RSA isn't used?
- *
- * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
- * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
- * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
- * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
- * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
- * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
+ * @param BigInteger $n
+ * @return BigInteger[]
*/
- function modPow($e, $n)
+ public function extendedGCD(BigInteger $n)
{
- $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
-
- if ($e->compare(new static()) < 0) {
- $e = $e->abs();
-
- $temp = $this->modInverse($n);
- if ($temp === false) {
- return false;
- }
-
- return $this->_normalize($temp->modPow($e, $n));
- }
-
- if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
- $temp = new static();
- $temp->value = gmp_powm($this->value, $e->value, $n->value);
-
- return $this->_normalize($temp);
- }
-
- if ($this->compare(new static()) < 0 || $this->compare($n) > 0) {
- list(, $temp) = $this->divide($n);
- return $temp->modPow($e, $n);
- }
-
- if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- $components = array(
- 'modulus' => $n->toBytes(true),
- 'publicExponent' => $e->toBytes(true)
- );
-
- $components = array(
- 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']),
- 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent'])
- );
-
- $RSAPublicKey = pack(
- 'Ca*a*a*',
- 48,
- $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])),
- $components['modulus'],
- $components['publicExponent']
- );
-
- $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
- $RSAPublicKey = chr(0) . $RSAPublicKey;
- $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey;
-
- $encapsulated = pack(
- 'Ca*a*',
- 48,
- $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)),
- $rsaOID . $RSAPublicKey
- );
-
- $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
- chunk_split(base64_encode($encapsulated)) .
- '-----END PUBLIC KEY-----';
-
- $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
-
- if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
- return new static($result, 256);
- }
- }
-
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $temp = new static();
- $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
-
- return $this->_normalize($temp);
- }
-
- if (empty($e->value)) {
- $temp = new static();
- $temp->value = array(1);
- return $this->_normalize($temp);
- }
-
- if ($e->value == array(1)) {
- list(, $temp) = $this->divide($n);
- return $this->_normalize($temp);
- }
-
- if ($e->value == array(2)) {
- $temp = new static();
- $temp->value = $this->_square($this->value);
- list(, $temp) = $temp->divide($n);
- return $this->_normalize($temp);
- }
-
- return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT));
-
- // the following code, although not callable, can be run independently of the above code
- // although the above code performed better in my benchmarks the following could might
- // perform better under different circumstances. in lieu of deleting it it's just been
- // made uncallable
-
- // is the modulo odd?
- if ($n->value[0] & 1) {
- return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY));
- }
- // if it's not, it's even
-
- // find the lowest set bit (eg. the max pow of 2 that divides $n)
- for ($i = 0; $i < count($n->value); ++$i) {
- if ($n->value[$i]) {
- $temp = decbin($n->value[$i]);
- $j = strlen($temp) - strrpos($temp, '1') - 1;
- $j+= 26 * $i;
- break;
- }
- }
- // at this point, 2^$j * $n/(2^$j) == $n
-
- $mod1 = $n->copy();
- $mod1->_rshift($j);
- $mod2 = new static();
- $mod2->value = array(1);
- $mod2->_lshift($j);
-
- $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
- $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
-
- $y1 = $mod2->modInverse($mod1);
- $y2 = $mod1->modInverse($mod2);
-
- $result = $part1->multiply($mod2);
- $result = $result->multiply($y1);
-
- $temp = $part2->multiply($mod1);
- $temp = $temp->multiply($y2);
-
- $result = $result->add($temp);
- list(, $result) = $result->divide($n);
-
- return $this->_normalize($result);
+ $extended = $this->value->extendedGCD($n->value);
+ $gcd = $extended['gcd'];
+ $x = $extended['x'];
+ $y = $extended['y'];
+ return [
+ 'gcd' => new static($gcd),
+ 'x' => new static($x),
+ 'y' => new static($y)
+ ];
}
/**
- * Performs modular exponentiation.
+ * Calculates the greatest common divisor
*
- * Alias for modPow().
+ * Say you have 693 and 609. The GCD is 21.
*
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @param BigInteger $n
+ * @return BigInteger
*/
- function powMod($e, $n)
+ public function gcd(BigInteger $n)
{
- return $this->modPow($e, $n);
+ return new static($this->value->gcd($n->value));
}
/**
- * Sliding Window k-ary Modular Exponentiation
- *
- * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
- * however, this function performs a modular reduction after every multiplication and squaring operation.
- * As such, this function has the same preconditions that the reductions being used do.
- *
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @param int $mode
- * @return \phpseclib\Math\BigInteger
- * @access private
- */
- function _slidingWindow($e, $n, $mode)
- {
- static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
- //static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
-
- $e_value = $e->value;
- $e_length = count($e_value) - 1;
- $e_bits = decbin($e_value[$e_length]);
- for ($i = $e_length - 1; $i >= 0; --$i) {
- $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT);
- }
-
- $e_length = strlen($e_bits);
-
- // calculate the appropriate window size.
- // $window_size == 3 if $window_ranges is between 25 and 81, for example.
- for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
- }
-
- $n_value = $n->value;
-
- // precompute $this^0 through $this^$window_size
- $powers = array();
- $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
- $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
-
- // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
- // in a 1. ie. it's supposed to be odd.
- $temp = 1 << ($window_size - 1);
- for ($i = 1; $i < $temp; ++$i) {
- $i2 = $i << 1;
- $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
- }
-
- $result = array(1);
- $result = $this->_prepareReduce($result, $n_value, $mode);
-
- for ($i = 0; $i < $e_length;) {
- if (!$e_bits[$i]) {
- $result = $this->_squareReduce($result, $n_value, $mode);
- ++$i;
- } else {
- for ($j = $window_size - 1; $j > 0; --$j) {
- if (!empty($e_bits[$i + $j])) {
- break;
- }
- }
-
- // eg. the length of substr($e_bits, $i, $j + 1)
- for ($k = 0; $k <= $j; ++$k) {
- $result = $this->_squareReduce($result, $n_value, $mode);
- }
-
- $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
-
- $i += $j + 1;
- }
- }
-
- $temp = new static();
- $temp->value = $this->_reduce($result, $n_value, $mode);
-
- return $temp;
- }
-
- /**
- * Modular reduction
- *
- * For most $modes this will return the remainder.
+ * Absolute value.
*
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
+ * @return BigInteger
*/
- function _reduce($x, $n, $mode)
+ public function abs()
{
- switch ($mode) {
- case self::MONTGOMERY:
- return $this->_montgomery($x, $n);
- case self::BARRETT:
- return $this->_barrett($x, $n);
- case self::POWEROF2:
- $lhs = new static();
- $lhs->value = $x;
- $rhs = new static();
- $rhs->value = $n;
- return $x->_mod2($n);
- case self::CLASSIC:
- $lhs = new static();
- $lhs->value = $x;
- $rhs = new static();
- $rhs->value = $n;
- list(, $temp) = $lhs->divide($rhs);
- return $temp->value;
- case self::NONE:
- return $x;
- default:
- // an invalid $mode was provided
- }
+ return new static($this->value->abs());
}
/**
- * Modular reduction preperation
+ * Set Precision
*
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
- */
- function _prepareReduce($x, $n, $mode)
- {
- if ($mode == self::MONTGOMERY) {
- return $this->_prepMontgomery($x, $n);
- }
- return $this->_reduce($x, $n, $mode);
- }
-
- /**
- * Modular multiply
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $y
- * @param array $n
- * @param int $mode
- * @return array
- */
- function _multiplyReduce($x, $y, $n, $mode)
- {
- if ($mode == self::MONTGOMERY) {
- return $this->_montgomeryMultiply($x, $y, $n);
- }
- $temp = $this->_multiply($x, false, $y, false);
- return $this->_reduce($temp[self::VALUE], $n, $mode);
- }
-
- /**
- * Modular square
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
+ * shift, not, and rotates.
*
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
+ * @param int $bits
*/
- function _squareReduce($x, $n, $mode)
+ public function setPrecision($bits)
{
- if ($mode == self::MONTGOMERY) {
- return $this->_montgomeryMultiply($x, $x, $n);
- }
- return $this->_reduce($this->_square($x), $n, $mode);
+ $this->value->setPrecision($bits);
}
/**
- * Modulos for Powers of Two
+ * Get Precision
*
- * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
- * we'll just use this function as a wrapper for doing that.
+ * Returns the precision if it exists, false if it doesn't
*
- * @see self::_slidingWindow()
- * @access private
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
+ * @return int|bool
*/
- function _mod2($n)
+ public function getPrecision()
{
- $temp = new static();
- $temp->value = array(1);
- return $this->bitwise_and($n->subtract($temp));
- }
-
- /**
- * Barrett Modular Reduction
- *
- * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
- * so as not to require negative numbers (initially, this script didn't support negative numbers).
- *
- * Employs "folding", as described at
- * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
- * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
- *
- * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
- * usable on account of (1) its not using reasonable radix points as discussed in
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
- * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
- * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
- * comments for details.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $n
- * @param array $m
- * @return array
- */
- function _barrett($n, $m)
- {
- static $cache = array(
- self::VARIABLE => array(),
- self::DATA => array()
- );
-
- $m_length = count($m);
-
- // if ($this->_compare($n, $this->_square($m)) >= 0) {
- if (count($n) > 2 * $m_length) {
- $lhs = new static();
- $rhs = new static();
- $lhs->value = $n;
- $rhs->value = $m;
- list(, $temp) = $lhs->divide($rhs);
- return $temp->value;
- }
-
- // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
- if ($m_length < 5) {
- return $this->_regularBarrett($n, $m);
- }
-
- // n = 2 * m.length
-
- if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
- $key = count($cache[self::VARIABLE]);
- $cache[self::VARIABLE][] = $m;
-
- $lhs = new static();
- $lhs_value = &$lhs->value;
- $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
- $lhs_value[] = 1;
- $rhs = new static();
- $rhs->value = $m;
-
- list($u, $m1) = $lhs->divide($rhs);
- $u = $u->value;
- $m1 = $m1->value;
-
- $cache[self::DATA][] = array(
- 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
- 'm1'=> $m1 // m.length
- );
- } else {
- extract($cache[self::DATA][$key]);
- }
-
- $cutoff = $m_length + ($m_length >> 1);
- $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
- $msd = array_slice($n, $cutoff); // m.length >> 1
- $lsd = $this->_trim($lsd);
- $temp = $this->_multiply($msd, false, $m1, false);
- $n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1
-
- if ($m_length & 1) {
- return $this->_regularBarrett($n[self::VALUE], $m);
- }
-
- // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
- $temp = array_slice($n[self::VALUE], $m_length - 1);
- // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
- // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
- $temp = $this->_multiply($temp, false, $u, false);
- // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
- // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
- $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
- // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
- // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
- $temp = $this->_multiply($temp, false, $m, false);
-
- // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
- // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
- // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
-
- $result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false);
-
- while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
- $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false);
- }
-
- return $result[self::VALUE];
+ return $this->value->getPrecision();
}
/**
- * (Regular) Barrett Modular Reduction
+ * Serialize
*
- * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
- * is that this function does not fold the denominator into a smaller form.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- function _regularBarrett($x, $n)
- {
- static $cache = array(
- self::VARIABLE => array(),
- self::DATA => array()
- );
-
- $n_length = count($n);
-
- if (count($x) > 2 * $n_length) {
- $lhs = new static();
- $rhs = new static();
- $lhs->value = $x;
- $rhs->value = $n;
- list(, $temp) = $lhs->divide($rhs);
- return $temp->value;
- }
-
- if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
- $key = count($cache[self::VARIABLE]);
- $cache[self::VARIABLE][] = $n;
- $lhs = new static();
- $lhs_value = &$lhs->value;
- $lhs_value = $this->_array_repeat(0, 2 * $n_length);
- $lhs_value[] = 1;
- $rhs = new static();
- $rhs->value = $n;
- list($temp, ) = $lhs->divide($rhs); // m.length
- $cache[self::DATA][] = $temp->value;
- }
-
- // 2 * m.length - (m.length - 1) = m.length + 1
- $temp = array_slice($x, $n_length - 1);
- // (m.length + 1) + m.length = 2 * m.length + 1
- $temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false);
- // (2 * m.length + 1) - (m.length - 1) = m.length + 2
- $temp = array_slice($temp[self::VALUE], $n_length + 1);
-
- // m.length + 1
- $result = array_slice($x, 0, $n_length + 1);
- // m.length + 1
- $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
- // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
-
- if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
- $corrector_value = $this->_array_repeat(0, $n_length + 1);
- $corrector_value[count($corrector_value)] = 1;
- $result = $this->_add($result, false, $corrector_value, false);
- $result = $result[self::VALUE];
- }
-
- // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
- $result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]);
- while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
- $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false);
- }
-
- return $result[self::VALUE];
- }
-
- /**
- * Performs long multiplication up to $stop digits
+ * Will be called, automatically, when serialize() is called on a BigInteger object.
*
- * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
+ * __sleep() / __wakeup() have been around since PHP 4.0
*
- * @see self::_regularBarrett()
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @param int $stop
- * @return array
- * @access private
- */
- function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
- {
- $x_length = count($x_value);
- $y_length = count($y_value);
-
- if (!$x_length || !$y_length) { // a 0 is being multiplied
- return array(
- self::VALUE => array(),
- self::SIGN => false
- );
- }
-
- if ($x_length < $y_length) {
- $temp = $x_value;
- $x_value = $y_value;
- $y_value = $temp;
-
- $x_length = count($x_value);
- $y_length = count($y_value);
- }
-
- $product_value = $this->_array_repeat(0, $x_length + $y_length);
-
- // the following for loop could be removed if the for loop following it
- // (the one with nested for loops) initially set $i to 0, but
- // doing so would also make the result in one set of unnecessary adds,
- // since on the outermost loops first pass, $product->value[$k] is going
- // to always be 0
-
- $carry = 0;
-
- for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
- $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$j] = (int) ($temp - self::$baseFull * $carry);
- }
-
- if ($j < $stop) {
- $product_value[$j] = $carry;
- }
-
- // the above for loop is what the previous comment was talking about. the
- // following for loop is the "one with nested for loops"
-
- for ($i = 1; $i < $y_length; ++$i) {
- $carry = 0;
-
- for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
- $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- if ($k < $stop) {
- $product_value[$k] = $carry;
- }
- }
-
- return array(
- self::VALUE => $this->_trim($product_value),
- self::SIGN => $x_negative != $y_negative
- );
- }
-
- /**
- * Montgomery Modular Reduction
- *
- * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
- * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
- * to work correctly.
- *
- * @see self::_prepMontgomery()
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- function _montgomery($x, $n)
- {
- static $cache = array(
- self::VARIABLE => array(),
- self::DATA => array()
- );
-
- if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
- $key = count($cache[self::VARIABLE]);
- $cache[self::VARIABLE][] = $x;
- $cache[self::DATA][] = $this->_modInverse67108864($n);
- }
-
- $k = count($n);
-
- $result = array(self::VALUE => $x);
-
- for ($i = 0; $i < $k; ++$i) {
- $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
- $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = $this->_regularMultiply(array($temp), $n);
- $temp = array_merge($this->_array_repeat(0, $i), $temp);
- $result = $this->_add($result[self::VALUE], false, $temp, false);
- }
-
- $result[self::VALUE] = array_slice($result[self::VALUE], $k);
-
- if ($this->_compare($result, false, $n, false) >= 0) {
- $result = $this->_subtract($result[self::VALUE], false, $n, false);
- }
-
- return $result[self::VALUE];
- }
-
- /**
- * Montgomery Multiply
+ * \Serializable was introduced in PHP 5.1 and deprecated in PHP 8.1:
+ * https://wiki.php.net/rfc/phase_out_serializable
*
- * Interleaves the montgomery reduction and long multiplication algorithms together as described in
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
+ * __serialize() / __unserialize() were introduced in PHP 7.4:
+ * https://wiki.php.net/rfc/custom_object_serialization
*
- * @see self::_prepMontgomery()
- * @see self::_montgomery()
- * @access private
- * @param array $x
- * @param array $y
- * @param array $m
* @return array
*/
- function _montgomeryMultiply($x, $y, $m)
+ public function __sleep()
{
- $temp = $this->_multiply($x, false, $y, false);
- return $this->_montgomery($temp[self::VALUE], $m);
-
- // the following code, although not callable, can be run independently of the above code
- // although the above code performed better in my benchmarks the following could might
- // perform better under different circumstances. in lieu of deleting it it's just been
- // made uncallable
-
- static $cache = array(
- self::VARIABLE => array(),
- self::DATA => array()
- );
-
- if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
- $key = count($cache[self::VARIABLE]);
- $cache[self::VARIABLE][] = $m;
- $cache[self::DATA][] = $this->_modInverse67108864($m);
- }
-
- $n = max(count($x), count($y), count($m));
- $x = array_pad($x, $n, 0);
- $y = array_pad($y, $n, 0);
- $m = array_pad($m, $n, 0);
- $a = array(self::VALUE => $this->_array_repeat(0, $n + 1));
- for ($i = 0; $i < $n; ++$i) {
- $temp = $a[self::VALUE][0] + $x[$i] * $y[0];
- $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = $temp * $cache[self::DATA][$key];
- $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
- $a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false);
- $a[self::VALUE] = array_slice($a[self::VALUE], 1);
- }
- if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) {
- $a = $this->_subtract($a[self::VALUE], false, $m, false);
+ $this->hex = $this->toHex(true);
+ $vars = ['hex'];
+ if ($this->getPrecision() > 0) {
+ $vars[] = 'precision';
}
- return $a[self::VALUE];
- }
-
- /**
- * Prepare a number for use in Montgomery Modular Reductions
- *
- * @see self::_montgomery()
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- function _prepMontgomery($x, $n)
- {
- $lhs = new static();
- $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
- $rhs = new static();
- $rhs->value = $n;
-
- list(, $temp) = $lhs->divide($rhs);
- return $temp->value;
- }
-
- /**
- * Modular Inverse of a number mod 2**26 (eg. 67108864)
- *
- * Based off of the bnpInvDigit function implemented and justified in the following URL:
- *
- * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
- *
- * The following URL provides more info:
- *
- * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
- *
- * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
- * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
- * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
- * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
- * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
- * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
- * 40 bits, which only 64-bit floating points will support.
- *
- * Thanks to Pedro Gimeno Fortea for input!
- *
- * @see self::_montgomery()
- * @access private
- * @param array $x
- * @return int
- */
- function _modInverse67108864($x) // 2**26 == 67,108,864
- {
- $x = -$x[0];
- $result = $x & 0x3; // x**-1 mod 2**2
- $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
- $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
- $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
- $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26
- return $result & self::$maxDigit;
+ return $vars;
}
/**
- * Calculates modular inverses.
- *
- * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger(30);
- * $b = new \phpseclib\Math\BigInteger(17);
- *
- * $c = $a->modInverse($b);
- * echo $c->toString(); // outputs 4
- *
- * echo "\r\n";
- *
- * $d = $a->multiply($c);
- * list(, $d) = $d->divide($b);
- * echo $d; // outputs 1 (as per the definition of modular inverse)
- * ?>
- * </code>
+ * Serialize
*
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger|false
- * @access public
- * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
+ * Will be called, automatically, when unserialize() is called on a BigInteger object.
*/
- function modInverse($n)
+ public function __wakeup()
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_invert($this->value, $n->value);
-
- return ($temp->value === false) ? false : $this->_normalize($temp);
- }
-
- static $zero, $one;
- if (!isset($zero)) {
- $zero = new static();
- $one = new static(1);
- }
-
- // $x mod -$n == $x mod $n.
- $n = $n->abs();
-
- if ($this->compare($zero) < 0) {
- $temp = $this->abs();
- $temp = $temp->modInverse($n);
- return $this->_normalize($n->subtract($temp));
- }
-
- extract($this->extendedGCD($n));
-
- if (!$gcd->equals($one)) {
- return false;
+ $temp = new static($this->hex, -16);
+ $this->value = $temp->value;
+ if ($this->precision > 0) {
+ // recalculate $this->bitmask
+ $this->setPrecision($this->precision);
}
-
- $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
-
- return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
}
/**
- * Calculates the greatest common divisor and Bezout's identity.
+ * JSON Serialize
*
- * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
- * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
- * combination is returned is dependent upon which mode is in use. See
- * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ * Will be called, automatically, when json_encode() is called on a BigInteger object.
*
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger(693);
- * $b = new \phpseclib\Math\BigInteger(609);
- *
- * extract($a->extendedGCD($b));
- *
- * echo $gcd->toString() . "\r\n"; // outputs 21
- * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
- * ?>
- * </code>
- *
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Calculates the GCD using the binary xGCD algorithim described in
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
- * the more traditional algorithim requires "relatively costly multiple-precision divisions".
+ * @return array{hex: string, precision?: int]
*/
- function extendedGCD($n)
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- extract(gmp_gcdext($this->value, $n->value));
-
- return array(
- 'gcd' => $this->_normalize(new static($g)),
- 'x' => $this->_normalize(new static($s)),
- 'y' => $this->_normalize(new static($t))
- );
- case self::MODE_BCMATH:
- // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
- // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
- // the basic extended euclidean algorithim is what we're using.
-
- $u = $this->value;
- $v = $n->value;
-
- $a = '1';
- $b = '0';
- $c = '0';
- $d = '1';
-
- while (bccomp($v, '0', 0) != 0) {
- $q = bcdiv($u, $v, 0);
-
- $temp = $u;
- $u = $v;
- $v = bcsub($temp, bcmul($v, $q, 0), 0);
-
- $temp = $a;
- $a = $c;
- $c = bcsub($temp, bcmul($a, $q, 0), 0);
-
- $temp = $b;
- $b = $d;
- $d = bcsub($temp, bcmul($b, $q, 0), 0);
- }
-
- return array(
- 'gcd' => $this->_normalize(new static($u)),
- 'x' => $this->_normalize(new static($a)),
- 'y' => $this->_normalize(new static($b))
- );
- }
-
- $y = $n->copy();
- $x = $this->copy();
- $g = new static();
- $g->value = array(1);
-
- while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
- $x->_rshift(1);
- $y->_rshift(1);
- $g->_lshift(1);
- }
-
- $u = $x->copy();
- $v = $y->copy();
-
- $a = new static();
- $b = new static();
- $c = new static();
- $d = new static();
-
- $a->value = $d->value = $g->value = array(1);
- $b->value = $c->value = array();
-
- while (!empty($u->value)) {
- while (!($u->value[0] & 1)) {
- $u->_rshift(1);
- if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
- $a = $a->add($y);
- $b = $b->subtract($x);
- }
- $a->_rshift(1);
- $b->_rshift(1);
- }
-
- while (!($v->value[0] & 1)) {
- $v->_rshift(1);
- if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
- $c = $c->add($y);
- $d = $d->subtract($x);
- }
- $c->_rshift(1);
- $d->_rshift(1);
- }
-
- if ($u->compare($v) >= 0) {
- $u = $u->subtract($v);
- $a = $a->subtract($c);
- $b = $b->subtract($d);
- } else {
- $v = $v->subtract($u);
- $c = $c->subtract($a);
- $d = $d->subtract($b);
- }
+ $result = ['hex' => $this->toHex(true)];
+ if ($this->precision > 0) {
+ $result['precision'] = $this->getPrecision();
}
-
- return array(
- 'gcd' => $this->_normalize($g->multiply($v)),
- 'x' => $this->_normalize($c),
- 'y' => $this->_normalize($d)
- );
+ return $result;
}
/**
- * Calculates the greatest common divisor
- *
- * Say you have 693 and 609. The GCD is 21.
- *
- * Here's an example:
- * <code>
- * <?php
- * $a = new \phpseclib\Math\BigInteger(693);
- * $b = new \phpseclib\Math\BigInteger(609);
- *
- * $gcd = a->extendedGCD($b);
- *
- * echo $gcd->toString() . "\r\n"; // outputs 21
- * ?>
- * </code>
+ * Performs modular exponentiation.
*
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @param BigInteger $e
+ * @param BigInteger $n
+ * @return BigInteger
*/
- function gcd($n)
+ public function powMod(BigInteger $e, BigInteger $n)
{
- extract($this->extendedGCD($n));
- return $gcd;
+ return new static($this->value->powMod($e->value, $n->value));
}
/**
- * Absolute value.
+ * Performs modular exponentiation.
*
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @param BigInteger $e
+ * @param BigInteger $n
+ * @return BigInteger
*/
- function abs()
+ public function modPow(BigInteger $e, BigInteger $n)
{
- $temp = new static();
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp->value = gmp_abs($this->value);
- break;
- case self::MODE_BCMATH:
- $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
- break;
- default:
- $temp->value = $this->value;
- }
-
- return $temp;
+ return new static($this->value->modPow($e->value, $n->value));
}
/**
* Compares two numbers.
*
- * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
- * demonstrated thusly:
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this
+ * is demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
@@ -2723,65 +490,15 @@ class BigInteger
*
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
*
- * @param \phpseclib\Math\BigInteger $y
- * @return int that is < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
- * @access public
- * @see self::equals()
- * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
- */
- function compare($y)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $r = gmp_cmp($this->value, $y->value);
- if ($r < -1) {
- $r = -1;
- }
- if ($r > 1) {
- $r = 1;
- }
- return $r;
- case self::MODE_BCMATH:
- return bccomp($this->value, $y->value, 0);
- }
-
- return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
- }
-
- /**
- * Compares two numbers.
+ * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
*
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return int
- * @see self::compare()
- * @access private
+ * @param BigInteger $y
+ * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
*/
- function _compare($x_value, $x_negative, $y_value, $y_negative)
+ public function compare(BigInteger $y)
{
- if ($x_negative != $y_negative) {
- return (!$x_negative && $y_negative) ? 1 : -1;
- }
-
- $result = $x_negative ? -1 : 1;
-
- if (count($x_value) != count($y_value)) {
- return (count($x_value) > count($y_value)) ? $result : -$result;
- }
- $size = max(count($x_value), count($y_value));
-
- $x_value = array_pad($x_value, $size, 0);
- $y_value = array_pad($y_value, $size, 0);
-
- for ($i = count($x_value) - 1; $i >= 0; --$i) {
- if ($x_value[$i] != $y_value[$i]) {
- return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
- }
- }
-
- return 0;
+ return $this->value->compare($y->value);
}
/**
@@ -2789,201 +506,55 @@ class BigInteger
*
* If you need to see if one number is greater than or less than another number, use BigInteger::compare()
*
- * @param \phpseclib\Math\BigInteger $x
+ * @param BigInteger $x
* @return bool
- * @access public
- * @see self::compare()
*/
- function equals($x)
+ public function equals(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_cmp($this->value, $x->value) == 0;
- default:
- return $this->value === $x->value && $this->is_negative == $x->is_negative;
- }
+ return $this->value->equals($x->value);
}
/**
- * Set Precision
- *
- * Some bitwise operations give different results depending on the precision being used. Examples include left
- * shift, not, and rotates.
+ * Logical Not
*
- * @param int $bits
- * @access public
+ * @return BigInteger
*/
- function setPrecision($bits)
+ public function bitwise_not()
{
- $this->precision = $bits;
- if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
- $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
- } else {
- $this->bitmask = new static(bcpow('2', $bits, 0));
- }
-
- $temp = $this->_normalize($this);
- $this->value = $temp->value;
+ return new static($this->value->bitwise_not());
}
/**
* Logical And
*
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
- * @return \phpseclib\Math\BigInteger
+ * @param BigInteger $x
+ * @return BigInteger
*/
- function bitwise_and($x)
+ public function bitwise_and(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_and($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left & $right, 256));
- }
-
- $result = $this->copy();
-
- $length = min(count($x->value), count($this->value));
-
- $result->value = array_slice($result->value, 0, $length);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]&= $x->value[$i];
- }
-
- return $this->_normalize($result);
+ return new static($this->value->bitwise_and($x->value));
}
/**
* Logical Or
*
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
- * @return \phpseclib\Math\BigInteger
+ * @param BigInteger $x
+ * @return BigInteger
*/
- function bitwise_or($x)
+ public function bitwise_or(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_or($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left | $right, 256));
- }
-
- $length = max(count($this->value), count($x->value));
- $result = $this->copy();
- $result->value = array_pad($result->value, $length, 0);
- $x->value = array_pad($x->value, $length, 0);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]|= $x->value[$i];
- }
-
- return $this->_normalize($result);
+ return new static($this->value->bitwise_or($x->value));
}
/**
- * Logical Exclusive-Or
+ * Logical Exclusive Or
*
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
- * @return \phpseclib\Math\BigInteger
+ * @param BigInteger $x
+ * @return BigInteger
*/
- function bitwise_xor($x)
+ public function bitwise_xor(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value));
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left ^ $right, 256));
- }
-
- $length = max(count($this->value), count($x->value));
- $result = $this->copy();
- $result->is_negative = false;
- $result->value = array_pad($result->value, $length, 0);
- $x->value = array_pad($x->value, $length, 0);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]^= $x->value[$i];
- }
-
- return $this->_normalize($result);
- }
-
- /**
- * Logical Not
- *
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez <lluis _a_ pamies.cat>
- * @return \phpseclib\Math\BigInteger
- */
- function bitwise_not()
- {
- // calculuate "not" without regard to $this->precision
- // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
- $temp = $this->toBytes();
- if ($temp == '') {
- return $this->_normalize(new static());
- }
- $pre_msb = decbin(ord($temp[0]));
- $temp = ~$temp;
- $msb = decbin(ord($temp[0]));
- if (strlen($msb) == 8) {
- $msb = substr($msb, strpos($msb, '0'));
- }
- $temp[0] = chr(bindec($msb));
-
- // see if we need to add extra leading 1's
- $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
- $new_bits = $this->precision - $current_bits;
- if ($new_bits <= 0) {
- return $this->_normalize(new static($temp, 256));
- }
-
- // generate as many leading 1's as we need to.
- $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
- $this->_base256_lshift($leading_ones, $current_bits);
-
- $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($leading_ones | $temp, 256));
+ return new static($this->value->bitwise_xor($x->value));
}
/**
@@ -2992,36 +563,11 @@ class BigInteger
* Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
*
* @param int $shift
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The only version that yields any speed increases is the internal version.
+ * @return BigInteger
*/
- function bitwise_rightShift($shift)
+ public function bitwise_rightShift($shift)
{
- $temp = new static();
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- static $two;
-
- if (!isset($two)) {
- $two = gmp_init('2');
- }
-
- $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
-
- break;
- case self::MODE_BCMATH:
- $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
-
- break;
- default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
- // and I don't want to do that...
- $temp->value = $this->value;
- $temp->_rshift($shift);
- }
-
- return $this->_normalize($temp);
+ return new static($this->value->bitwise_rightShift($shift));
}
/**
@@ -3030,36 +576,11 @@ class BigInteger
* Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
*
* @param int $shift
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The only version that yields any speed increases is the internal version.
+ * @return BigInteger
*/
- function bitwise_leftShift($shift)
+ public function bitwise_leftShift($shift)
{
- $temp = new static();
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- static $two;
-
- if (!isset($two)) {
- $two = gmp_init('2');
- }
-
- $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
-
- break;
- case self::MODE_BCMATH:
- $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
-
- break;
- default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
- // and I don't want to do that...
- $temp->value = $this->value;
- $temp->_lshift($shift);
- }
-
- return $this->_normalize($temp);
+ return new static($this->value->bitwise_leftShift($shift));
}
/**
@@ -3068,43 +589,11 @@ class BigInteger
* Instead of the top x bits being dropped they're appended to the shifted bit string.
*
* @param int $shift
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @return BigInteger
*/
- function bitwise_leftRotate($shift)
+ public function bitwise_leftRotate($shift)
{
- $bits = $this->toBytes();
-
- if ($this->precision > 0) {
- $precision = $this->precision;
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $mask = $this->bitmask->subtract(new static(1));
- $mask = $mask->toBytes();
- } else {
- $mask = $this->bitmask->toBytes();
- }
- } else {
- $temp = ord($bits[0]);
- for ($i = 0; $temp >> $i; ++$i) {
- }
- $precision = 8 * strlen($bits) - 8 + $i;
- $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
- }
-
- if ($shift < 0) {
- $shift+= $precision;
- }
- $shift%= $precision;
-
- if (!$shift) {
- return $this->copy();
- }
-
- $left = $this->bitwise_leftShift($shift);
- $left = $left->bitwise_and(new static($mask, 256));
- $right = $this->bitwise_rightShift($precision - $shift);
- $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
- return $this->_normalize($result);
+ return new static($this->value->bitwise_leftRotate($shift));
}
/**
@@ -3113,260 +602,117 @@ class BigInteger
* Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
*
* @param int $shift
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @return BigInteger
*/
- function bitwise_rightRotate($shift)
+ public function bitwise_rightRotate($shift)
{
- return $this->bitwise_leftRotate(-$shift);
+ return new static($this->value->bitwise_rightRotate($shift));
}
/**
- * Generates a random BigInteger
+ * Returns the smallest and largest n-bit number
*
- * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
- *
- * @param int $size
- * @return \phpseclib\Math\BigInteger
- * @access private
+ * @param int $bits
+ * @return BigInteger[]
*/
- function _random_number_helper($size)
+ public static function minMaxBits($bits)
{
- if (class_exists('\phpseclib\Crypt\Random')) {
- $random = Random::string($size);
- } else {
- $random = '';
+ self::initialize_static_variables();
- if ($size & 1) {
- $random.= chr(mt_rand(0, 255));
- }
-
- $blocks = $size >> 1;
- for ($i = 0; $i < $blocks; ++$i) {
- // mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
- $random.= pack('n', mt_rand(0, 0xFFFF));
- }
- }
+ $class = self::$mainEngine;
+ $minMax = $class::minMaxBits($bits);
+ $min = $minMax['min'];
+ $max = $minMax['max'];
+ return [
+ 'min' => new static($min),
+ 'max' => new static($max)
+ ];
+ }
- return new static($random, 256);
+ /**
+ * Return the size of a BigInteger in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return $this->value->getLength();
}
/**
- * Generate a random number
+ * Return the size of a BigInteger in bytes
*
- * Returns a random number between $min and $max where $min and $max
- * can be defined using one of the two methods:
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return $this->value->getLengthInBytes();
+ }
+
+ /**
+ * Generates a random number of a certain size
*
- * $min->random($max)
- * $max->random($min)
+ * Bit length is equal to $size
*
- * @param \phpseclib\Math\BigInteger $arg1
- * @param \phpseclib\Math\BigInteger $arg2
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object.
- * That method is still supported for BC purposes.
+ * @param int $size
+ * @return BigInteger
*/
- function random($arg1, $arg2 = false)
+ public static function random($size)
{
- if ($arg1 === false) {
- return false;
- }
+ self::initialize_static_variables();
- if ($arg2 === false) {
- $max = $arg1;
- $min = $this;
- } else {
- $min = $arg1;
- $max = $arg2;
- }
-
- $compare = $max->compare($min);
-
- if (!$compare) {
- return $this->_normalize($min);
- } elseif ($compare < 0) {
- // if $min is bigger then $max, swap $min and $max
- $temp = $max;
- $max = $min;
- $min = $temp;
- }
-
- static $one;
- if (!isset($one)) {
- $one = new static(1);
- }
+ $class = self::$mainEngine;
+ return new static($class::random($size));
+ }
- $max = $max->subtract($min->subtract($one));
- $size = strlen(ltrim($max->toBytes(), chr(0)));
-
- /*
- doing $random % $max doesn't work because some numbers will be more likely to occur than others.
- eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
- would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
- not all numbers would be equally likely. some would be more likely than others.
-
- creating a whole new random number until you find one that is within the range doesn't work
- because, for sufficiently small ranges, the likelihood that you'd get a number within that range
- would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
- would be pretty high that $random would be greater than $max.
-
- phpseclib works around this using the technique described here:
-
- http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
- */
- $random_max = new static(chr(1) . str_repeat("\0", $size), 256);
- $random = $this->_random_number_helper($size);
-
- list($max_multiple) = $random_max->divide($max);
- $max_multiple = $max_multiple->multiply($max);
-
- while ($random->compare($max_multiple) >= 0) {
- $random = $random->subtract($max_multiple);
- $random_max = $random_max->subtract($max_multiple);
- $random = $random->bitwise_leftShift(8);
- $random = $random->add($this->_random_number_helper(1));
- $random_max = $random_max->bitwise_leftShift(8);
- list($max_multiple) = $random_max->divide($max);
- $max_multiple = $max_multiple->multiply($max);
- }
- list(, $random) = $random->divide($max);
+ /**
+ * Generates a random prime number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return BigInteger
+ */
+ public static function randomPrime($size)
+ {
+ self::initialize_static_variables();
- return $this->_normalize($random->add($min));
+ $class = self::$mainEngine;
+ return new static($class::randomPrime($size));
}
/**
- * Generate a random prime number.
+ * Generate a random prime number between a range
*
* If there's not a prime within the given range, false will be returned.
- * If more than $timeout seconds have elapsed, give up and return false.
- *
- * @param \phpseclib\Math\BigInteger $arg1
- * @param \phpseclib\Math\BigInteger $arg2
- * @param int $timeout
- * @return Math_BigInteger|false
- * @access public
- * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
+ *
+ * @param BigInteger $min
+ * @param BigInteger $max
+ * @return false|BigInteger
*/
- function randomPrime($arg1, $arg2 = false, $timeout = false)
+ public static function randomRangePrime(BigInteger $min, BigInteger $max)
{
- if ($arg1 === false) {
- return false;
- }
-
- if ($arg2 === false) {
- $max = $arg1;
- $min = $this;
- } else {
- $min = $arg1;
- $max = $arg2;
- }
-
- $compare = $max->compare($min);
-
- if (!$compare) {
- return $min->isPrime() ? $min : false;
- } elseif ($compare < 0) {
- // if $min is bigger then $max, swap $min and $max
- $temp = $max;
- $max = $min;
- $min = $temp;
- }
-
- $length = $max->getLength();
- if ($length > 8196) {
- user_error('Generation of random prime numbers larger than 8196 has been disabled');
- }
-
- static $one, $two;
- if (!isset($one)) {
- $one = new static(1);
- $two = new static(2);
- }
-
- $start = time();
-
- $x = $this->random($min, $max);
-
- // gmp_nextprime() requires PHP 5 >= 5.2.0 per <http://php.net/gmp-nextprime>.
- if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
- $p = new static();
- $p->value = gmp_nextprime($x->value);
-
- if ($p->compare($max) <= 0) {
- return $p;
- }
-
- if (!$min->equals($x)) {
- $x = $x->subtract($one);
- }
-
- return $x->randomPrime($min, $x);
- }
-
- if ($x->equals($two)) {
- return $x;
- }
-
- $x->_make_odd();
- if ($x->compare($max) > 0) {
- // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
- if ($min->equals($max)) {
- return false;
- }
- $x = $min->copy();
- $x->_make_odd();
- }
-
- $initial_x = $x->copy();
-
- while (true) {
- if ($timeout !== false && time() - $start > $timeout) {
- return false;
- }
-
- if ($x->isPrime()) {
- return $x;
- }
-
- $x = $x->add($two);
-
- if ($x->compare($max) > 0) {
- $x = $min->copy();
- if ($x->equals($two)) {
- return $x;
- }
- $x->_make_odd();
- }
-
- if ($x->equals($initial_x)) {
- return false;
- }
- }
+ $class = self::$mainEngine;
+ return new static($class::randomRangePrime($min->value, $max->value));
}
/**
- * Make the current number odd
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
*
- * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
*
- * @see self::randomPrime()
- * @access private
+ * @param BigInteger $min
+ * @param BigInteger $max
+ * @return BigInteger
*/
- function _make_odd()
+ public static function randomRange(BigInteger $min, BigInteger $max)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- gmp_setbit($this->value, 0);
- break;
- case self::MODE_BCMATH:
- if ($this->value[strlen($this->value) - 1] % 2 == 0) {
- $this->value = bcadd($this->value, '1');
- }
- break;
- default:
- $this->value[0] |= 1;
- }
+ $class = self::$mainEngine;
+ return new static($class::randomRange($min->value, $max->value));
}
/**
@@ -3376,460 +722,173 @@ class BigInteger
* $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
* on a website instead of just one.
*
- * @param \phpseclib\Math\BigInteger $t
+ * @param int|bool $t
* @return bool
- * @access public
- * @internal Uses the
- * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
*/
- function isPrime($t = false)
+ public function isPrime($t = false)
{
- $length = $this->getLength();
- // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is
- // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is
- // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest
- // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on
- if ($length > 8196) {
- user_error('Primality testing is not supported for numbers larger than 8196 bits');
- }
-
- if (!$t) {
- // see HAC 4.49 "Note (controlling the error probability)"
- // @codingStandardsIgnoreStart
- if ($length >= 163) { $t = 2; } // floor(1300 / 8)
- else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
- else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
- else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
- else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
- else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
- else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
- else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
- else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
- else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
- else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
- else { $t = 27; }
- // @codingStandardsIgnoreEnd
- }
-
- // ie. gmp_testbit($this, 0)
- // ie. isEven() or !isOdd()
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_prob_prime($this->value, $t) != 0;
- case self::MODE_BCMATH:
- if ($this->value === '2') {
- return true;
- }
- if ($this->value[strlen($this->value) - 1] % 2 == 0) {
- return false;
- }
- break;
- default:
- if ($this->value == array(2)) {
- return true;
- }
- if (~$this->value[0] & 1) {
- return false;
- }
- }
-
- static $primes, $zero, $one, $two;
-
- if (!isset($primes)) {
- $primes = array(
- 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
- 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
- 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
- 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
- 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
- 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
- 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
- 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
- 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
- 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
- 953, 967, 971, 977, 983, 991, 997
- );
-
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- for ($i = 0; $i < count($primes); ++$i) {
- $primes[$i] = new static($primes[$i]);
- }
- }
-
- $zero = new static();
- $one = new static(1);
- $two = new static(2);
- }
-
- if ($this->equals($one)) {
- return false;
- }
-
- // see HAC 4.4.1 "Random search for probable primes"
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- foreach ($primes as $prime) {
- list(, $r) = $this->divide($prime);
- if ($r->equals($zero)) {
- return $this->equals($prime);
- }
- }
- } else {
- $value = $this->value;
- foreach ($primes as $prime) {
- list(, $r) = $this->_divide_digit($value, $prime);
- if (!$r) {
- return count($value) == 1 && $value[0] == $prime;
- }
- }
- }
-
- $n = $this->copy();
- $n_1 = $n->subtract($one);
- $n_2 = $n->subtract($two);
-
- $r = $n_1->copy();
- $r_value = $r->value;
- // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $s = 0;
- // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
- while ($r->value[strlen($r->value) - 1] % 2 == 0) {
- $r->value = bcdiv($r->value, '2', 0);
- ++$s;
- }
- } else {
- for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
- $temp = ~$r_value[$i] & 0xFFFFFF;
- for ($j = 1; ($temp >> $j) & 1; ++$j) {
- }
- if ($j != 25) {
- break;
- }
- }
- $s = 26 * $i + $j;
- $r->_rshift($s);
- }
-
- for ($i = 0; $i < $t; ++$i) {
- $a = $this->random($two, $n_2);
- $y = $a->modPow($r, $n);
-
- if (!$y->equals($one) && !$y->equals($n_1)) {
- for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
- $y = $y->modPow($two, $n);
- if ($y->equals($one)) {
- return false;
- }
- }
-
- if (!$y->equals($n_1)) {
- return false;
- }
- }
- }
- return true;
+ return $this->value->isPrime($t);
}
/**
- * Logical Left Shift
+ * Calculates the nth root of a biginteger.
*
- * Shifts BigInteger's by $shift bits.
+ * Returns the nth root of a positive biginteger, where n defaults to 2
*
- * @param int $shift
- * @access private
+ * @param int $n optional
+ * @return BigInteger
*/
- function _lshift($shift)
+ public function root($n = 2)
{
- if ($shift == 0) {
- return;
- }
-
- $num_digits = (int) ($shift / self::$base);
- $shift %= self::$base;
- $shift = 1 << $shift;
-
- $carry = 0;
-
- for ($i = 0; $i < count($this->value); ++$i) {
- $temp = $this->value[$i] * $shift + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $this->value[$i] = (int) ($temp - $carry * self::$baseFull);
- }
-
- if ($carry) {
- $this->value[count($this->value)] = $carry;
- }
-
- while ($num_digits--) {
- array_unshift($this->value, 0);
- }
+ return new static($this->value->root($n));
}
/**
- * Logical Right Shift
- *
- * Shifts BigInteger's by $shift bits.
+ * Performs exponentiation.
*
- * @param int $shift
- * @access private
+ * @param BigInteger $n
+ * @return BigInteger
*/
- function _rshift($shift)
+ public function pow(BigInteger $n)
{
- if ($shift == 0) {
- return;
- }
-
- $num_digits = (int) ($shift / self::$base);
- $shift %= self::$base;
- $carry_shift = self::$base - $shift;
- $carry_mask = (1 << $shift) - 1;
-
- if ($num_digits) {
- $this->value = array_slice($this->value, $num_digits);
- }
-
- $carry = 0;
-
- for ($i = count($this->value) - 1; $i >= 0; --$i) {
- $temp = $this->value[$i] >> $shift | $carry;
- $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
- $this->value[$i] = $temp;
- }
-
- $this->value = $this->_trim($this->value);
+ return new static($this->value->pow($n->value));
}
/**
- * Normalize
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
*
- * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
- *
- * @param \phpseclib\Math\BigInteger $result
- * @return \phpseclib\Math\BigInteger
- * @see self::_trim()
- * @access private
+ * @param BigInteger ...$nums
+ * @return BigInteger
*/
- function _normalize($result)
+ public static function min(BigInteger ...$nums)
{
- $result->precision = $this->precision;
- $result->bitmask = $this->bitmask;
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- if ($this->bitmask !== false) {
- $flip = gmp_cmp($result->value, gmp_init(0)) < 0;
- if ($flip) {
- $result->value = gmp_neg($result->value);
- }
- $result->value = gmp_and($result->value, $result->bitmask->value);
- if ($flip) {
- $result->value = gmp_neg($result->value);
- }
- }
-
- return $result;
- case self::MODE_BCMATH:
- if (!empty($result->bitmask->value)) {
- $result->value = bcmod($result->value, $result->bitmask->value);
- }
-
- return $result;
- }
-
- $value = &$result->value;
-
- if (!count($value)) {
- $result->is_negative = false;
- return $result;
- }
-
- $value = $this->_trim($value);
-
- if (!empty($result->bitmask->value)) {
- $length = min(count($value), count($this->bitmask->value));
- $value = array_slice($value, 0, $length);
-
- for ($i = 0; $i < $length; ++$i) {
- $value[$i] = $value[$i] & $this->bitmask->value[$i];
- }
- }
-
- return $result;
+ $class = self::$mainEngine;
+ $nums = array_map(function ($num) {
+ return $num->value;
+ }, $nums);
+ return new static($class::min(...$nums));
}
/**
- * Trim
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
*
- * Removes leading zeros
+ * @param BigInteger ...$nums
+ * @return BigInteger
+ */
+ public static function max(BigInteger ...$nums)
+ {
+ $class = self::$mainEngine;
+ $nums = array_map(function ($num) {
+ return $num->value;
+ }, $nums);
+ return new static($class::max(...$nums));
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
*
- * @param array $value
- * @return \phpseclib\Math\BigInteger
- * @access private
+ * @param BigInteger $min
+ * @param BigInteger $max
+ * @return bool
*/
- function _trim($value)
+ public function between(BigInteger $min, BigInteger $max)
{
- for ($i = count($value) - 1; $i >= 0; --$i) {
- if ($value[$i]) {
- break;
- }
- unset($value[$i]);
- }
+ return $this->value->between($min->value, $max->value);
+ }
- return $value;
+ /**
+ * Clone
+ */
+ public function __clone()
+ {
+ $this->value = clone $this->value;
}
/**
- * Array Repeat
+ * Is Odd?
*
- * @param array $input
- * @param mixed $multiplier
- * @return array
- * @access private
+ * @return bool
*/
- function _array_repeat($input, $multiplier)
+ public function isOdd()
{
- return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
+ return $this->value->isOdd();
}
/**
- * Logical Left Shift
+ * Tests if a bit is set
*
- * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
- *
- * @param string $x (by reference)
- * @param int $shift
- * @return string
- * @access private
+ * @param int $x
+ * @return bool
*/
- function _base256_lshift(&$x, $shift)
+ public function testBit($x)
{
- if ($shift == 0) {
- return;
- }
-
- $num_bytes = $shift >> 3; // eg. floor($shift/8)
- $shift &= 7; // eg. $shift % 8
-
- $carry = 0;
- for ($i = strlen($x) - 1; $i >= 0; --$i) {
- $temp = ord($x[$i]) << $shift | $carry;
- $x[$i] = chr($temp);
- $carry = $temp >> 8;
- }
- $carry = ($carry != 0) ? chr($carry) : '';
- $x = $carry . $x . str_repeat(chr(0), $num_bytes);
+ return $this->value->testBit($x);
}
/**
- * Logical Right Shift
- *
- * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
+ * Is Negative?
*
- * @param string $x (by referenc)
- * @param int $shift
- * @return string
- * @access private
+ * @return bool
*/
- function _base256_rshift(&$x, $shift)
+ public function isNegative()
{
- if ($shift == 0) {
- $x = ltrim($x, chr(0));
- return '';
- }
-
- $num_bytes = $shift >> 3; // eg. floor($shift/8)
- $shift &= 7; // eg. $shift % 8
-
- $remainder = '';
- if ($num_bytes) {
- $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
- $remainder = substr($x, $start);
- $x = substr($x, 0, -$num_bytes);
- }
-
- $carry = 0;
- $carry_shift = 8 - $shift;
- for ($i = 0; $i < strlen($x); ++$i) {
- $temp = (ord($x[$i]) >> $shift) | $carry;
- $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
- $x[$i] = chr($temp);
- }
- $x = ltrim($x, chr(0));
-
- $remainder = chr($carry >> $carry_shift) . $remainder;
-
- return ltrim($remainder, chr(0));
+ return $this->value->isNegative();
}
- // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
- // at 32-bits, while java's longs are 64-bits.
-
/**
- * Converts 32-bit integers to bytes.
+ * Negate
*
- * @param int $x
- * @return string
- * @access private
+ * Given $k, returns -$k
+ *
+ * @return BigInteger
*/
- function _int2bytes($x)
+ public function negate()
{
- return ltrim(pack('N', $x), chr(0));
+ return new static($this->value->negate());
}
/**
- * Converts bytes to 32-bit integers
+ * Scan for 1 and right shift by that amount
*
- * @param string $x
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @param BigInteger $r
* @return int
- * @access private
*/
- function _bytes2int($x)
+ public static function scan1divide(BigInteger $r)
{
- $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
- return $temp['int'];
+ $class = self::$mainEngine;
+ return $class::scan1divide($r->value);
}
/**
- * DER-encode an integer
+ * Create Recurring Modulo Function
*
- * The ability to DER-encode integers is needed to create RSA public keys for use with OpenSSL
+ * Sometimes it may be desirable to do repeated modulos with the same number outside of
+ * modular exponentiation
*
- * @see self::modPow()
- * @access private
- * @param int $length
- * @return string
+ * @return callable
*/
- function _encodeASN1Length($length)
+ public function createRecurringModuloFunction()
{
- if ($length <= 0x7F) {
- return chr($length);
- }
-
- $temp = ltrim(pack('N', $length), chr(0));
- return pack('Ca*', 0x80 | strlen($temp), $temp);
+ $func = $this->value->createRecurringModuloFunction();
+ return function (BigInteger $x) use ($func) {
+ return new static($func($x->value));
+ };
}
/**
- * Single digit division
+ * Bitwise Split
*
- * Even if int64 is being used the division operator will return a float64 value
- * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
- * have the precision of int64 this is a problem so, when int64 is being used,
- * we'll guarantee that the dividend is divisible by first subtracting the remainder.
+ * Splits BigInteger's into chunks of $split bits
*
- * @access private
- * @param int $x
- * @param int $y
- * @return int
+ * @param int $split
+ * @return BigInteger[]
*/
- function _safe_divide($x, $y)
+ public function bitwise_split($split)
{
- if (self::$base === 26) {
- return (int) ($x / $y);
- }
-
- // self::$base === 31
- return ($x - ($x % $y)) / $y;
+ return array_map(function ($val) {
+ return new static($val);
+ }, $this->value->bitwise_split($split));
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php
new file mode 100644
index 000000000..7b6283002
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath.php
@@ -0,0 +1,702 @@
+<?php
+
+/**
+ * BCMath BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\BadConfigurationException;
+
+/**
+ * BCMath Engine.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class BCMath extends Engine
+{
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ */
+ const FAST_BITWISE = false;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ */
+ const ENGINE_DIR = 'BCMath';
+
+ /**
+ * Test to see if bcmod() accepts 2 or 3 parameters
+ */
+ const BCMOD_THREE_PARAMS = PHP_VERSION_ID >= 72000;
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ * @see parent::__construct()
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('bcmath');
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param mixed $x integer Base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @see parent::__construct()
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(static::$isValidEngine[static::class])) {
+ static::$isValidEngine[static::class] = self::isValidEngine();
+ }
+ if (!static::$isValidEngine[static::class]) {
+ throw new BadConfigurationException('BCMath is not setup correctly on this system');
+ }
+
+ $this->value = '0';
+
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a BCMath BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 256:
+ // round $len to the nearest 4
+ $len = (strlen($this->value) + 3) & ~3;
+
+ $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);
+
+ $this->value = '0';
+ for ($i = 0; $i < $len; $i += 4) {
+ $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
+ $this->value = bcadd(
+ $this->value,
+ 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord(
+ $x[$i + 2]
+ ) << 8) | ord($x[$i + 3])),
+ 0
+ );
+ }
+
+ if ($this->is_negative) {
+ $this->value = '-' . $this->value;
+ }
+ break;
+ case 16:
+ $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
+ $temp = new self(Strings::hex2bin($x), 256);
+ $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
+ $this->is_negative = false;
+ break;
+ case 10:
+ // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
+ // results then doing it on '-1' does (modInverse does $x[0])
+ $this->value = $this->value === '-' ? '0' : (string)$this->value;
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if ($this->value === '0') {
+ return '0';
+ }
+
+ return ltrim($this->value, '0');
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ $value = '';
+ $current = $this->value;
+
+ if ($current[0] == '-') {
+ $current = substr($current, 1);
+ }
+
+ while (bccomp($current, '0', 0) > 0) {
+ $temp = self::BCMOD_THREE_PARAMS ? bcmod($current, '16777216', 0) : bcmod($current, '16777216');
+ $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
+ $current = bcdiv($current, '16777216', 0);
+ }
+
+ return $this->precision > 0 ?
+ substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($value, chr(0));
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @param BCMath $y
+ * @return BCMath
+ */
+ public function add(BCMath $y)
+ {
+ $temp = new self();
+ $temp->value = bcadd($this->value, $y->value, 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @param BCMath $y
+ * @return BCMath
+ */
+ public function subtract(BCMath $y)
+ {
+ $temp = new self();
+ $temp->value = bcsub($this->value, $y->value, 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @param BCMath $x
+ * @return BCMath
+ */
+ public function multiply(BCMath $x)
+ {
+ $temp = new self();
+ $temp->value = bcmul($this->value, $x->value, 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @param BCMath $y
+ * @return array{static, static}
+ */
+ public function divide(BCMath $y)
+ {
+ $quotient = new self();
+ $remainder = new self();
+
+ $quotient->value = bcdiv($this->value, $y->value, 0);
+ $remainder->value = self::BCMOD_THREE_PARAMS ? bcmod($this->value, $y->value, 0) : bcmod($this->value, $y->value);
+
+ if ($remainder->value[0] == '-') {
+ $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * @param BCMath $n
+ * @return false|BCMath
+ */
+ public function modInverse(BCMath $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependent upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ *
+ * @param BCMath $n
+ * @return array{gcd: static, x: static, y: static}
+ */
+ public function extendedGCD(BCMath $n)
+ {
+ // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
+ // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
+ // the basic extended euclidean algorithim is what we're using.
+
+ $u = $this->value;
+ $v = $n->value;
+
+ $a = '1';
+ $b = '0';
+ $c = '0';
+ $d = '1';
+
+ while (bccomp($v, '0', 0) != 0) {
+ $q = bcdiv($u, $v, 0);
+
+ $temp = $u;
+ $u = $v;
+ $v = bcsub($temp, bcmul($v, $q, 0), 0);
+
+ $temp = $a;
+ $a = $c;
+ $c = bcsub($temp, bcmul($a, $q, 0), 0);
+
+ $temp = $b;
+ $b = $d;
+ $d = bcsub($temp, bcmul($b, $q, 0), 0);
+ }
+
+ return [
+ 'gcd' => $this->normalize(new static($u)),
+ 'x' => $this->normalize(new static($a)),
+ 'y' => $this->normalize(new static($b))
+ ];
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @param BCMath $n
+ * @return BCMath
+ */
+ public function gcd(BCMath $n)
+ {
+ $gcd = $this->extendedGCD($n)['gcd'];
+ return $gcd;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return BCMath
+ */
+ public function abs()
+ {
+ $temp = new static();
+ $temp->value = strlen($this->value) && $this->value[0] == '-' ?
+ substr($this->value, 1) :
+ $this->value;
+
+ return $temp;
+ }
+
+ /**
+ * Logical And
+ *
+ * @param BCMath $x
+ * @return BCMath
+ */
+ public function bitwise_and(BCMath $x)
+ {
+ return $this->bitwiseAndHelper($x);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param BCMath $x
+ * @return BCMath
+ */
+ public function bitwise_or(BCMath $x)
+ {
+ return $this->bitwiseOrHelper($x);
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @param BCMath $x
+ * @return BCMath
+ */
+ public function bitwise_xor(BCMath $x)
+ {
+ return $this->bitwiseXorHelper($x);
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return BCMath
+ */
+ public function bitwise_rightShift($shift)
+ {
+ $temp = new static();
+ $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return BCMath
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new static();
+ $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this
+ * is demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
+ *
+ * @param BCMath $y
+ * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
+ */
+ public function compare(BCMath $y)
+ {
+ return bccomp($this->value, $y->value, 0);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @param BCMath $x
+ * @return bool
+ */
+ public function equals(BCMath $x)
+ {
+ return $this->value == $x->value;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param BCMath $e
+ * @param BCMath $n
+ * @return BCMath
+ */
+ public function modPow(BCMath $e, BCMath $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @param BCMath $e
+ * @param BCMath $n
+ * @return BCMath
+ */
+ public function powMod(BCMath $e, BCMath $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param BCMath $e
+ * @param BCMath $n
+ * @return BCMath
+ */
+ protected function powModInner(BCMath $e, BCMath $n)
+ {
+ try {
+ $class = static::$modexpEngine[static::class];
+ return $class::powModHelper($this, $e, $n, static::class);
+ } catch (\Exception $err) {
+ return BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class);
+ }
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @param BCMath $result
+ * @return BCMath
+ */
+ protected function normalize(BCMath $result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ if ($result->bitmask !== false) {
+ $result->value = self::BCMOD_THREE_PARAMS ? bcmod($result->value, $result->bitmask->value, 0) : bcmod($result->value, $result->bitmask->value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @param BCMath $min
+ * @param BCMath $max
+ * @return false|BCMath
+ */
+ public static function randomRangePrime(BCMath $min, BCMath $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @param BCMath $min
+ * @param BCMath $max
+ * @return BCMath
+ */
+ public static function randomRange(BCMath $min, BCMath $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ if (!$this->isOdd()) {
+ $this->value = bcadd($this->value, '1', 0);
+ }
+ }
+
+ /**
+ * Test the number against small primes.
+ *
+ * @see self::isPrime()
+ */
+ protected function testSmallPrimes()
+ {
+ if ($this->value === '1') {
+ return false;
+ }
+ if ($this->value === '2') {
+ return true;
+ }
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
+ return false;
+ }
+
+ $value = $this->value;
+
+ foreach (self::PRIMES as $prime) {
+ $r = self::BCMOD_THREE_PARAMS ? bcmod($this->value, $prime, 0) : bcmod($this->value, $prime);
+ if ($r == '0') {
+ return $this->value == $prime;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scan for 1 and right shift by that amount
+ *
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @param BCMath $r
+ * @return int
+ * @see self::isPrime()
+ */
+ public static function scan1divide(BCMath $r)
+ {
+ $r_value = &$r->value;
+ $s = 0;
+ // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one[static::class]) check earlier
+ while ($r_value[strlen($r_value) - 1] % 2 == 0) {
+ $r_value = bcdiv($r_value, '2', 0);
+ ++$s;
+ }
+
+ return $s;
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @param BCMath $n
+ * @return BCMath
+ */
+ public function pow(BCMath $n)
+ {
+ $temp = new self();
+ $temp->value = bcpow($this->value, $n->value, 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param BCMath ...$nums
+ * @return BCMath
+ */
+ public static function min(BCMath ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param BCMath ...$nums
+ * @return BCMath
+ */
+ public static function max(BCMath ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @param BCMath $min
+ * @param BCMath $max
+ * @return bool
+ */
+ public function between(BCMath $min, BCMath $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+
+ /**
+ * Set Bitmask
+ *
+ * @param int $bits
+ * @return Engine
+ * @see self::setPrecision()
+ */
+ protected static function setBitmask($bits)
+ {
+ $temp = parent::setBitmask($bits);
+ return $temp->add(static::$one[static::class]);
+ }
+
+ /**
+ * Is Odd?
+ *
+ * @return bool
+ */
+ public function isOdd()
+ {
+ return $this->value[strlen($this->value) - 1] % 2 == 1;
+ }
+
+ /**
+ * Tests if a bit is set
+ *
+ * @return bool
+ */
+ public function testBit($x)
+ {
+ $divisor = bcpow('2', $x + 1, 0);
+ return bccomp(
+ self::BCMOD_THREE_PARAMS ? bcmod($this->value, $divisor, 0) : bcmod($this->value, $divisor),
+ bcpow('2', $x, 0),
+ 0
+ ) >= 0;
+ }
+
+ /**
+ * Is Negative?
+ *
+ * @return bool
+ */
+ public function isNegative()
+ {
+ return strlen($this->value) && $this->value[0] == '-';
+ }
+
+ /**
+ * Negate
+ *
+ * Given $k, returns -$k
+ *
+ * @return BCMath
+ */
+ public function negate()
+ {
+ $temp = clone $this;
+
+ if (!strlen($temp->value)) {
+ return $temp;
+ }
+
+ $temp->value = $temp->value[0] == '-' ?
+ substr($this->value, 1) :
+ '-' . $this->value;
+
+ return $temp;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php
new file mode 100644
index 000000000..88cd93e94
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Base.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath;
+
+use phpseclib3\Math\BigInteger\Engines\BCMath;
+
+/**
+ * Sliding Window Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Base extends BCMath
+{
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ *
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ *
+ */
+ const DATA = 1;
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param BCMath $x
+ * @param BCMath $e
+ * @param BCMath $n
+ * @param string $class
+ * @return BCMath
+ */
+ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n, $class)
+ {
+ if (empty($e->value)) {
+ $temp = new $class();
+ $temp->value = '1';
+ return $x->normalize($temp);
+ }
+
+ return $x->normalize(static::slidingWindow($x, $e, $n, $class));
+ }
+
+ /**
+ * Modular reduction preparation
+ *
+ * @param string $x
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function prepareReduce($x, $n, $class)
+ {
+ return static::reduce($x, $n);
+ }
+
+ /**
+ * Modular multiply
+ *
+ * @param string $x
+ * @param string $y
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function multiplyReduce($x, $y, $n, $class)
+ {
+ return static::reduce(bcmul($x, $y, 0), $n);
+ }
+
+ /**
+ * Modular square
+ *
+ * @param string $x
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function squareReduce($x, $n, $class)
+ {
+ return static::reduce(bcmul($x, $x, 0), $n);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php
new file mode 100644
index 000000000..f8bbcfa27
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Built-In BCMath Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath;
+
+use phpseclib3\Math\BigInteger\Engines\BCMath;
+
+/**
+ * Built-In BCMath Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class BuiltIn extends BCMath
+{
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param BCMath $x
+ * @param BCMath $e
+ * @param BCMath $n
+ * @return BCMath
+ */
+ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n)
+ {
+ $temp = new BCMath();
+ $temp->value = bcpowmod($x->value, $e->value, $n->value, 0);
+
+ return $x->normalize($temp);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php
new file mode 100644
index 000000000..b2d9fa95c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * BCMath Default Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath;
+
+use phpseclib3\Math\BigInteger\Engines\BCMath\Reductions\Barrett;
+
+/**
+ * PHP Default Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DefaultEngine extends Barrett
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php
new file mode 100644
index 000000000..aed949420
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath;
+
+use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSL extends Progenitor
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php
new file mode 100644
index 000000000..1bec0a11f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * BCMath Barrett Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\BCMath\Base;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Barrett extends Base
+{
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ *
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ *
+ */
+ const DATA = 1;
+
+ /**
+ * Barrett Modular Reduction
+ *
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
+ * so as not to require negative numbers (initially, this script didn't support negative numbers).
+ *
+ * Employs "folding", as described at
+ * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
+ * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
+ *
+ * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
+ * usable on account of (1) its not using reasonable radix points as discussed in
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
+ * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
+ * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
+ * comments for details.
+ *
+ * @param string $n
+ * @param string $m
+ * @return string
+ */
+ protected static function reduce($n, $m)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $m_length = strlen($m);
+
+ if (strlen($n) > 2 * $m_length) {
+ return self::BCMOD_THREE_PARAMS ? bcmod($n, $m, 0) : bcmod($n, $m);
+ }
+
+ // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
+ if ($m_length < 5) {
+ return self::regularBarrett($n, $m);
+ }
+ // n = 2 * m.length
+ $correctionNeeded = false;
+ if ($m_length & 1) {
+ $correctionNeeded = true;
+ $n .= '0';
+ $m .= '0';
+ $m_length++;
+ }
+
+ if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $m;
+
+ $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
+ $u = bcdiv($lhs, $m, 0);
+ $m1 = bcsub($lhs, bcmul($u, $m, 0), 0);
+
+ $cache[self::DATA][] = [
+ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
+ 'm1' => $m1 // m.length
+ ];
+ } else {
+ $cacheValues = $cache[self::DATA][$key];
+ $u = $cacheValues['u'];
+ $m1 = $cacheValues['m1'];
+ }
+
+ $cutoff = $m_length + ($m_length >> 1);
+
+ $lsd = substr($n, -$cutoff);
+ $msd = substr($n, 0, -$cutoff);
+
+ $temp = bcmul($msd, $m1, 0); // m.length + (m.length >> 1)
+ $n = bcadd($lsd, $temp, 0); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
+ //if ($m_length & 1) {
+ // return self::regularBarrett($n, $m);
+ //}
+
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
+ $temp = substr($n, 0, -$m_length + 1);
+ // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
+ // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
+ $temp = bcmul($temp, $u, 0);
+ // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
+ // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
+ $temp = substr($temp, 0, -($m_length >> 1) - 1);
+ // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
+ // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
+ $temp = bcmul($temp, $m, 0);
+
+ // at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
+ // number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
+ // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
+
+ $result = bcsub($n, $temp, 0);
+
+ //if (bccomp($result, '0') < 0) {
+ if ($result[0] == '-') {
+ $temp = '1' . str_repeat('0', $m_length + 1);
+ $result = bcadd($result, $temp, 0);
+ }
+
+ while (bccomp($result, $m, 0) >= 0) {
+ $result = bcsub($result, $m, 0);
+ }
+
+ return $correctionNeeded && $result != '0' ? substr($result, 0, -1) : $result;
+ }
+
+ /**
+ * (Regular) Barrett Modular Reduction
+ *
+ * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
+ * is that this function does not fold the denominator into a smaller form.
+ *
+ * @param string $x
+ * @param string $n
+ * @return string
+ */
+ private static function regularBarrett($x, $n)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $n_length = strlen($n);
+
+ if (strlen($x) > 2 * $n_length) {
+ return self::BCMOD_THREE_PARAMS ? bcmod($x, $n, 0) : bcmod($x, $n);
+ }
+
+ if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $n;
+ $lhs = '1' . str_repeat('0', 2 * $n_length);
+ $cache[self::DATA][] = bcdiv($lhs, $n, 0);
+ }
+
+ $temp = substr($x, 0, -$n_length + 1);
+ $temp = bcmul($temp, $cache[self::DATA][$key], 0);
+ $temp = substr($temp, 0, -$n_length - 1);
+
+ $r1 = substr($x, -$n_length - 1);
+ $r2 = substr(bcmul($temp, $n, 0), -$n_length - 1);
+ $result = bcsub($r1, $r2);
+
+ //if (bccomp($result, '0') < 0) {
+ if ($result[0] == '-') {
+ $q = '1' . str_repeat('0', $n_length + 1);
+ $result = bcadd($result, $q, 0);
+ }
+
+ while (bccomp($result, $n, 0) >= 0) {
+ $result = bcsub($result, $n, 0);
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php
new file mode 100644
index 000000000..040d7b5a7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * BCMath Dynamic Barrett Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\BCMath\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\BCMath;
+use phpseclib3\Math\BigInteger\Engines\BCMath\Base;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EvalBarrett extends Base
+{
+ /**
+ * Custom Reduction Function
+ *
+ * @see self::generateCustomReduction
+ */
+ private static $custom_reduction;
+
+ /**
+ * Barrett Modular Reduction
+ *
+ * This calls a dynamically generated loop unrolled function that's specific to a given modulo.
+ * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc.
+ *
+ * @param string $n
+ * @param string $m
+ * @return string
+ */
+ protected static function reduce($n, $m)
+ {
+ $inline = self::$custom_reduction;
+ return $inline($n);
+ }
+
+ /**
+ * Generate Custom Reduction
+ *
+ * @param BCMath $m
+ * @param string $class
+ * @return callable|void
+ */
+ protected static function generateCustomReduction(BCMath $m, $class)
+ {
+ $m_length = strlen($m);
+
+ if ($m_length < 5) {
+ $code = 'return self::BCMOD_THREE_PARAMS ? bcmod($x, $n, 0) : bcmod($x, $n);';
+ eval('$func = function ($n) { ' . $code . '};');
+ self::$custom_reduction = $func;
+ return;
+ }
+
+ $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
+ $u = bcdiv($lhs, $m, 0);
+ $m1 = bcsub($lhs, bcmul($u, $m, 0), 0);
+
+ $cutoff = $m_length + ($m_length >> 1);
+
+ $m = "'$m'";
+ $u = "'$u'";
+ $m1 = "'$m1'";
+
+ $code = '
+ $lsd = substr($n, -' . $cutoff . ');
+ $msd = substr($n, 0, -' . $cutoff . ');
+
+ $temp = bcmul($msd, ' . $m1 . ', 0);
+ $n = bcadd($lsd, $temp, 0);
+
+ $temp = substr($n, 0, ' . (-$m_length + 1) . ');
+ $temp = bcmul($temp, ' . $u . ', 0);
+ $temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . ');
+ $temp = bcmul($temp, ' . $m . ', 0);
+
+ $result = bcsub($n, $temp, 0);
+
+ if ($result[0] == \'-\') {
+ $temp = \'1' . str_repeat('0', $m_length + 1) . '\';
+ $result = bcadd($result, $temp, 0);
+ }
+
+ while (bccomp($result, ' . $m . ') >= 0) {
+ $result = bcsub($result, ' . $m . ', 0);
+ }
+
+ return $result;';
+
+ eval('$func = function ($n) { ' . $code . '};');
+
+ self::$custom_reduction = $func;
+
+ return $func;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php
new file mode 100644
index 000000000..1892042c5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php
@@ -0,0 +1,1293 @@
+<?php
+
+/**
+ * Base BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * Base Engine.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Engine implements \JsonSerializable
+{
+ /* final protected */ const PRIMES = [
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
+ 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
+ 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
+ 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
+ 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
+ 953, 967, 971, 977, 983, 991, 997,
+ ];
+
+ /**
+ * BigInteger(0)
+ *
+ * @var array<class-string<static>, static>
+ */
+ protected static $zero = [];
+
+ /**
+ * BigInteger(1)
+ *
+ * @var array<class-string<static>, static>
+ */
+ protected static $one = [];
+
+ /**
+ * BigInteger(2)
+ *
+ * @var array<class-string<static>, static>
+ */
+ protected static $two = [];
+
+ /**
+ * Modular Exponentiation Engine
+ *
+ * @var array<class-string<static>, class-string<static>>
+ */
+ protected static $modexpEngine;
+
+ /**
+ * Engine Validity Flag
+ *
+ * @var array<class-string<static>, bool>
+ */
+ protected static $isValidEngine;
+
+ /**
+ * Holds the BigInteger's value
+ *
+ * @var \GMP|string|array|int
+ */
+ protected $value;
+
+ /**
+ * Holds the BigInteger's sign
+ *
+ * @var bool
+ */
+ protected $is_negative;
+
+ /**
+ * Precision
+ *
+ * @see static::setPrecision()
+ * @var int
+ */
+ protected $precision = -1;
+
+ /**
+ * Precision Bitmask
+ *
+ * @see static::setPrecision()
+ * @var static|false
+ */
+ protected $bitmask = false;
+
+ /**
+ * Recurring Modulo Function
+ *
+ * @var callable
+ */
+ protected $reduce;
+
+ /**
+ * Mode independent value used for serialization.
+ *
+ * @see self::__sleep()
+ * @see self::__wakeup()
+ * @var string
+ */
+ protected $hex;
+
+ /**
+ * Default constructor
+ *
+ * @param int|numeric-string $x integer Base-10 number or base-$base number if $base set.
+ * @param int $base
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!array_key_exists(static::class, static::$zero)) {
+ static::$zero[static::class] = null; // Placeholder to prevent infinite loop.
+ static::$zero[static::class] = new static(0);
+ static::$one[static::class] = new static(1);
+ static::$two[static::class] = new static(2);
+ }
+
+ // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
+ // '0' is the only value like this per http://php.net/empty
+ if (empty($x) && (abs($base) != 256 || $x !== '0')) {
+ return;
+ }
+
+ switch ($base) {
+ case -256:
+ case 256:
+ if ($base == -256 && (ord($x[0]) & 0x80)) {
+ $this->value = ~$x;
+ $this->is_negative = true;
+ } else {
+ $this->value = $x;
+ $this->is_negative = false;
+ }
+
+ $this->initialize($base);
+
+ if ($this->is_negative) {
+ $temp = $this->add(new static('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case -16:
+ case 16:
+ if ($base > 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x);
+
+ $is_negative = false;
+ if ($base < 0 && hexdec($x[0]) >= 8) {
+ $this->is_negative = $is_negative = true;
+ $x = Strings::bin2hex(~Strings::hex2bin($x));
+ }
+
+ $this->value = $x;
+ $this->initialize($base);
+
+ if ($is_negative) {
+ $temp = $this->add(new static('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case -10:
+ case 10:
+ // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
+ // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
+ // [^-0-9].*: find any non-numeric characters and then any characters that follow that
+ $this->value = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s', '', $x);
+ if (!strlen($this->value) || $this->value == '-') {
+ $this->value = '0';
+ }
+ $this->initialize($base);
+ break;
+ case -2:
+ case 2:
+ if ($base > 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^([01]*).*#s', '$1', $x);
+
+ $temp = new static(Strings::bits2bin($x), 128 * $base); // ie. either -16 or +16
+ $this->value = $temp->value;
+ if ($temp->is_negative) {
+ $this->is_negative = true;
+ }
+
+ break;
+ default:
+ // base not supported, so we'll let $this == 0
+ }
+ }
+
+ /**
+ * Sets engine type.
+ *
+ * Throws an exception if the type is invalid
+ *
+ * @param class-string<Engine> $engine
+ */
+ public static function setModExpEngine($engine)
+ {
+ $fqengine = '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine;
+ if (!class_exists($fqengine) || !method_exists($fqengine, 'isValidEngine')) {
+ throw new \InvalidArgumentException("$engine is not a valid engine");
+ }
+ if (!$fqengine::isValidEngine()) {
+ throw new BadConfigurationException("$engine is not setup correctly on this system");
+ }
+ static::$modexpEngine[static::class] = $fqengine;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ * @return string
+ */
+ protected function toBytesHelper()
+ {
+ $comparison = $this->compare(new static());
+ if ($comparison == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
+ $bytes = $temp->toBytes();
+
+ if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
+ $bytes = chr(0);
+ }
+
+ if (ord($bytes[0]) & 0x80) {
+ $bytes = chr(0) . $bytes;
+ }
+
+ return $comparison < 0 ? ~$bytes : $bytes;
+ }
+
+ /**
+ * Converts a BigInteger to a hex string (eg. base-16).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toHex($twos_compliment = false)
+ {
+ return Strings::bin2hex($this->toBytes($twos_compliment));
+ }
+
+ /**
+ * Converts a BigInteger to a bit string (eg. base-2).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBits($twos_compliment = false)
+ {
+ $hex = $this->toBytes($twos_compliment);
+ $bits = Strings::bin2bits($hex);
+
+ $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
+
+ if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
+ return '0' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * {@internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.}
+ *
+ * @param Engine $n
+ * @return static|false
+ */
+ protected function modInverseHelper(Engine $n)
+ {
+ // $x mod -$n == $x mod $n.
+ $n = $n->abs();
+
+ if ($this->compare(static::$zero[static::class]) < 0) {
+ $temp = $this->abs();
+ $temp = $temp->modInverse($n);
+ return $this->normalize($n->subtract($temp));
+ }
+
+ $extended = $this->extendedGCD($n);
+ $gcd = $extended['gcd'];
+ $x = $extended['x'];
+
+ if (!$gcd->equals(static::$one[static::class])) {
+ return false;
+ }
+
+ $x = $x->compare(static::$zero[static::class]) < 0 ? $x->add($n) : $x;
+
+ return $this->compare(static::$zero[static::class]) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x);
+ }
+
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when serialize() is called on a BigInteger object.
+ *
+ * @return array
+ */
+ public function __sleep()
+ {
+ $this->hex = $this->toHex(true);
+ $vars = ['hex'];
+ if ($this->precision > 0) {
+ $vars[] = 'precision';
+ }
+ return $vars;
+ }
+
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when unserialize() is called on a BigInteger object.
+ *
+ * @return void
+ */
+ public function __wakeup()
+ {
+ $temp = new static($this->hex, -16);
+ $this->value = $temp->value;
+ $this->is_negative = $temp->is_negative;
+ if ($this->precision > 0) {
+ // recalculate $this->bitmask
+ $this->setPrecision($this->precision);
+ }
+ }
+
+ /**
+ * JSON Serialize
+ *
+ * Will be called, automatically, when json_encode() is called on a BigInteger object.
+ *
+ * @return array{hex: string, precision?: int]
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ $result = ['hex' => $this->toHex(true)];
+ if ($this->precision > 0) {
+ $result['precision'] = $this->precision;
+ }
+ return $result;
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * __debugInfo() magic method
+ *
+ * Will be called, automatically, when print_r() or var_dump() are called
+ *
+ * @return array
+ */
+ public function __debugInfo()
+ {
+ $result = [
+ 'value' => '0x' . $this->toHex(true),
+ 'engine' => basename(static::class)
+ ];
+ return $this->precision > 0 ? $result + ['precision' => $this->precision] : $result;
+ }
+
+ /**
+ * Set Precision
+ *
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
+ * shift, not, and rotates.
+ *
+ * @param int $bits
+ */
+ public function setPrecision($bits)
+ {
+ if ($bits < 1) {
+ $this->precision = -1;
+ $this->bitmask = false;
+
+ return;
+ }
+ $this->precision = $bits;
+ $this->bitmask = static::setBitmask($bits);
+
+ $temp = $this->normalize($this);
+ $this->value = $temp->value;
+ }
+
+ /**
+ * Get Precision
+ *
+ * Returns the precision if it exists, -1 if it doesn't
+ *
+ * @return int
+ */
+ public function getPrecision()
+ {
+ return $this->precision;
+ }
+
+ /**
+ * Set Bitmask
+ * @return static
+ * @param int $bits
+ * @see self::setPrecision()
+ */
+ protected static function setBitmask($bits)
+ {
+ return new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
+ }
+
+ /**
+ * Logical Not
+ *
+ * @return Engine|string
+ */
+ public function bitwise_not()
+ {
+ // calculuate "not" without regard to $this->precision
+ // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
+ $temp = $this->toBytes();
+ if ($temp == '') {
+ return $this->normalize(static::$zero[static::class]);
+ }
+ $pre_msb = decbin(ord($temp[0]));
+ $temp = ~$temp;
+ $msb = decbin(ord($temp[0]));
+ if (strlen($msb) == 8) {
+ $msb = substr($msb, strpos($msb, '0'));
+ }
+ $temp[0] = chr(bindec($msb));
+
+ // see if we need to add extra leading 1's
+ $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
+ $new_bits = $this->precision - $current_bits;
+ if ($new_bits <= 0) {
+ return $this->normalize(new static($temp, 256));
+ }
+
+ // generate as many leading 1's as we need to.
+ $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
+
+ self::base256_lshift($leading_ones, $current_bits);
+
+ $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($leading_ones | $temp, 256));
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
+ *
+ * @param string $x
+ * @param int $shift
+ * @return void
+ */
+ protected static function base256_lshift(&$x, $shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
+ $shift &= 7; // eg. $shift % 8
+
+ $carry = 0;
+ for ($i = strlen($x) - 1; $i >= 0; --$i) {
+ $temp = ord($x[$i]) << $shift | $carry;
+ $x[$i] = chr($temp);
+ $carry = $temp >> 8;
+ }
+ $carry = ($carry != 0) ? chr($carry) : '';
+ $x = $carry . $x . str_repeat(chr(0), $num_bytes);
+ }
+
+ /**
+ * Logical Left Rotate
+ *
+ * Instead of the top x bits being dropped they're appended to the shifted bit string.
+ *
+ * @param int $shift
+ * @return Engine
+ */
+ public function bitwise_leftRotate($shift)
+ {
+ $bits = $this->toBytes();
+
+ if ($this->precision > 0) {
+ $precision = $this->precision;
+ if (static::FAST_BITWISE) {
+ $mask = $this->bitmask->toBytes();
+ } else {
+ $mask = $this->bitmask->subtract(new static(1));
+ $mask = $mask->toBytes();
+ }
+ } else {
+ $temp = ord($bits[0]);
+ for ($i = 0; $temp >> $i; ++$i) {
+ }
+ $precision = 8 * strlen($bits) - 8 + $i;
+ $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
+ }
+
+ if ($shift < 0) {
+ $shift += $precision;
+ }
+ $shift %= $precision;
+
+ if (!$shift) {
+ return clone $this;
+ }
+
+ $left = $this->bitwise_leftShift($shift);
+ $left = $left->bitwise_and(new static($mask, 256));
+ $right = $this->bitwise_rightShift($precision - $shift);
+ $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right);
+ return $this->normalize($result);
+ }
+
+ /**
+ * Logical Right Rotate
+ *
+ * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
+ *
+ * @param int $shift
+ * @return Engine
+ */
+ public function bitwise_rightRotate($shift)
+ {
+ return $this->bitwise_leftRotate(-$shift);
+ }
+
+ /**
+ * Returns the smallest and largest n-bit number
+ *
+ * @param int $bits
+ * @return array{min: static, max: static}
+ */
+ public static function minMaxBits($bits)
+ {
+ $bytes = $bits >> 3;
+ $min = str_repeat(chr(0), $bytes);
+ $max = str_repeat(chr(0xFF), $bytes);
+ $msb = $bits & 7;
+ if ($msb) {
+ $min = chr(1 << ($msb - 1)) . $min;
+ $max = chr((1 << $msb) - 1) . $max;
+ } else {
+ $min[0] = chr(0x80);
+ }
+ return [
+ 'min' => new static($min, 256),
+ 'max' => new static($max, 256)
+ ];
+ }
+
+ /**
+ * Return the size of a BigInteger in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return strlen($this->toBits());
+ }
+
+ /**
+ * Return the size of a BigInteger in bytes
+ *
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return (int) ceil($this->getLength() / 8);
+ }
+
+ /**
+ * Performs some pre-processing for powMod
+ *
+ * @param Engine $e
+ * @param Engine $n
+ * @return static|false
+ */
+ protected function powModOuter(Engine $e, Engine $n)
+ {
+ $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
+
+ if ($e->compare(new static()) < 0) {
+ $e = $e->abs();
+
+ $temp = $this->modInverse($n);
+ if ($temp === false) {
+ return false;
+ }
+
+ return $this->normalize($temp->powModInner($e, $n));
+ }
+
+ if ($this->compare($n) > 0 || $this->isNegative()) {
+ list(, $temp) = $this->divide($n);
+ return $temp->powModInner($e, $n);
+ }
+
+ return $this->powModInner($e, $n);
+ }
+
+ /**
+ * Sliding Window k-ary Modular Exponentiation
+ *
+ * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
+ * however, this function performs a modular reduction after every multiplication and squaring operation.
+ * As such, this function has the same preconditions that the reductions being used do.
+ *
+ * @template T of Engine
+ * @param Engine $x
+ * @param Engine $e
+ * @param Engine $n
+ * @param class-string<T> $class
+ * @return T
+ */
+ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class)
+ {
+ static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function
+ //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1
+
+ $e_bits = $e->toBits();
+ $e_length = strlen($e_bits);
+
+ // calculate the appropriate window size.
+ // $window_size == 3 if $window_ranges is between 25 and 81, for example.
+ for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
+ }
+
+ $n_value = $n->value;
+
+ if (method_exists(static::class, 'generateCustomReduction')) {
+ static::generateCustomReduction($n, $class);
+ }
+
+ // precompute $this^0 through $this^$window_size
+ $powers = [];
+ $powers[1] = static::prepareReduce($x->value, $n_value, $class);
+ $powers[2] = static::squareReduce($powers[1], $n_value, $class);
+
+ // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
+ // in a 1. ie. it's supposed to be odd.
+ $temp = 1 << ($window_size - 1);
+ for ($i = 1; $i < $temp; ++$i) {
+ $i2 = $i << 1;
+ $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class);
+ }
+
+ $result = new $class(1);
+ $result = static::prepareReduce($result->value, $n_value, $class);
+
+ for ($i = 0; $i < $e_length;) {
+ if (!$e_bits[$i]) {
+ $result = static::squareReduce($result, $n_value, $class);
+ ++$i;
+ } else {
+ for ($j = $window_size - 1; $j > 0; --$j) {
+ if (!empty($e_bits[$i + $j])) {
+ break;
+ }
+ }
+
+ // eg. the length of substr($e_bits, $i, $j + 1)
+ for ($k = 0; $k <= $j; ++$k) {
+ $result = static::squareReduce($result, $n_value, $class);
+ }
+
+ $result = static::multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class);
+
+ $i += $j + 1;
+ }
+ }
+
+ $temp = new $class();
+ $temp->value = static::reduce($result, $n_value, $class);
+
+ return $temp;
+ }
+
+ /**
+ * Generates a random number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return Engine
+ */
+ public static function random($size)
+ {
+ $minMax = static::minMaxBits($size);
+ $min = $minMax['min'];
+ $max = $minMax['max'];
+ return static::randomRange($min, $max);
+ }
+
+ /**
+ * Generates a random prime number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return Engine
+ */
+ public static function randomPrime($size)
+ {
+ $minMax = static::minMaxBits($size);
+ $min = $minMax['min'];
+ $max = $minMax['max'];
+ return static::randomRangePrime($min, $max);
+ }
+
+ /**
+ * Performs some pre-processing for randomRangePrime
+ *
+ * @param Engine $min
+ * @param Engine $max
+ * @return static|false
+ */
+ protected static function randomRangePrimeOuter(Engine $min, Engine $max)
+ {
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $min->isPrime() ? $min : false;
+ } elseif ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ $length = $max->getLength();
+ if ($length > 8196) {
+ throw new \RuntimeException("Generation of random prime numbers larger than 8196 has been disabled ($length)");
+ }
+
+ $x = static::randomRange($min, $max);
+
+ return static::randomRangePrimeInner($x, $min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @param Engine $min
+ * @param Engine $max
+ * @return Engine
+ */
+ protected static function randomRangeHelper(Engine $min, Engine $max)
+ {
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $min;
+ } elseif ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ if (!isset(static::$one[static::class])) {
+ static::$one[static::class] = new static(1);
+ }
+
+ $max = $max->subtract($min->subtract(static::$one[static::class]));
+
+ $size = strlen(ltrim($max->toBytes(), chr(0)));
+
+ /*
+ doing $random % $max doesn't work because some numbers will be more likely to occur than others.
+ eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
+ would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
+ not all numbers would be equally likely. some would be more likely than others.
+
+ creating a whole new random number until you find one that is within the range doesn't work
+ because, for sufficiently small ranges, the likelihood that you'd get a number within that range
+ would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
+ would be pretty high that $random would be greater than $max.
+
+ phpseclib works around this using the technique described here:
+
+ http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
+ */
+ $random_max = new static(chr(1) . str_repeat("\0", $size), 256);
+ $random = new static(Random::string($size), 256);
+
+ list($max_multiple) = $random_max->divide($max);
+ $max_multiple = $max_multiple->multiply($max);
+
+ while ($random->compare($max_multiple) >= 0) {
+ $random = $random->subtract($max_multiple);
+ $random_max = $random_max->subtract($max_multiple);
+ $random = $random->bitwise_leftShift(8);
+ $random = $random->add(new static(Random::string(1), 256));
+ $random_max = $random_max->bitwise_leftShift(8);
+ list($max_multiple) = $random_max->divide($max);
+ $max_multiple = $max_multiple->multiply($max);
+ }
+ list(, $random) = $random->divide($max);
+
+ return $random->add($min);
+ }
+
+ /**
+ * Performs some post-processing for randomRangePrime
+ *
+ * @param Engine $x
+ * @param Engine $min
+ * @param Engine $max
+ * @return static|false
+ */
+ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
+ {
+ if (!isset(static::$two[static::class])) {
+ static::$two[static::class] = new static('2');
+ }
+
+ $x->make_odd();
+ if ($x->compare($max) > 0) {
+ // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
+ if ($min->equals($max)) {
+ return false;
+ }
+ $x = clone $min;
+ $x->make_odd();
+ }
+
+ $initial_x = clone $x;
+
+ while (true) {
+ if ($x->isPrime()) {
+ return $x;
+ }
+
+ $x = $x->add(static::$two[static::class]);
+
+ if ($x->compare($max) > 0) {
+ $x = clone $min;
+ if ($x->equals(static::$two[static::class])) {
+ return $x;
+ }
+ $x->make_odd();
+ }
+
+ if ($x->equals($initial_x)) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Sets the $t parameter for primality testing
+ *
+ * @return int
+ */
+ protected function setupIsPrime()
+ {
+ $length = $this->getLengthInBytes();
+
+ // see HAC 4.49 "Note (controlling the error probability)"
+ // @codingStandardsIgnoreStart
+ if ($length >= 163) { $t = 2; } // floor(1300 / 8)
+ else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
+ else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
+ else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
+ else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
+ else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
+ else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
+ else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
+ else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
+ else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
+ else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
+ else { $t = 27; }
+ // @codingStandardsIgnoreEnd
+
+ return $t;
+ }
+
+ /**
+ * Tests Primality
+ *
+ * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}.
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info.
+ *
+ * @param int $t
+ * @return bool
+ */
+ protected function testPrimality($t)
+ {
+ if (!$this->testSmallPrimes()) {
+ return false;
+ }
+
+ $n = clone $this;
+ $n_1 = $n->subtract(static::$one[static::class]);
+ $n_2 = $n->subtract(static::$two[static::class]);
+
+ $r = clone $n_1;
+ $s = static::scan1divide($r);
+
+ for ($i = 0; $i < $t; ++$i) {
+ $a = static::randomRange(static::$two[static::class], $n_2);
+ $y = $a->modPow($r, $n);
+
+ if (!$y->equals(static::$one[static::class]) && !$y->equals($n_1)) {
+ for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
+ $y = $y->modPow(static::$two[static::class], $n);
+ if ($y->equals(static::$one[static::class])) {
+ return false;
+ }
+ }
+
+ if (!$y->equals($n_1)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks a numer to see if it's prime
+ *
+ * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
+ * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
+ * on a website instead of just one.
+ *
+ * @param int|bool $t
+ * @return bool
+ */
+ public function isPrime($t = false)
+ {
+ // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is
+ // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is
+ // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest
+ // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on
+ $length = $this->getLength();
+ if ($length > 8196) {
+ throw new \RuntimeException("Primality testing is not supported for numbers larger than 8196 bits ($length)");
+ }
+
+ if (!$t) {
+ $t = $this->setupIsPrime();
+ }
+ return $this->testPrimality($t);
+ }
+
+ /**
+ * Performs a few preliminary checks on root
+ *
+ * @param int $n
+ * @return Engine
+ */
+ protected function rootHelper($n)
+ {
+ if ($n < 1) {
+ return clone static::$zero[static::class];
+ } // we want positive exponents
+ if ($this->compare(static::$one[static::class]) < 0) {
+ return clone static::$zero[static::class];
+ } // we want positive numbers
+ if ($this->compare(static::$two[static::class]) < 0) {
+ return clone static::$one[static::class];
+ } // n-th root of 1 or 2 is 1
+
+ return $this->rootInner($n);
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * Returns the nth root of a positive biginteger, where n defaults to 2
+ *
+ * {@internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.}
+ *
+ * @param int $n
+ * @return Engine
+ */
+ protected function rootInner($n)
+ {
+ $n = new static($n);
+
+ // g is our guess number
+ $g = static::$two[static::class];
+ // while (g^n < num) g=g*2
+ while ($g->pow($n)->compare($this) < 0) {
+ $g = $g->multiply(static::$two[static::class]);
+ }
+ // if (g^n==num) num is a power of 2, we're lucky, end of job
+ // == 0 bccomp(bcpow($g, $n), $n->value)==0
+ if ($g->pow($n)->equals($this) > 0) {
+ $root = $g;
+ return $this->normalize($root);
+ }
+
+ // if we're here num wasn't a power of 2 :(
+ $og = $g; // og means original guess and here is our upper bound
+ $g = $g->divide(static::$two[static::class])[0]; // g is set to be our lower bound
+ $step = $og->subtract($g)->divide(static::$two[static::class])[0]; // step is the half of upper bound - lower bound
+ $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval
+
+ // while step>1
+
+ while ($step->compare(static::$one[static::class]) == 1) {
+ $guess = $g->pow($n);
+ $step = $step->divide(static::$two[static::class])[0];
+ $comp = $guess->compare($this); // compare our guess with real number
+ switch ($comp) {
+ case -1: // if guess is lower we add the new step
+ $g = $g->add($step);
+ break;
+ case 1: // if guess is higher we sub the new step
+ $g = $g->subtract($step);
+ break;
+ case 0: // if guess is exactly the num we're done, we return the value
+ $root = $g;
+ break 2;
+ }
+ }
+
+ if ($comp == 1) {
+ $g = $g->subtract($step);
+ }
+
+ // whatever happened, g is the closest guess we can make so return it
+ $root = $g;
+
+ return $this->normalize($root);
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * @param int $n
+ * @return Engine
+ */
+ public function root($n = 2)
+ {
+ return $this->rootHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param array $nums
+ * @return Engine
+ */
+ protected static function minHelper(array $nums)
+ {
+ if (count($nums) == 1) {
+ return $nums[0];
+ }
+ $min = $nums[0];
+ for ($i = 1; $i < count($nums); $i++) {
+ $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min;
+ }
+ return $min;
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param array $nums
+ * @return Engine
+ */
+ protected static function maxHelper(array $nums)
+ {
+ if (count($nums) == 1) {
+ return $nums[0];
+ }
+ $max = $nums[0];
+ for ($i = 1; $i < count($nums); $i++) {
+ $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max;
+ }
+ return $max;
+ }
+
+ /**
+ * Create Recurring Modulo Function
+ *
+ * Sometimes it may be desirable to do repeated modulos with the same number outside of
+ * modular exponentiation
+ *
+ * @return callable
+ */
+ public function createRecurringModuloFunction()
+ {
+ $class = static::class;
+
+ $fqengine = !method_exists(static::$modexpEngine[static::class], 'reduce') ?
+ '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' :
+ static::$modexpEngine[static::class];
+ if (method_exists($fqengine, 'generateCustomReduction')) {
+ $func = $fqengine::generateCustomReduction($this, static::class);
+ return eval('return function(' . static::class . ' $x) use ($func, $class) {
+ $r = new $class();
+ $r->value = $func($x->value);
+ return $r;
+ };');
+ }
+ $n = $this->value;
+ return eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) {
+ $r = new $class();
+ $r->value = $fqengine::reduce($x->value, $n, $class);
+ return $r;
+ };');
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * @param Engine $n
+ * @return array{gcd: Engine, x: Engine, y: Engine}
+ */
+ protected function extendedGCDHelper(Engine $n)
+ {
+ $u = clone $this;
+ $v = clone $n;
+
+ $one = new static(1);
+ $zero = new static();
+
+ $a = clone $one;
+ $b = clone $zero;
+ $c = clone $zero;
+ $d = clone $one;
+
+ while (!$v->equals($zero)) {
+ list($q) = $u->divide($v);
+
+ $temp = $u;
+ $u = $v;
+ $v = $temp->subtract($v->multiply($q));
+
+ $temp = $a;
+ $a = $c;
+ $c = $temp->subtract($a->multiply($q));
+
+ $temp = $b;
+ $b = $d;
+ $d = $temp->subtract($b->multiply($q));
+ }
+
+ return [
+ 'gcd' => $u,
+ 'x' => $a,
+ 'y' => $b
+ ];
+ }
+
+ /**
+ * Bitwise Split
+ *
+ * Splits BigInteger's into chunks of $split bits
+ *
+ * @param int $split
+ * @return Engine[]
+ */
+ public function bitwise_split($split)
+ {
+ if ($split < 1) {
+ throw new \RuntimeException('Offset must be greater than 1');
+ }
+
+ $mask = static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]);
+
+ $num = clone $this;
+
+ $vals = [];
+ while (!$num->equals(static::$zero[static::class])) {
+ $vals[] = $num->bitwise_and($mask);
+ $num = $num->bitwise_rightShift($split);
+ }
+
+ return array_reverse($vals);
+ }
+
+ /**
+ * Logical And
+ *
+ * @param Engine $x
+ * @return Engine
+ */
+ protected function bitwiseAndHelper(Engine $x)
+ {
+ $left = $this->toBytes(true);
+ $right = $x->toBytes(true);
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($left & $right, -256));
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param Engine $x
+ * @return Engine
+ */
+ protected function bitwiseOrHelper(Engine $x)
+ {
+ $left = $this->toBytes(true);
+ $right = $x->toBytes(true);
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($left | $right, -256));
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @param Engine $x
+ * @return Engine
+ */
+ protected function bitwiseXorHelper(Engine $x)
+ {
+ $left = $this->toBytes(true);
+ $right = $x->toBytes(true);
+
+ $length = max(strlen($left), strlen($right));
+
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+ return $this->normalize(new static($left ^ $right, -256));
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php
new file mode 100644
index 000000000..0db43ae63
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP.php
@@ -0,0 +1,697 @@
+<?php
+
+/**
+ * GMP BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+use phpseclib3\Exception\BadConfigurationException;
+
+/**
+ * GMP Engine.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class GMP extends Engine
+{
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ */
+ const FAST_BITWISE = true;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ */
+ const ENGINE_DIR = 'GMP';
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ * @see parent::__construct()
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('gmp');
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param mixed $x integer Base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @see parent::__construct()
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(static::$isValidEngine[static::class])) {
+ static::$isValidEngine[static::class] = self::isValidEngine();
+ }
+ if (!static::$isValidEngine[static::class]) {
+ throw new BadConfigurationException('GMP is not setup correctly on this system');
+ }
+
+ if ($x instanceof \GMP) {
+ $this->value = $x;
+ return;
+ }
+
+ $this->value = gmp_init(0);
+
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a GMP BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 256:
+ $this->value = gmp_import($this->value);
+ if ($this->is_negative) {
+ $this->value = -$this->value;
+ }
+ break;
+ case 16:
+ $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value;
+ $this->value = gmp_init($temp);
+ break;
+ case 10:
+ $this->value = gmp_init(isset($this->value) ? $this->value : '0');
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return (string)$this->value;
+ }
+
+ /**
+ * Converts a BigInteger to a bit string (eg. base-2).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBits($twos_compliment = false)
+ {
+ $hex = $this->toHex($twos_compliment);
+
+ $bits = gmp_strval(gmp_init($hex, 16), 2);
+
+ if ($this->precision > 0) {
+ $bits = substr($bits, -$this->precision);
+ }
+
+ if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
+ return '0' . $bits;
+ }
+
+ return $bits;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ if (gmp_cmp($this->value, gmp_init(0)) == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = gmp_export($this->value);
+
+ return $this->precision > 0 ?
+ substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($temp, chr(0));
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @param GMP $y
+ * @return GMP
+ */
+ public function add(GMP $y)
+ {
+ $temp = new self();
+ $temp->value = $this->value + $y->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @param GMP $y
+ * @return GMP
+ */
+ public function subtract(GMP $y)
+ {
+ $temp = new self();
+ $temp->value = $this->value - $y->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @param GMP $x
+ * @return GMP
+ */
+ public function multiply(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value * $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @param GMP $y
+ * @return array{GMP, GMP}
+ */
+ public function divide(GMP $y)
+ {
+ $quotient = new self();
+ $remainder = new self();
+
+ list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
+
+ if (gmp_sign($remainder->value) < 0) {
+ $remainder->value = $remainder->value + gmp_abs($y->value);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this
+ * is demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
+ *
+ * @param GMP $y
+ * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
+ */
+ public function compare(GMP $y)
+ {
+ $r = gmp_cmp($this->value, $y->value);
+ if ($r < -1) {
+ $r = -1;
+ }
+ if ($r > 1) {
+ $r = 1;
+ }
+ return $r;
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @param GMP $x
+ * @return bool
+ */
+ public function equals(GMP $x)
+ {
+ return $this->value == $x->value;
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * @param GMP $n
+ * @return false|GMP
+ */
+ public function modInverse(GMP $n)
+ {
+ $temp = new self();
+ $temp->value = gmp_invert($this->value, $n->value);
+
+ return $temp->value === false ? false : $this->normalize($temp);
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependent upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ *
+ * @param GMP $n
+ * @return GMP[]
+ */
+ public function extendedGCD(GMP $n)
+ {
+ $extended = gmp_gcdext($this->value, $n->value);
+ $g = $extended['g'];
+ $s = $extended['s'];
+ $t = $extended['t'];
+
+ return [
+ 'gcd' => $this->normalize(new self($g)),
+ 'x' => $this->normalize(new self($s)),
+ 'y' => $this->normalize(new self($t))
+ ];
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @param GMP $n
+ * @return GMP
+ */
+ public function gcd(GMP $n)
+ {
+ $r = gmp_gcd($this->value, $n->value);
+ return $this->normalize(new self($r));
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return GMP
+ */
+ public function abs()
+ {
+ $temp = new self();
+ $temp->value = gmp_abs($this->value);
+
+ return $temp;
+ }
+
+ /**
+ * Logical And
+ *
+ * @param GMP $x
+ * @return GMP
+ */
+ public function bitwise_and(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value & $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param GMP $x
+ * @return GMP
+ */
+ public function bitwise_or(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value | $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @param GMP $x
+ * @return GMP
+ */
+ public function bitwise_xor(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value ^ $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return GMP
+ */
+ public function bitwise_rightShift($shift)
+ {
+ // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems)
+ // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF')
+
+ $temp = new self();
+ $temp->value = $this->value >> $shift;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return GMP
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new self();
+ $temp->value = $this->value << $shift;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param GMP $e
+ * @param GMP $n
+ * @return GMP
+ */
+ public function modPow(GMP $e, GMP $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @param GMP $e
+ * @param GMP $n
+ * @return GMP
+ */
+ public function powMod(GMP $e, GMP $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param GMP $e
+ * @param GMP $n
+ * @return GMP
+ */
+ protected function powModInner(GMP $e, GMP $n)
+ {
+ $class = static::$modexpEngine[static::class];
+ return $class::powModHelper($this, $e, $n);
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @param GMP $result
+ * @return GMP
+ */
+ protected function normalize(GMP $result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ if ($result->bitmask !== false) {
+ $flip = $result->value < 0;
+ if ($flip) {
+ $result->value = -$result->value;
+ }
+ $result->value = $result->value & $result->bitmask->value;
+ if ($flip) {
+ $result->value = -$result->value;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs some post-processing for randomRangePrime
+ *
+ * @param Engine $x
+ * @param Engine $min
+ * @param Engine $max
+ * @return GMP
+ */
+ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
+ {
+ $p = gmp_nextprime($x->value);
+
+ if ($p <= $max->value) {
+ return new self($p);
+ }
+
+ if ($min->value != $x->value) {
+ $x = new self($x->value - 1);
+ }
+
+ return self::randomRangePrime($min, $x);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @param GMP $min
+ * @param GMP $max
+ * @return false|GMP
+ */
+ public static function randomRangePrime(GMP $min, GMP $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @param GMP $min
+ * @param GMP $max
+ * @return GMP
+ */
+ public static function randomRange(GMP $min, GMP $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ gmp_setbit($this->value, 0);
+ }
+
+ /**
+ * Tests Primality
+ *
+ * @param int $t
+ * @return bool
+ */
+ protected function testPrimality($t)
+ {
+ return gmp_prob_prime($this->value, $t) != 0;
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * Returns the nth root of a positive biginteger, where n defaults to 2
+ *
+ * @param int $n
+ * @return GMP
+ */
+ protected function rootInner($n)
+ {
+ $root = new self();
+ $root->value = gmp_root($this->value, $n);
+ return $this->normalize($root);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @param GMP $n
+ * @return GMP
+ */
+ public function pow(GMP $n)
+ {
+ $temp = new self();
+ $temp->value = $this->value ** $n->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param GMP ...$nums
+ * @return GMP
+ */
+ public static function min(GMP ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param GMP ...$nums
+ * @return GMP
+ */
+ public static function max(GMP ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @param GMP $min
+ * @param GMP $max
+ * @return bool
+ */
+ public function between(GMP $min, GMP $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+
+ /**
+ * Create Recurring Modulo Function
+ *
+ * Sometimes it may be desirable to do repeated modulos with the same number outside of
+ * modular exponentiation
+ *
+ * @return callable
+ */
+ public function createRecurringModuloFunction()
+ {
+ $temp = $this->value;
+ return function (GMP $x) use ($temp) {
+ return new GMP($x->value % $temp);
+ };
+ }
+
+ /**
+ * Scan for 1 and right shift by that amount
+ *
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @param GMP $r
+ * @return int
+ */
+ public static function scan1divide(GMP $r)
+ {
+ $s = gmp_scan1($r->value, 0);
+ $r->value >>= $s;
+ return $s;
+ }
+
+ /**
+ * Is Odd?
+ *
+ * @return bool
+ */
+ public function isOdd()
+ {
+ return gmp_testbit($this->value, 0);
+ }
+
+ /**
+ * Tests if a bit is set
+ *
+ * @return bool
+ */
+ public function testBit($x)
+ {
+ return gmp_testbit($this->value, $x);
+ }
+
+ /**
+ * Is Negative?
+ *
+ * @return bool
+ */
+ public function isNegative()
+ {
+ return gmp_sign($this->value) == -1;
+ }
+
+ /**
+ * Negate
+ *
+ * Given $k, returns -$k
+ *
+ * @return GMP
+ */
+ public function negate()
+ {
+ $temp = clone $this;
+ $temp->value = -$this->value;
+
+ return $temp;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php
new file mode 100644
index 000000000..bc219fbee
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * GMP Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\GMP;
+
+use phpseclib3\Math\BigInteger\Engines\GMP;
+
+/**
+ * GMP Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DefaultEngine extends GMP
+{
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param GMP $x
+ * @param GMP $e
+ * @param GMP $n
+ * @return GMP
+ */
+ protected static function powModHelper(GMP $x, GMP $e, GMP $n)
+ {
+ $temp = new GMP();
+ $temp->value = gmp_powm($x->value, $e->value, $n->value);
+
+ return $x->normalize($temp);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php
new file mode 100644
index 000000000..e33a9f196
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/OpenSSL.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+use phpseclib3\Crypt\RSA\Formats\Keys\PKCS8;
+use phpseclib3\Math\BigInteger;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSL
+{
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('openssl') && static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param Engine $x
+ * @param Engine $e
+ * @param Engine $n
+ * @return Engine
+ */
+ public static function powModHelper(Engine $x, Engine $e, Engine $n)
+ {
+ if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes() > 16384) {
+ throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted');
+ }
+
+ $key = PKCS8::savePublicKey(
+ new BigInteger($n),
+ new BigInteger($e)
+ );
+
+ $plaintext = str_pad($x->toBytes(), $n->getLengthInBytes(), "\0", STR_PAD_LEFT);
+
+ // this is easily prone to failure. if the modulo is a multiple of 2 or 3 or whatever it
+ // won't work and you'll get a "failure: error:0906D06C:PEM routines:PEM_read_bio:no start line"
+ // error. i suppose, for even numbers, we could do what PHP\Montgomery.php does, but then what
+ // about odd numbers divisible by 3, by 5, etc?
+ if (!openssl_public_encrypt($plaintext, $result, $key, OPENSSL_NO_PADDING)) {
+ throw new \UnexpectedValueException(openssl_error_string());
+ }
+
+ $class = get_class($x);
+ return new $class($result, 256);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php
new file mode 100644
index 000000000..de556a3b2
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php
@@ -0,0 +1,1360 @@
+<?php
+
+/**
+ * Pure-PHP BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\BadConfigurationException;
+
+/**
+ * Pure-PHP Engine.
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PHP extends Engine
+{
+ /**#@+
+ * Array constants
+ *
+ * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
+ *
+ */
+ /**
+ * $result[self::VALUE] contains the value.
+ */
+ const VALUE = 0;
+ /**
+ * $result[self::SIGN] contains the sign.
+ */
+ const SIGN = 1;
+ /**#@-*/
+
+ /**
+ * Karatsuba Cutoff
+ *
+ * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
+ *
+ */
+ const KARATSUBA_CUTOFF = 25;
+
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ */
+ const FAST_BITWISE = true;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ */
+ const ENGINE_DIR = 'PHP';
+
+ /**
+ * Default constructor
+ *
+ * @param mixed $x integer Base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @return PHP
+ * @see parent::__construct()
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(static::$isValidEngine[static::class])) {
+ static::$isValidEngine[static::class] = static::isValidEngine();
+ }
+ if (!static::$isValidEngine[static::class]) {
+ throw new BadConfigurationException(static::class . ' is not setup correctly on this system');
+ }
+
+ $this->value = [];
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a PHP BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 16:
+ $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
+ $temp = new static(Strings::hex2bin($x), 256);
+ $this->value = $temp->value;
+ break;
+ case 10:
+ $temp = new static();
+
+ $multiplier = new static();
+ $multiplier->value = [static::MAX10];
+
+ $x = $this->value;
+
+ if ($x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = str_pad(
+ $x,
+ strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN,
+ 0,
+ STR_PAD_LEFT
+ );
+ while (strlen($x)) {
+ $temp = $temp->multiply($multiplier);
+ $temp = $temp->add(new static($this->int2bytes(substr($x, 0, static::MAX10LEN)), 256));
+ $x = substr($x, static::MAX10LEN);
+ }
+
+ $this->value = $temp->value;
+ }
+ }
+
+ /**
+ * Pads strings so that unpack may be used on them
+ *
+ * @param string $str
+ * @return string
+ */
+ protected function pad($str)
+ {
+ $length = strlen($str);
+
+ $pad = 4 - (strlen($str) % 4);
+
+ return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT);
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if (!count($this->value)) {
+ return '0';
+ }
+
+ $temp = clone $this;
+ $temp->bitmask = false;
+ $temp->is_negative = false;
+
+ $divisor = new static();
+ $divisor->value = [static::MAX10];
+ $result = '';
+ while (count($temp->value)) {
+ list($temp, $mod) = $temp->divide($divisor);
+ $result = str_pad(
+ isset($mod->value[0]) ? $mod->value[0] : '',
+ static::MAX10LEN,
+ '0',
+ STR_PAD_LEFT
+ ) . $result;
+ }
+ $result = ltrim($result, '0');
+ if (empty($result)) {
+ $result = '0';
+ }
+
+ if ($this->is_negative) {
+ $result = '-' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ if (!count($this->value)) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $result = $this->bitwise_small_split(8);
+ $result = implode('', array_map('chr', $result));
+
+ return $this->precision > 0 ?
+ str_pad(
+ substr($result, -(($this->precision + 7) >> 3)),
+ ($this->precision + 7) >> 3,
+ chr(0),
+ STR_PAD_LEFT
+ ) :
+ $result;
+ }
+
+ /**
+ * Performs addition.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ protected static function addHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return [
+ self::VALUE => $y_value,
+ self::SIGN => $y_negative
+ ];
+ } elseif ($y_size == 0) {
+ return [
+ self::VALUE => $x_value,
+ self::SIGN => $x_negative
+ ];
+ }
+
+ // subtract, if appropriate
+ if ($x_negative != $y_negative) {
+ if ($x_value == $y_value) {
+ return [
+ self::VALUE => [],
+ self::SIGN => false
+ ];
+ }
+
+ $temp = self::subtractHelper($x_value, false, $y_value, false);
+ $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ?
+ $x_negative : $y_negative;
+
+ return $temp;
+ }
+
+ if ($x_size < $y_size) {
+ $size = $x_size;
+ $value = $y_value;
+ } else {
+ $size = $y_size;
+ $value = $x_value;
+ }
+
+ $value[count($value)] = 0; // just in case the carry adds an extra digit
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $size; $i += 2, $j += 2) {
+ //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry;
+ $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry;
+ $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum;
+
+ $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
+
+ $value[$i] = (int)($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
+ $value[$j] = $temp;
+ }
+
+ if ($j == $size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] + $y_value[$i] + $carry;
+ $carry = $sum >= static::BASE_FULL;
+ $value[$i] = $carry ? $sum - static::BASE_FULL : $sum;
+ ++$i; // ie. let $i = $j since we've just done $value[$i]
+ }
+
+ if ($carry) {
+ for (; $value[$i] == static::MAX_DIGIT; ++$i) {
+ $value[$i] = 0;
+ }
+ ++$value[$i];
+ }
+
+ return [
+ self::VALUE => self::trim($value),
+ self::SIGN => $x_negative
+ ];
+ }
+
+ /**
+ * Performs subtraction.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ public static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return [
+ self::VALUE => $y_value,
+ self::SIGN => !$y_negative
+ ];
+ } elseif ($y_size == 0) {
+ return [
+ self::VALUE => $x_value,
+ self::SIGN => $x_negative
+ ];
+ }
+
+ // add, if appropriate (ie. -$x - +$y or +$x - -$y)
+ if ($x_negative != $y_negative) {
+ $temp = self::addHelper($x_value, false, $y_value, false);
+ $temp[self::SIGN] = $x_negative;
+
+ return $temp;
+ }
+
+ $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative);
+
+ if (!$diff) {
+ return [
+ self::VALUE => [],
+ self::SIGN => false
+ ];
+ }
+
+ // switch $x and $y around, if appropriate.
+ if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_negative = !$x_negative;
+
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+ }
+
+ // at this point, $x_value should be at least as big as - if not bigger than - $y_value
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $y_size; $i += 2, $j += 2) {
+ $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry;
+
+ $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum;
+
+ $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
+
+ $x_value[$i] = (int)($sum - static::BASE_FULL * $temp);
+ $x_value[$j] = $temp;
+ }
+
+ if ($j == $y_size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] - $y_value[$i] - $carry;
+ $carry = $sum < 0;
+ $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum;
+ ++$i;
+ }
+
+ if ($carry) {
+ for (; !$x_value[$i]; ++$i) {
+ $x_value[$i] = static::MAX_DIGIT;
+ }
+ --$x_value[$i];
+ }
+
+ return [
+ self::VALUE => self::trim($x_value),
+ self::SIGN => $x_negative
+ ];
+ }
+
+ /**
+ * Performs multiplication.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ protected static function multiplyHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ //if ( $x_value == $y_value ) {
+ // return [
+ // self::VALUE => self::square($x_value),
+ // self::SIGN => $x_sign != $y_value
+ // ];
+ //}
+
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if (!$x_length || !$y_length) { // a 0 is being multiplied
+ return [
+ self::VALUE => [],
+ self::SIGN => false
+ ];
+ }
+
+ return [
+ self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
+ self::trim(self::regularMultiply($x_value, $y_value)) :
+ self::trim(self::karatsuba($x_value, $y_value)),
+ self::SIGN => $x_negative != $y_negative
+ ];
+ }
+
+ /**
+ * Performs Karatsuba multiplication on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
+ *
+ * @param array $x_value
+ * @param array $y_value
+ * @return array
+ */
+ private static function karatsuba(array $x_value, array $y_value)
+ {
+ $m = min(count($x_value) >> 1, count($y_value) >> 1);
+
+ if ($m < self::KARATSUBA_CUTOFF) {
+ return self::regularMultiply($x_value, $y_value);
+ }
+
+ $x1 = array_slice($x_value, $m);
+ $x0 = array_slice($x_value, 0, $m);
+ $y1 = array_slice($y_value, $m);
+ $y0 = array_slice($y_value, 0, $m);
+
+ $z2 = self::karatsuba($x1, $y1);
+ $z0 = self::karatsuba($x0, $y0);
+
+ $z1 = self::addHelper($x1, false, $x0, false);
+ $temp = self::addHelper($y1, false, $y0, false);
+ $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]);
+ $temp = self::addHelper($z2, false, $z0, false);
+ $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
+
+ $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
+ $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false);
+
+ return $xy[self::VALUE];
+ }
+
+ /**
+ * Performs long multiplication on two BigIntegers
+ *
+ * Modeled after 'multiply' in MutableBigInteger.java.
+ *
+ * @param array $x_value
+ * @param array $y_value
+ * @return array
+ */
+ protected static function regularMultiply(array $x_value, array $y_value)
+ {
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if (!$x_length || !$y_length) { // a 0 is being multiplied
+ return [];
+ }
+
+ $product_value = self::array_repeat(0, $x_length + $y_length);
+
+ // the following for loop could be removed if the for loop following it
+ // (the one with nested for loops) initially set $i to 0, but
+ // doing so would also make the result in one set of unnecessary adds,
+ // since on the outermost loops first pass, $product->value[$k] is going
+ // to always be 0
+
+ $carry = 0;
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$j] = (int)($temp - static::BASE_FULL * $carry);
+ }
+
+ $product_value[$j] = $carry;
+
+ // the above for loop is what the previous comment was talking about. the
+ // following for loop is the "one with nested for loops"
+ for ($i = 1; $i < $y_length; ++$i) {
+ $carry = 0;
+
+ for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$k] = (int)($temp - static::BASE_FULL * $carry);
+ }
+
+ $product_value[$k] = $carry;
+ }
+
+ return $product_value;
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @return array{static, static}
+ * @internal This function is based off of
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
+ */
+ protected function divideHelper(PHP $y)
+ {
+ if (count($y->value) == 1) {
+ list($q, $r) = $this->divide_digit($this->value, $y->value[0]);
+ $quotient = new static();
+ $remainder = new static();
+ $quotient->value = $q;
+ if ($this->is_negative) {
+ $r = $y->value[0] - $r;
+ }
+ $remainder->value = [$r];
+ $quotient->is_negative = $this->is_negative != $y->is_negative;
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ $x = clone $this;
+ $y = clone $y;
+
+ $x_sign = $x->is_negative;
+ $y_sign = $y->is_negative;
+
+ $x->is_negative = $y->is_negative = false;
+
+ $diff = $x->compare($y);
+
+ if (!$diff) {
+ $temp = new static();
+ $temp->value = [1];
+ $temp->is_negative = $x_sign != $y_sign;
+ return [$this->normalize($temp), $this->normalize(static::$zero[static::class])];
+ }
+
+ if ($diff < 0) {
+ // if $x is negative, "add" $y.
+ if ($x_sign) {
+ $x = $y->subtract($x);
+ }
+ return [$this->normalize(static::$zero[static::class]), $this->normalize($x)];
+ }
+
+ // normalize $x and $y as described in HAC 14.23 / 14.24
+ $msb = $y->value[count($y->value) - 1];
+ for ($shift = 0; !($msb & static::MSB); ++$shift) {
+ $msb <<= 1;
+ }
+ $x->lshift($shift);
+ $y->lshift($shift);
+ $y_value = &$y->value;
+
+ $x_max = count($x->value) - 1;
+ $y_max = count($y->value) - 1;
+
+ $quotient = new static();
+ $quotient_value = &$quotient->value;
+ $quotient_value = self::array_repeat(0, $x_max - $y_max + 1);
+
+ static $temp, $lhs, $rhs;
+ if (!isset($temp)) {
+ $temp = new static();
+ $lhs = new static();
+ $rhs = new static();
+ }
+ if (static::class != get_class($temp)) {
+ $temp = new static();
+ $lhs = new static();
+ $rhs = new static();
+ }
+ $temp_value = &$temp->value;
+ $rhs_value = &$rhs->value;
+
+ // $temp = $y << ($x_max - $y_max-1) in base 2**26
+ $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value);
+
+ while ($x->compare($temp) >= 0) {
+ // calculate the "common residue"
+ ++$quotient_value[$x_max - $y_max];
+ $x = $x->subtract($temp);
+ $x_max = count($x->value) - 1;
+ }
+
+ for ($i = $x_max; $i >= $y_max + 1; --$i) {
+ $x_value = &$x->value;
+ $x_window = [
+ isset($x_value[$i]) ? $x_value[$i] : 0,
+ isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
+ isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
+ ];
+ $y_window = [
+ $y_value[$y_max],
+ ($y_max > 0) ? $y_value[$y_max - 1] : 0
+ ];
+
+ $q_index = $i - $y_max - 1;
+ if ($x_window[0] == $y_window[0]) {
+ $quotient_value[$q_index] = static::MAX_DIGIT;
+ } else {
+ $quotient_value[$q_index] = self::safe_divide(
+ $x_window[0] * static::BASE_FULL + $x_window[1],
+ $y_window[0]
+ );
+ }
+
+ $temp_value = [$y_window[1], $y_window[0]];
+
+ $lhs->value = [$quotient_value[$q_index]];
+ $lhs = $lhs->multiply($temp);
+
+ $rhs_value = [$x_window[2], $x_window[1], $x_window[0]];
+
+ while ($lhs->compare($rhs) > 0) {
+ --$quotient_value[$q_index];
+
+ $lhs->value = [$quotient_value[$q_index]];
+ $lhs = $lhs->multiply($temp);
+ }
+
+ $adjust = self::array_repeat(0, $q_index);
+ $temp_value = [$quotient_value[$q_index]];
+ $temp = $temp->multiply($y);
+ $temp_value = &$temp->value;
+ if (count($temp_value)) {
+ $temp_value = array_merge($adjust, $temp_value);
+ }
+
+ $x = $x->subtract($temp);
+
+ if ($x->compare(static::$zero[static::class]) < 0) {
+ $temp_value = array_merge($adjust, $y_value);
+ $x = $x->add($temp);
+
+ --$quotient_value[$q_index];
+ }
+
+ $x_max = count($x_value) - 1;
+ }
+
+ // unnormalize the remainder
+ $x->rshift($shift);
+
+ $quotient->is_negative = $x_sign != $y_sign;
+
+ // calculate the "common residue", if appropriate
+ if ($x_sign) {
+ $y->rshift($shift);
+ $x = $y->subtract($x);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($x)];
+ }
+
+ /**
+ * Divides a BigInteger by a regular integer
+ *
+ * abc / x = a00 / x + b0 / x + c / x
+ *
+ * @param array $dividend
+ * @param int $divisor
+ * @return array
+ */
+ private static function divide_digit(array $dividend, $divisor)
+ {
+ $carry = 0;
+ $result = [];
+
+ for ($i = count($dividend) - 1; $i >= 0; --$i) {
+ $temp = static::BASE_FULL * $carry + $dividend[$i];
+ $result[$i] = self::safe_divide($temp, $divisor);
+ $carry = (int)($temp - $divisor * $result[$i]);
+ }
+
+ return [$result, $carry];
+ }
+
+ /**
+ * Single digit division
+ *
+ * Even if int64 is being used the division operator will return a float64 value
+ * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
+ * have the precision of int64 this is a problem so, when int64 is being used,
+ * we'll guarantee that the dividend is divisible by first subtracting the remainder.
+ *
+ * @param int $x
+ * @param int $y
+ * @return int
+ */
+ private static function safe_divide($x, $y)
+ {
+ if (static::BASE === 26) {
+ return (int)($x / $y);
+ }
+
+ // static::BASE === 31
+ /** @var int */
+ return ($x - ($x % $y)) / $y;
+ }
+
+ /**
+ * Convert an array / boolean to a PHP BigInteger object
+ *
+ * @param array $arr
+ * @return static
+ */
+ protected function convertToObj(array $arr)
+ {
+ $result = new static();
+ $result->value = $arr[self::VALUE];
+ $result->is_negative = $arr[self::SIGN];
+
+ return $this->normalize($result);
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @param PHP $result
+ * @return static
+ */
+ protected function normalize(PHP $result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ $value = &$result->value;
+
+ if (!count($value)) {
+ $result->is_negative = false;
+ return $result;
+ }
+
+ $value = static::trim($value);
+
+ if (!empty($result->bitmask->value)) {
+ $length = min(count($value), count($result->bitmask->value));
+ $value = array_slice($value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $value[$i] = $value[$i] & $result->bitmask->value[$i];
+ }
+
+ $value = static::trim($value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return int
+ * @see static::compare()
+ */
+ protected static function compareHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ if ($x_negative != $y_negative) {
+ return (!$x_negative && $y_negative) ? 1 : -1;
+ }
+
+ $result = $x_negative ? -1 : 1;
+
+ if (count($x_value) != count($y_value)) {
+ return (count($x_value) > count($y_value)) ? $result : -$result;
+ }
+ $size = max(count($x_value), count($y_value));
+
+ $x_value = array_pad($x_value, $size, 0);
+ $y_value = array_pad($y_value, $size, 0);
+
+ for ($i = count($x_value) - 1; $i >= 0; --$i) {
+ if ($x_value[$i] != $y_value[$i]) {
+ return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return PHP
+ */
+ public function abs()
+ {
+ $temp = new static();
+ $temp->value = $this->value;
+
+ return $temp;
+ }
+
+ /**
+ * Trim
+ *
+ * Removes leading zeros
+ *
+ * @param list<static> $value
+ * @return list<static>
+ */
+ protected static function trim(array $value)
+ {
+ for ($i = count($value) - 1; $i >= 0; --$i) {
+ if ($value[$i]) {
+ break;
+ }
+ unset($value[$i]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return PHP
+ */
+ public function bitwise_rightShift($shift)
+ {
+ $temp = new static();
+
+ // could just replace lshift with this, but then all lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->rshift($shift);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return PHP
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new static();
+ // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->lshift($shift);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Converts 32-bit integers to bytes.
+ *
+ * @param int $x
+ * @return string
+ */
+ private static function int2bytes($x)
+ {
+ return ltrim(pack('N', $x), chr(0));
+ }
+
+ /**
+ * Array Repeat
+ *
+ * @param int $input
+ * @param int $multiplier
+ * @return array
+ */
+ protected static function array_repeat($input, $multiplier)
+ {
+ return $multiplier ? array_fill(0, $multiplier, $input) : [];
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param int $shift
+ */
+ protected function lshift($shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_digits = (int)($shift / static::BASE);
+ $shift %= static::BASE;
+ $shift = 1 << $shift;
+
+ $carry = 0;
+
+ for ($i = 0; $i < count($this->value); ++$i) {
+ $temp = $this->value[$i] * $shift + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $this->value[$i] = (int)($temp - $carry * static::BASE_FULL);
+ }
+
+ if ($carry) {
+ $this->value[count($this->value)] = $carry;
+ }
+
+ while ($num_digits--) {
+ array_unshift($this->value, 0);
+ }
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param int $shift
+ */
+ protected function rshift($shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_digits = (int)($shift / static::BASE);
+ $shift %= static::BASE;
+ $carry_shift = static::BASE - $shift;
+ $carry_mask = (1 << $shift) - 1;
+
+ if ($num_digits) {
+ $this->value = array_slice($this->value, $num_digits);
+ }
+
+ $carry = 0;
+
+ for ($i = count($this->value) - 1; $i >= 0; --$i) {
+ $temp = $this->value[$i] >> $shift | $carry;
+ $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
+ $this->value[$i] = $temp;
+ }
+
+ $this->value = static::trim($this->value);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param PHP $e
+ * @param PHP $n
+ * @return PHP
+ */
+ protected function powModInner(PHP $e, PHP $n)
+ {
+ try {
+ $class = static::$modexpEngine[static::class];
+ return $class::powModHelper($this, $e, $n, static::class);
+ } catch (\Exception $err) {
+ return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class);
+ }
+ }
+
+ /**
+ * Performs squaring
+ *
+ * @param list<static> $x
+ * @return list<static>
+ */
+ protected static function square(array $x)
+ {
+ return count($x) < 2 * self::KARATSUBA_CUTOFF ?
+ self::trim(self::baseSquare($x)) :
+ self::trim(self::karatsubaSquare($x));
+ }
+
+ /**
+ * Performs traditional squaring on two BigIntegers
+ *
+ * Squaring can be done faster than multiplying a number by itself can be. See
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
+ *
+ * @param array $value
+ * @return array
+ */
+ protected static function baseSquare(array $value)
+ {
+ if (empty($value)) {
+ return [];
+ }
+ $square_value = self::array_repeat(0, 2 * count($value));
+
+ for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
+ $i2 = $i << 1;
+
+ $temp = $square_value[$i2] + $value[$i] * $value[$i];
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $square_value[$i2] = (int)($temp - static::BASE_FULL * $carry);
+
+ // note how we start from $i+1 instead of 0 as we do in multiplication.
+ for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
+ $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $square_value[$k] = (int)($temp - static::BASE_FULL * $carry);
+ }
+
+ // the following line can yield values larger 2**15. at this point, PHP should switch
+ // over to floats.
+ $square_value[$i + $max_index + 1] = $carry;
+ }
+
+ return $square_value;
+ }
+
+ /**
+ * Performs Karatsuba "squaring" on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
+ *
+ * @param array $value
+ * @return array
+ */
+ protected static function karatsubaSquare(array $value)
+ {
+ $m = count($value) >> 1;
+
+ if ($m < self::KARATSUBA_CUTOFF) {
+ return self::baseSquare($value);
+ }
+
+ $x1 = array_slice($value, $m);
+ $x0 = array_slice($value, 0, $m);
+
+ $z2 = self::karatsubaSquare($x1);
+ $z0 = self::karatsubaSquare($x0);
+
+ $z1 = self::addHelper($x1, false, $x0, false);
+ $z1 = self::karatsubaSquare($z1[self::VALUE]);
+ $temp = self::addHelper($z2, false, $z0, false);
+ $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
+
+ $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
+ $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false);
+
+ return $xx[self::VALUE];
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ $this->value[0] |= 1;
+ }
+
+ /**
+ * Test the number against small primes.
+ *
+ * @see self::isPrime()
+ */
+ protected function testSmallPrimes()
+ {
+ if ($this->value == [1]) {
+ return false;
+ }
+ if ($this->value == [2]) {
+ return true;
+ }
+ if (~$this->value[0] & 1) {
+ return false;
+ }
+
+ $value = $this->value;
+ foreach (static::PRIMES as $prime) {
+ list(, $r) = self::divide_digit($value, $prime);
+ if (!$r) {
+ return count($value) == 1 && $value[0] == $prime;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scan for 1 and right shift by that amount
+ *
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @param PHP $r
+ * @return int
+ * @see self::isPrime()
+ */
+ public static function scan1divide(PHP $r)
+ {
+ $r_value = &$r->value;
+ for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
+ $temp = ~$r_value[$i] & static::MAX_DIGIT;
+ for ($j = 1; ($temp >> $j) & 1; ++$j) {
+ }
+ if ($j <= static::BASE) {
+ break;
+ }
+ }
+ $s = static::BASE * $i + $j;
+ $r->rshift($s);
+ return $s;
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @param PHP $n
+ * @return PHP
+ */
+ protected function powHelper(PHP $n)
+ {
+ if ($n->compare(static::$zero[static::class]) == 0) {
+ return new static(1);
+ } // n^0 = 1
+
+ $temp = clone $this;
+ while (!$n->equals(static::$one[static::class])) {
+ $temp = $temp->multiply($this);
+ $n = $n->subtract(static::$one[static::class]);
+ }
+
+ return $temp;
+ }
+
+ /**
+ * Is Odd?
+ *
+ * @return bool
+ */
+ public function isOdd()
+ {
+ return (bool)($this->value[0] & 1);
+ }
+
+ /**
+ * Tests if a bit is set
+ *
+ * @return bool
+ */
+ public function testBit($x)
+ {
+ $digit = (int) floor($x / static::BASE);
+ $bit = $x % static::BASE;
+
+ if (!isset($this->value[$digit])) {
+ return false;
+ }
+
+ return (bool)($this->value[$digit] & (1 << $bit));
+ }
+
+ /**
+ * Is Negative?
+ *
+ * @return bool
+ */
+ public function isNegative()
+ {
+ return $this->is_negative;
+ }
+
+ /**
+ * Negate
+ *
+ * Given $k, returns -$k
+ *
+ * @return static
+ */
+ public function negate()
+ {
+ $temp = clone $this;
+ $temp->is_negative = !$temp->is_negative;
+
+ return $temp;
+ }
+
+ /**
+ * Bitwise Split
+ *
+ * Splits BigInteger's into chunks of $split bits
+ *
+ * @param int $split
+ * @return list<static>
+ */
+ public function bitwise_split($split)
+ {
+ if ($split < 1) {
+ throw new \RuntimeException('Offset must be greater than 1');
+ }
+
+ $width = (int)($split / static::BASE);
+ if (!$width) {
+ $arr = $this->bitwise_small_split($split);
+ return array_map(function ($digit) {
+ $temp = new static();
+ $temp->value = $digit != 0 ? [$digit] : [];
+ return $temp;
+ }, $arr);
+ }
+
+ $vals = [];
+ $val = $this->value;
+
+ $i = $overflow = 0;
+ $len = count($val);
+ while ($i < $len) {
+ $digit = [];
+ if (!$overflow) {
+ $digit = array_slice($val, $i, $width);
+ $i += $width;
+ $overflow = $split % static::BASE;
+ if ($overflow) {
+ $mask = (1 << $overflow) - 1;
+ $temp = isset($val[$i]) ? $val[$i] : 0;
+ $digit[] = $temp & $mask;
+ }
+ } else {
+ $remaining = static::BASE - $overflow;
+ $tempsplit = $split - $remaining;
+ $tempwidth = (int)($tempsplit / static::BASE + 1);
+ $digit = array_slice($val, $i, $tempwidth);
+ $i += $tempwidth;
+ $tempoverflow = $tempsplit % static::BASE;
+ if ($tempoverflow) {
+ $tempmask = (1 << $tempoverflow) - 1;
+ $temp = isset($val[$i]) ? $val[$i] : 0;
+ $digit[] = $temp & $tempmask;
+ }
+ $newbits = 0;
+ for ($j = count($digit) - 1; $j >= 0; $j--) {
+ $temp = $digit[$j] & $mask;
+ $digit[$j] = ($digit[$j] >> $overflow) | ($newbits << $remaining);
+ $newbits = $temp;
+ }
+ $overflow = $tempoverflow;
+ $mask = $tempmask;
+ }
+ $temp = new static();
+ $temp->value = static::trim($digit);
+ $vals[] = $temp;
+ }
+
+ return array_reverse($vals);
+ }
+
+ /**
+ * Bitwise Split where $split < static::BASE
+ *
+ * @param int $split
+ * @return list<int>
+ */
+ private function bitwise_small_split($split)
+ {
+ $vals = [];
+ $val = $this->value;
+
+ $mask = (1 << $split) - 1;
+
+ $i = $overflow = 0;
+ $len = count($val);
+ $val[] = 0;
+ $remaining = static::BASE;
+ while ($i != $len) {
+ $digit = $val[$i] & $mask;
+ $val[$i] >>= $split;
+ if (!$overflow) {
+ $remaining -= $split;
+ $overflow = $split <= $remaining ? 0 : $split - $remaining;
+
+ if (!$remaining) {
+ $i++;
+ $remaining = static::BASE;
+ $overflow = 0;
+ }
+ } elseif (++$i != $len) {
+ $tempmask = (1 << $overflow) - 1;
+ $digit |= ($val[$i] & $tempmask) << $remaining;
+ $val[$i] >>= $overflow;
+ $remaining = static::BASE - $overflow;
+ $overflow = $split <= $remaining ? 0 : $split - $remaining;
+ }
+
+ $vals[] = $digit;
+ }
+
+ while ($vals[count($vals) - 1] == 0) {
+ unset($vals[count($vals) - 1]);
+ }
+
+ return array_reverse($vals);
+ }
+
+ /**
+ * @return bool
+ */
+ protected static function testJITOnWindows()
+ {
+ // see https://github.com/php/php-src/issues/11917
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) {
+ $status = opcache_get_status();
+ if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the size of a BigInteger in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ $max = count($this->value) - 1;
+ return $max != -1 ?
+ $max * static::BASE + intval(ceil(log($this->value[$max] + 1, 2))) :
+ 0;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php
new file mode 100644
index 000000000..40f64bd17
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * PHP Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP;
+
+use phpseclib3\Math\BigInteger\Engines\PHP;
+
+/**
+ * PHP Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Base extends PHP
+{
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ *
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ *
+ */
+ const DATA = 1;
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * The most naive approach to modular exponentiation has very unreasonable requirements, and
+ * and although the approach involving repeated squaring does vastly better, it, too, is impractical
+ * for our purposes. The reason being that division - by far the most complicated and time-consuming
+ * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
+ *
+ * Modular reductions resolve this issue. Although an individual modular reduction takes more time
+ * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
+ *
+ * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
+ * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
+ * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
+ * the product of two odd numbers is odd), but what about when RSA isn't used?
+ *
+ * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
+ * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
+ * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
+ * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
+ * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
+ * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
+ *
+ * @param PHP $x
+ * @param PHP $e
+ * @param PHP $n
+ * @param string $class
+ * @return PHP
+ */
+ protected static function powModHelper(PHP $x, PHP $e, PHP $n, $class)
+ {
+ if (empty($e->value)) {
+ $temp = new $class();
+ $temp->value = [1];
+ return $x->normalize($temp);
+ }
+
+ if ($e->value == [1]) {
+ list(, $temp) = $x->divide($n);
+ return $x->normalize($temp);
+ }
+
+ if ($e->value == [2]) {
+ $temp = new $class();
+ $temp->value = $class::square($x->value);
+ list(, $temp) = $temp->divide($n);
+ return $x->normalize($temp);
+ }
+
+ return $x->normalize(static::slidingWindow($x, $e, $n, $class));
+ }
+
+ /**
+ * Modular reduction preparation
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ return static::reduce($x, $n, $class);
+ }
+
+ /**
+ * Modular multiply
+ *
+ * @param array $x
+ * @param array $y
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function multiplyReduce(array $x, array $y, array $n, $class)
+ {
+ $temp = $class::multiplyHelper($x, false, $y, false);
+ return static::reduce($temp[self::VALUE], $n, $class);
+ }
+
+ /**
+ * Modular square
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function squareReduce(array $x, array $n, $class)
+ {
+ return static::reduce($class::square($x), $n, $class);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php
new file mode 100644
index 000000000..6d33532e1
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * PHP Default Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP;
+
+use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett;
+
+/**
+ * PHP Default Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class DefaultEngine extends EvalBarrett
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php
new file mode 100644
index 000000000..09f825f95
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP;
+
+use phpseclib3\Math\BigInteger\Engines\Engine;
+use phpseclib3\Math\BigInteger\Engines\PHP;
+use phpseclib3\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Montgomery extends Base
+{
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @template T of Engine
+ * @param Engine $x
+ * @param Engine $e
+ * @param Engine $n
+ * @param class-string<T> $class
+ * @return T
+ */
+ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class)
+ {
+ // is the modulo odd?
+ if ($n->value[0] & 1) {
+ return parent::slidingWindow($x, $e, $n, $class);
+ }
+ // if it's not, it's even
+
+ // find the lowest set bit (eg. the max pow of 2 that divides $n)
+ for ($i = 0; $i < count($n->value); ++$i) {
+ if ($n->value[$i]) {
+ $temp = decbin($n->value[$i]);
+ $j = strlen($temp) - strrpos($temp, '1') - 1;
+ $j += $class::BASE * $i;
+ break;
+ }
+ }
+ // at this point, 2^$j * $n/(2^$j) == $n
+
+ $mod1 = clone $n;
+ $mod1->rshift($j);
+ $mod2 = new $class();
+ $mod2->value = [1];
+ $mod2->lshift($j);
+
+ $part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e, $mod1, $class) : new $class();
+ $part2 = PowerOfTwo::slidingWindow($x, $e, $mod2, $class);
+
+ $y1 = $mod2->modInverse($mod1);
+ $y2 = $mod1->modInverse($mod2);
+
+ $result = $part1->multiply($mod2);
+ $result = $result->multiply($y1);
+
+ $temp = $part2->multiply($mod1);
+ $temp = $temp->multiply($y2);
+
+ $result = $result->add($temp);
+ list(, $result) = $result->divide($n);
+
+ return $result;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php
new file mode 100644
index 000000000..eddd25e2e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP;
+
+use phpseclib3\Math\BigInteger\Engines\OpenSSL as Progenitor;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class OpenSSL extends Progenitor
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php
new file mode 100644
index 000000000..e624f3cad
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php
@@ -0,0 +1,296 @@
+<?php
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP;
+use phpseclib3\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Barrett extends Base
+{
+ /**
+ * Barrett Modular Reduction
+ *
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
+ * so as not to require negative numbers (initially, this script didn't support negative numbers).
+ *
+ * Employs "folding", as described at
+ * {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
+ * it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
+ *
+ * Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
+ * usable on account of (1) its not using reasonable radix points as discussed in
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
+ * radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
+ * (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
+ * comments for details.
+ *
+ * @param array $n
+ * @param array $m
+ * @param class-string<PHP> $class
+ * @return array
+ */
+ protected static function reduce(array $n, array $m, $class)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $m_length = count($m);
+
+ // if (self::compareHelper($n, $static::square($m)) >= 0) {
+ if (count($n) > 2 * $m_length) {
+ $lhs = new $class();
+ $rhs = new $class();
+ $lhs->value = $n;
+ $rhs->value = $m;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
+ if ($m_length < 5) {
+ return self::regularBarrett($n, $m, $class);
+ }
+ // n = 2 * m.length
+ $correctionNeeded = false;
+ if ($m_length & 1) {
+ $correctionNeeded = true;
+ array_unshift($n, 0);
+ array_unshift($m, 0);
+ $m_length++;
+ }
+
+ if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $m;
+
+ $lhs = new $class();
+ $lhs_value = &$lhs->value;
+ $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
+ $lhs_value[] = 1;
+ $rhs = new $class();
+ $rhs->value = $m;
+
+ list($u, $m1) = $lhs->divide($rhs);
+ $u = $u->value;
+ $m1 = $m1->value;
+
+ $cache[self::DATA][] = [
+ 'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
+ 'm1' => $m1 // m.length
+ ];
+ } else {
+ $cacheValues = $cache[self::DATA][$key];
+ $u = $cacheValues['u'];
+ $m1 = $cacheValues['m1'];
+ }
+
+ $cutoff = $m_length + ($m_length >> 1);
+ $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
+ $msd = array_slice($n, $cutoff); // m.length >> 1
+
+ $lsd = self::trim($lsd);
+ $temp = $class::multiplyHelper($msd, false, $m1, false); // m.length + (m.length >> 1)
+ $n = $class::addHelper($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
+ //if ($m_length & 1) {
+ // return self::regularBarrett($n[self::VALUE], $m, $class);
+ //}
+
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
+ $temp = array_slice($n[self::VALUE], $m_length - 1);
+ // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
+ // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
+ // note that these are upper bounds. let's say m.length is 2. then you'd be multiplying a
+ // 3 digit number by a 1 digit number. if you're doing 999 * 9 (in base 10) the result will
+ // be a 4 digit number. but if you're multiplying 111 * 1 then the result will be a 3 digit
+ // number.
+ $temp = $class::multiplyHelper($temp, false, $u, false);
+ // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
+ // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
+ $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
+ // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
+ // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
+ $temp = $class::multiplyHelper($temp, false, $m, false);
+ // at this point, if m had an odd number of digits, we'd (probably) be subtracting a 2 * m.length - (m.length >> 1)
+ // digit number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
+ // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
+ $result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false);
+
+ while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
+ $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false);
+ }
+
+ if ($correctionNeeded) {
+ array_shift($result[self::VALUE]);
+ }
+
+ return $result[self::VALUE];
+ }
+
+ /**
+ * (Regular) Barrett Modular Reduction
+ *
+ * For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
+ * is that this function does not fold the denominator into a smaller form.
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ private static function regularBarrett(array $x, array $n, $class)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $n_length = count($n);
+
+ if (count($x) > 2 * $n_length) {
+ $lhs = new $class();
+ $rhs = new $class();
+ $lhs->value = $x;
+ $rhs->value = $n;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $n;
+ $lhs = new $class();
+ $lhs_value = &$lhs->value;
+ $lhs_value = self::array_repeat(0, 2 * $n_length);
+ $lhs_value[] = 1;
+ $rhs = new $class();
+ $rhs->value = $n;
+ list($temp, ) = $lhs->divide($rhs); // m.length
+ $cache[self::DATA][] = $temp->value;
+ }
+
+ // 2 * m.length - (m.length - 1) = m.length + 1
+ $temp = array_slice($x, $n_length - 1);
+ // (m.length + 1) + m.length = 2 * m.length + 1
+ $temp = $class::multiplyHelper($temp, false, $cache[self::DATA][$key], false);
+ // (2 * m.length + 1) - (m.length - 1) = m.length + 2
+ $temp = array_slice($temp[self::VALUE], $n_length + 1);
+
+ // m.length + 1
+ $result = array_slice($x, 0, $n_length + 1);
+ // m.length + 1
+ $temp = self::multiplyLower($temp, false, $n, false, $n_length + 1, $class);
+ // $temp == array_slice($class::regularMultiply($temp, false, $n, false)->value, 0, $n_length + 1)
+
+ if (self::compareHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
+ $corrector_value = self::array_repeat(0, $n_length + 1);
+ $corrector_value[count($corrector_value)] = 1;
+ $result = $class::addHelper($result, false, $corrector_value, false);
+ $result = $result[self::VALUE];
+ }
+
+ // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
+ $result = $class::subtractHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]);
+ while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
+ $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $n, false);
+ }
+
+ return $result[self::VALUE];
+ }
+
+ /**
+ * Performs long multiplication up to $stop digits
+ *
+ * If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
+ *
+ * @see self::regularBarrett()
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @param int $stop
+ * @param string $class
+ * @return array
+ */
+ private static function multiplyLower(array $x_value, $x_negative, array $y_value, $y_negative, $stop, $class)
+ {
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if (!$x_length || !$y_length) { // a 0 is being multiplied
+ return [
+ self::VALUE => [],
+ self::SIGN => false
+ ];
+ }
+
+ if ($x_length < $y_length) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+ }
+
+ $product_value = self::array_repeat(0, $x_length + $y_length);
+
+ // the following for loop could be removed if the for loop following it
+ // (the one with nested for loops) initially set $i to 0, but
+ // doing so would also make the result in one set of unnecessary adds,
+ // since on the outermost loops first pass, $product->value[$k] is going
+ // to always be 0
+
+ $carry = 0;
+
+ for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
+ $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$j] = (int) ($temp - $class::BASE_FULL * $carry);
+ }
+
+ if ($j < $stop) {
+ $product_value[$j] = $carry;
+ }
+
+ // the above for loop is what the previous comment was talking about. the
+ // following for loop is the "one with nested for loops"
+
+ for ($i = 1; $i < $y_length; ++$i) {
+ $carry = 0;
+
+ for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
+ $carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$k] = (int) ($temp - $class::BASE_FULL * $carry);
+ }
+
+ if ($k < $stop) {
+ $product_value[$k] = $carry;
+ }
+ }
+
+ return [
+ self::VALUE => self::trim($product_value),
+ self::SIGN => $x_negative != $y_negative
+ ];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php
new file mode 100644
index 000000000..54f3b863b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * PHP Classic Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Classic Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Classic extends Base
+{
+ /**
+ * Regular Division
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ $lhs = new $class();
+ $lhs->value = $x;
+ $rhs = new $class();
+ $rhs->value = $n;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php
new file mode 100644
index 000000000..01df0b611
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php
@@ -0,0 +1,500 @@
+<?php
+
+/**
+ * PHP Dynamic Barrett Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP;
+use phpseclib3\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Dynamic Barrett Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class EvalBarrett extends Base
+{
+ /**
+ * Custom Reduction Function
+ *
+ * @see self::generateCustomReduction
+ */
+ private static $custom_reduction;
+
+ /**
+ * Barrett Modular Reduction
+ *
+ * This calls a dynamically generated loop unrolled function that's specific to a given modulo.
+ * Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc.
+ *
+ * @param array $n
+ * @param array $m
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $n, array $m, $class)
+ {
+ $inline = self::$custom_reduction;
+ return $inline($n);
+ }
+
+ /**
+ * Generate Custom Reduction
+ *
+ * @param PHP $m
+ * @param string $class
+ * @return callable
+ */
+ protected static function generateCustomReduction(PHP $m, $class)
+ {
+ $m_length = count($m->value);
+
+ if ($m_length < 5) {
+ $code = '
+ $lhs = new ' . $class . '();
+ $lhs->value = $x;
+ $rhs = new ' . $class . '();
+ $rhs->value = [' .
+ implode(',', array_map(self::class . '::float2string', $m->value)) . '];
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ ';
+ eval('$func = function ($x) { ' . $code . '};');
+ self::$custom_reduction = $func;
+ //self::$custom_reduction = \Closure::bind($func, $m, $class);
+ return $func;
+ }
+
+ $correctionNeeded = false;
+ if ($m_length & 1) {
+ $correctionNeeded = true;
+ $m = clone $m;
+ array_unshift($m->value, 0);
+ $m_length++;
+ }
+
+ $lhs = new $class();
+ $lhs_value = &$lhs->value;
+
+ $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
+ $lhs_value[] = 1;
+ $rhs = new $class();
+
+ list($u, $m1) = $lhs->divide($m);
+
+ if ($class::BASE != 26) {
+ $u = $u->value;
+ } else {
+ $lhs_value = self::array_repeat(0, 2 * $m_length);
+ $lhs_value[] = 1;
+ $rhs = new $class();
+
+ list($u) = $lhs->divide($m);
+ $u = $u->value;
+ }
+
+ $m = $m->value;
+ $m1 = $m1->value;
+
+ $cutoff = count($m) + (count($m) >> 1);
+
+ $code = $correctionNeeded ?
+ 'array_unshift($n, 0);' :
+ '';
+
+ $code .= '
+ if (count($n) > ' . (2 * count($m)) . ') {
+ $lhs = new ' . $class . '();
+ $rhs = new ' . $class . '();
+ $lhs->value = $n;
+ $rhs->value = [' .
+ implode(',', array_map(self::class . '::float2string', $m)) . '];
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ $lsd = array_slice($n, 0, ' . $cutoff . ');
+ $msd = array_slice($n, ' . $cutoff . ');';
+
+ $code .= self::generateInlineTrim('msd');
+ $code .= self::generateInlineMultiply('msd', $m1, 'temp', $class);
+ $code .= self::generateInlineAdd('lsd', 'temp', 'n', $class);
+
+ $code .= '$temp = array_slice($n, ' . (count($m) - 1) . ');';
+ $code .= self::generateInlineMultiply('temp', $u, 'temp2', $class);
+ $code .= self::generateInlineTrim('temp2');
+
+ $code .= $class::BASE == 26 ?
+ '$temp = array_slice($temp2, ' . (count($m) + 1) . ');' :
+ '$temp = array_slice($temp2, ' . ((count($m) >> 1) + 1) . ');';
+ $code .= self::generateInlineMultiply('temp', $m, 'temp2', $class);
+ $code .= self::generateInlineTrim('temp2');
+
+ /*
+ if ($class::BASE == 26) {
+ $code.= '$n = array_slice($n, 0, ' . (count($m) + 1) . ');
+ $temp2 = array_slice($temp2, 0, ' . (count($m) + 1) . ');';
+ }
+ */
+
+ $code .= self::generateInlineSubtract2('n', 'temp2', 'temp', $class);
+
+ $subcode = self::generateInlineSubtract1('temp', $m, 'temp2', $class);
+ $subcode .= '$temp = $temp2;';
+
+ $code .= self::generateInlineCompare($m, 'temp', $subcode);
+
+ if ($correctionNeeded) {
+ $code .= 'array_shift($temp);';
+ }
+
+ $code .= 'return $temp;';
+
+ eval('$func = function ($n) { ' . $code . '};');
+
+ self::$custom_reduction = $func;
+
+ return $func;
+
+ //self::$custom_reduction = \Closure::bind($func, $m, $class);
+ }
+
+ /**
+ * Inline Trim
+ *
+ * Removes leading zeros
+ *
+ * @param string $name
+ * @return string
+ */
+ private static function generateInlineTrim($name)
+ {
+ return '
+ for ($i = count($' . $name . ') - 1; $i >= 0; --$i) {
+ if ($' . $name . '[$i]) {
+ break;
+ }
+ unset($' . $name . '[$i]);
+ }';
+ }
+
+ /**
+ * Inline Multiply (unknown, known)
+ *
+ * @param string $input
+ * @param array $arr
+ * @param string $output
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineMultiply($input, array $arr, $output, $class)
+ {
+ if (!count($arr)) {
+ return 'return [];';
+ }
+
+ $regular = '
+ $length = count($' . $input . ');
+ if (!$length) {
+ $' . $output . ' = [];
+ }else{
+ $' . $output . ' = array_fill(0, $length + ' . count($arr) . ', 0);
+ $carry = 0;';
+
+ for ($i = 0; $i < count($arr); $i++) {
+ $regular .= '
+ $subtemp = $' . $input . '[0] * ' . $arr[$i];
+ $regular .= $i ? ' + $carry;' : ';';
+
+ $regular .= '$carry = ';
+ $regular .= $class::BASE === 26 ?
+ 'intval($subtemp / 0x4000000);' :
+ '$subtemp >> 31;';
+ $regular .=
+ '$' . $output . '[' . $i . '] = ';
+ if ($class::BASE === 26) {
+ $regular .= '(int) (';
+ }
+ $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
+ $regular .= $class::BASE === 26 ? ');' : ';';
+ }
+
+ $regular .= '$' . $output . '[' . count($arr) . '] = $carry;';
+
+ $regular .= '
+ for ($i = 1; $i < $length; ++$i) {';
+
+ for ($j = 0; $j < count($arr); $j++) {
+ $regular .= $j ? '$k++;' : '$k = $i;';
+ $regular .= '
+ $subtemp = $' . $output . '[$k] + $' . $input . '[$i] * ' . $arr[$j];
+ $regular .= $j ? ' + $carry;' : ';';
+
+ $regular .= '$carry = ';
+ $regular .= $class::BASE === 26 ?
+ 'intval($subtemp / 0x4000000);' :
+ '$subtemp >> 31;';
+ $regular .=
+ '$' . $output . '[$k] = ';
+ if ($class::BASE === 26) {
+ $regular .= '(int) (';
+ }
+ $regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
+ $regular .= $class::BASE === 26 ? ');' : ';';
+ }
+
+ $regular .= '$' . $output . '[++$k] = $carry; $carry = 0;';
+
+ $regular .= '}}';
+
+ //if (count($arr) < 2 * self::KARATSUBA_CUTOFF) {
+ //}
+
+ return $regular;
+ }
+
+ /**
+ * Inline Addition
+ *
+ * @param string $x
+ * @param string $y
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineAdd($x, $y, $result, $class)
+ {
+ $code = '
+ $length = max(count($' . $x . '), count($' . $y . '));
+ $' . $result . ' = array_pad($' . $x . ', $length + 1, 0);
+ $_' . $y . ' = array_pad($' . $y . ', $length, 0);
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) {
+ $sum = ($' . $result . '[$j] + $_' . $y . '[$j]) * ' . $class::BASE_FULL . '
+ + $' . $result . '[$i] + $_' . $y . '[$i] +
+ $carry;
+ $carry = $sum >= ' . self::float2string($class::MAX_DIGIT2) . ';
+ $sum = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;';
+
+ $code .= $class::BASE === 26 ?
+ '$upper = intval($sum / 0x4000000); $' . $result . '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' * $upper);' :
+ '$upper = $sum >> 31; $' . $result . '[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;';
+ $code .= '
+ $' . $result . '[$j] = $upper;
+ }
+ if ($j == $length) {
+ $sum = $' . $result . '[$i] + $_' . $y . '[$i] + $carry;
+ $carry = $sum >= ' . self::float2string($class::BASE_FULL) . ';
+ $' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::BASE_FULL) . ' : $sum;
+ ++$i;
+ }
+ if ($carry) {
+ for (; $' . $result . '[$i] == ' . $class::MAX_DIGIT . '; ++$i) {
+ $' . $result . '[$i] = 0;
+ }
+ ++$' . $result . '[$i];
+ }';
+ $code .= self::generateInlineTrim($result);
+
+ return $code;
+ }
+
+ /**
+ * Inline Subtraction 2
+ *
+ * For when $known is more digits than $unknown. This is the harder use case to optimize for.
+ *
+ * @param string $known
+ * @param string $unknown
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineSubtract2($known, $unknown, $result, $class)
+ {
+ $code = '
+ $' . $result . ' = $' . $known . ';
+ $carry = 0;
+ $size = count($' . $unknown . ');
+ for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) {
+ $sum = ($' . $known . '[$j] - $' . $unknown . '[$j]) * ' . $class::BASE_FULL . ' + $' . $known . '[$i]
+ - $' . $unknown . '[$i]
+ - $carry;
+ $carry = $sum < 0;
+ if ($carry) {
+ $sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
+ }
+ $subtemp = ';
+ $code .= $class::BASE === 26 ?
+ 'intval($sum / 0x4000000);' :
+ '$sum >> 31;';
+ $code .= '$' . $result . '[$i] = ';
+ if ($class::BASE === 26) {
+ $code .= '(int) (';
+ }
+ $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
+ if ($class::BASE === 26) {
+ $code .= ')';
+ }
+ $code .= ';
+ $' . $result . '[$j] = $subtemp;
+ }
+ if ($j == $size) {
+ $sum = $' . $known . '[$i] - $' . $unknown . '[$i] - $carry;
+ $carry = $sum < 0;
+ $' . $result . '[$i] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
+ ++$i;
+ }
+
+ if ($carry) {
+ for (; !$' . $result . '[$i]; ++$i) {
+ $' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
+ }
+ --$' . $result . '[$i];
+ }';
+
+ $code .= self::generateInlineTrim($result);
+
+ return $code;
+ }
+
+ /**
+ * Inline Subtraction 1
+ *
+ * For when $unknown is more digits than $known. This is the easier use case to optimize for.
+ *
+ * @param string $unknown
+ * @param array $known
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineSubtract1($unknown, array $known, $result, $class)
+ {
+ $code = '$' . $result . ' = $' . $unknown . ';';
+ for ($i = 0, $j = 1; $j < count($known); $i += 2, $j += 2) {
+ $code .= '$sum = $' . $unknown . '[' . $j . '] * ' . $class::BASE_FULL . ' + $' . $unknown . '[' . $i . '] - ';
+ $code .= self::float2string($known[$j] * $class::BASE_FULL + $known[$i]);
+ if ($i != 0) {
+ $code .= ' - $carry';
+ }
+
+ $code .= ';
+ if ($carry = $sum < 0) {
+ $sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
+ }
+ $subtemp = ';
+ $code .= $class::BASE === 26 ?
+ 'intval($sum / 0x4000000);' :
+ '$sum >> 31;';
+ $code .= '
+ $' . $result . '[' . $i . '] = ';
+ if ($class::BASE === 26) {
+ $code .= ' (int) (';
+ }
+ $code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
+ if ($class::BASE === 26) {
+ $code .= ')';
+ }
+ $code .= ';
+ $' . $result . '[' . $j . '] = $subtemp;';
+ }
+
+ $code .= '$i = ' . $i . ';';
+
+ if ($j == count($known)) {
+ $code .= '
+ $sum = $' . $unknown . '[' . $i . '] - ' . $known[$i] . ' - $carry;
+ $carry = $sum < 0;
+ $' . $result . '[' . $i . '] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
+ ++$i;';
+ }
+
+ $code .= '
+ if ($carry) {
+ for (; !$' . $result . '[$i]; ++$i) {
+ $' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
+ }
+ --$' . $result . '[$i];
+ }';
+ $code .= self::generateInlineTrim($result);
+
+ return $code;
+ }
+
+ /**
+ * Inline Comparison
+ *
+ * If $unknown >= $known then loop
+ *
+ * @param array $known
+ * @param string $unknown
+ * @param string $subcode
+ * @return string
+ */
+ private static function generateInlineCompare(array $known, $unknown, $subcode)
+ {
+ $uniqid = uniqid();
+ $code = 'loop_' . $uniqid . ':
+ $clength = count($' . $unknown . ');
+ switch (true) {
+ case $clength < ' . count($known) . ':
+ goto end_' . $uniqid . ';
+ case $clength > ' . count($known) . ':';
+ for ($i = count($known) - 1; $i >= 0; $i--) {
+ $code .= '
+ case $' . $unknown . '[' . $i . '] > ' . $known[$i] . ':
+ goto subcode_' . $uniqid . ';
+ case $' . $unknown . '[' . $i . '] < ' . $known[$i] . ':
+ goto end_' . $uniqid . ';';
+ }
+ $code .= '
+ default:
+ // do subcode
+ }
+
+ subcode_' . $uniqid . ':' . $subcode . '
+ goto loop_' . $uniqid . ';
+
+ end_' . $uniqid . ':';
+
+ return $code;
+ }
+
+ /**
+ * Convert a float to a string
+ *
+ * If you do echo floatval(pow(2, 52)) you'll get 4.6116860184274E+18. It /can/ be displayed without a loss of
+ * precision but displayed in this way there will be precision loss, hence the need for this method.
+ *
+ * @param int|float $num
+ * @return string
+ */
+ private static function float2string($num)
+ {
+ if (!is_float($num)) {
+ return (string) $num;
+ }
+
+ if ($num < 0) {
+ return '-' . self::float2string(abs($num));
+ }
+
+ $temp = '';
+ while ($num) {
+ $temp = fmod($num, 10) . $temp;
+ $num = floor($num / 10);
+ }
+
+ return $temp;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php
new file mode 100644
index 000000000..a34035e7a
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Montgomery extends Progenitor
+{
+ /**
+ * Prepare a number for use in Montgomery Modular Reductions
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ $lhs = new $class();
+ $lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
+ $rhs = new $class();
+ $rhs->value = $n;
+
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+
+ /**
+ * Montgomery Multiply
+ *
+ * Interleaves the montgomery reduction and long multiplication algorithms together as described in
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $x;
+ $cache[self::DATA][] = self::modInverse67108864($n, $class);
+ }
+
+ $k = count($n);
+
+ $result = [self::VALUE => $x];
+
+ for ($i = 0; $i < $k; ++$i) {
+ $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
+ $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
+ $temp = $class::regularMultiply([$temp], $n);
+ $temp = array_merge(self::array_repeat(0, $i), $temp);
+ $result = $class::addHelper($result[self::VALUE], false, $temp, false);
+ }
+
+ $result[self::VALUE] = array_slice($result[self::VALUE], $k);
+
+ if (self::compareHelper($result, false, $n, false) >= 0) {
+ $result = $class::subtractHelper($result[self::VALUE], false, $n, false);
+ }
+
+ return $result[self::VALUE];
+ }
+
+ /**
+ * Modular Inverse of a number mod 2**26 (eg. 67108864)
+ *
+ * Based off of the bnpInvDigit function implemented and justified in the following URL:
+ *
+ * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
+ *
+ * The following URL provides more info:
+ *
+ * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
+ *
+ * As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
+ * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
+ * int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
+ * auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
+ * the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
+ * maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
+ * 40 bits, which only 64-bit floating points will support.
+ *
+ * Thanks to Pedro Gimeno Fortea for input!
+ *
+ * @param array $x
+ * @param string $class
+ * @return int
+ */
+ protected static function modInverse67108864(array $x, $class) // 2**26 == 67,108,864
+ {
+ $x = -$x[0];
+ $result = $x & 0x3; // x**-1 mod 2**2
+ $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
+ $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
+ $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
+ $result = $class::BASE == 26 ?
+ fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : // x**-1 mod 2**26
+ ($result * (2 - ($x * $result) % $class::BASE_FULL)) % $class::BASE_FULL;
+ return $result & $class::MAX_DIGIT;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php
new file mode 100644
index 000000000..4fed3c3fa
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class MontgomeryMult extends Montgomery
+{
+ /**
+ * Montgomery Multiply
+ *
+ * Interleaves the montgomery reduction and long multiplication algorithms together as described in
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
+ *
+ * @see self::_prepMontgomery()
+ * @see self::_montgomery()
+ * @param array $x
+ * @param array $y
+ * @param array $m
+ * @param class-string<PHP> $class
+ * @return array
+ */
+ public static function multiplyReduce(array $x, array $y, array $m, $class)
+ {
+ // the following code, although not callable, can be run independently of the above code
+ // although the above code performed better in my benchmarks the following could might
+ // perform better under different circumstances. in lieu of deleting it it's just been
+ // made uncallable
+
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $m;
+ $cache[self::DATA][] = self::modInverse67108864($m, $class);
+ }
+
+ $n = max(count($x), count($y), count($m));
+ $x = array_pad($x, $n, 0);
+ $y = array_pad($y, $n, 0);
+ $m = array_pad($m, $n, 0);
+ $a = [self::VALUE => self::array_repeat(0, $n + 1)];
+ for ($i = 0; $i < $n; ++$i) {
+ $temp = $a[self::VALUE][0] + $x[$i] * $y[0];
+ $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
+ $temp = $temp * $cache[self::DATA][$key];
+ $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
+ $temp = $class::addHelper($class::regularMultiply([$x[$i]], $y), false, $class::regularMultiply([$temp], $m), false);
+ $a = $class::addHelper($a[self::VALUE], false, $temp[self::VALUE], false);
+ $a[self::VALUE] = array_slice($a[self::VALUE], 1);
+ }
+ if (self::compareHelper($a[self::VALUE], false, $m, false) >= 0) {
+ $a = $class::subtractHelper($a[self::VALUE], false, $m, false);
+ }
+ return $a[self::VALUE];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php
new file mode 100644
index 000000000..9da133a14
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * PHP Power of Two Modular Exponentiation Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib3\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Power Of Two Modular Exponentiation Engine
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class PowerOfTwo extends Base
+{
+ /**
+ * Prepare a number for use in Montgomery Modular Reductions
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ return self::reduce($x, $n, $class);
+ }
+
+ /**
+ * Power Of Two Reduction
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ $lhs = new $class();
+ $lhs->value = $x;
+ $rhs = new $class();
+ $rhs->value = $n;
+
+ $temp = new $class();
+ $temp->value = [1];
+
+ $result = $lhs->bitwise_and($rhs->subtract($temp));
+ return $result->value;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php
new file mode 100644
index 000000000..3a775e7db
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php
@@ -0,0 +1,371 @@
+<?php
+
+/**
+ * Pure-PHP 32-bit BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+/**
+ * Pure-PHP 32-bit Engine.
+ *
+ * Uses 64-bit floats if int size is 4 bits
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class PHP32 extends PHP
+{
+ // Constants used by PHP.php
+ const BASE = 26;
+ const BASE_FULL = 0x4000000;
+ const MAX_DIGIT = 0x3FFFFFF;
+ const MSB = 0x2000000;
+
+ /**
+ * MAX10 in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10 = 10000000;
+
+ /**
+ * MAX10LEN in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10LEN = 7;
+ const MAX_DIGIT2 = 4503599627370496;
+
+ /**
+ * Initialize a PHP32 BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::initialize()
+ */
+ protected function initialize($base)
+ {
+ if ($base != 256 && $base != -256) {
+ return parent::initialize($base);
+ }
+
+ $val = $this->value;
+ $this->value = [];
+ $vals = &$this->value;
+ $i = strlen($val);
+ if (!$i) {
+ return;
+ }
+
+ while (true) {
+ $i -= 4;
+ if ($i < 0) {
+ if ($i == -4) {
+ break;
+ }
+ $val = substr($val, 0, 4 + $i);
+ $val = str_pad($val, 4, "\0", STR_PAD_LEFT);
+ if ($val == "\0\0\0\0") {
+ break;
+ }
+ $i = 0;
+ }
+ list(, $digit) = unpack('N', substr($val, $i, 4));
+ if ($digit < 0) {
+ $digit += 0xFFFFFFFF + 1;
+ }
+ $step = count($vals) & 3;
+ if ($step) {
+ $digit = (int) floor($digit / pow(2, 2 * $step));
+ }
+ if ($step != 3) {
+ $digit = (int) fmod($digit, static::BASE_FULL);
+ $i++;
+ }
+ $vals[] = $digit;
+ }
+ while (end($vals) === 0) {
+ array_pop($vals);
+ }
+ reset($vals);
+ }
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return PHP_INT_SIZE >= 4 && !self::testJITOnWindows();
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @param PHP32 $y
+ * @return PHP32
+ */
+ public function add(PHP32 $y)
+ {
+ $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @param PHP32 $y
+ * @return PHP32
+ */
+ public function subtract(PHP32 $y)
+ {
+ $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @param PHP32 $y
+ * @return PHP32
+ */
+ public function multiply(PHP32 $y)
+ {
+ $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @param PHP32 $y
+ * @return array{PHP32, PHP32}
+ */
+ public function divide(PHP32 $y)
+ {
+ return $this->divideHelper($y);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ * @param PHP32 $n
+ * @return false|PHP32
+ */
+ public function modInverse(PHP32 $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ * @param PHP32 $n
+ * @return PHP32[]
+ */
+ public function extendedGCD(PHP32 $n)
+ {
+ return $this->extendedGCDHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @param PHP32 $n
+ * @return PHP32
+ */
+ public function gcd(PHP32 $n)
+ {
+ return $this->extendedGCD($n)['gcd'];
+ }
+
+ /**
+ * Logical And
+ *
+ * @param PHP32 $x
+ * @return PHP32
+ */
+ public function bitwise_and(PHP32 $x)
+ {
+ return $this->bitwiseAndHelper($x);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param PHP32 $x
+ * @return PHP32
+ */
+ public function bitwise_or(PHP32 $x)
+ {
+ return $this->bitwiseOrHelper($x);
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @param PHP32 $x
+ * @return PHP32
+ */
+ public function bitwise_xor(PHP32 $x)
+ {
+ return $this->bitwiseXorHelper($x);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
+ *
+ * @param PHP32 $y
+ * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
+ */
+ public function compare(PHP32 $y)
+ {
+ return $this->compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @param PHP32 $x
+ * @return bool
+ */
+ public function equals(PHP32 $x)
+ {
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param PHP32 $e
+ * @param PHP32 $n
+ * @return PHP32
+ */
+ public function modPow(PHP32 $e, PHP32 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @param PHP32 $e
+ * @param PHP32 $n
+ * @return PHP32
+ */
+ public function powMod(PHP32 $e, PHP32 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @param PHP32 $min
+ * @param PHP32 $max
+ * @return false|PHP32
+ */
+ public static function randomRangePrime(PHP32 $min, PHP32 $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @param PHP32 $min
+ * @param PHP32 $max
+ * @return PHP32
+ */
+ public static function randomRange(PHP32 $min, PHP32 $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @param PHP32 $n
+ * @return PHP32
+ */
+ public function pow(PHP32 $n)
+ {
+ return $this->powHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param PHP32 ...$nums
+ * @return PHP32
+ */
+ public static function min(PHP32 ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param PHP32 ...$nums
+ * @return PHP32
+ */
+ public static function max(PHP32 ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @param PHP32 $min
+ * @param PHP32 $max
+ * @return bool
+ */
+ public function between(PHP32 $min, PHP32 $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php
new file mode 100644
index 000000000..70a2e173b
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php
@@ -0,0 +1,372 @@
+<?php
+
+/**
+ * Pure-PHP 64-bit BigInteger Engine
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math\BigInteger\Engines;
+
+/**
+ * Pure-PHP 64-bit Engine.
+ *
+ * Uses 64-bit integers if int size is 8 bits
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class PHP64 extends PHP
+{
+ // Constants used by PHP.php
+ const BASE = 31;
+ const BASE_FULL = 0x80000000;
+ const MAX_DIGIT = 0x7FFFFFFF;
+ const MSB = 0x40000000;
+
+ /**
+ * MAX10 in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10 = 1000000000;
+
+ /**
+ * MAX10LEN in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10LEN = 9;
+ const MAX_DIGIT2 = 4611686018427387904;
+
+ /**
+ * Initialize a PHP64 BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::initialize()
+ */
+ protected function initialize($base)
+ {
+ if ($base != 256 && $base != -256) {
+ return parent::initialize($base);
+ }
+
+ $val = $this->value;
+ $this->value = [];
+ $vals = &$this->value;
+ $i = strlen($val);
+ if (!$i) {
+ return;
+ }
+
+ while (true) {
+ $i -= 4;
+ if ($i < 0) {
+ if ($i == -4) {
+ break;
+ }
+ $val = substr($val, 0, 4 + $i);
+ $val = str_pad($val, 4, "\0", STR_PAD_LEFT);
+ if ($val == "\0\0\0\0") {
+ break;
+ }
+ $i = 0;
+ }
+ list(, $digit) = unpack('N', substr($val, $i, 4));
+ $step = count($vals) & 7;
+ if (!$step) {
+ $digit &= static::MAX_DIGIT;
+ $i++;
+ } else {
+ $shift = 8 - $step;
+ $digit >>= $shift;
+ $shift = 32 - $shift;
+ $digit &= (1 << $shift) - 1;
+ $temp = $i > 0 ? ord($val[$i - 1]) : 0;
+ $digit |= ($temp << $shift) & 0x7F000000;
+ }
+ $vals[] = $digit;
+ }
+ while (end($vals) === 0) {
+ array_pop($vals);
+ }
+ reset($vals);
+ }
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return PHP_INT_SIZE >= 8 && !self::testJITOnWindows();
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @param PHP64 $y
+ * @return PHP64
+ */
+ public function add(PHP64 $y)
+ {
+ $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @param PHP64 $y
+ * @return PHP64
+ */
+ public function subtract(PHP64 $y)
+ {
+ $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @param PHP64 $y
+ * @return PHP64
+ */
+ public function multiply(PHP64 $y)
+ {
+ $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @param PHP64 $y
+ * @return array{PHP64, PHP64}
+ */
+ public function divide(PHP64 $y)
+ {
+ return $this->divideHelper($y);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ * @param PHP64 $n
+ * @return false|PHP64
+ */
+ public function modInverse(PHP64 $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ * @param PHP64 $n
+ * @return PHP64[]
+ */
+ public function extendedGCD(PHP64 $n)
+ {
+ return $this->extendedGCDHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @param PHP64 $n
+ * @return PHP64
+ */
+ public function gcd(PHP64 $n)
+ {
+ return $this->extendedGCD($n)['gcd'];
+ }
+
+ /**
+ * Logical And
+ *
+ * @param PHP64 $x
+ * @return PHP64
+ */
+ public function bitwise_and(PHP64 $x)
+ {
+ return $this->bitwiseAndHelper($x);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @param PHP64 $x
+ * @return PHP64
+ */
+ public function bitwise_or(PHP64 $x)
+ {
+ return $this->bitwiseOrHelper($x);
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @param PHP64 $x
+ * @return PHP64
+ */
+ public function bitwise_xor(PHP64 $x)
+ {
+ return $this->bitwiseXorHelper($x);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
+ *
+ * @param PHP64 $y
+ * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
+ */
+ public function compare(PHP64 $y)
+ {
+ return parent::compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @param PHP64 $x
+ * @return bool
+ */
+ public function equals(PHP64 $x)
+ {
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param PHP64 $e
+ * @param PHP64 $n
+ * @return PHP64
+ */
+ public function modPow(PHP64 $e, PHP64 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @param PHP64 $e
+ * @param PHP64 $n
+ * @return PHP64|false
+ */
+ public function powMod(PHP64 $e, PHP64 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @param PHP64 $min
+ * @param PHP64 $max
+ * @return false|PHP64
+ */
+ public static function randomRangePrime(PHP64 $min, PHP64 $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @param PHP64 $min
+ * @param PHP64 $max
+ * @return PHP64
+ */
+ public static function randomRange(PHP64 $min, PHP64 $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @param PHP64 $n
+ * @return PHP64
+ */
+ public function pow(PHP64 $n)
+ {
+ return $this->powHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param PHP64 ...$nums
+ * @return PHP64
+ */
+ public static function min(PHP64 ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @param PHP64 ...$nums
+ * @return PHP64
+ */
+ public static function max(PHP64 ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @param PHP64 $min
+ * @param PHP64 $max
+ * @return bool
+ */
+ public function between(PHP64 $min, PHP64 $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php
new file mode 100644
index 000000000..5da8c937e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php
@@ -0,0 +1,203 @@
+<?php
+
+/**
+ * Binary Finite Fields
+ *
+ * Utilizes the factory design pattern
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Math;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BinaryField\Integer;
+use phpseclib3\Math\Common\FiniteField;
+
+/**
+ * Binary Finite Fields
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class BinaryField extends FiniteField
+{
+ /**
+ * Instance Counter
+ *
+ * @var int
+ */
+ private static $instanceCounter = 0;
+
+ /**
+ * Keeps track of current instance
+ *
+ * @var int
+ */
+ protected $instanceID;
+
+ /** @var BigInteger */
+ private $randomMax;
+
+ /**
+ * Default constructor
+ */
+ public function __construct(...$indices)
+ {
+ $m = array_shift($indices);
+ if ($m > 571) {
+ /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines
+ altho theoretically there may be legit reasons to use binary finite fields with larger degrees
+ imposing a limit on the maximum size is both reasonable and precedented. in particular,
+ http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says
+ "implementations SHOULD check that the packet length is reasonable in order for the implementation to
+ avoid denial of service and/or buffer overflow attacks" */
+ throw new \OutOfBoundsException('Degrees larger than 571 are not supported');
+ }
+ $val = str_repeat('0', $m) . '1';
+ foreach ($indices as $index) {
+ $val[$index] = '1';
+ }
+ $modulo = static::base2ToBase256(strrev($val));
+
+ $mStart = 2 * $m - 2;
+ $t = ceil($m / 8);
+ $finalMask = chr((1 << ($m % 8)) - 1);
+ if ($finalMask == "\0") {
+ $finalMask = "\xFF";
+ }
+ $bitLen = $mStart + 1;
+ $pad = ceil($bitLen / 8);
+ $h = $bitLen & 7;
+ $h = $h ? 8 - $h : 0;
+
+ $r = rtrim(substr($val, 0, -1), '0');
+ $u = [static::base2ToBase256(strrev($r))];
+ for ($i = 1; $i < 8; $i++) {
+ $u[] = static::base2ToBase256(strrev(str_repeat('0', $i) . $r));
+ }
+
+ // implements algorithm 2.40 (in section 2.3.5) in "Guide to Elliptic Curve Cryptography"
+ // with W = 8
+ $reduce = function ($c) use ($u, $mStart, $m, $t, $finalMask, $pad, $h) {
+ $c = str_pad($c, $pad, "\0", STR_PAD_LEFT);
+ for ($i = $mStart; $i >= $m;) {
+ $g = $h >> 3;
+ $mask = $h & 7;
+ $mask = $mask ? 1 << (7 - $mask) : 0x80;
+ for (; $mask > 0; $mask >>= 1, $i--, $h++) {
+ if (ord($c[$g]) & $mask) {
+ $temp = $i - $m;
+ $j = $temp >> 3;
+ $k = $temp & 7;
+ $t1 = $j ? substr($c, 0, -$j) : $c;
+ $length = strlen($t1);
+ if ($length) {
+ $t2 = str_pad($u[$k], $length, "\0", STR_PAD_LEFT);
+ $temp = $t1 ^ $t2;
+ $c = $j ? substr_replace($c, $temp, 0, $length) : $temp;
+ }
+ }
+ }
+ }
+ $c = substr($c, -$t);
+ if (strlen($c) == $t) {
+ $c[0] = $c[0] & $finalMask;
+ }
+ return ltrim($c, "\0");
+ };
+
+ $this->instanceID = self::$instanceCounter++;
+ Integer::setModulo($this->instanceID, $modulo);
+ Integer::setRecurringModuloFunction($this->instanceID, $reduce);
+
+ $this->randomMax = new BigInteger($modulo, 2);
+ }
+
+ /**
+ * Returns an instance of a dynamically generated PrimeFieldInteger class
+ *
+ * @param string $num
+ * @return Integer
+ */
+ public function newInteger($num)
+ {
+ return new Integer($this->instanceID, $num instanceof BigInteger ? $num->toBytes() : $num);
+ }
+
+ /**
+ * Returns an integer on the finite field between one and the prime modulo
+ *
+ * @return Integer
+ */
+ public function randomInteger()
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ return new Integer($this->instanceID, BigInteger::randomRange($one, $this->randomMax)->toBytes());
+ }
+
+ /**
+ * Returns the length of the modulo in bytes
+ *
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return strlen(Integer::getModulo($this->instanceID));
+ }
+
+ /**
+ * Returns the length of the modulo in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return strlen(Integer::getModulo($this->instanceID)) << 3;
+ }
+
+ /**
+ * Converts a base-2 string to a base-256 string
+ *
+ * @param string $x
+ * @param int|null $size
+ * @return string
+ */
+ public static function base2ToBase256($x, $size = null)
+ {
+ $str = Strings::bits2bin($x);
+
+ $pad = strlen($x) >> 3;
+ if (strlen($x) & 3) {
+ $pad++;
+ }
+ $str = str_pad($str, $pad, "\0", STR_PAD_LEFT);
+ if (isset($size)) {
+ $str = str_pad($str, $size, "\0", STR_PAD_LEFT);
+ }
+
+ return $str;
+ }
+
+ /**
+ * Converts a base-256 string to a base-2 string
+ *
+ * @param string $x
+ * @return string
+ */
+ public static function base256ToBase2($x)
+ {
+ if (function_exists('gmp_import')) {
+ return gmp_strval(gmp_import($x), 2);
+ }
+
+ return Strings::bin2bits($x);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php
new file mode 100644
index 000000000..8e880589c
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField/Integer.php
@@ -0,0 +1,516 @@
+<?php
+
+/**
+ * Binary Finite Fields
+ *
+ * In a binary finite field numbers are actually polynomial equations. If you
+ * represent the number as a sequence of bits you get a sequence of 1's or 0's.
+ * These 1's or 0's represent the coefficients of the x**n, where n is the
+ * location of the given bit. When you add numbers over a binary finite field
+ * the result should have a coefficient of 1 or 0 as well. Hence addition
+ * and subtraction become the same operation as XOR.
+ * eg. 1 + 1 + 1 == 3 % 2 == 1 or 0 - 1 == -1 % 2 == 1
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Math\BinaryField;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\BinaryField;
+use phpseclib3\Math\Common\FiniteField\Integer as Base;
+
+/**
+ * Binary Finite Fields
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Integer extends Base
+{
+ /**
+ * Holds the BinaryField's value
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Keeps track of current instance
+ *
+ * @var int
+ */
+ protected $instanceID;
+
+ /**
+ * Holds the PrimeField's modulo
+ *
+ * @var array<int, string>
+ */
+ protected static $modulo;
+
+ /**
+ * Holds a pre-generated function to perform modulo reductions
+ *
+ * @var callable[]
+ */
+ protected static $reduce;
+
+ /**
+ * Default constructor
+ */
+ public function __construct($instanceID, $num = '')
+ {
+ $this->instanceID = $instanceID;
+ if (!strlen($num)) {
+ $this->value = '';
+ } else {
+ $reduce = static::$reduce[$instanceID];
+ $this->value = $reduce($num);
+ }
+ }
+
+ /**
+ * Set the modulo for a given instance
+ * @param int $instanceID
+ * @param string $modulo
+ */
+ public static function setModulo($instanceID, $modulo)
+ {
+ static::$modulo[$instanceID] = $modulo;
+ }
+
+ /**
+ * Set the modulo for a given instance
+ */
+ public static function setRecurringModuloFunction($instanceID, callable $function)
+ {
+ static::$reduce[$instanceID] = $function;
+ }
+
+ /**
+ * Tests a parameter to see if it's of the right instance
+ *
+ * Throws an exception if the incorrect class is being utilized
+ */
+ private static function checkInstance(self $x, self $y)
+ {
+ if ($x->instanceID != $y->instanceID) {
+ throw new \UnexpectedValueException('The instances of the two BinaryField\Integer objects do not match');
+ }
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * @return bool
+ */
+ public function equals(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ return $this->value == $x->value;
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * @return int
+ */
+ public function compare(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ $a = $this->value;
+ $b = $x->value;
+
+ $length = max(strlen($a), strlen($b));
+
+ $a = str_pad($a, $length, "\0", STR_PAD_LEFT);
+ $b = str_pad($b, $length, "\0", STR_PAD_LEFT);
+
+ return strcmp($a, $b);
+ }
+
+ /**
+ * Returns the degree of the polynomial
+ *
+ * @param string $x
+ * @return int
+ */
+ private static function deg($x)
+ {
+ $x = ltrim($x, "\0");
+ $xbit = decbin(ord($x[0]));
+ $xlen = $xbit == '0' ? 0 : strlen($xbit);
+ $len = strlen($x);
+ if (!$len) {
+ return -1;
+ }
+ return 8 * strlen($x) - 9 + $xlen;
+ }
+
+ /**
+ * Perform polynomial division
+ *
+ * @return string[]
+ * @link https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
+ */
+ private static function polynomialDivide($x, $y)
+ {
+ // in wikipedia's description of the algorithm, lc() is the leading coefficient. over a binary field that's
+ // always going to be 1.
+
+ $q = chr(0);
+ $d = static::deg($y);
+ $r = $x;
+ while (($degr = static::deg($r)) >= $d) {
+ $s = '1' . str_repeat('0', $degr - $d);
+ $s = BinaryField::base2ToBase256($s);
+ $length = max(strlen($s), strlen($q));
+ $q = !isset($q) ? $s :
+ str_pad($q, $length, "\0", STR_PAD_LEFT) ^
+ str_pad($s, $length, "\0", STR_PAD_LEFT);
+ $s = static::polynomialMultiply($s, $y);
+ $length = max(strlen($r), strlen($s));
+ $r = str_pad($r, $length, "\0", STR_PAD_LEFT) ^
+ str_pad($s, $length, "\0", STR_PAD_LEFT);
+ }
+
+ return [ltrim($q, "\0"), ltrim($r, "\0")];
+ }
+
+ /**
+ * Perform polynomial multiplation in the traditional way
+ *
+ * @return string
+ * @link https://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplication
+ */
+ private static function regularPolynomialMultiply($x, $y)
+ {
+ $precomputed = [ltrim($x, "\0")];
+ $x = strrev(BinaryField::base256ToBase2($x));
+ $y = strrev(BinaryField::base256ToBase2($y));
+ if (strlen($x) == strlen($y)) {
+ $length = strlen($x);
+ } else {
+ $length = max(strlen($x), strlen($y));
+ $x = str_pad($x, $length, '0');
+ $y = str_pad($y, $length, '0');
+ }
+ $result = str_repeat('0', 2 * $length - 1);
+ $result = BinaryField::base2ToBase256($result);
+ $size = strlen($result);
+ $x = strrev($x);
+
+ // precompute left shift 1 through 7
+ for ($i = 1; $i < 8; $i++) {
+ $precomputed[$i] = BinaryField::base2ToBase256($x . str_repeat('0', $i));
+ }
+ for ($i = 0; $i < strlen($y); $i++) {
+ if ($y[$i] == '1') {
+ $temp = $precomputed[$i & 7] . str_repeat("\0", $i >> 3);
+ $result ^= str_pad($temp, $size, "\0", STR_PAD_LEFT);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Perform polynomial multiplation
+ *
+ * Uses karatsuba multiplication to reduce x-bit multiplications to a series of 32-bit multiplications
+ *
+ * @return string
+ * @link https://en.wikipedia.org/wiki/Karatsuba_algorithm
+ */
+ private static function polynomialMultiply($x, $y)
+ {
+ if (strlen($x) == strlen($y)) {
+ $length = strlen($x);
+ } else {
+ $length = max(strlen($x), strlen($y));
+ $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
+ $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
+ }
+
+ switch (true) {
+ case PHP_INT_SIZE == 8 && $length <= 4:
+ return $length != 4 ?
+ self::subMultiply(str_pad($x, 4, "\0", STR_PAD_LEFT), str_pad($y, 4, "\0", STR_PAD_LEFT)) :
+ self::subMultiply($x, $y);
+ case PHP_INT_SIZE == 4 || $length > 32:
+ return self::regularPolynomialMultiply($x, $y);
+ }
+
+ $m = $length >> 1;
+
+ $x1 = substr($x, 0, -$m);
+ $x0 = substr($x, -$m);
+ $y1 = substr($y, 0, -$m);
+ $y0 = substr($y, -$m);
+
+ $z2 = self::polynomialMultiply($x1, $y1);
+ $z0 = self::polynomialMultiply($x0, $y0);
+ $z1 = self::polynomialMultiply(
+ self::subAdd2($x1, $x0),
+ self::subAdd2($y1, $y0)
+ );
+
+ $z1 = self::subAdd3($z1, $z2, $z0);
+
+ $xy = self::subAdd3(
+ $z2 . str_repeat("\0", 2 * $m),
+ $z1 . str_repeat("\0", $m),
+ $z0
+ );
+
+ return ltrim($xy, "\0");
+ }
+
+ /**
+ * Perform polynomial multiplication on 2x 32-bit numbers, returning
+ * a 64-bit number
+ *
+ * @param string $x
+ * @param string $y
+ * @return string
+ * @link https://www.bearssl.org/constanttime.html#ghash-for-gcm
+ */
+ private static function subMultiply($x, $y)
+ {
+ $x = unpack('N', $x)[1];
+ $y = unpack('N', $y)[1];
+
+ $x0 = $x & 0x11111111;
+ $x1 = $x & 0x22222222;
+ $x2 = $x & 0x44444444;
+ $x3 = $x & 0x88888888;
+
+ $y0 = $y & 0x11111111;
+ $y1 = $y & 0x22222222;
+ $y2 = $y & 0x44444444;
+ $y3 = $y & 0x88888888;
+
+ $z0 = ($x0 * $y0) ^ ($x1 * $y3) ^ ($x2 * $y2) ^ ($x3 * $y1);
+ $z1 = ($x0 * $y1) ^ ($x1 * $y0) ^ ($x2 * $y3) ^ ($x3 * $y2);
+ $z2 = ($x0 * $y2) ^ ($x1 * $y1) ^ ($x2 * $y0) ^ ($x3 * $y3);
+ $z3 = ($x0 * $y3) ^ ($x1 * $y2) ^ ($x2 * $y1) ^ ($x3 * $y0);
+
+ $z0 &= 0x1111111111111111;
+ $z1 &= 0x2222222222222222;
+ $z2 &= 0x4444444444444444;
+ $z3 &= -8608480567731124088; // 0x8888888888888888 gets interpreted as a float
+
+ $z = $z0 | $z1 | $z2 | $z3;
+
+ return pack('J', $z);
+ }
+
+ /**
+ * Adds two numbers
+ *
+ * @param string $x
+ * @param string $y
+ * @return string
+ */
+ private static function subAdd2($x, $y)
+ {
+ $length = max(strlen($x), strlen($y));
+ $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
+ $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
+ return $x ^ $y;
+ }
+
+ /**
+ * Adds three numbers
+ *
+ * @param string $x
+ * @param string $y
+ * @return string
+ */
+ private static function subAdd3($x, $y, $z)
+ {
+ $length = max(strlen($x), strlen($y), strlen($z));
+ $x = str_pad($x, $length, "\0", STR_PAD_LEFT);
+ $y = str_pad($y, $length, "\0", STR_PAD_LEFT);
+ $z = str_pad($z, $length, "\0", STR_PAD_LEFT);
+ return $x ^ $y ^ $z;
+ }
+
+ /**
+ * Adds two BinaryFieldIntegers.
+ *
+ * @return static
+ */
+ public function add(self $y)
+ {
+ static::checkInstance($this, $y);
+
+ $length = strlen(static::$modulo[$this->instanceID]);
+
+ $x = str_pad($this->value, $length, "\0", STR_PAD_LEFT);
+ $y = str_pad($y->value, $length, "\0", STR_PAD_LEFT);
+
+ return new static($this->instanceID, $x ^ $y);
+ }
+
+ /**
+ * Subtracts two BinaryFieldIntegers.
+ *
+ * @return static
+ */
+ public function subtract(self $x)
+ {
+ return $this->add($x);
+ }
+
+ /**
+ * Multiplies two BinaryFieldIntegers.
+ *
+ * @return static
+ */
+ public function multiply(self $y)
+ {
+ static::checkInstance($this, $y);
+
+ return new static($this->instanceID, static::polynomialMultiply($this->value, $y->value));
+ }
+
+ /**
+ * Returns the modular inverse of a BinaryFieldInteger
+ *
+ * @return static
+ */
+ public function modInverse()
+ {
+ $remainder0 = static::$modulo[$this->instanceID];
+ $remainder1 = $this->value;
+
+ if ($remainder1 == '') {
+ return new static($this->instanceID);
+ }
+
+ $aux0 = "\0";
+ $aux1 = "\1";
+ while ($remainder1 != "\1") {
+ list($q, $r) = static::polynomialDivide($remainder0, $remainder1);
+ $remainder0 = $remainder1;
+ $remainder1 = $r;
+ // the auxiliary in row n is given by the sum of the auxiliary in
+ // row n-2 and the product of the quotient and the auxiliary in row
+ // n-1
+ $temp = static::polynomialMultiply($aux1, $q);
+ $aux = str_pad($aux0, strlen($temp), "\0", STR_PAD_LEFT) ^
+ str_pad($temp, strlen($aux0), "\0", STR_PAD_LEFT);
+ $aux0 = $aux1;
+ $aux1 = $aux;
+ }
+
+ $temp = new static($this->instanceID);
+ $temp->value = ltrim($aux1, "\0");
+ return $temp;
+ }
+
+ /**
+ * Divides two PrimeFieldIntegers.
+ *
+ * @return static
+ */
+ public function divide(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ $x = $x->modInverse();
+ return $this->multiply($x);
+ }
+
+ /**
+ * Negate
+ *
+ * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo
+ * so 0-12 is the same thing as modulo-12
+ *
+ * @return object
+ */
+ public function negate()
+ {
+ $x = str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT);
+
+ return new static($this->instanceID, $x ^ static::$modulo[$this->instanceID]);
+ }
+
+ /**
+ * Returns the modulo
+ *
+ * @return string
+ */
+ public static function getModulo($instanceID)
+ {
+ return static::$modulo[$instanceID];
+ }
+
+ /**
+ * Converts an Integer to a byte string (eg. base-256).
+ *
+ * @return string
+ */
+ public function toBytes()
+ {
+ return str_pad($this->value, strlen(static::$modulo[$this->instanceID]), "\0", STR_PAD_LEFT);
+ }
+
+ /**
+ * Converts an Integer to a hex string (eg. base-16).
+ *
+ * @return string
+ */
+ public function toHex()
+ {
+ return Strings::bin2hex($this->toBytes());
+ }
+
+ /**
+ * Converts an Integer to a bit string (eg. base-2).
+ *
+ * @return string
+ */
+ public function toBits()
+ {
+ //return str_pad(BinaryField::base256ToBase2($this->value), strlen(static::$modulo[$this->instanceID]), '0', STR_PAD_LEFT);
+ return BinaryField::base256ToBase2($this->value);
+ }
+
+ /**
+ * Converts an Integer to a BigInteger
+ *
+ * @return string
+ */
+ public function toBigInteger()
+ {
+ return new BigInteger($this->value, 256);
+ }
+
+ /**
+ * __toString() magic method
+ *
+ */
+ public function __toString()
+ {
+ return (string) $this->toBigInteger();
+ }
+
+ /**
+ * __debugInfo() magic method
+ *
+ */
+ public function __debugInfo()
+ {
+ return ['value' => $this->toHex()];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php b/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php
new file mode 100644
index 000000000..2ea5f4858
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * Finite Fields Base Class
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Math\Common;
+
+/**
+ * Finite Fields
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class FiniteField
+{
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php b/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php
new file mode 100644
index 000000000..3c959e94f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/Common/FiniteField/Integer.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * Finite Field Integer Base Class
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Math\Common\FiniteField;
+
+/**
+ * Finite Field Integer
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+abstract class Integer implements \JsonSerializable
+{
+ /**
+ * JSON Serialize
+ *
+ * Will be called, automatically, when json_encode() is called on a BigInteger object.
+ *
+ * PHP Serialize isn't supported because unserializing would require the factory be
+ * serialized as well and that just sounds like too much
+ *
+ * @return array{hex: string}
+ */
+ #[\ReturnTypeWillChange]
+ public function jsonSerialize()
+ {
+ return ['hex' => $this->toHex(true)];
+ }
+
+ /**
+ * Converts an Integer to a hex string (eg. base-16).
+ *
+ * @return string
+ */
+ abstract public function toHex();
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php b/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php
new file mode 100644
index 000000000..1a0667f09
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * Prime Finite Fields
+ *
+ * Utilizes the factory design pattern
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib3\Math;
+
+use phpseclib3\Math\Common\FiniteField;
+use phpseclib3\Math\PrimeField\Integer;
+
+/**
+ * Prime Finite Fields
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class PrimeField extends FiniteField
+{
+ /**
+ * Instance Counter
+ *
+ * @var int
+ */
+ private static $instanceCounter = 0;
+
+ /**
+ * Keeps track of current instance
+ *
+ * @var int
+ */
+ protected $instanceID;
+
+ /**
+ * Default constructor
+ */
+ public function __construct(BigInteger $modulo)
+ {
+ if (!$modulo->isPrime()) {
+ throw new \UnexpectedValueException('PrimeField requires a prime number be passed to the constructor');
+ }
+
+ $this->instanceID = self::$instanceCounter++;
+ Integer::setModulo($this->instanceID, $modulo);
+ Integer::setRecurringModuloFunction($this->instanceID, $modulo->createRecurringModuloFunction());
+ }
+
+ /**
+ * Use a custom defined modular reduction function
+ *
+ * @return void
+ */
+ public function setReduction(\Closure $func)
+ {
+ $this->reduce = $func->bindTo($this, $this);
+ }
+
+ /**
+ * Returns an instance of a dynamically generated PrimeFieldInteger class
+ *
+ * @return Integer
+ */
+ public function newInteger(BigInteger $num)
+ {
+ return new Integer($this->instanceID, $num);
+ }
+
+ /**
+ * Returns an integer on the finite field between one and the prime modulo
+ *
+ * @return Integer
+ */
+ public function randomInteger()
+ {
+ static $one;
+ if (!isset($one)) {
+ $one = new BigInteger(1);
+ }
+
+ return new Integer($this->instanceID, BigInteger::randomRange($one, Integer::getModulo($this->instanceID)));
+ }
+
+ /**
+ * Returns the length of the modulo in bytes
+ *
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return Integer::getModulo($this->instanceID)->getLengthInBytes();
+ }
+
+ /**
+ * Returns the length of the modulo in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return Integer::getModulo($this->instanceID)->getLength();
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ Integer::cleanupCache($this->instanceID);
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php b/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php
new file mode 100644
index 000000000..1ebb2f5d7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php
@@ -0,0 +1,442 @@
+<?php
+
+/**
+ * Prime Finite Fields
+ *
+ * PHP version 5 and 7
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib3\Math\PrimeField;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\Math\Common\FiniteField\Integer as Base;
+
+/**
+ * Prime Finite Fields
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+class Integer extends Base
+{
+ /**
+ * Holds the PrimeField's value
+ *
+ * @var BigInteger
+ */
+ protected $value;
+
+ /**
+ * Keeps track of current instance
+ *
+ * @var int
+ */
+ protected $instanceID;
+
+ /**
+ * Holds the PrimeField's modulo
+ *
+ * @var array<int, BigInteger>
+ */
+ protected static $modulo;
+
+ /**
+ * Holds a pre-generated function to perform modulo reductions
+ *
+ * @var array<int, callable(BigInteger):BigInteger>
+ */
+ protected static $reduce;
+
+ /**
+ * Zero
+ *
+ * @var BigInteger[]
+ */
+ protected static $zero;
+
+ /**
+ * One
+ *
+ * @var BigInteger[]
+ */
+ protected static $one;
+
+ /**
+ * Two
+ *
+ * @var BigInteger[]
+ */
+ protected static $two;
+
+ /**
+ * Default constructor
+ *
+ * @param int $instanceID
+ * @param BigInteger $num
+ */
+ public function __construct($instanceID, $num = null)
+ {
+ $this->instanceID = $instanceID;
+ if (!isset($num)) {
+ $this->value = clone static::$zero[$instanceID];
+ } else {
+ $reduce = static::$reduce[$instanceID];
+ $this->value = $reduce($num);
+ }
+ }
+
+ /**
+ * Set the modulo for a given instance
+ *
+ * @param int $instanceID
+ * @return void
+ */
+ public static function setModulo($instanceID, BigInteger $modulo)
+ {
+ static::$modulo[$instanceID] = $modulo;
+ }
+
+ /**
+ * Set the modulo for a given instance
+ *
+ * @param int $instanceID
+ * @return void
+ */
+ public static function setRecurringModuloFunction($instanceID, callable $function)
+ {
+ static::$reduce[$instanceID] = $function;
+ if (!isset(static::$zero[$instanceID])) {
+ static::$zero[$instanceID] = new BigInteger();
+ }
+ }
+
+ /**
+ * Delete the modulo for a given instance
+ */
+ public static function cleanupCache($instanceID)
+ {
+ unset(static::$modulo[$instanceID]);
+ unset(static::$reduce[$instanceID]);
+ unset(static::$zero[$instanceID]);
+ unset(static::$one[$instanceID]);
+ unset(static::$two[$instanceID]);
+ }
+
+ /**
+ * Returns the modulo
+ *
+ * @param int $instanceID
+ * @return BigInteger
+ */
+ public static function getModulo($instanceID)
+ {
+ return static::$modulo[$instanceID];
+ }
+
+ /**
+ * Tests a parameter to see if it's of the right instance
+ *
+ * Throws an exception if the incorrect class is being utilized
+ *
+ * @return void
+ */
+ public static function checkInstance(self $x, self $y)
+ {
+ if ($x->instanceID != $y->instanceID) {
+ throw new \UnexpectedValueException('The instances of the two PrimeField\Integer objects do not match');
+ }
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * @return bool
+ */
+ public function equals(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ return $this->value->equals($x->value);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * @return int
+ */
+ public function compare(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ return $this->value->compare($x->value);
+ }
+
+ /**
+ * Adds two PrimeFieldIntegers.
+ *
+ * @return static
+ */
+ public function add(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ $temp = new static($this->instanceID);
+ $temp->value = $this->value->add($x->value);
+ if ($temp->value->compare(static::$modulo[$this->instanceID]) >= 0) {
+ $temp->value = $temp->value->subtract(static::$modulo[$this->instanceID]);
+ }
+
+ return $temp;
+ }
+
+ /**
+ * Subtracts two PrimeFieldIntegers.
+ *
+ * @return static
+ */
+ public function subtract(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ $temp = new static($this->instanceID);
+ $temp->value = $this->value->subtract($x->value);
+ if ($temp->value->isNegative()) {
+ $temp->value = $temp->value->add(static::$modulo[$this->instanceID]);
+ }
+
+ return $temp;
+ }
+
+ /**
+ * Multiplies two PrimeFieldIntegers.
+ *
+ * @return static
+ */
+ public function multiply(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ return new static($this->instanceID, $this->value->multiply($x->value));
+ }
+
+ /**
+ * Divides two PrimeFieldIntegers.
+ *
+ * @return static
+ */
+ public function divide(self $x)
+ {
+ static::checkInstance($this, $x);
+
+ $denominator = $x->value->modInverse(static::$modulo[$this->instanceID]);
+ return new static($this->instanceID, $this->value->multiply($denominator));
+ }
+
+ /**
+ * Performs power operation on a PrimeFieldInteger.
+ *
+ * @return static
+ */
+ public function pow(BigInteger $x)
+ {
+ $temp = new static($this->instanceID);
+ $temp->value = $this->value->powMod($x, static::$modulo[$this->instanceID]);
+
+ return $temp;
+ }
+
+ /**
+ * Calculates the square root
+ *
+ * @link https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
+ * @return static|false
+ */
+ public function squareRoot()
+ {
+ if (!isset(static::$one[$this->instanceID])) {
+ static::$one[$this->instanceID] = new BigInteger(1);
+ static::$two[$this->instanceID] = new BigInteger(2);
+ }
+ $one = &static::$one[$this->instanceID];
+ $two = &static::$two[$this->instanceID];
+ $modulo = &static::$modulo[$this->instanceID];
+ $reduce = &static::$reduce[$this->instanceID];
+
+ $p_1 = $modulo->subtract($one);
+ $q = clone $p_1;
+ $s = BigInteger::scan1divide($q);
+ list($pow) = $p_1->divide($two);
+ for ($z = $one; !$z->equals($modulo); $z = $z->add($one)) {
+ $temp = $z->powMod($pow, $modulo);
+ if ($temp->equals($p_1)) {
+ break;
+ }
+ }
+
+ $m = new BigInteger($s);
+ $c = $z->powMod($q, $modulo);
+ $t = $this->value->powMod($q, $modulo);
+ list($temp) = $q->add($one)->divide($two);
+ $r = $this->value->powMod($temp, $modulo);
+
+ while (!$t->equals($one)) {
+ for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) {
+ if ($t->powMod($two->pow($i), $modulo)->equals($one)) {
+ break;
+ }
+ }
+
+ if ($i->compare($m) == 0) {
+ return false;
+ }
+ $b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), $modulo);
+ $m = $i;
+ $c = $reduce($b->multiply($b));
+ $t = $reduce($t->multiply($c));
+ $r = $reduce($r->multiply($b));
+ }
+
+ return new static($this->instanceID, $r);
+ }
+
+ /**
+ * Is Odd?
+ *
+ * @return bool
+ */
+ public function isOdd()
+ {
+ return $this->value->isOdd();
+ }
+
+ /**
+ * Negate
+ *
+ * A negative number can be written as 0-12. With modulos, 0 is the same thing as the modulo
+ * so 0-12 is the same thing as modulo-12
+ *
+ * @return static
+ */
+ public function negate()
+ {
+ return new static($this->instanceID, static::$modulo[$this->instanceID]->subtract($this->value));
+ }
+
+ /**
+ * Converts an Integer to a byte string (eg. base-256).
+ *
+ * @return string
+ */
+ public function toBytes()
+ {
+ if (isset(static::$modulo[$this->instanceID])) {
+ $length = static::$modulo[$this->instanceID]->getLengthInBytes();
+ return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT);
+ }
+ return $this->value->toBytes();
+ }
+
+ /**
+ * Converts an Integer to a hex string (eg. base-16).
+ *
+ * @return string
+ */
+ public function toHex()
+ {
+ return Strings::bin2hex($this->toBytes());
+ }
+
+ /**
+ * Converts an Integer to a bit string (eg. base-2).
+ *
+ * @return string
+ */
+ public function toBits()
+ {
+ // return $this->value->toBits();
+ static $length;
+ if (!isset($length)) {
+ $length = static::$modulo[$this->instanceID]->getLength();
+ }
+
+ return str_pad($this->value->toBits(), $length, '0', STR_PAD_LEFT);
+ }
+
+ /**
+ * Returns the w-ary non-adjacent form (wNAF)
+ *
+ * @param int $w optional
+ * @return array<int, int>
+ */
+ public function getNAF($w = 1)
+ {
+ $w++;
+
+ $zero = &static::$zero[$this->instanceID];
+
+ $mask = new BigInteger((1 << $w) - 1);
+ $sub = new BigInteger(1 << $w);
+ //$sub = new BigInteger(1 << ($w - 1));
+ $d = $this->toBigInteger();
+ $d_i = [];
+
+ $i = 0;
+ while ($d->compare($zero) > 0) {
+ if ($d->isOdd()) {
+ // start mods
+
+ $bigInteger = $d->testBit($w - 1) ?
+ $d->bitwise_and($mask)->subtract($sub) :
+ //$sub->subtract($d->bitwise_and($mask)) :
+ $d->bitwise_and($mask);
+ // end mods
+ $d = $d->subtract($bigInteger);
+ $d_i[$i] = (int) $bigInteger->toString();
+ } else {
+ $d_i[$i] = 0;
+ }
+ $shift = !$d->equals($zero) && $d->bitwise_and($mask)->equals($zero) ? $w : 1; // $w or $w + 1?
+ $d = $d->bitwise_rightShift($shift);
+ while (--$shift > 0) {
+ $d_i[++$i] = 0;
+ }
+ $i++;
+ }
+
+ return $d_i;
+ }
+
+ /**
+ * Converts an Integer to a BigInteger
+ *
+ * @return BigInteger
+ */
+ public function toBigInteger()
+ {
+ return clone $this->value;
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->value;
+ }
+
+ /**
+ * __debugInfo() magic method
+ *
+ * @return array
+ */
+ public function __debugInfo()
+ {
+ return ['value' => $this->toHex()];
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php
deleted file mode 100644
index ee6e1c9d9..000000000
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php
+++ /dev/null
@@ -1,349 +0,0 @@
-<?php
-
-/**
- * Pure-PHP implementation of SCP.
- *
- * PHP version 5
- *
- * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
- *
- * Here's a short example of how to use this library:
- * <code>
- * <?php
- * include 'vendor/autoload.php';
- *
- * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
- * if (!$ssh->login('username', 'password')) {
- * exit('bad login');
- * }
- * $scp = new \phpseclib\Net\SCP($ssh);
- *
- * $scp->put('abcd', str_repeat('x', 1024*1024));
- * ?>
- * </code>
- *
- * @category Net
- * @package SCP
- * @author Jim Wigginton <terrafrost@php.net>
- * @copyright 2010 Jim Wigginton
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @link http://phpseclib.sourceforge.net
- */
-
-namespace phpseclib\Net;
-
-/**
- * Pure-PHP implementations of SCP.
- *
- * @package SCP
- * @author Jim Wigginton <terrafrost@php.net>
- * @access public
- */
-class SCP
-{
- /**#@+
- * @access public
- * @see \phpseclib\Net\SCP::put()
- */
- /**
- * Reads data from a local file.
- */
- const SOURCE_LOCAL_FILE = 1;
- /**
- * Reads data from a string.
- */
- const SOURCE_STRING = 2;
- /**#@-*/
-
- /**#@+
- * @access private
- * @see \phpseclib\Net\SCP::_send()
- * @see \phpseclib\Net\SCP::_receive()
- */
- /**
- * SSH1 is being used.
- */
- const MODE_SSH1 = 1;
- /**
- * SSH2 is being used.
- */
- const MODE_SSH2 = 2;
- /**#@-*/
-
- /**
- * SSH Object
- *
- * @var object
- * @access private
- */
- var $ssh;
-
- /**
- * Packet Size
- *
- * @var int
- * @access private
- */
- var $packet_size;
-
- /**
- * Mode
- *
- * @var int
- * @access private
- */
- var $mode;
-
- /**
- * Default Constructor.
- *
- * Connects to an SSH server
- *
- * @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh
- * @return \phpseclib\Net\SCP
- * @access public
- */
- function __construct($ssh)
- {
- if ($ssh instanceof SSH2) {
- $this->mode = self::MODE_SSH2;
- } elseif ($ssh instanceof SSH1) {
- $this->packet_size = 50000;
- $this->mode = self::MODE_SSH1;
- } else {
- return;
- }
-
- $this->ssh = $ssh;
- }
-
- /**
- * Uploads a file to the SCP server.
- *
- * By default, \phpseclib\Net\SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
- * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes
- * long, containing 'filename.ext' as its contents.
- *
- * Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will
- * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
- * large $remote_file will be, as well.
- *
- * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
- * care of that, yourself.
- *
- * @param string $remote_file
- * @param string $data
- * @param int $mode
- * @param callable $callback
- * @return bool
- * @access public
- */
- function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null)
- {
- if (!isset($this->ssh)) {
- return false;
- }
-
- if (empty($remote_file)) {
- user_error('remote_file cannot be blank', E_USER_NOTICE);
- return false;
- }
-
- if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
- return false;
- }
-
- $temp = $this->_receive();
- if ($temp !== chr(0)) {
- return false;
- }
-
- if ($this->mode == self::MODE_SSH2) {
- $this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4;
- }
-
- $remote_file = basename($remote_file);
-
- if ($mode == self::SOURCE_STRING) {
- $size = strlen($data);
- } else {
- if (!is_file($data)) {
- user_error("$data is not a valid file", E_USER_NOTICE);
- return false;
- }
-
- $fp = @fopen($data, 'rb');
- if (!$fp) {
- return false;
- }
- $size = filesize($data);
- }
-
- $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n");
-
- $temp = $this->_receive();
- if ($temp !== chr(0)) {
- return false;
- }
-
- $sent = 0;
- while ($sent < $size) {
- $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size);
- $this->_send($temp);
- $sent+= strlen($temp);
-
- if (is_callable($callback)) {
- call_user_func($callback, $sent);
- }
- }
- $this->_close();
-
- if ($mode != self::SOURCE_STRING) {
- fclose($fp);
- }
-
- return true;
- }
-
- /**
- * Downloads a file from the SCP server.
- *
- * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
- * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
- * operation
- *
- * @param string $remote_file
- * @param string $local_file
- * @return mixed
- * @access public
- */
- function get($remote_file, $local_file = false)
- {
- if (!isset($this->ssh)) {
- return false;
- }
-
- if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from
- return false;
- }
-
- $this->_send("\0");
-
- if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($this->_receive()), $info)) {
- return false;
- }
-
- $this->_send("\0");
-
- $size = 0;
-
- if ($local_file !== false) {
- $fp = @fopen($local_file, 'wb');
- if (!$fp) {
- return false;
- }
- }
-
- $content = '';
- while ($size < $info['size']) {
- $data = $this->_receive();
-
- // Terminate the loop in case the server repeatedly sends an empty response
- if ($data === false) {
- user_error('No data received from server', E_USER_NOTICE);
- return false;
- }
-
- // SCP usually seems to split stuff out into 16k chunks
- $size+= strlen($data);
-
- if ($local_file === false) {
- $content.= $data;
- } else {
- fputs($fp, $data);
- }
- }
-
- $this->_close();
-
- if ($local_file !== false) {
- fclose($fp);
- return true;
- }
-
- return $content;
- }
-
- /**
- * Sends a packet to an SSH server
- *
- * @param string $data
- * @access private
- */
- function _send($data)
- {
- switch ($this->mode) {
- case self::MODE_SSH2:
- $this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data);
- break;
- case self::MODE_SSH1:
- $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data);
- $this->ssh->_send_binary_packet($data);
- }
- }
-
- /**
- * Receives a packet from an SSH server
- *
- * @return string
- * @access private
- */
- function _receive()
- {
- switch ($this->mode) {
- case self::MODE_SSH2:
- return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true);
- case self::MODE_SSH1:
- if (!$this->ssh->bitmap) {
- return false;
- }
- while (true) {
- $response = $this->ssh->_get_binary_packet();
- switch ($response[SSH1::RESPONSE_TYPE]) {
- case NET_SSH1_SMSG_STDOUT_DATA:
- if (strlen($response[SSH1::RESPONSE_DATA]) < 4) {
- return false;
- }
- extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA]));
- return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
- case NET_SSH1_SMSG_STDERR_DATA:
- break;
- case NET_SSH1_SMSG_EXITSTATUS:
- $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
- fclose($this->ssh->fsock);
- $this->ssh->bitmap = 0;
- return false;
- default:
- user_error('Unknown packet received', E_USER_NOTICE);
- return false;
- }
- }
- }
- }
-
- /**
- * Closes the connection to an SSH server
- *
- * @access private
- */
- function _close()
- {
- switch ($this->mode) {
- case self::MODE_SSH2:
- $this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true);
- break;
- case self::MODE_SSH1:
- $this->ssh->disconnect();
- }
- }
-}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
index 1c6ef7f9a..7e25544cf 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
@@ -14,7 +14,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $sftp = new \phpseclib\Net\SFTP('www.domain.tld');
+ * $sftp = new \phpseclib3\Net\SFTP('www.domain.tld');
* if (!$sftp->login('username', 'password')) {
* exit('Login Failed');
* }
@@ -25,63 +25,66 @@
* ?>
* </code>
*
- * @category Net
- * @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Net;
+namespace phpseclib3\Net;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Exception\FileNotFoundException;
/**
* Pure-PHP implementations of SFTP.
*
- * @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class SFTP extends SSH2
{
/**
* SFTP channel constant
*
- * \phpseclib\Net\SSH2::exec() uses 0 and \phpseclib\Net\SSH2::read() / \phpseclib\Net\SSH2::write() use 1.
+ * \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1.
*
- * @see \phpseclib\Net\SSH2::_send_channel_packet()
- * @see \phpseclib\Net\SSH2::_get_channel_packet()
- * @access private
+ * @see \phpseclib3\Net\SSH2::send_channel_packet()
+ * @see \phpseclib3\Net\SSH2::get_channel_packet()
*/
const CHANNEL = 0x100;
- /**#@+
- * @access public
- * @see \phpseclib\Net\SFTP::put()
- */
/**
* Reads data from a local file.
+ *
+ * @see \phpseclib3\Net\SFTP::put()
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
+ *
+ * @see \phpseclib3\Net\SFTP::put()
*/
// this value isn't really used anymore but i'm keeping it reserved for historical reasons
const SOURCE_STRING = 2;
/**
* Reads data from callback:
* function callback($length) returns string to proceed, null for EOF
+ *
+ * @see \phpseclib3\Net\SFTP::put()
*/
const SOURCE_CALLBACK = 16;
/**
* Resumes an upload
+ *
+ * @see \phpseclib3\Net\SFTP::put()
*/
const RESUME = 4;
/**
* Append a local file to an already existing remote file
+ *
+ * @see \phpseclib3\Net\SFTP::put()
*/
const RESUME_START = 8;
- /**#@-*/
/**
* Packet Types
@@ -90,7 +93,7 @@ class SFTP extends SSH2
* @var array
* @access private
*/
- var $packet_types = array();
+ private static $packet_types = [];
/**
* Status Codes
@@ -99,7 +102,19 @@ class SFTP extends SSH2
* @var array
* @access private
*/
- var $status_codes = array();
+ private static $status_codes = [];
+
+ /** @var array<int, string> */
+ private static $attributes;
+
+ /** @var array<int, string> */
+ private static $open_flags;
+
+ /** @var array<int, string> */
+ private static $open_flags5;
+
+ /** @var array<int, string> */
+ private static $file_types;
/**
* The Request ID
@@ -109,9 +124,8 @@ class SFTP extends SSH2
*
* @var boolean
* @see self::_send_sftp_packet()
- * @access private
*/
- var $use_request_id = false;
+ private $use_request_id = false;
/**
* The Packet Type
@@ -121,82 +135,106 @@ class SFTP extends SSH2
*
* @var int
* @see self::_get_sftp_packet()
- * @access private
*/
- var $packet_type = -1;
+ private $packet_type = -1;
/**
* Packet Buffer
*
* @var string
* @see self::_get_sftp_packet()
- * @access private
*/
- var $packet_buffer = '';
+ private $packet_buffer = '';
/**
* Extensions supported by the server
*
* @var array
* @see self::_initChannel()
- * @access private
*/
- var $extensions = array();
+ private $extensions = [];
/**
* Server SFTP version
*
* @var int
* @see self::_initChannel()
- * @access private
*/
- var $version;
+ private $version;
/**
* Default Server SFTP version
*
* @var int
* @see self::_initChannel()
- * @access private
*/
- var $defaultVersion;
+ private $defaultVersion;
/**
* Preferred SFTP version
*
* @var int
* @see self::_initChannel()
- * @access private
*/
- var $preferredVersion = 3;
+ private $preferredVersion = 3;
/**
* Current working directory
*
- * @var string
+ * @var string|bool
* @see self::realpath()
* @see self::chdir()
- * @access private
*/
- var $pwd = false;
+ private $pwd = false;
/**
* Packet Type Log
*
* @see self::getLog()
* @var array
- * @access private
*/
- var $packet_type_log = array();
+ private $packet_type_log = [];
/**
* Packet Log
*
* @see self::getLog()
* @var array
- * @access private
*/
- var $packet_log = array();
+ private $packet_log = [];
+
+ /**
+ * Real-time log file pointer
+ *
+ * @see self::_append_log()
+ * @var resource|closed-resource
+ */
+ private $realtime_log_file;
+
+ /**
+ * Real-time log file size
+ *
+ * @see self::_append_log()
+ * @var int
+ */
+ private $realtime_log_size;
+
+ /**
+ * Real-time log file wrap boolean
+ *
+ * @see self::_append_log()
+ * @var bool
+ */
+ private $realtime_log_wrap;
+
+ /**
+ * Current log size
+ *
+ * Should never exceed self::LOG_MAX_SIZE
+ *
+ * @var int
+ */
+ private $log_size;
/**
* Error information
@@ -204,9 +242,8 @@ class SFTP extends SSH2
* @see self::getSFTPErrors()
* @see self::getLastSFTPError()
* @var array
- * @access private
*/
- var $sftp_errors = array();
+ private $sftp_errors = [];
/**
* Stat Cache
@@ -218,19 +255,17 @@ class SFTP extends SSH2
* @see self::_remove_from_stat_cache()
* @see self::_query_stat_cache()
* @var array
- * @access private
*/
- var $stat_cache = array();
+ private $stat_cache = [];
/**
* Max SFTP Packet Size
*
* @see self::__construct()
* @see self::get()
- * @var array
- * @access private
+ * @var int
*/
- var $max_sftp_packet;
+ private $max_sftp_packet;
/**
* Stat Cache Flag
@@ -238,9 +273,8 @@ class SFTP extends SSH2
* @see self::disableStatCache()
* @see self::enableStatCache()
* @var bool
- * @access private
*/
- var $use_stat_cache = true;
+ private $use_stat_cache = true;
/**
* Sort Options
@@ -248,9 +282,8 @@ class SFTP extends SSH2
* @see self::_comparator()
* @see self::setListOrder()
* @var array
- * @access private
*/
- var $sortOptions = array();
+ protected $sortOptions = [];
/**
* Canonicalization Flag
@@ -262,18 +295,16 @@ class SFTP extends SSH2
* @see self::disablePathCanonicalization()
* @see self::realpath()
* @var bool
- * @access private
*/
- var $canonicalize_paths = true;
+ private $canonicalize_paths = true;
/**
* Request Buffers
*
* @see self::_get_sftp_packet()
* @var array
- * @access private
*/
- var $requestBuffer = array();
+ private $requestBuffer = [];
/**
* Preserve timestamps on file downloads / uploads
@@ -281,9 +312,8 @@ class SFTP extends SSH2
* @see self::get()
* @see self::put()
* @var bool
- * @access private
*/
- var $preserveTime = false;
+ private $preserveTime = false;
/**
* Arbitrary Length Packets Flag
@@ -296,9 +326,8 @@ class SFTP extends SSH2
* @see self::enableArbitraryLengthPackets()
* @see self::_get_sftp_packet()
* @var bool
- * @access private
*/
- var $allow_arbitrary_length_packets = false;
+ private $allow_arbitrary_length_packets = false;
/**
* Was the last packet due to the channels being closed or not?
@@ -306,212 +335,184 @@ class SFTP extends SSH2
* @see self::get()
* @see self::get_sftp_packet()
* @var bool
- * @access private
*/
- var $channel_close = false;
+ private $channel_close = false;
/**
* Has the SFTP channel been partially negotiated?
*
* @var bool
- * @access private
- */
- var $partial_init = false;
-
- /**
- * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
- * the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why
- *
- * @var array
- * @access private
- */
- var $attributes = array();
-
- /**
- * @var array
- * @access private
- */
- var $open_flags = array();
-
- /**
- * SFTPv5+ changed the flags up:
- * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
- *
- * @var array
- * @access private
*/
- var $open_flags5 = array();
-
- /**
- * http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
- * see \phpseclib\Net\SFTP::_parseLongname() for an explanation
- *
- * @var array
- */
- var $file_types = array();
+ private $partial_init = false;
/**
* Default Constructor.
*
* Connects to an SFTP server
*
- * @param string $host
+ * $host can either be a string, representing the host, or a stream resource.
+ *
+ * @param mixed $host
* @param int $port
* @param int $timeout
- * @return \phpseclib\Net\SFTP
- * @access public
*/
- function __construct($host, $port = 22, $timeout = 10)
+ public function __construct($host, $port = 22, $timeout = 10)
{
parent::__construct($host, $port, $timeout);
$this->max_sftp_packet = 1 << 15;
- $this->packet_types = array(
- 1 => 'NET_SFTP_INIT',
- 2 => 'NET_SFTP_VERSION',
- 3 => 'NET_SFTP_OPEN',
- 4 => 'NET_SFTP_CLOSE',
- 5 => 'NET_SFTP_READ',
- 6 => 'NET_SFTP_WRITE',
- 7 => 'NET_SFTP_LSTAT',
- 9 => 'NET_SFTP_SETSTAT',
- 10 => 'NET_SFTP_FSETSTAT',
- 11 => 'NET_SFTP_OPENDIR',
- 12 => 'NET_SFTP_READDIR',
- 13 => 'NET_SFTP_REMOVE',
- 14 => 'NET_SFTP_MKDIR',
- 15 => 'NET_SFTP_RMDIR',
- 16 => 'NET_SFTP_REALPATH',
- 17 => 'NET_SFTP_STAT',
- 18 => 'NET_SFTP_RENAME',
- 19 => 'NET_SFTP_READLINK',
- 20 => 'NET_SFTP_SYMLINK',
- 21 => 'NET_SFTP_LINK',
-
- 101=> 'NET_SFTP_STATUS',
- 102=> 'NET_SFTP_HANDLE',
- 103=> 'NET_SFTP_DATA',
- 104=> 'NET_SFTP_NAME',
- 105=> 'NET_SFTP_ATTRS',
-
- 200=> 'NET_SFTP_EXTENDED'
- );
- $this->status_codes = array(
- 0 => 'NET_SFTP_STATUS_OK',
- 1 => 'NET_SFTP_STATUS_EOF',
- 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
- 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
- 4 => 'NET_SFTP_STATUS_FAILURE',
- 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
- 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
- 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
- 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
- 9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
- 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
- 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
- 12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
- 13 => 'NET_SFTP_STATUS_NO_MEDIA',
- 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
- 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
- 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
- 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
- 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
- 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
- 20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
- 21 => 'NET_SFTP_STATUS_LINK_LOOP',
- 22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
- 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
- 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
- 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
- 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
- 27 => 'NET_SFTP_STATUS_DELETE_PENDING',
- 28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
- 29 => 'NET_SFTP_STATUS_OWNER_INVALID',
- 30 => 'NET_SFTP_STATUS_GROUP_INVALID',
- 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
- );
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
- // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why
- $this->attributes = array(
- 0x00000001 => 'NET_SFTP_ATTR_SIZE',
- 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
- 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+
- 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
- 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
- 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+
- 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
- 0x00000040 => 'NET_SFTP_ATTR_ACL',
- 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
- 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+
- 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
- 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
- 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
- 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
- 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
- 0x00008000 => 'NET_SFTP_ATTR_CTIME',
- // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
- // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
- // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
- // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
- (PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED'
- );
- $this->open_flags = array(
- 0x00000001 => 'NET_SFTP_OPEN_READ',
- 0x00000002 => 'NET_SFTP_OPEN_WRITE',
- 0x00000004 => 'NET_SFTP_OPEN_APPEND',
- 0x00000008 => 'NET_SFTP_OPEN_CREATE',
- 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
- 0x00000020 => 'NET_SFTP_OPEN_EXCL',
- 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
- );
- // SFTPv5+ changed the flags up:
- // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
- $this->open_flags5 = array(
- // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
- 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
- 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
- 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
- 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
- 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
- // the rest of the flags are not supported
- 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
- 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
- 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
- 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
- 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
- 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
- 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
- 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
- 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
- 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
- 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
- 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
- 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
- );
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
- // see \phpseclib\Net\SFTP::_parseLongname() for an explanation
- $this->file_types = array(
- 1 => 'NET_SFTP_TYPE_REGULAR',
- 2 => 'NET_SFTP_TYPE_DIRECTORY',
- 3 => 'NET_SFTP_TYPE_SYMLINK',
- 4 => 'NET_SFTP_TYPE_SPECIAL',
- 5 => 'NET_SFTP_TYPE_UNKNOWN',
- // the followin types were first defined for use in SFTPv5+
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
- 6 => 'NET_SFTP_TYPE_SOCKET',
- 7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
- 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
- 9 => 'NET_SFTP_TYPE_FIFO'
- );
- $this->_define_array(
- $this->packet_types,
- $this->status_codes,
- $this->attributes,
- $this->open_flags,
- $this->open_flags5,
- $this->file_types
- );
+ if (empty(self::$packet_types)) {
+ self::$packet_types = [
+ 1 => 'NET_SFTP_INIT',
+ 2 => 'NET_SFTP_VERSION',
+ 3 => 'NET_SFTP_OPEN',
+ 4 => 'NET_SFTP_CLOSE',
+ 5 => 'NET_SFTP_READ',
+ 6 => 'NET_SFTP_WRITE',
+ 7 => 'NET_SFTP_LSTAT',
+ 9 => 'NET_SFTP_SETSTAT',
+ 10 => 'NET_SFTP_FSETSTAT',
+ 11 => 'NET_SFTP_OPENDIR',
+ 12 => 'NET_SFTP_READDIR',
+ 13 => 'NET_SFTP_REMOVE',
+ 14 => 'NET_SFTP_MKDIR',
+ 15 => 'NET_SFTP_RMDIR',
+ 16 => 'NET_SFTP_REALPATH',
+ 17 => 'NET_SFTP_STAT',
+ 18 => 'NET_SFTP_RENAME',
+ 19 => 'NET_SFTP_READLINK',
+ 20 => 'NET_SFTP_SYMLINK',
+ 21 => 'NET_SFTP_LINK',
+
+ 101 => 'NET_SFTP_STATUS',
+ 102 => 'NET_SFTP_HANDLE',
+ 103 => 'NET_SFTP_DATA',
+ 104 => 'NET_SFTP_NAME',
+ 105 => 'NET_SFTP_ATTRS',
+
+ 200 => 'NET_SFTP_EXTENDED',
+ 201 => 'NET_SFTP_EXTENDED_REPLY'
+ ];
+ self::$status_codes = [
+ 0 => 'NET_SFTP_STATUS_OK',
+ 1 => 'NET_SFTP_STATUS_EOF',
+ 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
+ 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
+ 4 => 'NET_SFTP_STATUS_FAILURE',
+ 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
+ 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
+ 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
+ 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED',
+ 9 => 'NET_SFTP_STATUS_INVALID_HANDLE',
+ 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH',
+ 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS',
+ 12 => 'NET_SFTP_STATUS_WRITE_PROTECT',
+ 13 => 'NET_SFTP_STATUS_NO_MEDIA',
+ 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM',
+ 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED',
+ 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL',
+ 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT',
+ 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY',
+ 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY',
+ 20 => 'NET_SFTP_STATUS_INVALID_FILENAME',
+ 21 => 'NET_SFTP_STATUS_LINK_LOOP',
+ 22 => 'NET_SFTP_STATUS_CANNOT_DELETE',
+ 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER',
+ 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY',
+ 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT',
+ 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED',
+ 27 => 'NET_SFTP_STATUS_DELETE_PENDING',
+ 28 => 'NET_SFTP_STATUS_FILE_CORRUPT',
+ 29 => 'NET_SFTP_STATUS_OWNER_INVALID',
+ 30 => 'NET_SFTP_STATUS_GROUP_INVALID',
+ 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK'
+ ];
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
+ // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why
+ self::$attributes = [
+ 0x00000001 => 'NET_SFTP_ATTR_SIZE',
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
+ 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+
+ 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
+ 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
+ 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+
+ 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
+ 0x00000040 => 'NET_SFTP_ATTR_ACL',
+ 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
+ 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+
+ 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
+ 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
+ 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
+ 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
+ 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
+ 0x00008000 => 'NET_SFTP_ATTR_CTIME',
+ // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
+ // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
+ // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
+ // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
+ (PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED'
+ ];
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
+ // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
+ // the array for that $this->open5_flags and similarly alter the constant names.
+ self::$open_flags = [
+ 0x00000001 => 'NET_SFTP_OPEN_READ',
+ 0x00000002 => 'NET_SFTP_OPEN_WRITE',
+ 0x00000004 => 'NET_SFTP_OPEN_APPEND',
+ 0x00000008 => 'NET_SFTP_OPEN_CREATE',
+ 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
+ 0x00000020 => 'NET_SFTP_OPEN_EXCL',
+ 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
+ ];
+ // SFTPv5+ changed the flags up:
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
+ self::$open_flags5 = [
+ // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
+ 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
+ 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
+ 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
+ 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
+ 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
+ // the rest of the flags are not supported
+ 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
+ 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
+ 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
+ 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
+ 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
+ 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
+ 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
+ 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
+ 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
+ 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
+ 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
+ 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
+ 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
+ ];
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
+ // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation
+ self::$file_types = [
+ 1 => 'NET_SFTP_TYPE_REGULAR',
+ 2 => 'NET_SFTP_TYPE_DIRECTORY',
+ 3 => 'NET_SFTP_TYPE_SYMLINK',
+ 4 => 'NET_SFTP_TYPE_SPECIAL',
+ 5 => 'NET_SFTP_TYPE_UNKNOWN',
+ // the following types were first defined for use in SFTPv5+
+ // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
+ 6 => 'NET_SFTP_TYPE_SOCKET',
+ 7 => 'NET_SFTP_TYPE_CHAR_DEVICE',
+ 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE',
+ 9 => 'NET_SFTP_TYPE_FIFO'
+ ];
+ self::define_array(
+ self::$packet_types,
+ self::$status_codes,
+ self::$attributes,
+ self::$open_flags,
+ self::$open_flags5,
+ self::$file_types
+ );
+ }
if (!defined('NET_SFTP_QUEUE_SIZE')) {
define('NET_SFTP_QUEUE_SIZE', 32);
@@ -525,16 +526,15 @@ class SFTP extends SSH2
* Check a few things before SFTP functions are called
*
* @return bool
- * @access public
*/
- function _precheck()
+ private function precheck()
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
if ($this->pwd === false) {
- return $this->_init_sftp_connection();
+ return $this->init_sftp_connection();
}
return true;
@@ -543,53 +543,29 @@ class SFTP extends SSH2
/**
* Partially initialize an SFTP connection
*
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access public
*/
- function _partial_init_sftp_connection()
+ private function partial_init_sftp_connection()
{
- $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
-
- $packet = pack(
- 'CNa*N3',
- NET_SSH2_MSG_CHANNEL_OPEN,
- strlen('session'),
- 'session',
- self::CHANNEL,
- $this->window_size,
- 0x4000
- );
-
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
-
- $response = $this->_get_channel_packet(self::CHANNEL, true);
- if ($response === false) {
- return false;
- } elseif ($response === true && $this->isTimeout()) {
+ $response = $this->open_channel(self::CHANNEL, true);
+ if ($response === true && $this->isTimeout()) {
return false;
}
- $packet = pack(
- 'CNNa*CNa*',
+ $packet = Strings::packSSH2(
+ 'CNsbs',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
- strlen('subsystem'),
'subsystem',
- 1,
- strlen('sftp'),
+ true,
'sftp'
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
- $response = $this->_get_channel_packet(self::CHANNEL, true);
+ $response = $this->get_channel_packet(self::CHANNEL, true);
if ($response === false) {
// from PuTTY's psftp.exe
$command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" .
@@ -597,23 +573,19 @@ class SFTP extends SSH2
"exec sftp-server";
// we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
// is redundant
- $packet = pack(
- 'CNNa*CNa*',
+ $packet = Strings::packSSH2(
+ 'CNsCs',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL],
- strlen('exec'),
'exec',
1,
- strlen($command),
$command
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
- $response = $this->_get_channel_packet(self::CHANNEL, true);
+ $response = $this->get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
}
@@ -622,35 +594,19 @@ class SFTP extends SSH2
}
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
+ $this->send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3");
- if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
- return false;
- }
-
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) {
- user_error('Expected SSH_FXP_VERSION');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. '
+ . 'Got packet type: ' . $this->packet_type);
}
$this->use_request_id = true;
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nversion', $this->_string_shift($response, 4)));
- $this->defaultVersion = $version;
+ list($this->defaultVersion) = Strings::unpackSSH2('N', $response);
while (!empty($response)) {
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $key = $this->_string_shift($response, $length);
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $value = $this->_string_shift($response, $length);
+ list($key, $value) = Strings::unpackSSH2('ss', $response);
$this->extensions[$key] = $value;
}
@@ -663,11 +619,10 @@ class SFTP extends SSH2
* (Re)initializes the SFTP channel
*
* @return bool
- * @access private
*/
- function _init_sftp_connection()
+ private function init_sftp_connection()
{
- if (!$this->partial_init && !$this->_partial_init_sftp_connection()) {
+ if (!$this->partial_init && !$this->partial_init_sftp_connection()) {
return false;
}
@@ -691,15 +646,15 @@ class SFTP extends SSH2
So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
- in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the
+ in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib3\Net\SFTP would do is close the
channel and reopen it with a new and updated SSH_FXP_INIT packet.
*/
$this->version = $this->defaultVersion;
if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
$versions = explode(',', $this->extensions['versions']);
- $supported = array(6, 5, 4);
+ $supported = [6, 5, 4];
if ($this->preferredVersion) {
- $supported = array_diff($supported, array($this->preferredVersion));
+ $supported = array_diff($supported, [$this->preferredVersion]);
array_unshift($supported, $this->preferredVersion);
}
foreach ($supported as $ver) {
@@ -708,25 +663,19 @@ class SFTP extends SSH2
break;
}
$this->version = (int) $ver;
- $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver);
- if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) {
- return false;
- }
- $response = $this->_get_sftp_packet();
+ $packet = Strings::packSSH2('ss', 'version-select', "$ver");
+ $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
- }
-
- if (strlen($response) < 4) {
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
- return false;
+ $this->logError($response, $status);
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS_OK. '
+ . ' Got ' . $status);
}
-
break;
}
}
@@ -745,24 +694,23 @@ class SFTP extends SSH2
unset($this->extensions['newline@vandyke.com']);
}
*/
-
if ($this->version < 2 || $this->version > 6) {
return false;
}
$this->pwd = true;
- $this->pwd = $this->_realpath('.');
- if ($this->pwd === false) {
+ try {
+ $this->pwd = $this->realpath('.');
+ } catch (\UnexpectedValueException $e) {
if (!$this->canonicalize_paths) {
- user_error('Unable to canonicalize current working directory');
- return false;
+ throw $e;
}
$this->canonicalize_paths = false;
- $this->_reset_sftp();
- return $this->_init_sftp_connection();
+ $this->reset_sftp();
+ return $this->init_sftp_connection();
}
- $this->_update_stat_cache($this->pwd, array());
+ $this->update_stat_cache($this->pwd, []);
return true;
}
@@ -770,9 +718,8 @@ class SFTP extends SSH2
/**
* Disable the stat cache
*
- * @access public
*/
- function disableStatCache()
+ public function disableStatCache()
{
$this->use_stat_cache = false;
}
@@ -780,9 +727,8 @@ class SFTP extends SSH2
/**
* Enable the stat cache
*
- * @access public
*/
- function enableStatCache()
+ public function enableStatCache()
{
$this->use_stat_cache = true;
}
@@ -790,19 +736,17 @@ class SFTP extends SSH2
/**
* Clear the stat cache
*
- * @access public
*/
- function clearStatCache()
+ public function clearStatCache()
{
- $this->stat_cache = array();
+ $this->stat_cache = [];
}
/**
* Enable path canonicalization
*
- * @access public
*/
- function enablePathCanonicalization()
+ public function enablePathCanonicalization()
{
$this->canonicalize_paths = true;
}
@@ -812,9 +756,8 @@ class SFTP extends SSH2
*
* If this is enabled then $sftp->pwd() will not return the canonicalized absolute path
*
- * @access public
*/
- function disablePathCanonicalization()
+ public function disablePathCanonicalization()
{
$this->canonicalize_paths = false;
}
@@ -822,9 +765,8 @@ class SFTP extends SSH2
/**
* Enable arbitrary length packets
*
- * @access public
*/
- function enableArbitraryLengthPackets()
+ public function enableArbitraryLengthPackets()
{
$this->allow_arbitrary_length_packets = true;
}
@@ -832,9 +774,8 @@ class SFTP extends SSH2
/**
* Disable arbitrary length packets
*
- * @access public
*/
- function disableArbitraryLengthPackets()
+ public function disableArbitraryLengthPackets()
{
$this->allow_arbitrary_length_packets = false;
}
@@ -842,12 +783,11 @@ class SFTP extends SSH2
/**
* Returns the current directory name
*
- * @return mixed
- * @access public
+ * @return string|bool
*/
- function pwd()
+ public function pwd()
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
@@ -859,47 +799,24 @@ class SFTP extends SSH2
*
* @param string $response
* @param int $status
- * @access public
*/
- function _logError($response, $status = -1)
+ private function logError($response, $status = -1)
{
if ($status == -1) {
- if (strlen($response) < 4) {
- return;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
}
- $error = $this->status_codes[$status];
+ $error = self::$status_codes[$status];
if ($this->version > 2) {
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
+ list($message) = Strings::unpackSSH2('s', $response);
+ $this->sftp_errors[] = "$error: $message";
} else {
$this->sftp_errors[] = $error;
}
}
/**
- * Returns canonicalized absolute pathname
- *
- * realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input
- * path and returns the canonicalized absolute pathname.
- *
- * @param string $path
- * @return mixed
- * @access public
- */
- function realpath($path)
- {
- if (!$this->_precheck()) {
- return false;
- }
-
- return $this->_realpath($path);
- }
-
- /**
* Canonicalize the Server-Side Path Name
*
* SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
@@ -910,11 +827,17 @@ class SFTP extends SSH2
* @see self::chdir()
* @see self::disablePathCanonicalization()
* @param string $path
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed
- * @access private
*/
- function _realpath($path)
+ public function realpath($path)
{
+ if ($this->precheck() === false) {
+ return false;
+ }
+
+ $path = (string) $path;
+
if (!$this->canonicalize_paths) {
if ($this->pwd === true) {
return '.';
@@ -922,9 +845,8 @@ class SFTP extends SSH2
if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}
-
$parts = explode('/', $path);
- $afterPWD = $beforePWD = array();
+ $afterPWD = $beforePWD = [];
foreach ($parts as $part) {
switch ($part) {
//case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137
@@ -941,34 +863,28 @@ class SFTP extends SSH2
$afterPWD[] = $part;
}
}
-
$beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.';
return $beforePWD . '/' . implode('/', $afterPWD);
}
if ($this->pwd === true) {
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
- if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_REALPATH, Strings::packSSH2('s', $path));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
// although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
// should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
// at is the first part and that part is defined the same in SFTP versions 3 through 6.
- $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- return $this->_string_shift($response, $length);
+ list(, $filename) = Strings::unpackSSH2('Ns', $response);
+ return $filename;
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
default:
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
}
@@ -977,7 +893,7 @@ class SFTP extends SSH2
}
$path = explode('/', $path);
- $new = array();
+ $new = [];
foreach ($path as $dir) {
if (!strlen($dir)) {
continue;
@@ -985,6 +901,7 @@ class SFTP extends SSH2
switch ($dir) {
case '..':
array_pop($new);
+ // fall-through
case '.':
break;
default:
@@ -999,27 +916,29 @@ class SFTP extends SSH2
* Changes the current directory
*
* @param string $dir
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access public
*/
- function chdir($dir)
+ public function chdir($dir)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
+ $dir = (string) $dir;
+
// assume current dir if $dir is empty
if ($dir === '') {
$dir = './';
// suffix a slash if needed
} elseif ($dir[strlen($dir) - 1] != '/') {
- $dir.= '/';
+ $dir .= '/';
}
- $dir = $this->_realpath($dir);
+ $dir = $this->realpath($dir);
// confirm that $dir is, in fact, a valid directory
- if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) {
+ if ($this->use_stat_cache && is_array($this->query_stat_cache($dir))) {
$this->pwd = $dir;
return true;
}
@@ -1029,29 +948,27 @@ class SFTP extends SSH2
// the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
// way to get those with SFTP
- if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir));
- // see \phpseclib\Net\SFTP::nlist() for a more thorough explanation of the following
- $response = $this->_get_sftp_packet();
+ // see \phpseclib3\Net\SFTP::nlist() for a more thorough explanation of the following
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' .
+ 'Got packet type: ' . $this->packet_type);
}
- if (!$this->_close_handle($handle)) {
+ if (!$this->close_handle($handle)) {
return false;
}
- $this->_update_stat_cache($dir, array());
+ $this->update_stat_cache($dir, []);
$this->pwd = $dir;
return true;
@@ -1062,12 +979,11 @@ class SFTP extends SSH2
*
* @param string $dir
* @param bool $recursive
- * @return mixed
- * @access public
+ * @return array|false
*/
- function nlist($dir = '.', $recursive = false)
+ public function nlist($dir = '.', $recursive = false)
{
- return $this->_nlist_helper($dir, $recursive, '');
+ return $this->nlist_helper($dir, $recursive, '');
}
/**
@@ -1076,12 +992,11 @@ class SFTP extends SSH2
* @param string $dir
* @param bool $recursive
* @param string $relativeDir
- * @return mixed
- * @access private
+ * @return array|false
*/
- function _nlist_helper($dir, $recursive, $relativeDir)
+ private function nlist_helper($dir, $recursive, $relativeDir)
{
- $files = $this->_list($dir, false);
+ $files = $this->readlist($dir, false);
// If we get an int back, then that is an "unexpected" status.
// We do not have a file list, so return false.
@@ -1093,17 +1008,15 @@ class SFTP extends SSH2
return $files;
}
- $result = array();
+ $result = [];
foreach ($files as $value) {
if ($value == '.' || $value == '..') {
- if ($relativeDir == '') {
- $result[] = $value;
- }
+ $result[] = $relativeDir . $value;
continue;
}
- if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) {
- $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
- $temp = is_array($temp) ? $temp : array();
+ if (is_array($this->query_stat_cache($this->realpath($dir . '/' . $value)))) {
+ $temp = $this->nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
+ $temp = is_array($temp) ? $temp : [];
$result = array_merge($result, $temp);
} else {
$result[] = $relativeDir . $value;
@@ -1118,12 +1031,11 @@ class SFTP extends SSH2
*
* @param string $dir
* @param bool $recursive
- * @return mixed
- * @access public
+ * @return array|false
*/
- function rawlist($dir = '.', $recursive = false)
+ public function rawlist($dir = '.', $recursive = false)
{
- $files = $this->_list($dir, true);
+ $files = $this->readlist($dir, true);
// If we get an int back, then that is an "unexpected" status.
// We do not have a file list, so return false.
@@ -1145,7 +1057,7 @@ class SFTP extends SSH2
$is_directory = false;
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
- $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)));
+ $is_directory = is_array($this->query_stat_cache($this->realpath($dir . '/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
@@ -1169,26 +1081,24 @@ class SFTP extends SSH2
*
* @param string $dir
* @param bool $raw
- * @return mixed
- * @access private
+ * @return array|false
+ * @throws \UnexpectedValueException on receipt of unexpected packets
*/
- function _list($dir, $raw = true)
+ private function readlist($dir, $raw = true)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $dir = $this->_realpath($dir . '/');
+ $dir = $this->realpath($dir . '/');
if ($dir === false) {
return false;
}
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
- if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_OPENDIR, Strings::packSSH2('s', $dir));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
@@ -1198,95 +1108,76 @@ class SFTP extends SSH2
break;
case NET_SFTP_STATUS:
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
- $this->_logError($response, $status);
+ list($status) = Strings::unpackSSH2('N', $response);
+ $this->logError($response, $status);
return $status;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- $this->_update_stat_cache($dir, array());
+ $this->update_stat_cache($dir, []);
- $contents = array();
+ $contents = [];
while (true) {
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
// why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
// SSH_MSG_CHANNEL_DATA messages is not known to me.
- if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_READDIR, Strings::packSSH2('s', $handle));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ list($count) = Strings::unpackSSH2('N', $response);
for ($i = 0; $i < $count; $i++) {
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $shortname = $this->_string_shift($response, $length);
+ list($shortname) = Strings::unpackSSH2('s', $response);
// SFTPv4 "removed the long filename from the names structure-- it can now be
// built from information available in the attrs structure."
if ($this->version < 4) {
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $longname = $this->_string_shift($response, $length);
+ list($longname) = Strings::unpackSSH2('s', $response);
}
- $attributes = $this->_parseAttributes($response);
+ $attributes = $this->parseAttributes($response);
if (!isset($attributes['type']) && $this->version < 4) {
- $fileType = $this->_parseLongname($longname);
+ $fileType = $this->parseLongname($longname);
if ($fileType) {
$attributes['type'] = $fileType;
}
}
- $contents[$shortname] = $attributes + array('filename' => $shortname);
+ $contents[$shortname] = $attributes + ['filename' => $shortname];
if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
- $this->_update_stat_cache($dir . '/' . $shortname, array());
+ $this->update_stat_cache($dir . '/' . $shortname, []);
} else {
if ($shortname == '..') {
- $temp = $this->_realpath($dir . '/..') . '/.';
+ $temp = $this->realpath($dir . '/..') . '/.';
} else {
$temp = $dir . '/' . $shortname;
}
- $this->_update_stat_cache($temp, (object) array('lstat' => $attributes));
+ $this->update_stat_cache($temp, (object) ['lstat' => $attributes]);
}
// SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
// final SSH_FXP_STATUS packet should tell us that, already.
}
break;
case NET_SFTP_STATUS:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_EOF) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return $status;
}
break 2;
default:
- user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
}
- if (!$this->_close_handle($handle)) {
+ if (!$this->close_handle($handle)) {
return false;
}
if (count($this->sortOptions)) {
- uasort($contents, array(&$this, '_comparator'));
+ uasort($contents, [&$this, 'comparator']);
}
return $raw ? $contents : array_map('strval', array_keys($contents));
@@ -1300,9 +1191,8 @@ class SFTP extends SSH2
* @param array $a
* @param array $b
* @return int
- * @access private
*/
- function _comparator($a, $b)
+ private function comparator(array $a, array $b)
{
switch (true) {
case $a['filename'] === '.' || $b['filename'] === '.':
@@ -1343,10 +1233,10 @@ class SFTP extends SSH2
return $order === SORT_DESC ? -$result : $result;
}
break;
- case 'permissions':
case 'mode':
- $a[$sort]&= 07777;
- $b[$sort]&= 07777;
+ $a[$sort] &= 07777;
+ $b[$sort] &= 07777;
+ // fall-through
default:
if ($a[$sort] === $b[$sort]) {
break;
@@ -1374,50 +1264,30 @@ class SFTP extends SSH2
* $sftp->setListOrder();
* Don't do any sort of sorting
*
- * @access public
+ * @param string ...$args
*/
- function setListOrder()
+ public function setListOrder(...$args)
{
- $this->sortOptions = array();
- $args = func_get_args();
+ $this->sortOptions = [];
if (empty($args)) {
return;
}
$len = count($args) & 0x7FFFFFFE;
- for ($i = 0; $i < $len; $i+=2) {
+ for ($i = 0; $i < $len; $i += 2) {
$this->sortOptions[$args[$i]] = $args[$i + 1];
}
if (!count($this->sortOptions)) {
- $this->sortOptions = array('bogus' => true);
+ $this->sortOptions = ['bogus' => true];
}
}
/**
- * Returns the file size, in bytes, or false, on failure
- *
- * Files larger than 4GB will show up as being exactly 4GB.
- *
- * @param string $filename
- * @return mixed
- * @access public
- */
- function size($filename)
- {
- $result = $this->stat($filename);
- if ($result === false) {
- return false;
- }
- return isset($result['size']) ? $result['size'] : -1;
- }
-
- /**
* Save files / directories to cache
*
* @param string $path
* @param mixed $value
- * @access private
*/
- function _update_stat_cache($path, $value)
+ private function update_stat_cache($path, $value)
{
if ($this->use_stat_cache === false) {
return;
@@ -1433,10 +1303,10 @@ class SFTP extends SSH2
// 1. a file was deleted and changed to a directory behind phpseclib's back
// 2. it's a symlink. when lstat is done it's unclear what it's a symlink to
if (is_object($temp)) {
- $temp = array();
+ $temp = [];
}
if (!isset($temp[$dir])) {
- $temp[$dir] = array();
+ $temp[$dir] = [];
}
if ($i === $max) {
if (is_object($temp[$dir]) && is_object($value)) {
@@ -1459,9 +1329,8 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access private
*/
- function _remove_from_stat_cache($path)
+ private function remove_from_stat_cache($path)
{
$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
@@ -1489,9 +1358,8 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access private
*/
- function _query_stat_cache($path)
+ private function query_stat_cache($path)
{
$dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path));
@@ -1514,22 +1382,21 @@ class SFTP extends SSH2
* Returns an array on success and false otherwise.
*
* @param string $filename
- * @return mixed
- * @access public
+ * @return array|false
*/
- function stat($filename)
+ public function stat($filename)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $filename = $this->_realpath($filename);
+ $filename = $this->realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
- $result = $this->_query_stat_cache($filename);
+ $result = $this->query_stat_cache($filename);
if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) {
return $result['.']->stat;
}
@@ -1538,16 +1405,16 @@ class SFTP extends SSH2
}
}
- $stat = $this->_stat($filename, NET_SFTP_STAT);
+ $stat = $this->stat_helper($filename, NET_SFTP_STAT);
if ($stat === false) {
- $this->_remove_from_stat_cache($filename);
+ $this->remove_from_stat_cache($filename);
return false;
}
if (isset($stat['type'])) {
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
- $filename.= '/.';
+ $filename .= '/.';
}
- $this->_update_stat_cache($filename, (object) array('stat' => $stat));
+ $this->update_stat_cache($filename, (object) ['stat' => $stat]);
return $stat;
}
@@ -1558,9 +1425,9 @@ class SFTP extends SSH2
$this->pwd = $pwd;
if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) {
- $filename.= '/.';
+ $filename .= '/.';
}
- $this->_update_stat_cache($filename, (object) array('stat' => $stat));
+ $this->update_stat_cache($filename, (object) ['stat' => $stat]);
return $stat;
}
@@ -1571,22 +1438,21 @@ class SFTP extends SSH2
* Returns an array on success and false otherwise.
*
* @param string $filename
- * @return mixed
- * @access public
+ * @return array|false
*/
- function lstat($filename)
+ public function lstat($filename)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $filename = $this->_realpath($filename);
+ $filename = $this->realpath($filename);
if ($filename === false) {
return false;
}
if ($this->use_stat_cache) {
- $result = $this->_query_stat_cache($filename);
+ $result = $this->query_stat_cache($filename);
if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) {
return $result['.']->lstat;
}
@@ -1595,24 +1461,24 @@ class SFTP extends SSH2
}
}
- $lstat = $this->_stat($filename, NET_SFTP_LSTAT);
+ $lstat = $this->stat_helper($filename, NET_SFTP_LSTAT);
if ($lstat === false) {
- $this->_remove_from_stat_cache($filename);
+ $this->remove_from_stat_cache($filename);
return false;
}
if (isset($lstat['type'])) {
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
- $filename.= '/.';
+ $filename .= '/.';
}
- $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
+ $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
return $lstat;
}
- $stat = $this->_stat($filename, NET_SFTP_STAT);
+ $stat = $this->stat_helper($filename, NET_SFTP_STAT);
if ($lstat != $stat) {
- $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK));
- $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
+ $lstat = array_merge($lstat, ['type' => NET_SFTP_TYPE_SYMLINK]);
+ $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
return $stat;
}
@@ -1623,9 +1489,9 @@ class SFTP extends SSH2
$this->pwd = $pwd;
if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) {
- $filename.= '/.';
+ $filename .= '/.';
}
- $this->_update_stat_cache($filename, (object) array('lstat' => $lstat));
+ $this->update_stat_cache($filename, (object) ['lstat' => $lstat]);
return $lstat;
}
@@ -1633,33 +1499,31 @@ class SFTP extends SSH2
/**
* Returns general information about a file or symbolic link
*
- * Determines information without calling \phpseclib\Net\SFTP::realpath().
+ * Determines information without calling \phpseclib3\Net\SFTP::realpath().
* The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
*
* @param string $filename
* @param int $type
- * @return mixed
- * @access private
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @return array|false
*/
- function _stat($filename, $type)
+ private function stat_helper($filename, $type)
{
// SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
- $packet = pack('Na*', strlen($filename), $filename);
- if (!$this->_send_sftp_packet($type, $packet)) {
- return false;
- }
+ $packet = Strings::packSSH2('s', $filename);
+ $this->send_sftp_packet($type, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
- return $this->_parseAttributes($response);
+ return $this->parseAttributes($response);
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
}
- user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
/**
@@ -1668,13 +1532,12 @@ class SFTP extends SSH2
* @param string $filename
* @param int $new_size
* @return bool
- * @access public
*/
- function truncate($filename, $new_size)
+ public function truncate($filename, $new_size)
{
- $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32
+ $attr = Strings::packSSH2('NQ', NET_SFTP_ATTR_SIZE, $new_size);
- return $this->_setstat($filename, $attr, false);
+ return $this->setstat($filename, $attr, false);
}
/**
@@ -1685,16 +1548,16 @@ class SFTP extends SSH2
* @param string $filename
* @param int $time
* @param int $atime
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access public
*/
- function touch($filename, $time = null, $atime = null)
+ public function touch($filename, $time = null, $atime = null)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $filename = $this->_realpath($filename);
+ $filename = $this->realpath($filename);
if ($filename === false) {
return false;
}
@@ -1706,42 +1569,31 @@ class SFTP extends SSH2
$atime = $time;
}
- if ($this->version < 4) {
- $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time);
- } else {
- $attr = pack(
- 'N5',
- NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
- $atime / 4294967296,
- $atime,
- $time / 4294967296,
- $time
- );
- }
+ $attr = $this->version < 4 ?
+ pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time) :
+ Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $atime, $time);
- $packet = pack('Na*', strlen($filename), $filename);
- $packet.= $this->version >= 5 ?
+ $packet = Strings::packSSH2('s', $filename);
+ $packet .= $this->version >= 5 ?
pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
- $packet.= $attr;
+ $packet .= $attr;
- if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
- return $this->_close_handle(substr($response, 4));
+ return $this->close_handle(substr($response, 4));
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
break;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- return $this->_setstat($filename, $attr, false);
+ return $this->setstat($filename, $attr, false);
}
/**
@@ -1758,9 +1610,8 @@ class SFTP extends SSH2
* @param int|string $uid
* @param bool $recursive
* @return bool
- * @access public
*/
- function chown($filename, $uid, $recursive = false)
+ public function chown($filename, $uid, $recursive = false)
{
/*
quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
@@ -1787,9 +1638,9 @@ class SFTP extends SSH2
// "If either the owner or group field is zero length, the field should be
// considered absent, and no change should be made to that specific field
// during a modification operation"
- pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');
+ Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, $uid, '');
- return $this->_setstat($filename, $attr, $recursive);
+ return $this->setstat($filename, $attr, $recursive);
}
/**
@@ -1806,15 +1657,14 @@ class SFTP extends SSH2
* @param int|string $gid
* @param bool $recursive
* @return bool
- * @access public
*/
- function chgrp($filename, $gid, $recursive = false)
+ public function chgrp($filename, $gid, $recursive = false)
{
$attr = $this->version < 4 ?
pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) :
- pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid);
+ Strings::packSSH2('Nss', NET_SFTP_ATTR_OWNERGROUP, '', $gid);
- return $this->_setstat($filename, $attr, $recursive);
+ return $this->setstat($filename, $attr, $recursive);
}
/**
@@ -1826,10 +1676,10 @@ class SFTP extends SSH2
* @param int $mode
* @param string $filename
* @param bool $recursive
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed
- * @access public
*/
- function chmod($mode, $filename, $recursive = false)
+ public function chmod($mode, $filename, $recursive = false)
{
if (is_string($mode) && is_int($filename)) {
$temp = $mode;
@@ -1838,7 +1688,7 @@ class SFTP extends SSH2
}
$attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
- if (!$this->_setstat($filename, $attr, $recursive)) {
+ if (!$this->setstat($filename, $attr, $recursive)) {
return false;
}
if ($recursive) {
@@ -1850,22 +1700,20 @@ class SFTP extends SSH2
// tell us if the file actually exists.
// incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
$packet = pack('Na*', strlen($filename), $filename);
- if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_STAT, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
- $attrs = $this->_parseAttributes($response);
- return $attrs['permissions'];
+ $attrs = $this->parseAttributes($response);
+ return $attrs['mode'];
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
}
- user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
/**
@@ -1874,35 +1722,34 @@ class SFTP extends SSH2
* @param string $filename
* @param string $attr
* @param bool $recursive
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access private
*/
- function _setstat($filename, $attr, $recursive)
+ private function setstat($filename, $attr, $recursive)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $filename = $this->_realpath($filename);
+ $filename = $this->realpath($filename);
if ($filename === false) {
return false;
}
- $this->_remove_from_stat_cache($filename);
+ $this->remove_from_stat_cache($filename);
if ($recursive) {
$i = 0;
- $result = $this->_setstat_recursive($filename, $attr, $i);
- $this->_read_put_responses($i);
+ $result = $this->setstat_recursive($filename, $attr, $i);
+ $this->read_put_responses($i);
return $result;
}
- $packet = $this->version >= 4 ?
- pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
- pack('Na*a*', strlen($filename), $filename, $attr);
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
- return false;
- }
+ $packet = Strings::packSSH2('s', $filename);
+ $packet .= $this->version >= 4 ?
+ pack('a*Ca*', substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
+ $attr;
+ $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
/*
"Because some systems must use separate system calls to set various attributes, it is possible that a failure
@@ -1911,18 +1758,15 @@ class SFTP extends SSH2
-- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
*/
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
@@ -1938,18 +1782,17 @@ class SFTP extends SSH2
* @param string $attr
* @param int $i
* @return bool
- * @access private
*/
- function _setstat_recursive($path, $attr, &$i)
+ private function setstat_recursive($path, $attr, &$i)
{
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
- $entries = $this->_list($path, true);
+ $entries = $this->readlist($path, true);
if ($entries === false || is_int($entries)) {
- return $this->_setstat($path, $attr, false);
+ return $this->setstat($path, $attr, false);
}
// normally $entries would have at least . and .. but it might not if the directories
@@ -1966,21 +1809,20 @@ class SFTP extends SSH2
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
- if (!$this->_setstat_recursive($temp, $attr, $i)) {
+ if (!$this->setstat_recursive($temp, $attr, $i)) {
return false;
}
} else {
- $packet = $this->version >= 4 ?
- pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
- pack('Na*a*', strlen($temp), $temp, $attr);
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
- return false;
- }
+ $packet = Strings::packSSH2('s', $temp);
+ $packet .= $this->version >= 4 ?
+ pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) :
+ $attr;
+ $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
@@ -1988,17 +1830,16 @@ class SFTP extends SSH2
}
}
- $packet = $this->version >= 4 ?
- pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
- pack('Na*a*', strlen($temp), $temp, $attr);
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
- return false;
- }
+ $packet = Strings::packSSH2('s', $path);
+ $packet .= $this->version >= 4 ?
+ pack('Ca*', NET_SFTP_TYPE_UNKNOWN, $attr) :
+ $attr;
+ $this->send_sftp_packet(NET_SFTP_SETSTAT, $packet);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
@@ -2011,47 +1852,40 @@ class SFTP extends SSH2
* Return the target of a symbolic link
*
* @param string $link
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed
- * @access public
*/
- function readlink($link)
+ public function readlink($link)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $link = $this->_realpath($link);
+ $link = $this->realpath($link);
- if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_READLINK, Strings::packSSH2('s', $link));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_NAME:
break;
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
default:
- user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ list($count) = Strings::unpackSSH2('N', $response);
// the file isn't a symlink
if (!$count) {
return false;
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- return $this->_string_shift($response, $length);
+ list($filename) = Strings::unpackSSH2('s', $response);
+
+ return $filename;
}
/**
@@ -2061,17 +1895,17 @@ class SFTP extends SSH2
*
* @param string $target
* @param string $link
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access public
*/
- function symlink($target, $link)
+ public function symlink($target, $link)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- //$target = $this->_realpath($target);
- $link = $this->_realpath($link);
+ //$target = $this->realpath($target);
+ $link = $this->realpath($link);
/* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 :
@@ -2082,7 +1916,7 @@ class SFTP extends SSH2
*/
if ($this->version == 6) {
$type = NET_SFTP_LINK;
- $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1);
+ $packet = Strings::packSSH2('ssC', $link, $target, 1);
} else {
$type = NET_SFTP_SYMLINK;
/* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
@@ -2100,25 +1934,20 @@ class SFTP extends SSH2
string targetpath
string linkpath */
$packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
- pack('Na*Na*', strlen($target), $target, strlen($link), $link) :
- pack('Na*Na*', strlen($link), $link, strlen($target), $target);
- }
- if (!$this->_send_sftp_packet($type, $packet)) {
- return false;
+ Strings::packSSH2('ss', $target, $link) :
+ Strings::packSSH2('ss', $link, $target);
}
+ $this->send_sftp_packet($type, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
@@ -2132,15 +1961,14 @@ class SFTP extends SSH2
* @param int $mode
* @param bool $recursive
* @return bool
- * @access public
*/
- function mkdir($dir, $mode = -1, $recursive = false)
+ public function mkdir($dir, $mode = -1, $recursive = false)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $dir = $this->_realpath($dir);
+ $dir = $this->realpath($dir);
if ($recursive) {
$dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir));
@@ -2151,12 +1979,12 @@ class SFTP extends SSH2
for ($i = 0; $i < count($dirs); $i++) {
$temp = array_slice($dirs, 0, $i + 1);
$temp = implode('/', $temp);
- $result = $this->_mkdir_helper($temp, $mode);
+ $result = $this->mkdir_helper($temp, $mode);
}
return $result;
}
- return $this->_mkdir_helper($dir, $mode);
+ return $this->mkdir_helper($dir, $mode);
}
/**
@@ -2165,27 +1993,21 @@ class SFTP extends SSH2
* @param string $dir
* @param int $mode
* @return bool
- * @access private
*/
- function _mkdir_helper($dir, $mode)
+ private function mkdir_helper($dir, $mode)
{
// send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing)
- if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_MKDIR, Strings::packSSH2('s', $dir) . "\0\0\0\0");
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
@@ -2200,45 +2022,40 @@ class SFTP extends SSH2
* Removes a directory.
*
* @param string $dir
+ * @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool
- * @access public
*/
- function rmdir($dir)
+ public function rmdir($dir)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $dir = $this->_realpath($dir);
+ $dir = $this->realpath($dir);
if ($dir === false) {
return false;
}
- if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $dir));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
- $this->_remove_from_stat_cache($dir);
+ $this->remove_from_stat_cache($dir);
// the following will do a soft delete, which would be useful if you deleted a file
// and then tried to do a stat on the deleted file. the above, in contrast, does
// a hard delete
- //$this->_update_stat_cache($dir, false);
+ //$this->update_stat_cache($dir, false);
return true;
}
@@ -2246,8 +2063,8 @@ class SFTP extends SSH2
/**
* Uploads a file to the SFTP server.
*
- * By default, \phpseclib\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
- * So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SFTP::get(), you will get a file, twelve bytes
+ * By default, \phpseclib3\Net\SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
+ * So, for example, if you set $data to 'filename.ext' and then do \phpseclib3\Net\SFTP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will
@@ -2279,28 +2096,31 @@ class SFTP extends SSH2
*
* Setting $local_start to > 0 or $mode | self::RESUME_START doesn't do anything unless $mode | self::SOURCE_LOCAL_FILE.
*
+ * {@internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib3\Net\SFTP::setMode().}
+ *
* @param string $remote_file
* @param string|resource $data
* @param int $mode
* @param int $start
* @param int $local_start
* @param callable|null $progressCallback
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid
+ * @throws FileNotFoundException if you're uploading via a file and the file doesn't exist
* @return bool
- * @access public
- * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode().
*/
- function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
+ public function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $remote_file = $this->_realpath($remote_file);
+ $remote_file = $this->realpath($remote_file);
if ($remote_file === false) {
return false;
}
- $this->_remove_from_stat_cache($remote_file);
+ $this->remove_from_stat_cache($remote_file);
if ($this->version >= 5) {
$flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
@@ -2308,43 +2128,43 @@ class SFTP extends SSH2
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
// according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
// in practice, it doesn't seem to do that.
- //$flags|= ($mode & SFTP::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
+ //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
}
if ($start >= 0) {
$offset = $start;
} elseif ($mode & (self::RESUME | self::RESUME_START)) {
// if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
- $size = $this->size($remote_file);
- $offset = $size !== false ? $size : 0;
+ $stat = $this->stat($remote_file);
+ $offset = $stat !== false && $stat['size'] ? $stat['size'] : 0;
} else {
$offset = 0;
if ($this->version >= 5) {
$flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
} else {
- $flags|= NET_SFTP_OPEN_TRUNCATE;
+ $flags |= NET_SFTP_OPEN_TRUNCATE;
}
}
- $packet = pack('Na*', strlen($remote_file), $remote_file);
- $packet.= $this->version >= 5 ?
+ $this->remove_from_stat_cache($remote_file);
+
+ $packet = Strings::packSSH2('s', $remote_file);
+ $packet .= $this->version >= 5 ?
pack('N3', 0, $flags, 0) :
pack('N2', $flags, 0);
- if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
- $this->_logError($response);
+ $this->logError($response);
return false;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
@@ -2352,7 +2172,7 @@ class SFTP extends SSH2
switch (true) {
case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) {
- user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
+ throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
}
$dataCallback = $data;
// do nothing
@@ -2370,8 +2190,7 @@ class SFTP extends SSH2
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
- user_error("$data is not a valid file");
- return false;
+ throw new FileNotFoundException("$data is not a valid file");
}
$fp = @fopen($data, 'rb');
if (!$fp) {
@@ -2385,12 +2204,11 @@ class SFTP extends SSH2
if ($local_start >= 0) {
fseek($fp, $local_start);
- $size-= $local_start;
+ $size -= $local_start;
} elseif ($mode & self::RESUME) {
fseek($fp, $offset);
- $size-= $offset;
+ $size -= $offset;
}
-
} elseif ($dataCallback) {
$size = 0;
} else {
@@ -2402,11 +2220,11 @@ class SFTP extends SSH2
$sftp_packet_size = $this->max_sftp_packet;
// make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header"
- $sftp_packet_size-= strlen($handle) + 25;
+ $sftp_packet_size -= strlen($handle) + 25;
$i = $j = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) {
if ($dataCallback) {
- $temp = call_user_func($dataCallback, $sftp_packet_size);
+ $temp = $dataCallback($sftp_packet_size);
if (is_null($temp)) {
break;
}
@@ -2419,22 +2237,23 @@ class SFTP extends SSH2
$subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
- if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j)) {
+ try {
+ $this->send_sftp_packet(NET_SFTP_WRITE, $packet, $j);
+ } catch (\Exception $e) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
- return false;
+ throw $e;
}
- $sent+= strlen($temp);
+ $sent += strlen($temp);
if (is_callable($progressCallback)) {
- call_user_func($progressCallback, $sent);
+ $progressCallback($sent);
}
$i++;
$j++;
-
if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
$i = 0;
break;
}
@@ -2442,13 +2261,13 @@ class SFTP extends SSH2
}
}
- $result = $this->_close_handle($handle);
+ $result = $this->close_handle($handle);
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
- $this->_close_handle($handle);
+ $this->close_handle($handle);
return false;
}
@@ -2459,21 +2278,11 @@ class SFTP extends SSH2
if ($this->preserveTime) {
$stat = stat($data);
- if ($this->version < 4) {
- $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']);
- } else {
- $attr = pack(
- 'N5',
- NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
- $stat['atime'] / 4294967296,
- $stat['atime'],
- $stat['mtime'] / 4294967296,
- $stat['mtime']
- );
- }
-
- if (!$this->_setstat($remote_file, $attr, false)) {
- user_error('Error setting file time');
+ $attr = $this->version < 4 ?
+ pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']) :
+ Strings::packSSH2('NQ2', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $stat['atime'], $stat['mtime']);
+ if (!$this->setstat($remote_file, $attr, false)) {
+ throw new \RuntimeException('Error setting file time');
}
}
}
@@ -2489,23 +2298,20 @@ class SFTP extends SSH2
*
* @param int $i
* @return bool
- * @access private
+ * @throws \UnexpectedValueException on receipt of unexpected packets
*/
- function _read_put_responses($i)
+ private function read_put_responses($i)
{
while ($i--) {
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
break;
}
}
@@ -2518,28 +2324,23 @@ class SFTP extends SSH2
*
* @param string $handle
* @return bool
- * @access private
+ * @throws \UnexpectedValueException on receipt of unexpected packets
*/
- function _close_handle($handle)
+ private function close_handle($handle)
{
- if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle));
// "The client MUST release all resources associated with the handle regardless of the status."
// -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
@@ -2556,43 +2357,41 @@ class SFTP extends SSH2
* $offset and $length can be used to download files in chunks.
*
* @param string $remote_file
- * @param string $local_file
+ * @param string|bool|resource|callable $local_file
* @param int $offset
* @param int $length
* @param callable|null $progressCallback
- * @return mixed
- * @access public
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @return string|bool
*/
- function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
+ public function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $remote_file = $this->_realpath($remote_file);
+ $remote_file = $this->realpath($remote_file);
if ($remote_file === false) {
return false;
}
- $packet = pack('Na*', strlen($remote_file), $remote_file);
- $packet.= $this->version >= 5 ?
+ $packet = Strings::packSSH2('s', $remote_file);
+ $packet .= $this->version >= 5 ?
pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
pack('N2', NET_SFTP_OPEN_READ, 0);
- if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
- $this->_logError($response);
+ $this->logError($response);
return false;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
if (is_resource($local_file)) {
@@ -2623,15 +2422,17 @@ class SFTP extends SSH2
$packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet;
- $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
- if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) {
+ $packet = Strings::packSSH2('sN3', $handle, $tempoffset / 4294967296, $tempoffset, $packet_size);
+ try {
+ $this->send_sftp_packet(NET_SFTP_READ, $packet, $i);
+ } catch (\Exception $e) {
if ($fclose_check) {
fclose($fp);
}
- return false;
+ throw $e;
}
$packet = null;
- $read+= $packet_size;
+ $read += $packet_size;
$i++;
}
@@ -2646,18 +2447,18 @@ class SFTP extends SSH2
$i--;
if ($clear_responses) {
- $this->_get_sftp_packet($packets_sent - $i);
+ $this->get_sftp_packet($packets_sent - $i);
continue;
} else {
- $response = $this->_get_sftp_packet($packets_sent - $i);
+ $response = $this->get_sftp_packet($packets_sent - $i);
}
switch ($this->packet_type) {
case NET_SFTP_DATA:
$temp = substr($response, 4);
- $offset+= strlen($temp);
+ $offset += strlen($temp);
if ($local_file === false) {
- $content.= $temp;
+ $content .= $temp;
} elseif (is_callable($local_file)) {
$local_file($temp);
} else {
@@ -2670,20 +2471,20 @@ class SFTP extends SSH2
break;
case NET_SFTP_STATUS:
// could, in theory, return false if !strlen($content) but we'll hold off for the time being
- $this->_logError($response);
+ $this->logError($response);
$clear_responses = true; // don't break out of the loop yet, so we can read the remaining responses
break;
default:
if ($fclose_check) {
fclose($fp);
}
- // maybe the file was successfully transferred, maybe it wasn't
if ($this->channel_close) {
$this->partial_init = false;
- $this->_init_sftp_connection();
+ $this->init_sftp_connection();
return false;
} else {
- user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
+ throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
}
$response = null;
@@ -2703,7 +2504,7 @@ class SFTP extends SSH2
}
}
- if (!$this->_close_handle($handle)) {
+ if (!$this->close_handle($handle)) {
return false;
}
@@ -2717,11 +2518,11 @@ class SFTP extends SSH2
* @param string $path
* @param bool $recursive
* @return bool
- * @access public
+ * @throws \UnexpectedValueException on receipt of unexpected packets
*/
- function delete($path, $recursive = true)
+ public function delete($path, $recursive = true)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
@@ -2734,39 +2535,35 @@ class SFTP extends SSH2
return false;
}
- $path = $this->_realpath($path);
+ $path = $this->realpath($path);
if ($path === false) {
return false;
}
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
- if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
- return false;
- }
+ $this->send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path));
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
if (!$recursive) {
return false;
}
+
$i = 0;
- $result = $this->_delete_recursive($path, $i);
- $this->_read_put_responses($i);
+ $result = $this->delete_recursive($path, $i);
+ $this->read_put_responses($i);
return $result;
}
- $this->_remove_from_stat_cache($path);
+ $this->remove_from_stat_cache($path);
return true;
}
@@ -2779,15 +2576,14 @@ class SFTP extends SSH2
* @param string $path
* @param int $i
* @return bool
- * @access private
*/
- function _delete_recursive($path, &$i)
+ private function delete_recursive($path, &$i)
{
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
- $entries = $this->_list($path, true);
+ $entries = $this->readlist($path, true);
// The folder does not exist at all, so we cannot delete it.
if ($entries === NET_SFTP_STATUS_NO_SUCH_FILE) {
@@ -2797,7 +2593,7 @@ class SFTP extends SSH2
// Normally $entries would have at least . and .. but it might not if the directories
// permissions didn't allow reading. If this happens then default to an empty list of files.
if ($entries === false || is_int($entries)) {
- $entries = array();
+ $entries = [];
}
unset($entries['.'], $entries['..']);
@@ -2808,19 +2604,17 @@ class SFTP extends SSH2
$temp = $path . '/' . $filename;
if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) {
- if (!$this->_delete_recursive($temp, $i)) {
+ if (!$this->delete_recursive($temp, $i)) {
return false;
}
} else {
- if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) {
- return false;
- }
- $this->_remove_from_stat_cache($temp);
+ $this->send_sftp_packet(NET_SFTP_REMOVE, Strings::packSSH2('s', $temp));
+ $this->remove_from_stat_cache($temp);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
@@ -2828,15 +2622,13 @@ class SFTP extends SSH2
}
}
- if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) {
- return false;
- }
- $this->_remove_from_stat_cache($path);
+ $this->send_sftp_packet(NET_SFTP_RMDIR, Strings::packSSH2('s', $path));
+ $this->remove_from_stat_cache($path);
$i++;
if ($i >= NET_SFTP_QUEUE_SIZE) {
- if (!$this->_read_put_responses($i)) {
+ if (!$this->read_put_responses($i)) {
return false;
}
$i = 0;
@@ -2850,18 +2642,17 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function file_exists($path)
+ public function file_exists($path)
{
if ($this->use_stat_cache) {
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $path = $this->_realpath($path);
+ $path = $this->realpath($path);
- $result = $this->_query_stat_cache($path);
+ $result = $this->query_stat_cache($path);
if (isset($result)) {
// return true if $result is an array or if it's an stdClass object
@@ -2877,11 +2668,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_dir($path)
+ public function is_dir($path)
{
- $result = $this->_get_stat_cache_prop($path, 'type');
+ $result = $this->get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
@@ -2893,11 +2683,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_file($path)
+ public function is_file($path)
{
- $result = $this->_get_stat_cache_prop($path, 'type');
+ $result = $this->get_stat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
@@ -2909,11 +2698,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_link($path)
+ public function is_link($path)
{
- $result = $this->_get_lstat_cache_prop($path, 'type');
+ $result = $this->get_lstat_cache_prop($path, 'type');
if ($result === false) {
return false;
}
@@ -2925,30 +2713,25 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_readable($path)
+ public function is_readable($path)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $path = $this->_realpath($path);
+ $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_READ, 0);
+ $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
- $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
- if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
- return false;
- }
-
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return false;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
}
@@ -2957,30 +2740,25 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_writable($path)
+ public function is_writable($path)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $path = $this->_realpath($path);
+ $packet = Strings::packSSH2('sNN', $this->realpath($path), NET_SFTP_OPEN_WRITE, 0);
+ $this->send_sftp_packet(NET_SFTP_OPEN, $packet);
- $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
- if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
- return false;
- }
-
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
return true;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return false;
default:
- user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
}
@@ -2991,9 +2769,8 @@ class SFTP extends SSH2
*
* @param string $path
* @return bool
- * @access public
*/
- function is_writeable($path)
+ public function is_writeable($path)
{
return $this->is_writable($path);
}
@@ -3003,11 +2780,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access public
*/
- function fileatime($path)
+ public function fileatime($path)
{
- return $this->_get_stat_cache_prop($path, 'atime');
+ return $this->get_stat_cache_prop($path, 'atime');
}
/**
@@ -3015,11 +2791,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access public
*/
- function filemtime($path)
+ public function filemtime($path)
{
- return $this->_get_stat_cache_prop($path, 'mtime');
+ return $this->get_stat_cache_prop($path, 'mtime');
}
/**
@@ -3027,11 +2802,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access public
*/
- function fileperms($path)
+ public function fileperms($path)
{
- return $this->_get_stat_cache_prop($path, 'permissions');
+ return $this->get_stat_cache_prop($path, 'mode');
}
/**
@@ -3039,11 +2813,10 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access public
*/
- function fileowner($path)
+ public function fileowner($path)
{
- return $this->_get_stat_cache_prop($path, 'uid');
+ return $this->get_stat_cache_prop($path, 'uid');
}
/**
@@ -3051,35 +2824,54 @@ class SFTP extends SSH2
*
* @param string $path
* @return mixed
- * @access public
*/
- function filegroup($path)
+ public function filegroup($path)
{
- return $this->_get_stat_cache_prop($path, 'gid');
+ return $this->get_stat_cache_prop($path, 'gid');
+ }
+
+ /**
+ * Recursively go through rawlist() output to get the total filesize
+ *
+ * @return int
+ */
+ private static function recursiveFilesize(array $files)
+ {
+ $size = 0;
+ foreach ($files as $name => $file) {
+ if ($name == '.' || $name == '..') {
+ continue;
+ }
+ $size += is_array($file) ?
+ self::recursiveFilesize($file) :
+ $file->size;
+ }
+ return $size;
}
/**
* Gets file size
*
* @param string $path
+ * @param bool $recursive
* @return mixed
- * @access public
*/
- function filesize($path)
+ public function filesize($path, $recursive = false)
{
- return $this->_get_stat_cache_prop($path, 'size');
+ return !$recursive || $this->filetype($path) != 'dir' ?
+ $this->get_stat_cache_prop($path, 'size') :
+ self::recursiveFilesize($this->rawlist($path, true));
}
/**
* Gets file type
*
* @param string $path
- * @return mixed
- * @access public
+ * @return string|false
*/
- function filetype($path)
+ public function filetype($path)
{
- $type = $this->_get_stat_cache_prop($path, 'type');
+ $type = $this->get_stat_cache_prop($path, 'type');
if ($type === false) {
return false;
}
@@ -3110,11 +2902,10 @@ class SFTP extends SSH2
* @param string $path
* @param string $prop
* @return mixed
- * @access private
*/
- function _get_stat_cache_prop($path, $prop)
+ private function get_stat_cache_prop($path, $prop)
{
- return $this->_get_xstat_cache_prop($path, $prop, 'stat');
+ return $this->get_xstat_cache_prop($path, $prop, 'stat');
}
/**
@@ -3125,11 +2916,10 @@ class SFTP extends SSH2
* @param string $path
* @param string $prop
* @return mixed
- * @access private
*/
- function _get_lstat_cache_prop($path, $prop)
+ private function get_lstat_cache_prop($path, $prop)
{
- return $this->_get_xstat_cache_prop($path, $prop, 'lstat');
+ return $this->get_xstat_cache_prop($path, $prop, 'lstat');
}
/**
@@ -3139,20 +2929,19 @@ class SFTP extends SSH2
*
* @param string $path
* @param string $prop
- * @param mixed $type
+ * @param string $type
* @return mixed
- * @access private
*/
- function _get_xstat_cache_prop($path, $prop, $type)
+ private function get_xstat_cache_prop($path, $prop, $type)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
if ($this->use_stat_cache) {
- $path = $this->_realpath($path);
+ $path = $this->realpath($path);
- $result = $this->_query_stat_cache($path);
+ $result = $this->query_stat_cache($path);
if (is_object($result) && isset($result->$type)) {
return $result->{$type}[$prop];
@@ -3176,22 +2965,22 @@ class SFTP extends SSH2
* @param string $oldname
* @param string $newname
* @return bool
- * @access public
+ * @throws \UnexpectedValueException on receipt of unexpected packets
*/
- function rename($oldname, $newname)
+ public function rename($oldname, $newname)
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
- $oldname = $this->_realpath($oldname);
- $newname = $this->_realpath($newname);
+ $oldname = $this->realpath($oldname);
+ $newname = $this->realpath($newname);
if ($oldname === false || $newname === false) {
return false;
}
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
- $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
+ $packet = Strings::packSSH2('ss', $oldname, $newname);
if ($this->version >= 5) {
/* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 ,
@@ -3202,33 +2991,28 @@ class SFTP extends SSH2
SSH_FXP_RENAME_NATIVE 0x00000004
(none of these are currently supported) */
- $packet.= "\0\0\0\0";
- }
- if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
- return false;
+ $packet .= "\0\0\0\0";
}
+ $this->send_sftp_packet(NET_SFTP_RENAME, $packet);
- $response = $this->_get_sftp_packet();
+ $response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
- user_error('Expected SSH_FXP_STATUS');
- return false;
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
}
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ list($status) = Strings::unpackSSH2('N', $response);
if ($status != NET_SFTP_STATUS_OK) {
- $this->_logError($response, $status);
+ $this->logError($response, $status);
return false;
}
// don't move the stat cache entry over since this operation could very well change the
// atime and mtime attributes
- //$this->_update_stat_cache($newname, $this->_query_stat_cache($oldname));
- $this->_remove_from_stat_cache($oldname);
- $this->_remove_from_stat_cache($newname);
+ //$this->update_stat_cache($newname, $this->query_stat_cache($oldname));
+ $this->remove_from_stat_cache($oldname);
+ $this->remove_from_stat_cache($newname);
return true;
}
@@ -3242,18 +3026,13 @@ class SFTP extends SSH2
* @param int $flags
* @param string $response
* @return array
- * @access private
*/
- function _parseTime($key, $flags, &$response)
+ private function parseTime($key, $flags, &$response)
{
- if (strlen($response) < 8) {
- user_error('Malformed file attributes');
- return array();
- }
- $attr = array();
- $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8)));
+ $attr = [];
+ list($attr[$key]) = Strings::unpackSSH2('Q', $response);
if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
- $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4)));
+ list($attr[$key . '-nseconds']) = Strings::unpackSSH2('N', $response);
}
return $attr;
}
@@ -3265,28 +3044,18 @@ class SFTP extends SSH2
*
* @param string $response
* @return array
- * @access private
*/
- function _parseAttributes(&$response)
+ protected function parseAttributes(&$response)
{
+ $attr = [];
+
if ($this->version >= 4) {
- $length = 5;
- $format = 'Nflags/Ctype';
+ list($flags, $attr['type']) = Strings::unpackSSH2('NC', $response);
} else {
- $length = 4;
- $format = 'Nflags';
+ list($flags) = Strings::unpackSSH2('N', $response);
}
- $attr = array();
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return array();
- }
- extract(unpack($format, $this->_string_shift($response, $length)));
- if (isset($type)) {
- $attr['type'] = $type;
- }
- foreach ($this->attributes as $key => $value) {
+ foreach (self::$attributes as $key => $value) {
switch ($flags & $key) {
case NET_SFTP_ATTR_UIDGID:
if ($this->version > 3) {
@@ -3325,90 +3094,42 @@ class SFTP extends SSH2
// IEEE 754 binary64 "double precision" on such platforms and
// as such can represent integers of at least 2^50 without loss
// of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
- $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
+ list($attr['size']) = Strings::unpackSSH2('Q', $response);
break;
- case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier)
- if (strlen($response) < 8) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
+ list($attr['uid'], $attr['gid']) = Strings::unpackSSH2('NN', $response);
break;
- case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
- // mode == permissions; permissions was the original array key and is retained for bc purposes.
- // mode was added because that's the more industry standard terminology
- $attr+= array('mode' => $attr['permissions']);
- $fileType = $this->_parseMode($attr['permissions']);
- if ($fileType !== false) {
- $attr+= array('type' => $fileType);
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
+ list($attr['mode']) = Strings::unpackSSH2('N', $response);
+ $fileType = $this->parseMode($attr['mode']);
+ if ($this->version < 4 && $fileType !== false) {
+ $attr += ['type' => $fileType];
}
break;
- case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
if ($this->version >= 4) {
- $attr+= $this->_parseTime('atime', $flags, $response);
+ $attr += $this->parseTime('atime', $flags, $response);
break;
}
- if (strlen($response) < 8) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
+ list($attr['atime'], $attr['mtime']) = Strings::unpackSSH2('NN', $response);
break;
case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+)
- $attr+= $this->_parseTime('createtime', $flags, $response);
+ $attr += $this->parseTime('createtime', $flags, $response);
break;
case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020
- $attr+= $this->_parseTime('mtime', $flags, $response);
+ $attr += $this->parseTime('mtime', $flags, $response);
break;
case NET_SFTP_ATTR_ACL: // 0x00000040
// access control list
// see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
// currently unsupported
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ list($count) = Strings::unpackSSH2('N', $response);
for ($i = 0; $i < $count; $i++) {
- if (strlen($response) < 16) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16)));
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $this->_string_shift($response, $length); // who
+ list($type, $flag, $mask, $who) = Strings::unpackSSH2('N3s', $result);
}
break;
case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr['owner'] = $this->_string_shift($response, $length);
-
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr['group'] = $this->_string_shift($response, $length);
+ list($attr['owner'], $attr['$group']) = Strings::unpackSSH2('ss', $response);
break;
case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100
break;
@@ -3418,82 +3139,46 @@ class SFTP extends SSH2
// tells if you file is:
// readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
// append only, immutable, sync
- if (strlen($response) < 8) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8)));
+ list($attrib_bits, $attrib_bits_valid) = Strings::unpackSSH2('N2', $response);
+ // if we were actually gonna implement the above it ought to be
+ // $attr['attrib-bits'] and $attr['attrib-bits-valid']
+ // eg. - instead of _
break;
case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+)
// see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
- // represents the number of bytes htat the file consumes on the disk. will
+ // represents the number of bytes that the file consumes on the disk. will
// usually be larger than the 'size' field
- $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
+ list($attr['allocation-size']) = Strings::unpackSSH2('Q', $response);
break;
case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800
// https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
// currently unsupported
// tells if file is "known text", "guessed text", "known binary", "guessed binary"
- extract(unpack('Ctext-hint', $this->_string_shift($response)));
+ list($text_hint) = Strings::unpackSSH2('C', $response);
+ // the above should be $attr['text-hint']
break;
case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000
// see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr['mime-type'] = $this->_string_shift($response, $length);
+ list($attr['mime-type']) = Strings::unpackSSH2('s', $response);
break;
case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000
// see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr+= unpack('Nlink-count', $this->_string_shift($response, 4));
+ list($attr['link-count']) = Strings::unpackSSH2('N', $response);
break;
case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000
// see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- if (strlen($response) < $length) {
- user_error('Malformed file attributes');
- return $attr;
- }
- $attr['untranslated-name'] = $this->_string_shift($response, $length);
+ list($attr['untranslated-name']) = Strings::unpackSSH2('s', $response);
break;
case NET_SFTP_ATTR_CTIME: // 0x00008000
// 'ctime' contains the last time the file attributes were changed. The
// exact meaning of this field depends on the server.
- $attr+= $this->_parseTime('ctime', $flags, $response);
+ $attr += $this->parseTime('ctime', $flags, $response);
break;
- case NET_SFTP_ATTR_EXTENDED: // 0x80000000
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
+ list($count) = Strings::unpackSSH2('N', $response);
for ($i = 0; $i < $count; $i++) {
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $key = $this->_string_shift($response, $length);
- if (strlen($response) < 4) {
- user_error('Malformed file attributes');
- return $attr;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $attr[$key] = $this->_string_shift($response, $length);
+ list($key, $value) = Strings::unpackSSH2('ss', $response);
+ $attr[$key] = $value;
}
}
}
@@ -3507,9 +3192,8 @@ class SFTP extends SSH2
*
* @param int $mode
* @return int
- * @access private
*/
- function _parseMode($mode)
+ private function parseMode($mode)
{
// values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
// see, also, http://linux.die.net/man/2/stat
@@ -3554,9 +3238,8 @@ class SFTP extends SSH2
*
* @param string $longname
* @return mixed
- * @access private
*/
- function _parseLongname($longname)
+ private function parseLongname($longname)
{
// http://en.wikipedia.org/wiki/Unix_file_types
// http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
@@ -3585,73 +3268,49 @@ class SFTP extends SSH2
* @param string $data
* @param int $request_id
* @see self::_get_sftp_packet()
- * @see self::_send_channel_packet()
- * @return bool
- * @access private
+ * @see self::send_channel_packet()
+ * @return void
*/
- function _send_sftp_packet($type, $data, $request_id = 1)
+ private function send_sftp_packet($type, $data, $request_id = 1)
{
// in SSH2.php the timeout is cumulative per function call. eg. exec() will
// timeout after 10s. but for SFTP.php it's cumulative per packet
$this->curTimeout = $this->timeout;
+ $this->is_timeout = false;
$packet = $this->use_request_id ?
pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
- pack('NCa*', strlen($data) + 1, $type, $data);
+ pack('NCa*', strlen($data) + 1, $type, $data);
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
- $result = $this->_send_channel_packet(self::CHANNEL, $packet);
- $stop = strtok(microtime(), ' ') + strtok('');
+ $start = microtime(true);
+ $this->send_channel_packet(self::CHANNEL, $packet);
+ $stop = microtime(true);
if (defined('NET_SFTP_LOGGING')) {
- $packet_type = '-> ' . $this->packet_types[$type] .
+ $packet_type = '-> ' . self::$packet_types[$type] .
' (' . round($stop - $start, 4) . 's)';
- if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
- switch (PHP_SAPI) {
- case 'cli':
- $start = $stop = "\r\n";
- break;
- default:
- $start = '<pre>';
- $stop = '</pre>';
- }
- echo $start . $this->_format_log(array($data), array($packet_type)) . $stop;
- @flush();
- @ob_flush();
- } else {
- $this->packet_type_log[] = $packet_type;
- if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
- $this->packet_log[] = $data;
- }
- }
+ $this->append_log($packet_type, $data);
}
-
- return $result;
}
/**
* Resets the SFTP channel for re-use
- *
- * @access private
*/
- function _reset_sftp()
+ private function reset_sftp()
{
$this->use_request_id = false;
$this->pwd = false;
- $this->requestBuffer = array();
+ $this->requestBuffer = [];
$this->partial_init = false;
}
/**
* Resets a connection for re-use
- *
- * @param int $reason
- * @access private
*/
- function _reset_connection($reason)
+ protected function reset_connection()
{
- parent::_reset_connection($reason);
- $this->_reset_sftp();
+ parent::reset_connection();
+ $this->reset_sftp();
}
/**
@@ -3665,9 +3324,8 @@ class SFTP extends SSH2
*
* @see self::_send_sftp_packet()
* @return string
- * @access private
*/
- function _get_sftp_packet($request_id = null)
+ private function get_sftp_packet($request_id = null)
{
$this->channel_close = false;
@@ -3681,12 +3339,13 @@ class SFTP extends SSH2
// in SSH2.php the timeout is cumulative per function call. eg. exec() will
// timeout after 10s. but for SFTP.php it's cumulative per packet
$this->curTimeout = $this->timeout;
+ $this->is_timeout = false;
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
+ $start = microtime(true);
// SFTP packet length
while (strlen($this->packet_buffer) < 4) {
- $temp = $this->_get_channel_packet(self::CHANNEL, true);
+ $temp = $this->get_channel_packet(self::CHANNEL, true);
if ($temp === true) {
if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
@@ -3695,95 +3354,97 @@ class SFTP extends SSH2
$this->packet_buffer = '';
return false;
}
- if ($temp === false) {
- return false;
- }
- $this->packet_buffer.= $temp;
+ $this->packet_buffer .= $temp;
}
if (strlen($this->packet_buffer) < 4) {
- return false;
+ throw new \RuntimeException('Packet is too small');
}
- extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
+ $length = unpack('Nlength', Strings::shift($this->packet_buffer, 4))['length'];
+
$tempLength = $length;
- $tempLength-= strlen($this->packet_buffer);
+ $tempLength -= strlen($this->packet_buffer);
// 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
- user_error('Invalid SFTP packet size');
- return false;
+ throw new \RuntimeException('Invalid Size');
}
// SFTP packet type and data payload
while ($tempLength > 0) {
- $temp = $this->_get_channel_packet(self::CHANNEL, true);
- if (is_bool($temp)) {
- if ($temp && $this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
+ $temp = $this->get_channel_packet(self::CHANNEL, true);
+ if ($temp === true) {
+ if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->channel_close = true;
}
$this->packet_type = false;
$this->packet_buffer = '';
return false;
}
- $this->packet_buffer.= $temp;
- $tempLength-= strlen($temp);
+ $this->packet_buffer .= $temp;
+ $tempLength -= strlen($temp);
}
- $stop = strtok(microtime(), ' ') + strtok('');
+ $stop = microtime(true);
- $this->packet_type = ord($this->_string_shift($this->packet_buffer));
+ $this->packet_type = ord(Strings::shift($this->packet_buffer));
if ($this->use_request_id) {
- extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id
- $length-= 5; // account for the request id and the packet type
+ $packet_id = unpack('Npacket_id', Strings::shift($this->packet_buffer, 4))['packet_id']; // remove the request id
+ $length -= 5; // account for the request id and the packet type
} else {
- $length-= 1; // account for the packet type
+ $length -= 1; // account for the packet type
}
- $packet = $this->_string_shift($this->packet_buffer, $length);
+ $packet = Strings::shift($this->packet_buffer, $length);
if (defined('NET_SFTP_LOGGING')) {
- $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
+ $packet_type = '<- ' . self::$packet_types[$this->packet_type] .
' (' . round($stop - $start, 4) . 's)';
- if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
- switch (PHP_SAPI) {
- case 'cli':
- $start = $stop = "\r\n";
- break;
- default:
- $start = '<pre>';
- $stop = '</pre>';
- }
- echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop;
- @flush();
- @ob_flush();
- } else {
- $this->packet_type_log[] = $packet_type;
- if (NET_SFTP_LOGGING == self::LOG_COMPLEX) {
- $this->packet_log[] = $packet;
- }
- }
+ $this->append_log($packet_type, $packet);
}
if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) {
- $this->requestBuffer[$packet_id] = array(
+ $this->requestBuffer[$packet_id] = [
'packet_type' => $this->packet_type,
'packet' => $packet
- );
- return $this->_get_sftp_packet($request_id);
+ ];
+ return $this->get_sftp_packet($request_id);
}
return $packet;
}
/**
+ * Logs data packets
+ *
+ * Makes sure that only the last 1MB worth of packets will be logged
+ *
+ * @param string $message_number
+ * @param string $message
+ */
+ private function append_log($message_number, $message)
+ {
+ $this->append_log_helper(
+ NET_SFTP_LOGGING,
+ $message_number,
+ $message,
+ $this->packet_type_log,
+ $this->packet_log,
+ $this->log_size,
+ $this->realtime_log_file,
+ $this->realtime_log_wrap,
+ $this->realtime_log_size
+ );
+ }
+
+ /**
* Returns a log of the packets that have been sent and received.
*
- * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
+ * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING')
*
- * @access public
- * @return string or Array
+ * @return array|string|false
*/
- function getSFTPLog()
+ public function getSFTPLog()
{
if (!defined('NET_SFTP_LOGGING')) {
return false;
@@ -3791,7 +3452,7 @@ class SFTP extends SSH2
switch (NET_SFTP_LOGGING) {
case self::LOG_COMPLEX:
- return $this->_format_log($this->packet_log, $this->packet_type_log);
+ return $this->format_log($this->packet_log, $this->packet_type_log);
break;
//case self::LOG_SIMPLE:
default:
@@ -3803,9 +3464,8 @@ class SFTP extends SSH2
* Returns all errors on the SFTP layer
*
* @return array
- * @access public
*/
- function getSFTPErrors()
+ public function getSFTPErrors()
{
return $this->sftp_errors;
}
@@ -3814,9 +3474,8 @@ class SFTP extends SSH2
* Returns the last error on the SFTP layer
*
* @return string
- * @access public
*/
- function getLastSFTPError()
+ public function getLastSFTPError()
{
return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
}
@@ -3825,19 +3484,18 @@ class SFTP extends SSH2
* Get supported SFTP versions
*
* @return array
- * @access public
*/
- function getSupportedVersions()
+ public function getSupportedVersions()
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
if (!$this->partial_init) {
- $this->_partial_init_sftp_connection();
+ $this->partial_init_sftp_connection();
}
- $temp = array('version' => $this->defaultVersion);
+ $temp = ['version' => $this->defaultVersion];
if (isset($this->extensions['versions'])) {
$temp['extensions'] = $this->extensions['versions'];
}
@@ -3845,14 +3503,31 @@ class SFTP extends SSH2
}
/**
- * Get supported SFTP versions
+ * Get supported SFTP extensions
*
* @return array
- * @access public
*/
- function getNegotiatedVersion()
+ public function getSupportedExtensions()
+ {
+ if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ return false;
+ }
+
+ if (!$this->partial_init) {
+ $this->partial_init_sftp_connection();
+ }
+
+ return $this->extensions;
+ }
+
+ /**
+ * Get supported SFTP versions
+ *
+ * @return int|false
+ */
+ public function getNegotiatedVersion()
{
- if (!$this->_precheck()) {
+ if (!$this->precheck()) {
return false;
}
@@ -3867,9 +3542,8 @@ class SFTP extends SSH2
* unset the preferred version
*
* @param int $version
- * @access public
*/
- function setPreferredVersion($version)
+ public function setPreferredVersion($version)
{
$this->preferredVersion = $version;
}
@@ -3878,21 +3552,19 @@ class SFTP extends SSH2
* Disconnect
*
* @param int $reason
- * @return bool
- * @access private
+ * @return false
*/
- function _disconnect($reason)
+ protected function disconnect_helper($reason)
{
$this->pwd = false;
- parent::_disconnect($reason);
+ return parent::disconnect_helper($reason);
}
/**
* Enable Date Preservation
*
- * @access public
*/
- function enableDatePreservation()
+ public function enableDatePreservation()
{
$this->preserveTime = true;
}
@@ -3900,10 +3572,129 @@ class SFTP extends SSH2
/**
* Disable Date Preservation
*
- * @access public
*/
- function disableDatePreservation()
+ public function disableDatePreservation()
{
$this->preserveTime = false;
}
+
+ /**
+ * POSIX Rename
+ *
+ * Where rename() fails "if there already exists a file with the name specified by newpath"
+ * (draft-ietf-secsh-filexfer-02#section-6.5), posix_rename() overwrites the existing file in an atomic fashion.
+ * ie. "there is no observable instant in time where the name does not refer to either the old or the new file"
+ * (draft-ietf-secsh-filexfer-13#page-39).
+ *
+ * @param string $oldname
+ * @param string $newname
+ * @return bool
+ */
+ public function posix_rename($oldname, $newname)
+ {
+ if (!$this->precheck()) {
+ return false;
+ }
+
+ $oldname = $this->realpath($oldname);
+ $newname = $this->realpath($newname);
+ if ($oldname === false || $newname === false) {
+ return false;
+ }
+
+ if ($this->version >= 5) {
+ $packet = Strings::packSSH2('ssN', $oldname, $newname, 2); // 2 = SSH_FXP_RENAME_ATOMIC
+ $this->send_sftp_packet(NET_SFTP_RENAME, $packet);
+ } elseif (isset($this->extensions['posix-rename@openssh.com']) && $this->extensions['posix-rename@openssh.com'] === '1') {
+ $packet = Strings::packSSH2('sss', 'posix-rename@openssh.com', $oldname, $newname);
+ $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
+ } else {
+ throw new \RuntimeException(
+ "Extension 'posix-rename@openssh.com' is not supported by the server. " .
+ "Call getSupportedVersions() to see a list of supported extension"
+ );
+ }
+
+ $response = $this->get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
+ . 'Got packet type: ' . $this->packet_type);
+ }
+
+ // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
+ list($status) = Strings::unpackSSH2('N', $response);
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->logError($response, $status);
+ return false;
+ }
+
+ // don't move the stat cache entry over since this operation could very well change the
+ // atime and mtime attributes
+ //$this->update_stat_cache($newname, $this->query_stat_cache($oldname));
+ $this->remove_from_stat_cache($oldname);
+ $this->remove_from_stat_cache($newname);
+
+ return true;
+ }
+
+ /**
+ * Returns general information about a file system.
+ *
+ * The function statvfs() returns information about a mounted filesystem.
+ * @see https://man7.org/linux/man-pages/man3/statvfs.3.html
+ *
+ * @param string $path
+ * @return false|array{bsize: int, frsize: int, blocks: int, bfree: int, bavail: int, files: int, ffree: int, favail: int, fsid: int, flag: int, namemax: int}
+ */
+ public function statvfs($path)
+ {
+ if (!$this->precheck()) {
+ return false;
+ }
+
+ if (!isset($this->extensions['statvfs@openssh.com']) || $this->extensions['statvfs@openssh.com'] !== '2') {
+ throw new \RuntimeException(
+ "Extension 'statvfs@openssh.com' is not supported by the server. " .
+ "Call getSupportedVersions() to see a list of supported extension"
+ );
+ }
+
+ $realpath = $this->realpath($path);
+ if ($realpath === false) {
+ return false;
+ }
+
+ $packet = Strings::packSSH2('ss', 'statvfs@openssh.com', $realpath);
+ $this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
+
+ $response = $this->get_sftp_packet();
+ if ($this->packet_type !== NET_SFTP_EXTENDED_REPLY) {
+ throw new \UnexpectedValueException(
+ 'Expected SSH_FXP_EXTENDED_REPLY. '
+ . 'Got packet type: ' . $this->packet_type
+ );
+ }
+
+ /**
+ * These requests return a SSH_FXP_STATUS reply on failure. On success they
+ * return the following SSH_FXP_EXTENDED_REPLY reply:
+ *
+ * uint32 id
+ * uint64 f_bsize file system block size
+ * uint64 f_frsize fundamental fs block size
+ * uint64 f_blocks number of blocks (unit f_frsize)
+ * uint64 f_bfree free blocks in file system
+ * uint64 f_bavail free blocks for non-root
+ * uint64 f_files total file inodes
+ * uint64 f_ffree free file inodes
+ * uint64 f_favail free file inodes for to non-root
+ * uint64 f_fsid file system id
+ * uint64 f_flag bit mask of f_flag values
+ * uint64 f_namemax maximum filename length
+ */
+ return array_combine(
+ ['bsize', 'frsize', 'blocks', 'bfree', 'bavail', 'files', 'ffree', 'favail', 'fsid', 'flag', 'namemax'],
+ Strings::unpackSSH2('QQQQQQQQQQQ', $response)
+ );
+ }
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php
index ec9e5841a..a1f2fa245 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php
@@ -7,25 +7,22 @@
*
* PHP version 5
*
- * @category Net
- * @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2013 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Net\SFTP;
+namespace phpseclib3\Net\SFTP;
-use phpseclib\Crypt\RSA;
-use phpseclib\Net\SFTP;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Net\SFTP;
+use phpseclib3\Net\SSH2;
/**
* SFTP Stream Wrapper
*
- * @package SFTP
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class Stream
{
@@ -36,90 +33,80 @@ class Stream
*
* @var array
*/
- static $instances;
+ public static $instances;
/**
* SFTP instance
*
* @var object
- * @access private
*/
- var $sftp;
+ private $sftp;
/**
* Path
*
* @var string
- * @access private
*/
- var $path;
+ private $path;
/**
* Mode
*
* @var string
- * @access private
*/
- var $mode;
+ private $mode;
/**
* Position
*
* @var int
- * @access private
*/
- var $pos;
+ private $pos;
/**
* Size
*
* @var int
- * @access private
*/
- var $size;
+ private $size;
/**
* Directory entries
*
* @var array
- * @access private
*/
- var $entries;
+ private $entries;
/**
* EOF flag
*
* @var bool
- * @access private
*/
- var $eof;
+ private $eof;
/**
* Context resource
*
- * Technically this needs to be publically accessible so PHP can set it directly
+ * Technically this needs to be publicly accessible so PHP can set it directly
*
* @var resource
- * @access public
*/
- var $context;
+ public $context;
/**
* Notification callback function
*
* @var callable
- * @access public
*/
- var $notification;
+ private $notification;
/**
* Registers this class as a URL wrapper.
*
* @param string $protocol The wrapper name to be registered.
* @return bool True on success, false otherwise.
- * @access public
*/
- static function register($protocol = 'sftp')
+ public static function register($protocol = 'sftp')
{
if (in_array($protocol, stream_get_wrappers(), true)) {
return false;
@@ -130,9 +117,8 @@ class Stream
/**
* The Constructor
*
- * @access public
*/
- function __construct()
+ public function __construct()
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo "__construct()\r\n";
@@ -149,21 +135,28 @@ class Stream
*
* @param string $path
* @return string
- * @access private
*/
- function _parse_path($path)
+ protected function parse_path($path)
{
$orig = $path;
- extract(parse_url($path) + array('port' => 22));
+ $url = parse_url($path) + ['port' => 22];
+
+ $keys = ['scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'];
+ foreach ($keys as $key) {
+ if (isset($url[$key])) {
+ $$key = $url[$key];
+ }
+ }
+
if (isset($query)) {
- $path.= '?' . $query;
+ $path .= '?' . $query;
} elseif (preg_match('/(\?|\?#)$/', $orig)) {
- $path.= '?';
+ $path .= '?';
}
if (isset($fragment)) {
- $path.= '#' . $fragment;
+ $path .= '#' . $fragment;
} elseif ($orig[strlen($orig) - 1] == '#') {
- $path.= '#';
+ $path .= '#';
}
if (!isset($host)) {
@@ -177,13 +170,12 @@ class Stream
}
}
- if ($host[0] == '$') {
- $host = substr($host, 1);
- global ${$host};
- if (($$host instanceof SFTP) === false) {
+ if (preg_match('/^{[a-z0-9]+}$/i', $host)) {
+ $host = SSH2::getConnectionByResourceId($host);
+ if ($host === false) {
return false;
}
- $this->sftp = $$host;
+ $this->sftp = $host;
} else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context);
@@ -204,7 +196,7 @@ class Stream
if (isset($context[$scheme]['password'])) {
$pass = $context[$scheme]['password'];
}
- if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) {
+ if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof PrivateKey) {
$pass = $context[$scheme]['privkey'];
}
@@ -212,7 +204,7 @@ class Stream
return false;
}
- // casting $pass to a string is necessary in the event that it's a \phpseclib\Crypt\RSA object
+ // casting $pass to a string is necessary in the event that it's a \phpseclib3\Crypt\RSA object
if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
$this->sftp = self::$instances[$host][$port][$user][(string) $pass];
} else {
@@ -255,18 +247,17 @@ class Stream
* @param int $options
* @param string $opened_path
* @return bool
- * @access public
*/
- function _stream_open($path, $mode, $options, &$opened_path)
+ private function _stream_open($path, $mode, $options, &$opened_path)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
$this->path = $path;
- $this->size = $this->sftp->size($path);
+ $this->size = $this->sftp->filesize($path);
$this->mode = preg_replace('#[bt]$#', '', $mode);
$this->eof = false;
@@ -297,9 +288,8 @@ class Stream
*
* @param int $count
* @return mixed
- * @access public
*/
- function _stream_read($count)
+ private function _stream_read($count)
{
switch ($this->mode) {
case 'w':
@@ -329,7 +319,7 @@ class Stream
$this->eof = true;
return false;
}
- $this->pos+= strlen($result);
+ $this->pos += strlen($result);
return $result;
}
@@ -338,10 +328,9 @@ class Stream
* Write to stream
*
* @param string $data
- * @return mixed
- * @access public
+ * @return int|false
*/
- function _stream_write($data)
+ private function _stream_write($data)
{
switch ($this->mode) {
case 'r':
@@ -361,7 +350,7 @@ class Stream
if ($result === false) {
return false;
}
- $this->pos+= strlen($data);
+ $this->pos += strlen($data);
if ($this->pos > $this->size) {
$this->size = $this->pos;
}
@@ -373,9 +362,8 @@ class Stream
* Retrieve the current position of a stream
*
* @return int
- * @access public
*/
- function _stream_tell()
+ private function _stream_tell()
{
return $this->pos;
}
@@ -391,9 +379,8 @@ class Stream
* will return false. do fread($fp, 1) and feof() will then return true.
*
* @return bool
- * @access public
*/
- function _stream_eof()
+ private function _stream_eof()
{
return $this->eof;
}
@@ -404,9 +391,8 @@ class Stream
* @param int $offset
* @param int $whence
* @return bool
- * @access public
*/
- function _stream_seek($offset, $whence)
+ private function _stream_seek($offset, $whence)
{
switch ($whence) {
case SEEK_SET:
@@ -415,10 +401,10 @@ class Stream
}
break;
case SEEK_CUR:
- $offset+= $this->pos;
+ $offset += $this->pos;
break;
case SEEK_END:
- $offset+= $this->size;
+ $offset += $this->size;
}
$this->pos = $offset;
@@ -433,11 +419,10 @@ class Stream
* @param int $option
* @param mixed $var
* @return bool
- * @access public
*/
- function _stream_metadata($path, $option, $var)
+ private function _stream_metadata($path, $option, $var)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -467,9 +452,8 @@ class Stream
*
* @param int $cast_as
* @return resource
- * @access public
*/
- function _stream_cast($cast_as)
+ private function _stream_cast($cast_as)
{
return $this->sftp->fsock;
}
@@ -479,9 +463,8 @@ class Stream
*
* @param int $operation
* @return bool
- * @access public
*/
- function _stream_lock($operation)
+ private function _stream_lock($operation)
{
return false;
}
@@ -490,15 +473,14 @@ class Stream
* Renames a file or directory
*
* Attempts to rename oldname to newname, moving it between directories if necessary.
- * If newname exists, it will be overwritten. This is a departure from what \phpseclib\Net\SFTP
+ * If newname exists, it will be overwritten. This is a departure from what \phpseclib3\Net\SFTP
* does.
*
* @param string $path_from
* @param string $path_to
* @return bool
- * @access public
*/
- function _rename($path_from, $path_to)
+ private function _rename($path_from, $path_to)
{
$path1 = parse_url($path_from);
$path2 = parse_url($path_to);
@@ -507,7 +489,7 @@ class Stream
return false;
}
- $path_from = $this->_parse_path($path_from);
+ $path_from = $this->parse_path($path_from);
$path_to = parse_url($path_to);
if ($path_from === false) {
return false;
@@ -548,11 +530,10 @@ class Stream
* @param string $path
* @param int $options
* @return bool
- * @access public
*/
- function _dir_opendir($path, $options)
+ private function _dir_opendir($path, $options)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -565,9 +546,8 @@ class Stream
* Read entry from directory handle
*
* @return mixed
- * @access public
*/
- function _dir_readdir()
+ private function _dir_readdir()
{
if (isset($this->entries[$this->pos])) {
return $this->entries[$this->pos++];
@@ -579,9 +559,8 @@ class Stream
* Rewind directory handle
*
* @return bool
- * @access public
*/
- function _dir_rewinddir()
+ private function _dir_rewinddir()
{
$this->pos = 0;
return true;
@@ -591,9 +570,8 @@ class Stream
* Close directory handle
*
* @return bool
- * @access public
*/
- function _dir_closedir()
+ private function _dir_closedir()
{
return true;
}
@@ -607,11 +585,10 @@ class Stream
* @param int $mode
* @param int $options
* @return bool
- * @access public
*/
- function _mkdir($path, $mode, $options)
+ private function _mkdir($path, $mode, $options)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -630,11 +607,10 @@ class Stream
* @param string $path
* @param int $options
* @return bool
- * @access public
*/
- function _rmdir($path, $options)
+ private function _rmdir($path, $options)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -645,12 +621,11 @@ class Stream
/**
* Flushes the output
*
- * See <http://php.net/fflush>. Always returns true because \phpseclib\Net\SFTP doesn't cache stuff before writing
+ * See <http://php.net/fflush>. Always returns true because \phpseclib3\Net\SFTP doesn't cache stuff before writing
*
* @return bool
- * @access public
*/
- function _stream_flush()
+ private function _stream_flush()
{
return true;
}
@@ -659,9 +634,8 @@ class Stream
* Retrieve information about a file resource
*
* @return mixed
- * @access public
*/
- function _stream_stat()
+ private function _stream_stat()
{
$results = $this->sftp->stat($this->path);
if ($results === false) {
@@ -675,11 +649,10 @@ class Stream
*
* @param string $path
* @return bool
- * @access public
*/
- function _unlink($path)
+ private function _unlink($path)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -690,18 +663,17 @@ class Stream
/**
* Retrieve information about a file
*
- * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib\Net\SFTP\Stream is quiet by default
+ * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of \phpseclib3\Net\SFTP\Stream is quiet by default
* might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
* cross that bridge when and if it's reached
*
* @param string $path
* @param int $flags
* @return mixed
- * @access public
*/
- function _url_stat($path, $flags)
+ private function _url_stat($path, $flags)
{
- $path = $this->_parse_path($path);
+ $path = $this->parse_path($path);
if ($path === false) {
return false;
}
@@ -719,9 +691,8 @@ class Stream
*
* @param int $new_size
* @return bool
- * @access public
*/
- function _stream_truncate($new_size)
+ private function _stream_truncate($new_size)
{
if (!$this->sftp->truncate($this->path, $new_size)) {
return false;
@@ -737,15 +708,14 @@ class Stream
* Change stream options
*
* STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
- * The other two aren't supported because of limitations in \phpseclib\Net\SFTP.
+ * The other two aren't supported because of limitations in \phpseclib3\Net\SFTP.
*
* @param int $option
* @param int $arg1
* @param int $arg2
* @return bool
- * @access public
*/
- function _stream_set_option($option, $arg1, $arg2)
+ private function _stream_set_option($option, $arg1, $arg2)
{
return false;
}
@@ -753,9 +723,8 @@ class Stream
/**
* Close an resource
*
- * @access public
*/
- function _stream_close()
+ private function _stream_close()
{
}
@@ -772,9 +741,8 @@ class Stream
* @param string $name
* @param array $arguments
* @return mixed
- * @access public
*/
- function __call($name, $arguments)
+ public function __call($name, array $arguments)
{
if (defined('NET_SFTP_STREAM_LOGGING')) {
echo $name . '(';
@@ -791,6 +759,6 @@ class Stream
if (!method_exists($this, $name)) {
return false;
}
- return call_user_func_array(array($this, $name), $arguments);
+ return $this->$name(...$arguments);
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php
deleted file mode 100644
index fc8d2acd8..000000000
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php
+++ /dev/null
@@ -1,1662 +0,0 @@
-<?php
-
-/**
- * Pure-PHP implementation of SSHv1.
- *
- * PHP version 5
- *
- * Here's a short example of how to use this library:
- * <code>
- * <?php
- * include 'vendor/autoload.php';
- *
- * $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
- * if (!$ssh->login('username', 'password')) {
- * exit('Login Failed');
- * }
- *
- * echo $ssh->exec('ls -la');
- * ?>
- * </code>
- *
- * Here's another short example:
- * <code>
- * <?php
- * include 'vendor/autoload.php';
- *
- * $ssh = new \phpseclib\Net\SSH1('www.domain.tld');
- * if (!$ssh->login('username', 'password')) {
- * exit('Login Failed');
- * }
- *
- * echo $ssh->read('username@username:~$');
- * $ssh->write("ls -la\n");
- * echo $ssh->read('username@username:~$');
- * ?>
- * </code>
- *
- * More information on the SSHv1 specification can be found by reading
- * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
- *
- * @category Net
- * @package SSH1
- * @author Jim Wigginton <terrafrost@php.net>
- * @copyright 2007 Jim Wigginton
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @link http://phpseclib.sourceforge.net
- */
-
-namespace phpseclib\Net;
-
-use phpseclib\Crypt\DES;
-use phpseclib\Crypt\Random;
-use phpseclib\Crypt\TripleDES;
-use phpseclib\Math\BigInteger;
-
-/**
- * Pure-PHP implementation of SSHv1.
- *
- * @package SSH1
- * @author Jim Wigginton <terrafrost@php.net>
- * @access public
- */
-class SSH1
-{
- /**#@+
- * Encryption Methods
- *
- * @see \phpseclib\Net\SSH1::getSupportedCiphers()
- * @access public
- */
- /**
- * No encryption
- *
- * Not supported.
- */
- const CIPHER_NONE = 0;
- /**
- * IDEA in CFB mode
- *
- * Not supported.
- */
- const CIPHER_IDEA = 1;
- /**
- * DES in CBC mode
- */
- const CIPHER_DES = 2;
- /**
- * Triple-DES in CBC mode
- *
- * All implementations are required to support this
- */
- const CIPHER_3DES = 3;
- /**
- * TRI's Simple Stream encryption CBC
- *
- * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, does define it (see cipher.h),
- * although it doesn't use it (see cipher.c)
- */
- const CIPHER_BROKEN_TSS = 4;
- /**
- * RC4
- *
- * Not supported.
- *
- * @internal According to the SSH1 specs:
- *
- * "The first 16 bytes of the session key are used as the key for
- * the server to client direction. The remaining 16 bytes are used
- * as the key for the client to server direction. This gives
- * independent 128-bit keys for each direction."
- *
- * This library currently only supports encryption when the same key is being used for both directions. This is
- * because there's only one $crypto object. Two could be added ($encrypt and $decrypt, perhaps).
- */
- const CIPHER_RC4 = 5;
- /**
- * Blowfish
- *
- * Not supported nor is it defined in the official SSH1 specs. OpenSSH, however, defines it (see cipher.h) and
- * uses it (see cipher.c)
- */
- const CIPHER_BLOWFISH = 6;
- /**#@-*/
-
- /**#@+
- * Authentication Methods
- *
- * @see \phpseclib\Net\SSH1::getSupportedAuthentications()
- * @access public
- */
- /**
- * .rhosts or /etc/hosts.equiv
- */
- const AUTH_RHOSTS = 1;
- /**
- * pure RSA authentication
- */
- const AUTH_RSA = 2;
- /**
- * password authentication
- *
- * This is the only method that is supported by this library.
- */
- const AUTH_PASSWORD = 3;
- /**
- * .rhosts with RSA host authentication
- */
- const AUTH_RHOSTS_RSA = 4;
- /**#@-*/
-
- /**#@+
- * Terminal Modes
- *
- * @link http://3sp.com/content/developer/maverick-net/docs/Maverick.SSH.PseudoTerminalModesMembers.html
- * @access private
- */
- const TTY_OP_END = 0;
- /**#@-*/
-
- /**
- * The Response Type
- *
- * @see \phpseclib\Net\SSH1::_get_binary_packet()
- * @access private
- */
- const RESPONSE_TYPE = 1;
-
- /**
- * The Response Data
- *
- * @see \phpseclib\Net\SSH1::_get_binary_packet()
- * @access private
- */
- const RESPONSE_DATA = 2;
-
- /**#@+
- * Execution Bitmap Masks
- *
- * @see \phpseclib\Net\SSH1::bitmap
- * @access private
- */
- const MASK_CONSTRUCTOR = 0x00000001;
- const MASK_CONNECTED = 0x00000002;
- const MASK_LOGIN = 0x00000004;
- const MASK_SHELL = 0x00000008;
- /**#@-*/
-
- /**#@+
- * @access public
- * @see \phpseclib\Net\SSH1::getLog()
- */
- /**
- * Returns the message numbers
- */
- const LOG_SIMPLE = 1;
- /**
- * Returns the message content
- */
- const LOG_COMPLEX = 2;
- /**
- * Outputs the content real-time
- */
- const LOG_REALTIME = 3;
- /**
- * Dumps the content real-time to a file
- */
- const LOG_REALTIME_FILE = 4;
- /**
- * Make sure that the log never gets larger than this
- */
- const LOG_MAX_SIZE = 1048576; // 1024 * 1024
- /**#@-*/
-
- /**#@+
- * @access public
- * @see \phpseclib\Net\SSH1::read()
- */
- /**
- * Returns when a string matching $expect exactly is found
- */
- const READ_SIMPLE = 1;
- /**
- * Returns when a string matching the regular expression $expect is found
- */
- const READ_REGEX = 2;
- /**#@-*/
-
- /**
- * The SSH identifier
- *
- * @var string
- * @access private
- */
- var $identifier = 'SSH-1.5-phpseclib';
-
- /**
- * The Socket Object
- *
- * @var object
- * @access private
- */
- var $fsock;
-
- /**
- * The cryptography object
- *
- * @var object
- * @access private
- */
- var $crypto = false;
-
- /**
- * Execution Bitmap
- *
- * The bits that are set represent functions that have been called already. This is used to determine
- * if a requisite function has been successfully executed. If not, an error should be thrown.
- *
- * @var int
- * @access private
- */
- var $bitmap = 0;
-
- /**
- * The Server Key Public Exponent
- *
- * Logged for debug purposes
- *
- * @see self::getServerKeyPublicExponent()
- * @var string
- * @access private
- */
- var $server_key_public_exponent;
-
- /**
- * The Server Key Public Modulus
- *
- * Logged for debug purposes
- *
- * @see self::getServerKeyPublicModulus()
- * @var string
- * @access private
- */
- var $server_key_public_modulus;
-
- /**
- * The Host Key Public Exponent
- *
- * Logged for debug purposes
- *
- * @see self::getHostKeyPublicExponent()
- * @var string
- * @access private
- */
- var $host_key_public_exponent;
-
- /**
- * The Host Key Public Modulus
- *
- * Logged for debug purposes
- *
- * @see self::getHostKeyPublicModulus()
- * @var string
- * @access private
- */
- var $host_key_public_modulus;
-
- /**
- * Supported Ciphers
- *
- * Logged for debug purposes
- *
- * @see self::getSupportedCiphers()
- * @var array
- * @access private
- */
- var $supported_ciphers = array(
- self::CIPHER_NONE => 'No encryption',
- self::CIPHER_IDEA => 'IDEA in CFB mode',
- self::CIPHER_DES => 'DES in CBC mode',
- self::CIPHER_3DES => 'Triple-DES in CBC mode',
- self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC',
- self::CIPHER_RC4 => 'RC4',
- self::CIPHER_BLOWFISH => 'Blowfish'
- );
-
- /**
- * Supported Authentications
- *
- * Logged for debug purposes
- *
- * @see self::getSupportedAuthentications()
- * @var array
- * @access private
- */
- var $supported_authentications = array(
- self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv',
- self::AUTH_RSA => 'pure RSA authentication',
- self::AUTH_PASSWORD => 'password authentication',
- self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication'
- );
-
- /**
- * Server Identification
- *
- * @see self::getServerIdentification()
- * @var string
- * @access private
- */
- var $server_identification = '';
-
- /**
- * Protocol Flags
- *
- * @see self::__construct()
- * @var array
- * @access private
- */
- var $protocol_flags = array();
-
- /**
- * Protocol Flag Log
- *
- * @see self::getLog()
- * @var array
- * @access private
- */
- var $protocol_flags_log = array();
-
- /**
- * Message Log
- *
- * @see self::getLog()
- * @var array
- * @access private
- */
- var $message_log = array();
-
- /**
- * Real-time log file pointer
- *
- * @see self::_append_log()
- * @var resource
- * @access private
- */
- var $realtime_log_file;
-
- /**
- * Real-time log file size
- *
- * @see self::_append_log()
- * @var int
- * @access private
- */
- var $realtime_log_size;
-
- /**
- * Real-time log file wrap boolean
- *
- * @see self::_append_log()
- * @var bool
- * @access private
- */
- var $realtime_log_wrap;
-
- /**
- * Interactive Buffer
- *
- * @see self::read()
- * @var array
- * @access private
- */
- var $interactiveBuffer = '';
-
- /**
- * Current log size
- *
- * Should never exceed self::LOG_MAX_SIZE
- *
- * @see self::_send_binary_packet()
- * @see self::_get_binary_packet()
- * @var int
- * @access private
- */
- var $log_size;
-
- /**
- * Timeout
- *
- * @see self::setTimeout()
- * @access private
- */
- var $timeout;
-
- /**
- * Current Timeout
- *
- * @see self::_get_channel_packet()
- * @access private
- */
- var $curTimeout;
-
- /**
- * Log Boundary
- *
- * @see self::_format_log()
- * @access private
- */
- var $log_boundary = ':';
-
- /**
- * Log Long Width
- *
- * @see self::_format_log()
- * @access private
- */
- var $log_long_width = 65;
-
- /**
- * Log Short Width
- *
- * @see self::_format_log()
- * @access private
- */
- var $log_short_width = 16;
-
- /**
- * Hostname
- *
- * @see self::__construct()
- * @see self::_connect()
- * @var string
- * @access private
- */
- var $host;
-
- /**
- * Port Number
- *
- * @see self::__construct()
- * @see self::_connect()
- * @var int
- * @access private
- */
- var $port;
-
- /**
- * Timeout for initial connection
- *
- * Set by the constructor call. Calling setTimeout() is optional. If it's not called functions like
- * exec() won't timeout unless some PHP setting forces it too. The timeout specified in the constructor,
- * however, is non-optional. There will be a timeout, whether or not you set it. If you don't it'll be
- * 10 seconds. It is used by fsockopen() in that function.
- *
- * @see self::__construct()
- * @see self::_connect()
- * @var int
- * @access private
- */
- var $connectionTimeout;
-
- /**
- * Default cipher
- *
- * @see self::__construct()
- * @see self::_connect()
- * @var int
- * @access private
- */
- var $cipher;
-
- /**
- * Default Constructor.
- *
- * Connects to an SSHv1 server
- *
- * @param string $host
- * @param int $port
- * @param int $timeout
- * @param int $cipher
- * @return \phpseclib\Net\SSH1
- * @access public
- */
- function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES)
- {
- $this->protocol_flags = array(
- 1 => 'NET_SSH1_MSG_DISCONNECT',
- 2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
- 3 => 'NET_SSH1_CMSG_SESSION_KEY',
- 4 => 'NET_SSH1_CMSG_USER',
- 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
- 10 => 'NET_SSH1_CMSG_REQUEST_PTY',
- 12 => 'NET_SSH1_CMSG_EXEC_SHELL',
- 13 => 'NET_SSH1_CMSG_EXEC_CMD',
- 14 => 'NET_SSH1_SMSG_SUCCESS',
- 15 => 'NET_SSH1_SMSG_FAILURE',
- 16 => 'NET_SSH1_CMSG_STDIN_DATA',
- 17 => 'NET_SSH1_SMSG_STDOUT_DATA',
- 18 => 'NET_SSH1_SMSG_STDERR_DATA',
- 19 => 'NET_SSH1_CMSG_EOF',
- 20 => 'NET_SSH1_SMSG_EXITSTATUS',
- 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
- );
-
- $this->_define_array($this->protocol_flags);
-
- $this->host = $host;
- $this->port = $port;
- $this->connectionTimeout = $timeout;
- $this->cipher = $cipher;
- }
-
- /**
- * Connect to an SSHv1 server
- *
- * @return bool
- * @access private
- */
- function _connect()
- {
- $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
- if (!$this->fsock) {
- user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr"));
- return false;
- }
-
- $this->server_identification = $init_line = fgets($this->fsock, 255);
-
- if (defined('NET_SSH1_LOGGING')) {
- $this->_append_log('<-', $this->server_identification);
- $this->_append_log('->', $this->identifier . "\r\n");
- }
-
- if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
- user_error('Can only connect to SSH servers');
- return false;
- }
- if ($parts[1][0] != 1) {
- user_error("Cannot connect to SSH $parts[1] servers");
- return false;
- }
-
- fputs($this->fsock, $this->identifier."\r\n");
-
- $response = $this->_get_binary_packet();
- if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
- user_error('Expected SSH_SMSG_PUBLIC_KEY');
- return false;
- }
-
- $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);
-
- $this->_string_shift($response[self::RESPONSE_DATA], 4);
-
- if (strlen($response[self::RESPONSE_DATA]) < 2) {
- return false;
- }
- $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
- $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
- $this->server_key_public_exponent = $server_key_public_exponent;
-
- if (strlen($response[self::RESPONSE_DATA]) < 2) {
- return false;
- }
- $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
- $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
-
- $this->server_key_public_modulus = $server_key_public_modulus;
-
- $this->_string_shift($response[self::RESPONSE_DATA], 4);
-
- if (strlen($response[self::RESPONSE_DATA]) < 2) {
- return false;
- }
- $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
- $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
- $this->host_key_public_exponent = $host_key_public_exponent;
-
- if (strlen($response[self::RESPONSE_DATA]) < 2) {
- return false;
- }
- $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
- $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
-
- $this->host_key_public_modulus = $host_key_public_modulus;
-
- $this->_string_shift($response[self::RESPONSE_DATA], 4);
-
- // get a list of the supported ciphers
- if (strlen($response[self::RESPONSE_DATA]) < 4) {
- return false;
- }
- extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
-
- foreach ($this->supported_ciphers as $mask => $name) {
- if (($supported_ciphers_mask & (1 << $mask)) == 0) {
- unset($this->supported_ciphers[$mask]);
- }
- }
-
- // get a list of the supported authentications
- if (strlen($response[self::RESPONSE_DATA]) < 4) {
- return false;
- }
- extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
- foreach ($this->supported_authentications as $mask => $name) {
- if (($supported_authentications_mask & (1 << $mask)) == 0) {
- unset($this->supported_authentications[$mask]);
- }
- }
-
- $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
-
- $session_key = Random::string(32);
- $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
-
- if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
- $double_encrypted_session_key = $this->_rsa_crypt(
- $double_encrypted_session_key,
- array(
- $server_key_public_exponent,
- $server_key_public_modulus
- )
- );
- $double_encrypted_session_key = $this->_rsa_crypt(
- $double_encrypted_session_key,
- array(
- $host_key_public_exponent,
- $host_key_public_modulus
- )
- );
- } else {
- $double_encrypted_session_key = $this->_rsa_crypt(
- $double_encrypted_session_key,
- array(
- $host_key_public_exponent,
- $host_key_public_modulus
- )
- );
- $double_encrypted_session_key = $this->_rsa_crypt(
- $double_encrypted_session_key,
- array(
- $server_key_public_exponent,
- $server_key_public_modulus
- )
- );
- }
-
- $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES;
- $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_SESSION_KEY');
- return false;
- }
-
- switch ($cipher) {
- //case self::CIPHER_NONE:
- // $this->crypto = new \phpseclib\Crypt\Null();
- // break;
- case self::CIPHER_DES:
- $this->crypto = new DES();
- $this->crypto->disablePadding();
- $this->crypto->enableContinuousBuffer();
- $this->crypto->setKey(substr($session_key, 0, 8));
- break;
- case self::CIPHER_3DES:
- $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
- $this->crypto->disablePadding();
- $this->crypto->enableContinuousBuffer();
- $this->crypto->setKey(substr($session_key, 0, 24));
- break;
- //case self::CIPHER_RC4:
- // $this->crypto = new RC4();
- // $this->crypto->enableContinuousBuffer();
- // $this->crypto->setKey(substr($session_key, 0, 16));
- // break;
- }
-
- $response = $this->_get_binary_packet();
-
- if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
- user_error('Expected SSH_SMSG_SUCCESS');
- return false;
- }
-
- $this->bitmap = self::MASK_CONNECTED;
-
- return true;
- }
-
- /**
- * Login
- *
- * @param string $username
- * @param string $password
- * @return bool
- * @access public
- */
- function login($username, $password = '')
- {
- if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
- $this->bitmap |= self::MASK_CONSTRUCTOR;
- if (!$this->_connect()) {
- return false;
- }
- }
-
- if (!($this->bitmap & self::MASK_CONNECTED)) {
- return false;
- }
-
- $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_USER');
- return false;
- }
-
- $response = $this->_get_binary_packet();
-
- if ($response === true) {
- return false;
- }
- if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
- $this->bitmap |= self::MASK_LOGIN;
- return true;
- } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
- user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
- return false;
- }
-
- $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_AUTH_PASSWORD');
- return false;
- }
-
- // remove the username and password from the last logged packet
- if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) {
- $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
- $this->message_log[count($this->message_log) - 1] = $data;
- }
-
- $response = $this->_get_binary_packet();
-
- if ($response === true) {
- return false;
- }
- if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
- $this->bitmap |= self::MASK_LOGIN;
- return true;
- } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
- return false;
- } else {
- user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
- return false;
- }
- }
-
- /**
- * Set Timeout
- *
- * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
- * Setting $timeout to false or 0 will mean there is no timeout.
- *
- * @param mixed $timeout
- */
- function setTimeout($timeout)
- {
- $this->timeout = $this->curTimeout = $timeout;
- }
-
- /**
- * Executes a command on a non-interactive shell, returns the output, and quits.
- *
- * An SSH1 server will close the connection after a command has been executed on a non-interactive shell. SSH2
- * servers don't, however, this isn't an SSH2 client. The way this works, on the server, is by initiating a
- * shell with the -s option, as discussed in the following links:
- *
- * {@link http://www.faqs.org/docs/bashman/bashref_65.html http://www.faqs.org/docs/bashman/bashref_65.html}
- * {@link http://www.faqs.org/docs/bashman/bashref_62.html http://www.faqs.org/docs/bashman/bashref_62.html}
- *
- * To execute further commands, a new \phpseclib\Net\SSH1 object will need to be created.
- *
- * Returns false on failure and the output, otherwise.
- *
- * @see self::interactiveRead()
- * @see self::interactiveWrite()
- * @param string $cmd
- * @param bool $block
- * @return mixed
- * @access public
- */
- function exec($cmd, $block = true)
- {
- if (!($this->bitmap & self::MASK_LOGIN)) {
- user_error('Operation disallowed prior to login()');
- return false;
- }
-
- $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_EXEC_CMD');
- return false;
- }
-
- if (!$block) {
- return true;
- }
-
- $output = '';
- $response = $this->_get_binary_packet();
-
- if ($response !== false) {
- do {
- $output.= substr($response[self::RESPONSE_DATA], 4);
- $response = $this->_get_binary_packet();
- } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS);
- }
-
- $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
-
- // i don't think it's really all that important if this packet gets sent or not.
- $this->_send_binary_packet($data);
-
- fclose($this->fsock);
-
- // reset the execution bitmap - a new \phpseclib\Net\SSH1 object needs to be created.
- $this->bitmap = 0;
-
- return $output;
- }
-
- /**
- * Creates an interactive shell
- *
- * @see self::interactiveRead()
- * @see self::interactiveWrite()
- * @return bool
- * @access private
- */
- function _initShell()
- {
- // connect using the sample parameters in protocol-1.5.txt.
- // according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
- // terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
- $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_REQUEST_PTY');
- return false;
- }
-
- $response = $this->_get_binary_packet();
-
- if ($response === true) {
- return false;
- }
- if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
- user_error('Expected SSH_SMSG_SUCCESS');
- return false;
- }
-
- $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_EXEC_SHELL');
- return false;
- }
-
- $this->bitmap |= self::MASK_SHELL;
-
- //stream_set_blocking($this->fsock, 0);
-
- return true;
- }
-
- /**
- * Inputs a command into an interactive shell.
- *
- * @see self::interactiveWrite()
- * @param string $cmd
- * @return bool
- * @access public
- */
- function write($cmd)
- {
- return $this->interactiveWrite($cmd);
- }
-
- /**
- * Returns the output of an interactive shell when there's a match for $expect
- *
- * $expect can take the form of a string literal or, if $mode == self::READ_REGEX,
- * a regular expression.
- *
- * @see self::write()
- * @param string $expect
- * @param int $mode
- * @return bool
- * @access public
- */
- function read($expect, $mode = self::READ_SIMPLE)
- {
- if (!($this->bitmap & self::MASK_LOGIN)) {
- user_error('Operation disallowed prior to login()');
- return false;
- }
-
- if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
- user_error('Unable to initiate an interactive shell session');
- return false;
- }
-
- $match = $expect;
- while (true) {
- if ($mode == self::READ_REGEX) {
- preg_match($expect, $this->interactiveBuffer, $matches);
- $match = isset($matches[0]) ? $matches[0] : '';
- }
- $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
- if ($pos !== false) {
- return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
- }
- $response = $this->_get_binary_packet();
-
- if ($response === true) {
- return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
- }
- $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4);
- }
- }
-
- /**
- * Inputs a command into an interactive shell.
- *
- * @see self::interactiveRead()
- * @param string $cmd
- * @return bool
- * @access public
- */
- function interactiveWrite($cmd)
- {
- if (!($this->bitmap & self::MASK_LOGIN)) {
- user_error('Operation disallowed prior to login()');
- return false;
- }
-
- if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
- user_error('Unable to initiate an interactive shell session');
- return false;
- }
-
- $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
-
- if (!$this->_send_binary_packet($data)) {
- user_error('Error sending SSH_CMSG_STDIN');
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns the output of an interactive shell when no more output is available.
- *
- * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
- * "^[[00m", you're seeing ANSI escape codes. According to
- * {@link http://support.microsoft.com/kb/101875 How to Enable ANSI.SYS in a Command Window}, "Windows NT
- * does not support ANSI escape sequences in Win32 Console applications", so if you're a Windows user,
- * there's not going to be much recourse.
- *
- * @see self::interactiveRead()
- * @return string
- * @access public
- */
- function interactiveRead()
- {
- if (!($this->bitmap & self::MASK_LOGIN)) {
- user_error('Operation disallowed prior to login()');
- return false;
- }
-
- if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
- user_error('Unable to initiate an interactive shell session');
- return false;
- }
-
- $read = array($this->fsock);
- $write = $except = null;
- if (stream_select($read, $write, $except, 0)) {
- $response = $this->_get_binary_packet();
- return substr($response[self::RESPONSE_DATA], 4);
- } else {
- return '';
- }
- }
-
- /**
- * Disconnect
- *
- * @access public
- */
- function disconnect()
- {
- $this->_disconnect();
- }
-
- /**
- * Destructor.
- *
- * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
- * disconnect().
- *
- * @access public
- */
- function __destruct()
- {
- $this->_disconnect();
- }
-
- /**
- * Disconnect
- *
- * @param string $msg
- * @access private
- */
- function _disconnect($msg = 'Client Quit')
- {
- if ($this->bitmap) {
- $data = pack('C', NET_SSH1_CMSG_EOF);
- $this->_send_binary_packet($data);
- /*
- $response = $this->_get_binary_packet();
- if ($response === true) {
- $response = array(self::RESPONSE_TYPE => -1);
- }
- switch ($response[self::RESPONSE_TYPE]) {
- case NET_SSH1_SMSG_EXITSTATUS:
- $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
- break;
- default:
- $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
- }
- */
- $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
-
- $this->_send_binary_packet($data);
- fclose($this->fsock);
- $this->bitmap = 0;
- }
- }
-
- /**
- * Gets Binary Packets
- *
- * See 'The Binary Packet Protocol' of protocol-1.5.txt for more info.
- *
- * Also, this function could be improved upon by adding detection for the following exploit:
- * http://www.securiteam.com/securitynews/5LP042K3FY.html
- *
- * @see self::_send_binary_packet()
- * @return array
- * @access private
- */
- function _get_binary_packet()
- {
- if (feof($this->fsock)) {
- //user_error('connection closed prematurely');
- return false;
- }
-
- if ($this->curTimeout) {
- $read = array($this->fsock);
- $write = $except = null;
-
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
- $sec = floor($this->curTimeout);
- $usec = 1000000 * ($this->curTimeout - $sec);
- // on windows this returns a "Warning: Invalid CRT parameters detected" error
- if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
- //$this->_disconnect('Timeout');
- return true;
- }
- $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
- $this->curTimeout-= $elapsed;
- }
-
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
- $data = fread($this->fsock, 4);
- if (strlen($data) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $data);
-
- $padding_length = 8 - ($temp['length'] & 7);
- $length = $temp['length'] + $padding_length;
- $raw = '';
-
- while ($length > 0) {
- $temp = fread($this->fsock, $length);
- if (strlen($temp) != $length) {
- return false;
- }
- $raw.= $temp;
- $length-= strlen($temp);
- }
- $stop = strtok(microtime(), ' ') + strtok('');
-
- if (strlen($raw) && $this->crypto !== false) {
- $raw = $this->crypto->decrypt($raw);
- }
-
- $padding = substr($raw, 0, $padding_length);
- $type = $raw[$padding_length];
- $data = substr($raw, $padding_length + 1, -4);
-
- if (strlen($raw) < 4) {
- return false;
- }
- $temp = unpack('Ncrc', substr($raw, -4));
-
- //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
- // user_error('Bad CRC in packet from server');
- // return false;
- //}
-
- $type = ord($type);
-
- if (defined('NET_SSH1_LOGGING')) {
- $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
- $temp = '<- ' . $temp .
- ' (' . round($stop - $start, 4) . 's)';
- $this->_append_log($temp, $data);
- }
-
- return array(
- self::RESPONSE_TYPE => $type,
- self::RESPONSE_DATA => $data
- );
- }
-
- /**
- * Sends Binary Packets
- *
- * Returns true on success, false on failure.
- *
- * @see self::_get_binary_packet()
- * @param string $data
- * @return bool
- * @access private
- */
- function _send_binary_packet($data)
- {
- if (feof($this->fsock)) {
- //user_error('connection closed prematurely');
- return false;
- }
-
- $length = strlen($data) + 4;
-
- $padding = Random::string(8 - ($length & 7));
-
- $orig = $data;
- $data = $padding . $data;
- $data.= pack('N', $this->_crc($data));
-
- if ($this->crypto !== false) {
- $data = $this->crypto->encrypt($data);
- }
-
- $packet = pack('Na*', $length, $data);
-
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
- $result = strlen($packet) == fputs($this->fsock, $packet);
- $stop = strtok(microtime(), ' ') + strtok('');
-
- if (defined('NET_SSH1_LOGGING')) {
- $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN';
- $temp = '-> ' . $temp .
- ' (' . round($stop - $start, 4) . 's)';
- $this->_append_log($temp, $orig);
- }
-
- return $result;
- }
-
- /**
- * Cyclic Redundancy Check (CRC)
- *
- * PHP's crc32 function is implemented slightly differently than the one that SSH v1 uses, so
- * we've reimplemented it. A more detailed discussion of the differences can be found after
- * $crc_lookup_table's initialization.
- *
- * @see self::_get_binary_packet()
- * @see self::_send_binary_packet()
- * @param string $data
- * @return int
- * @access private
- */
- function _crc($data)
- {
- static $crc_lookup_table = array(
- 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
- 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
- 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
- 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
- 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
- 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
- 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
- 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
- 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
- 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
- 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
- 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
- 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
- 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
- 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
- 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
- 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
- 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
- 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
- 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
- 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
- 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
- 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
- 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
- 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
- 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
- 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
- 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
- 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
- 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
- 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
- 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
- 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
- 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
- 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
- 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
- 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
- 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
- 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
- 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
- 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
- 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
- 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
- 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
- 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
- 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
- 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
- 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
- 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
- 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
- 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
- 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
- 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
- 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
- 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
- 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
- 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
- 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
- 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
- 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
- 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
- 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
- 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
- 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
- );
-
- // For this function to yield the same output as PHP's crc32 function, $crc would have to be
- // set to 0xFFFFFFFF, initially - not 0x00000000 as it currently is.
- $crc = 0x00000000;
- $length = strlen($data);
-
- for ($i=0; $i<$length; $i++) {
- // We AND $crc >> 8 with 0x00FFFFFF because we want the eight newly added bits to all
- // be zero. PHP, unfortunately, doesn't always do this. 0x80000000 >> 8, as an example,
- // yields 0xFF800000 - not 0x00800000. The following link elaborates:
- // http://www.php.net/manual/en/language.operators.bitwise.php#57281
- $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])];
- }
-
- // In addition to having to set $crc to 0xFFFFFFFF, initially, the return value must be XOR'd with
- // 0xFFFFFFFF for this function to return the same thing that PHP's crc32 function would.
- return $crc;
- }
-
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @return string
- * @access private
- */
- function _string_shift(&$string, $index = 1)
- {
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
- }
-
- /**
- * RSA Encrypt
- *
- * Returns mod(pow($m, $e), $n), where $n should be the product of two (large) primes $p and $q and where $e
- * should be a number with the property that gcd($e, ($p - 1) * ($q - 1)) == 1. Could just make anything that
- * calls this call modexp, instead, but I think this makes things clearer, maybe...
- *
- * @see self::__construct()
- * @param BigInteger $m
- * @param array $key
- * @return BigInteger
- * @access private
- */
- function _rsa_crypt($m, $key)
- {
- /*
- $rsa = new RSA();
- $rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW);
- $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
- return $rsa->encrypt($m);
- */
-
- // To quote from protocol-1.5.txt:
- // The most significant byte (which is only partial as the value must be
- // less than the public modulus, which is never a power of two) is zero.
- //
- // The next byte contains the value 2 (which stands for public-key
- // encrypted data in the PKCS standard [PKCS#1]). Then, there are non-
- // zero random bytes to fill any unused space, a zero byte, and the data
- // to be encrypted in the least significant bytes, the last byte of the
- // data in the least significant byte.
-
- // Presumably the part of PKCS#1 they're refering to is "Section 7.2.1 Encryption Operation",
- // under "7.2 RSAES-PKCS1-v1.5" and "7 Encryption schemes" of the following URL:
- // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
- $modulus = $key[1]->toBytes();
- $length = strlen($modulus) - strlen($m) - 3;
- $random = '';
- while (strlen($random) != $length) {
- $block = Random::string($length - strlen($random));
- $block = str_replace("\x00", '', $block);
- $random.= $block;
- }
- $temp = chr(0) . chr(2) . $random . chr(0) . $m;
-
- $m = new BigInteger($temp, 256);
- $m = $m->modPow($key[0], $key[1]);
-
- return $m->toBytes();
- }
-
- /**
- * Define Array
- *
- * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
- * named constants from it, using the value as the name of the constant and the index as the value of the constant.
- * If any of the constants that would be defined already exists, none of the constants will be defined.
- *
- * @access private
- */
- function _define_array()
- {
- $args = func_get_args();
- foreach ($args as $arg) {
- foreach ($arg as $key => $value) {
- if (!defined($value)) {
- define($value, $key);
- } else {
- break 2;
- }
- }
- }
- }
-
- /**
- * Returns a log of the packets that have been sent and received.
- *
- * Returns a string if NET_SSH1_LOGGING == self::LOG_COMPLEX, an array if NET_SSH1_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH1_LOGGING')
- *
- * @access public
- * @return array|false|string
- */
- function getLog()
- {
- if (!defined('NET_SSH1_LOGGING')) {
- return false;
- }
-
- switch (NET_SSH1_LOGGING) {
- case self::LOG_SIMPLE:
- return $this->protocol_flags_log;
- break;
- case self::LOG_COMPLEX:
- return $this->_format_log($this->message_log, $this->protocol_flags_log);
- break;
- default:
- return false;
- }
- }
-
- /**
- * Formats a log for printing
- *
- * @param array $message_log
- * @param array $message_number_log
- * @access private
- * @return string
- */
- function _format_log($message_log, $message_number_log)
- {
- $output = '';
- for ($i = 0; $i < count($message_log); $i++) {
- $output.= $message_number_log[$i] . "\r\n";
- $current_log = $message_log[$i];
- $j = 0;
- do {
- if (strlen($current_log)) {
- $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
- }
- $fragment = $this->_string_shift($current_log, $this->log_short_width);
- $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
- // replace non ASCII printable characters with dots
- // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
- // also replace < with a . since < messes up the output on web browsers
- $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
- $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
- $j++;
- } while (strlen($current_log));
- $output.= "\r\n";
- }
-
- return $output;
- }
-
- /**
- * Helper function for _format_log
- *
- * For use with preg_replace_callback()
- *
- * @param array $matches
- * @access private
- * @return string
- */
- function _format_log_helper($matches)
- {
- return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
- }
-
- /**
- * Return the server key public exponent
- *
- * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
- * the raw bytes. This behavior is similar to PHP's md5() function.
- *
- * @param bool $raw_output
- * @return string
- * @access public
- */
- function getServerKeyPublicExponent($raw_output = false)
- {
- return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString();
- }
-
- /**
- * Return the server key public modulus
- *
- * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
- * the raw bytes. This behavior is similar to PHP's md5() function.
- *
- * @param bool $raw_output
- * @return string
- * @access public
- */
- function getServerKeyPublicModulus($raw_output = false)
- {
- return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString();
- }
-
- /**
- * Return the host key public exponent
- *
- * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
- * the raw bytes. This behavior is similar to PHP's md5() function.
- *
- * @param bool $raw_output
- * @return string
- * @access public
- */
- function getHostKeyPublicExponent($raw_output = false)
- {
- return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString();
- }
-
- /**
- * Return the host key public modulus
- *
- * Returns, by default, the base-10 representation. If $raw_output is set to true, returns, instead,
- * the raw bytes. This behavior is similar to PHP's md5() function.
- *
- * @param bool $raw_output
- * @return string
- * @access public
- */
- function getHostKeyPublicModulus($raw_output = false)
- {
- return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString();
- }
-
- /**
- * Return a list of ciphers supported by SSH1 server.
- *
- * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
- * is set to true, returns, instead, an array of constants. ie. instead of array('Triple-DES in CBC mode'), you'll
- * get array(self::CIPHER_3DES).
- *
- * @param bool $raw_output
- * @return array
- * @access public
- */
- function getSupportedCiphers($raw_output = false)
- {
- return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers);
- }
-
- /**
- * Return a list of authentications supported by SSH1 server.
- *
- * Just because a cipher is supported by an SSH1 server doesn't mean it's supported by this library. If $raw_output
- * is set to true, returns, instead, an array of constants. ie. instead of array('password authentication'), you'll
- * get array(self::AUTH_PASSWORD).
- *
- * @param bool $raw_output
- * @return array
- * @access public
- */
- function getSupportedAuthentications($raw_output = false)
- {
- return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications);
- }
-
- /**
- * Return the server identification.
- *
- * @return string
- * @access public
- */
- function getServerIdentification()
- {
- return rtrim($this->server_identification);
- }
-
- /**
- * Logs data packets
- *
- * Makes sure that only the last 1MB worth of packets will be logged
- *
- * @param int $protocol_flags
- * @param string $message
- * @access private
- */
- function _append_log($protocol_flags, $message)
- {
- switch (NET_SSH1_LOGGING) {
- // useful for benchmarks
- case self::LOG_SIMPLE:
- $this->protocol_flags_log[] = $protocol_flags;
- break;
- // the most useful log for SSH1
- case self::LOG_COMPLEX:
- $this->protocol_flags_log[] = $protocol_flags;
- $this->_string_shift($message);
- $this->log_size+= strlen($message);
- $this->message_log[] = $message;
- while ($this->log_size > self::LOG_MAX_SIZE) {
- $this->log_size-= strlen(array_shift($this->message_log));
- array_shift($this->protocol_flags_log);
- }
- break;
- // dump the output out realtime; packets may be interspersed with non packets,
- // passwords won't be filtered out and select other packets may not be correctly
- // identified
- case self::LOG_REALTIME:
- echo "<pre>\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n</pre>\r\n";
- @flush();
- @ob_flush();
- break;
- // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
- // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
- // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
- // at the beginning of the file
- case self::LOG_REALTIME_FILE:
- if (!isset($this->realtime_log_file)) {
- // PHP doesn't seem to like using constants in fopen()
- $filename = self::LOG_REALTIME_FILE;
- $fp = fopen($filename, 'w');
- $this->realtime_log_file = $fp;
- }
- if (!is_resource($this->realtime_log_file)) {
- break;
- }
- $entry = $this->_format_log(array($message), array($protocol_flags));
- if ($this->realtime_log_wrap) {
- $temp = "<<< START >>>\r\n";
- $entry.= $temp;
- fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
- }
- $this->realtime_log_size+= strlen($entry);
- if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
- fseek($this->realtime_log_file, 0);
- $this->realtime_log_size = strlen($entry);
- $this->realtime_log_wrap = true;
- }
- fputs($this->realtime_log_file, $entry);
- }
- }
-}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
index 775d894fb..1c8a0e265 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
@@ -10,7 +10,7 @@
* <?php
* include 'vendor/autoload.php';
*
- * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
+ * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', 'password')) {
* exit('Login Failed');
* }
@@ -24,11 +24,9 @@
* <?php
* include 'vendor/autoload.php';
*
- * $key = new \phpseclib\Crypt\RSA();
- * //$key->setPassword('whatever');
- * $key->loadKey(file_get_contents('privatekey'));
+ * $key = \phpseclib3\Crypt\PublicKeyLoader::load('...', '(optional) password');
*
- * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
+ * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $key)) {
* exit('Login Failed');
* }
@@ -39,41 +37,52 @@
* ?>
* </code>
*
- * @category Net
- * @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2007 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
-namespace phpseclib\Net;
-
-use phpseclib\Crypt\Base;
-use phpseclib\Crypt\Blowfish;
-use phpseclib\Crypt\Hash;
-use phpseclib\Crypt\Random;
-use phpseclib\Crypt\RC4;
-use phpseclib\Crypt\Rijndael;
-use phpseclib\Crypt\RSA;
-use phpseclib\Crypt\TripleDES;
-use phpseclib\Crypt\Twofish;
-use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
-use phpseclib\System\SSH\Agent;
+namespace phpseclib3\Net;
+
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Blowfish;
+use phpseclib3\Crypt\ChaCha20;
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Crypt\Common\SymmetricKey;
+use phpseclib3\Crypt\DH;
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\EC;
+use phpseclib3\Crypt\Hash;
+use phpseclib3\Crypt\Random;
+use phpseclib3\Crypt\RC4;
+use phpseclib3\Crypt\Rijndael;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Crypt\TripleDES; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
+use phpseclib3\Crypt\Twofish;
+use phpseclib3\Exception\ConnectionClosedException;
+use phpseclib3\Exception\InsufficientSetupException;
+use phpseclib3\Exception\InvalidPacketLengthException;
+use phpseclib3\Exception\NoSupportedAlgorithmsException;
+use phpseclib3\Exception\TimeoutException;
+use phpseclib3\Exception\UnableToConnectException;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Exception\UnsupportedCurveException;
+use phpseclib3\Math\BigInteger;
+use phpseclib3\System\SSH\Agent;
/**
* Pure-PHP implementation of SSHv2.
*
- * @package SSH2
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class SSH2
{
/**#@+
* Compression Types
*
- * @access private
*/
/**
* No compression
@@ -89,53 +98,45 @@ class SSH2
const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;
/**#@-*/
- /**#@+
- * Execution Bitmap Masks
- *
- * @see \phpseclib\Net\SSH2::bitmap
- * @access private
- */
+ // Execution Bitmap Masks
const MASK_CONSTRUCTOR = 0x00000001;
const MASK_CONNECTED = 0x00000002;
const MASK_LOGIN_REQ = 0x00000004;
const MASK_LOGIN = 0x00000008;
const MASK_SHELL = 0x00000010;
- const MASK_WINDOW_ADJUST = 0x00000020;
- /**#@-*/
+ const MASK_DISCONNECT = 0x00000020;
- /**#@+
+ /*
* Channel constants
*
* RFC4254 refers not to client and server channels but rather to sender and recipient channels. we don't refer
* to them in that way because RFC4254 toggles the meaning. the client sends a SSH_MSG_CHANNEL_OPEN message with
* a sender channel and the server sends a SSH_MSG_CHANNEL_OPEN_CONFIRMATION in response, with a sender and a
- * recepient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
- * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snipet:
+ * recipient channel. at first glance, you might conclude that SSH_MSG_CHANNEL_OPEN_CONFIRMATION's sender channel
+ * would be the same thing as SSH_MSG_CHANNEL_OPEN's sender channel, but it's not, per this snippet:
* The 'recipient channel' is the channel number given in the original
* open request, and 'sender channel' is the channel number allocated by
* the other side.
*
- * @see \phpseclib\Net\SSH2::_send_channel_packet()
- * @see \phpseclib\Net\SSH2::_get_channel_packet()
- * @access private
- */
+ * @see \phpseclib3\Net\SSH2::send_channel_packet()
+ * @see \phpseclib3\Net\SSH2::get_channel_packet()
+ */
const CHANNEL_EXEC = 1; // PuTTy uses 0x100
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
const CHANNEL_KEEP_ALIVE = 5;
- /**#@-*/
- /**#@+
- * @access public
- * @see \phpseclib\Net\SSH2::getLog()
- */
/**
* Returns the message numbers
+ *
+ * @see \phpseclib3\Net\SSH2::getLog()
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
+ *
+ * @see \phpseclib3\Net\SSH2::getLog()
*/
const LOG_COMPLEX = 2;
/**
@@ -147,25 +148,30 @@ class SSH2
*/
const LOG_REALTIME_FILE = 4;
/**
+ * Outputs the message numbers real-time
+ */
+ const LOG_SIMPLE_REALTIME = 5;
+ /*
* Dumps the message numbers real-time
*/
const LOG_REALTIME_SIMPLE = 5;
/**
* Make sure that the log never gets larger than this
+ *
+ * @see \phpseclib3\Net\SSH2::getLog()
*/
const LOG_MAX_SIZE = 1048576; // 1024 * 1024
- /**#@-*/
- /**#@+
- * @access public
- * @see \phpseclib\Net\SSH2::read()
- */
/**
* Returns when a string matching $expect exactly is found
+ *
+ * @see \phpseclib3\Net\SSH2::read()
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is found
+ *
+ * @see \phpseclib3\Net\SSH2::read()
*/
const READ_REGEX = 2;
/**
@@ -173,25 +179,24 @@ class SSH2
*
* Some data packets may only contain a single character so it may be necessary
* to call read() multiple times when using this option
+ *
+ * @see \phpseclib3\Net\SSH2::read()
*/
const READ_NEXT = 3;
- /**#@-*/
/**
* The SSH identifier
*
* @var string
- * @access private
*/
- var $identifier;
+ private $identifier;
/**
* The Socket Object
*
- * @var object
- * @access private
+ * @var resource|closed-resource|null
*/
- var $fsock;
+ public $fsock;
/**
* Execution Bitmap
@@ -200,82 +205,73 @@ class SSH2
* if a requisite function has been successfully executed. If not, an error should be thrown.
*
* @var int
- * @access private
*/
- var $bitmap = 0;
+ protected $bitmap = 0;
/**
* Error information
*
* @see self::getErrors()
* @see self::getLastError()
- * @var string
- * @access private
+ * @var array
*/
- var $errors = array();
+ private $errors = [];
/**
* Server Identifier
*
* @see self::getServerIdentification()
- * @var array|false
- * @access private
+ * @var string|false
*/
- var $server_identifier = false;
+ protected $server_identifier = false;
/**
* Key Exchange Algorithms
*
* @see self::getKexAlgorithims()
* @var array|false
- * @access private
*/
- var $kex_algorithms = false;
+ private $kex_algorithms = false;
/**
* Key Exchange Algorithm
*
* @see self::getMethodsNegotiated()
* @var string|false
- * @access private
*/
- var $kex_algorithm = false;
+ private $kex_algorithm = false;
/**
* Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
*
* @see self::_key_exchange()
* @var int
- * @access private
*/
- var $kex_dh_group_size_min = 1536;
+ private $kex_dh_group_size_min = 1536;
/**
* Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
*
* @see self::_key_exchange()
* @var int
- * @access private
*/
- var $kex_dh_group_size_preferred = 2048;
+ private $kex_dh_group_size_preferred = 2048;
/**
* Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
*
* @see self::_key_exchange()
* @var int
- * @access private
*/
- var $kex_dh_group_size_max = 4096;
+ private $kex_dh_group_size_max = 4096;
/**
* Server Host Key Algorithms
*
* @see self::getServerHostKeyAlgorithms()
* @var array|false
- * @access private
*/
- var $server_host_key_algorithms = false;
+ private $server_host_key_algorithms = false;
/**
* Supported Private Key Algorithms
@@ -287,88 +283,79 @@ class SSH2
* @see self::privatekey_login()
* @var array|false
*/
- var $supported_private_key_algorithms = false;
+ private $supported_private_key_algorithms = false;
/**
* Encryption Algorithms: Client to Server
*
* @see self::getEncryptionAlgorithmsClient2Server()
* @var array|false
- * @access private
*/
- var $encryption_algorithms_client_to_server = false;
+ private $encryption_algorithms_client_to_server = false;
/**
* Encryption Algorithms: Server to Client
*
* @see self::getEncryptionAlgorithmsServer2Client()
* @var array|false
- * @access private
*/
- var $encryption_algorithms_server_to_client = false;
+ private $encryption_algorithms_server_to_client = false;
/**
* MAC Algorithms: Client to Server
*
* @see self::getMACAlgorithmsClient2Server()
* @var array|false
- * @access private
*/
- var $mac_algorithms_client_to_server = false;
+ private $mac_algorithms_client_to_server = false;
/**
* MAC Algorithms: Server to Client
*
* @see self::getMACAlgorithmsServer2Client()
* @var array|false
- * @access private
*/
- var $mac_algorithms_server_to_client = false;
+ private $mac_algorithms_server_to_client = false;
/**
* Compression Algorithms: Client to Server
*
* @see self::getCompressionAlgorithmsClient2Server()
* @var array|false
- * @access private
*/
- var $compression_algorithms_client_to_server = false;
+ private $compression_algorithms_client_to_server = false;
/**
* Compression Algorithms: Server to Client
*
* @see self::getCompressionAlgorithmsServer2Client()
* @var array|false
- * @access private
*/
- var $compression_algorithms_server_to_client = false;
+ private $compression_algorithms_server_to_client = false;
/**
* Languages: Server to Client
*
* @see self::getLanguagesServer2Client()
* @var array|false
- * @access private
*/
- var $languages_server_to_client = false;
+ private $languages_server_to_client = false;
/**
* Languages: Client to Server
*
* @see self::getLanguagesClient2Server()
* @var array|false
- * @access private
*/
- var $languages_client_to_server = false;
+ private $languages_client_to_server = false;
/**
* Preferred Algorithms
*
* @see self::setPreferredAlgorithms()
* @var array
- * @access private
*/
- var $preferred = array();
+ private $preferred = [];
/**
* Block Size for Server to Client Encryption
@@ -383,9 +370,8 @@ class SSH2
* @see self::__construct()
* @see self::_send_binary_packet()
* @var int
- * @access private
*/
- var $encrypt_block_size = 8;
+ private $encrypt_block_size = 8;
/**
* Block Size for Client to Server Encryption
@@ -393,52 +379,98 @@ class SSH2
* @see self::__construct()
* @see self::_get_binary_packet()
* @var int
- * @access private
*/
- var $decrypt_block_size = 8;
+ private $decrypt_block_size = 8;
/**
* Server to Client Encryption Object
*
* @see self::_get_binary_packet()
- * @var object
- * @access private
+ * @var SymmetricKey|false
*/
- var $decrypt = false;
+ private $decrypt = false;
/**
* Decryption Algorithm Name
*
* @var string|null
- * @access private
*/
- var $decryptName;
+ private $decryptName;
+
+ /**
+ * Decryption Invocation Counter
+ *
+ * Used by GCM
+ *
+ * @var string|null
+ */
+ private $decryptInvocationCounter;
+
+ /**
+ * Fixed Part of Nonce
+ *
+ * Used by GCM
+ *
+ * @var string|null
+ */
+ private $decryptFixedPart;
+
+ /**
+ * Server to Client Length Encryption Object
+ *
+ * @see self::_get_binary_packet()
+ * @var object
+ */
+ private $lengthDecrypt = false;
/**
* Client to Server Encryption Object
*
* @see self::_send_binary_packet()
- * @var object
- * @access private
+ * @var SymmetricKey|false
*/
- var $encrypt = false;
+ private $encrypt = false;
/**
* Encryption Algorithm Name
*
* @var string|null
- * @access private
*/
- var $encryptName;
+ private $encryptName;
+
+ /**
+ * Encryption Invocation Counter
+ *
+ * Used by GCM
+ *
+ * @var string|null
+ */
+ private $encryptInvocationCounter;
+
+ /**
+ * Fixed Part of Nonce
+ *
+ * Used by GCM
+ *
+ * @var string|null
+ */
+ private $encryptFixedPart;
+
+ /**
+ * Client to Server Length Encryption Object
+ *
+ * @see self::_send_binary_packet()
+ * @var object
+ */
+ private $lengthEncrypt = false;
/**
* Client to Server HMAC Object
*
* @see self::_send_binary_packet()
* @var object
- * @access private
*/
- var $hmac_create = false;
+ private $hmac_create = false;
/**
* Client to Server HMAC Name
@@ -448,20 +480,33 @@ class SSH2
private $hmac_create_name;
/**
+ * Client to Server ETM
+ *
+ * @var int|false
+ */
+ private $hmac_create_etm;
+
+ /**
* Server to Client HMAC Object
*
* @see self::_get_binary_packet()
* @var object
- * @access private
*/
- var $hmac_check = false;
+ private $hmac_check = false;
/**
* Server to Client HMAC Name
*
* @var string|false
*/
- var $hmac_check_name;
+ private $hmac_check_name;
+
+ /**
+ * Server to Client ETM
+ *
+ * @var int|false
+ */
+ private $hmac_check_etm;
/**
* Size of server to client HMAC
@@ -472,18 +517,16 @@ class SSH2
*
* @see self::_get_binary_packet()
* @var int
- * @access private
*/
- var $hmac_size = false;
+ private $hmac_size = false;
/**
* Server Public Host Key
*
* @see self::getServerPublicHostKey()
* @var string
- * @access private
*/
- var $server_public_host_key;
+ private $server_public_host_key;
/**
* Session identifier
@@ -496,9 +539,8 @@ class SSH2
*
* @see self::_key_exchange()
* @var string
- * @access private
*/
- var $session_id = false;
+ private $session_id = false;
/**
* Exchange hash
@@ -507,9 +549,8 @@ class SSH2
*
* @see self::_key_exchange()
* @var string
- * @access private
*/
- var $exchange_hash = false;
+ private $exchange_hash = false;
/**
* Message Numbers
@@ -518,7 +559,7 @@ class SSH2
* @var array
* @access private
*/
- var $message_numbers = array();
+ private static $message_numbers = [];
/**
* Disconnection Message 'reason codes' defined in RFC4253
@@ -527,7 +568,7 @@ class SSH2
* @var array
* @access private
*/
- var $disconnect_reasons = array();
+ private static $disconnect_reasons = [];
/**
* SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254
@@ -536,7 +577,7 @@ class SSH2
* @var array
* @access private
*/
- var $channel_open_failure_reasons = array();
+ private static $channel_open_failure_reasons = [];
/**
* Terminal Modes
@@ -546,7 +587,7 @@ class SSH2
* @var array
* @access private
*/
- var $terminal_modes = array();
+ private static $terminal_modes = [];
/**
* SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes
@@ -556,7 +597,7 @@ class SSH2
* @var array
* @access private
*/
- var $channel_extended_data_type_codes = array();
+ private static $channel_extended_data_type_codes = [];
/**
* Send Sequence Number
@@ -565,9 +606,8 @@ class SSH2
*
* @see self::_send_binary_packet()
* @var int
- * @access private
*/
- var $send_seq_no = 0;
+ private $send_seq_no = 0;
/**
* Get Sequence Number
@@ -576,74 +616,86 @@ class SSH2
*
* @see self::_get_binary_packet()
* @var int
- * @access private
*/
- var $get_seq_no = 0;
+ private $get_seq_no = 0;
/**
* Server Channels
*
* Maps client channels to server channels
*
- * @see self::_get_channel_packet()
+ * @see self::get_channel_packet()
* @see self::exec()
* @var array
- * @access private
*/
- var $server_channels = array();
+ protected $server_channels = [];
/**
- * Channel Buffers
+ * Channel Read Buffers
*
* If a client requests a packet from one channel but receives two packets from another those packets should
* be placed in a buffer
*
- * @see self::_get_channel_packet()
+ * @see self::get_channel_packet()
* @see self::exec()
* @var array
- * @access private
*/
- var $channel_buffers = array();
+ private $channel_buffers = [];
+
+ /**
+ * Channel Write Buffers
+ *
+ * If a client sends a packet and receives a timeout error mid-transmission, buffer the data written so it
+ * can be de-duplicated upon resuming write
+ *
+ * @see self::send_channel_packet()
+ * @var array
+ */
+ private $channel_buffers_write = [];
/**
* Channel Status
*
* Contains the type of the last sent message
*
- * @see self::_get_channel_packet()
+ * @see self::get_channel_packet()
* @var array
- * @access private
*/
- var $channel_status = array();
+ protected $channel_status = [];
+
+ /**
+ * The identifier of the interactive channel which was opened most recently
+ *
+ * @see self::getInteractiveChannelId()
+ * @var int
+ */
+ private $channel_id_last_interactive = 0;
/**
* Packet Size
*
* Maximum packet size indexed by channel
*
- * @see self::_send_channel_packet()
+ * @see self::send_channel_packet()
* @var array
- * @access private
*/
- var $packet_size_client_to_server = array();
+ private $packet_size_client_to_server = [];
/**
* Message Number Log
*
* @see self::getLog()
* @var array
- * @access private
*/
- var $message_number_log = array();
+ private $message_number_log = [];
/**
* Message Log
*
* @see self::getLog()
* @var array
- * @access private
*/
- var $message_log = array();
+ private $message_log = [];
/**
* The Window Size
@@ -651,11 +703,10 @@ class SSH2
* Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB)
*
* @var int
- * @see self::_send_channel_packet()
+ * @see self::send_channel_packet()
* @see self::exec()
- * @access private
*/
- var $window_size = 0x7FFFFFFF;
+ protected $window_size = 0x7FFFFFFF;
/**
* What we resize the window to
@@ -667,31 +718,28 @@ class SSH2
* @var int
* @see self::_send_channel_packet()
* @see self::exec()
- * @access private
*/
- var $window_resize = 0x40000000;
+ private $window_resize = 0x40000000;
/**
* Window size, server to client
*
* Window size indexed by channel
*
- * @see self::_send_channel_packet()
+ * @see self::send_channel_packet()
* @var array
- * @access private
*/
- var $window_size_server_to_client = array();
+ protected $window_size_server_to_client = [];
/**
* Window size, client to server
*
* Window size indexed by channel
*
- * @see self::_get_channel_packet()
+ * @see self::get_channel_packet()
* @var array
- * @access private
*/
- var $window_size_client_to_server = array();
+ private $window_size_client_to_server = [];
/**
* Server signature
@@ -700,9 +748,8 @@ class SSH2
*
* @see self::getServerPublicHostKey()
* @var string
- * @access private
*/
- var $signature = '';
+ private $signature = '';
/**
* Server signature format
@@ -711,18 +758,16 @@ class SSH2
*
* @see self::getServerPublicHostKey()
* @var string
- * @access private
*/
- var $signature_format = '';
+ private $signature_format = '';
/**
* Interactive Buffer
*
* @see self::read()
- * @var array
- * @access private
+ * @var string
*/
- var $interactiveBuffer = '';
+ private $interactiveBuffer = '';
/**
* Current log size
@@ -732,143 +777,113 @@ class SSH2
* @see self::_send_binary_packet()
* @see self::_get_binary_packet()
* @var int
- * @access private
*/
- var $log_size;
+ private $log_size;
/**
* Timeout
*
* @see self::setTimeout()
- * @access private
*/
- var $timeout;
+ protected $timeout;
/**
* Current Timeout
*
- * @see self::_get_channel_packet()
- * @access private
+ * @see self::get_channel_packet()
*/
- var $curTimeout;
+ protected $curTimeout;
/**
* Keep Alive Interval
*
* @see self::setKeepAlive()
- * @access private
*/
- var $keepAlive;
+ private $keepAlive;
/**
* Real-time log file pointer
*
* @see self::_append_log()
- * @var resource
- * @access private
+ * @var resource|closed-resource
*/
- var $realtime_log_file;
+ private $realtime_log_file;
/**
* Real-time log file size
*
* @see self::_append_log()
* @var int
- * @access private
*/
- var $realtime_log_size;
+ private $realtime_log_size;
/**
* Has the signature been validated?
*
* @see self::getServerPublicHostKey()
* @var bool
- * @access private
*/
- var $signature_validated = false;
+ private $signature_validated = false;
/**
* Real-time log file wrap boolean
*
* @see self::_append_log()
- * @access private
+ * @var bool
*/
- var $realtime_log_wrap;
+ private $realtime_log_wrap;
/**
* Flag to suppress stderr from output
*
* @see self::enableQuietMode()
- * @access private
*/
- var $quiet_mode = false;
+ private $quiet_mode = false;
/**
- * Time of first network activity
+ * Time of last read/write network activity
*
- * @var int
- * @access private
+ * @var float
*/
- var $last_packet;
+ private $last_packet = null;
/**
* Exit status returned from ssh if any
*
* @var int
- * @access private
*/
- var $exit_status;
+ private $exit_status;
/**
* Flag to request a PTY when using exec()
*
* @var bool
* @see self::enablePTY()
- * @access private
*/
- var $request_pty = false;
-
- /**
- * Flag set while exec() is running when using enablePTY()
- *
- * @var bool
- * @access private
- */
- var $in_request_pty_exec = false;
-
- /**
- * Flag set after startSubsystem() is called
- *
- * @var bool
- * @access private
- */
- var $in_subsystem;
+ private $request_pty = false;
/**
* Contents of stdError
*
* @var string
- * @access private
*/
- var $stdErrorLog;
+ private $stdErrorLog;
/**
* The Last Interactive Response
*
* @see self::_keyboard_interactive_process()
* @var string
- * @access private
*/
- var $last_interactive_response = '';
+ private $last_interactive_response = '';
/**
* Keyboard Interactive Request / Responses
*
* @see self::_keyboard_interactive_process()
* @var array
- * @access private
*/
- var $keyboard_requests_responses = array();
+ private $keyboard_requests_responses = [];
/**
* Banner Message
@@ -879,45 +894,40 @@ class SSH2
* @see self::_filter()
* @see self::getBannerMessage()
* @var string
- * @access private
*/
- var $banner_message = '';
+ private $banner_message = '';
/**
* Did read() timeout or return normally?
*
* @see self::isTimeout()
* @var bool
- * @access private
*/
- var $is_timeout = false;
+ protected $is_timeout = false;
/**
* Log Boundary
*
* @see self::_format_log()
* @var string
- * @access private
*/
- var $log_boundary = ':';
+ private $log_boundary = ':';
/**
* Log Long Width
*
* @see self::_format_log()
* @var int
- * @access private
*/
- var $log_long_width = 65;
+ private $log_long_width = 65;
/**
* Log Short Width
*
* @see self::_format_log()
* @var int
- * @access private
*/
- var $log_short_width = 16;
+ private $log_short_width = 16;
/**
* Hostname
@@ -925,9 +935,8 @@ class SSH2
* @see self::__construct()
* @see self::_connect()
* @var string
- * @access private
*/
- var $host;
+ private $host;
/**
* Port Number
@@ -935,9 +944,8 @@ class SSH2
* @see self::__construct()
* @see self::_connect()
* @var int
- * @access private
*/
- var $port;
+ private $port;
/**
* Number of columns for terminal window size
@@ -946,9 +954,8 @@ class SSH2
* @see self::setWindowColumns()
* @see self::setWindowSize()
* @var int
- * @access private
*/
- var $windowColumns = 80;
+ private $windowColumns = 80;
/**
* Number of columns for terminal window size
@@ -957,9 +964,8 @@ class SSH2
* @see self::setWindowRows()
* @see self::setWindowSize()
* @var int
- * @access private
*/
- var $windowRows = 24;
+ private $windowRows = 24;
/**
* Crypto Engine
@@ -967,164 +973,175 @@ class SSH2
* @see self::setCryptoEngine()
* @see self::_key_exchange()
* @var int
- * @access private
*/
- var $crypto_engine = false;
+ private static $crypto_engine = false;
/**
* A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario
*
- * @var System_SSH_Agent
- * @access private
+ * @var Agent
*/
- var $agent;
+ private $agent;
+
+ /**
+ * Connection storage to replicates ssh2 extension functionality:
+ * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
+ *
+ * @var array<string, SSH2|\WeakReference<SSH2>>
+ */
+ private static $connections;
/**
* Send the identification string first?
*
* @var bool
- * @access private
*/
- var $send_id_string_first = true;
+ private $send_id_string_first = true;
/**
* Send the key exchange initiation packet first?
*
* @var bool
- * @access private
*/
- var $send_kex_first = true;
+ private $send_kex_first = true;
/**
* Some versions of OpenSSH incorrectly calculate the key size
*
* @var bool
- * @access private
*/
- var $bad_key_size_fix = false;
+ private $bad_key_size_fix = false;
/**
* Should we try to re-connect to re-establish keys?
*
* @var bool
- * @access private
*/
- var $login_credentials_finalized = false;
+ private $login_credentials_finalized = false;
/**
* Binary Packet Buffer
*
- * @var string|false
- * @access private
+ * @var object|null
*/
- var $binary_packet_buffer = false;
+ private $binary_packet_buffer = null;
/**
* Preferred Signature Format
*
* @var string|false
- * @access private
*/
- var $preferred_signature_format = false;
+ protected $preferred_signature_format = false;
/**
* Authentication Credentials
*
* @var array
- * @access private
*/
- var $auth = array();
+ protected $auth = [];
+
+ /**
+ * Terminal
+ *
+ * @var string
+ */
+ private $term = 'vt100';
/**
* The authentication methods that may productively continue authentication.
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @var array|null
- * @access private
*/
- var $auth_methods_to_continue = null;
+ private $auth_methods_to_continue = null;
/**
* Compression method
*
* @var int
- * @access private
*/
- var $compress = self::NET_SSH2_COMPRESSION_NONE;
+ private $compress = self::NET_SSH2_COMPRESSION_NONE;
/**
* Decompression method
*
- * @var resource|object
- * @access private
+ * @var int
*/
- var $decompress = self::NET_SSH2_COMPRESSION_NONE;
+ private $decompress = self::NET_SSH2_COMPRESSION_NONE;
/**
* Compression context
*
- * @var int
- * @access private
+ * @var resource|false|null
*/
- var $compress_context;
+ private $compress_context;
/**
* Decompression context
*
* @var resource|object
- * @access private
*/
- var $decompress_context;
+ private $decompress_context;
/**
* Regenerate Compression Context
*
* @var bool
- * @access private
*/
- var $regenerate_compression_context = false;
+ private $regenerate_compression_context = false;
/**
* Regenerate Decompression Context
*
* @var bool
- * @access private
*/
- var $regenerate_decompression_context = false;
+ private $regenerate_decompression_context = false;
/**
* Smart multi-factor authentication flag
*
* @var bool
- * @access private
*/
- var $smartMFA = true;
+ private $smartMFA = true;
+
+ /**
+ * How many channels are currently opened
+ *
+ * @var int
+ */
+ private $channelCount = 0;
+
+ /**
+ * Does the server support multiple channels? If not then error out
+ * when multiple channels are attempted to be opened
+ *
+ * @var bool
+ */
+ private $errorOnMultipleChannels;
/**
* Bytes Transferred Since Last Key Exchange
- *
+ *
* Includes outbound and inbound totals
*
* @var int
- * @access private
*/
- var $bytesTransferredSinceLastKEX = 0;
+ private $bytesTransferredSinceLastKEX = 0;
/**
* After how many transferred byte should phpseclib initiate a key re-exchange?
- *
+ *
* @var int
- * @access private
*/
- var $doKeyReexchangeAfterXBytes = 1073741824;
+ private $doKeyReexchangeAfterXBytes = 1024 * 1024 * 1024;
/**
* Has a key re-exchange been initialized?
- *
+ *
* @var bool
* @access private
*/
- var $keyExchangeInProgress = false;
+ private $keyExchangeInProgress = false;
/**
* KEX Buffer
@@ -1138,7 +1155,7 @@ class SSH2
* @var array
* @access private
*/
- var $kex_buffer = array();
+ private $kex_buffer = [];
/**
* Strict KEX Flag
@@ -1151,7 +1168,7 @@ class SSH2
* @var array
* @access private
*/
- var $strict_kex_flag = false;
+ private $strict_kex_flag = false;
/**
* Default Constructor.
@@ -1164,90 +1181,98 @@ class SSH2
* @param int $port
* @param int $timeout
* @see self::login()
- * @return \phpseclib\Net\SSH2
- * @access public
*/
- function __construct($host, $port = 22, $timeout = 10)
+ public function __construct($host, $port = 22, $timeout = 10)
{
- $this->message_numbers = array(
- 1 => 'NET_SSH2_MSG_DISCONNECT',
- 2 => 'NET_SSH2_MSG_IGNORE',
- 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
- 4 => 'NET_SSH2_MSG_DEBUG',
- 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
- 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
- 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308
- 20 => 'NET_SSH2_MSG_KEXINIT',
- 21 => 'NET_SSH2_MSG_NEWKEYS',
- 30 => 'NET_SSH2_MSG_KEXDH_INIT',
- 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
- 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
- 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
- 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
- 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
-
- 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
- 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
- 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
- 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
- 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
- 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
- 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
- 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
- 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
- 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
- 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
- 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
- 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
- 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
- );
- $this->disconnect_reasons = array(
- 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
- 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
- 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
- 4 => 'NET_SSH2_DISCONNECT_RESERVED',
- 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
- 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
- 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
- 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
- 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
- 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
- 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
- 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
- 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
- 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
- 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
- );
- $this->channel_open_failure_reasons = array(
- 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
- );
- $this->terminal_modes = array(
- 0 => 'NET_SSH2_TTY_OP_END'
- );
- $this->channel_extended_data_type_codes = array(
- 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
- );
+ if (empty(self::$message_numbers)) {
+ self::$message_numbers = [
+ 1 => 'NET_SSH2_MSG_DISCONNECT',
+ 2 => 'NET_SSH2_MSG_IGNORE',
+ 3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
+ 4 => 'NET_SSH2_MSG_DEBUG',
+ 5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
+ 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
+ 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308
+ 20 => 'NET_SSH2_MSG_KEXINIT',
+ 21 => 'NET_SSH2_MSG_NEWKEYS',
+ 30 => 'NET_SSH2_MSG_KEXDH_INIT',
+ 31 => 'NET_SSH2_MSG_KEXDH_REPLY',
+ 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
+ 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
+ 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
+ 53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
+
+ 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
+ 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
+ 82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
+ 90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
+ 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
+ 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
+ 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
+ 94 => 'NET_SSH2_MSG_CHANNEL_DATA',
+ 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
+ 96 => 'NET_SSH2_MSG_CHANNEL_EOF',
+ 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
+ 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
+ 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
+ 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
+ ];
+ self::$disconnect_reasons = [
+ 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
+ 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
+ 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
+ 4 => 'NET_SSH2_DISCONNECT_RESERVED',
+ 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
+ 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
+ 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
+ 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
+ 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
+ 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
+ 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
+ 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
+ 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
+ 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
+ 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
+ ];
+ self::$channel_open_failure_reasons = [
+ 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
+ ];
+ self::$terminal_modes = [
+ 0 => 'NET_SSH2_TTY_OP_END'
+ ];
+ self::$channel_extended_data_type_codes = [
+ 1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
+ ];
+
+ self::define_array(
+ self::$message_numbers,
+ self::$disconnect_reasons,
+ self::$channel_open_failure_reasons,
+ self::$terminal_modes,
+ self::$channel_extended_data_type_codes,
+ [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'],
+ [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'],
+ [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
+ 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'],
+ // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
+ [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
+ 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
+ 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
+ 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
+ 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'],
+ // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
+ [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
+ 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY']
+ );
+ }
- $this->_define_array(
- $this->message_numbers,
- $this->disconnect_reasons,
- $this->channel_open_failure_reasons,
- $this->terminal_modes,
- $this->channel_extended_data_type_codes,
- array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
- array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
- array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
- 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
- // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
- array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
- 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
- 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
- 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
- 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
- // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
- array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
- 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
- );
+ /**
+ * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508
+ * @var \WeakReference<SSH2>|SSH2
+ */
+ self::$connections[$this->getResourceId()] = class_exists('WeakReference')
+ ? \WeakReference::create($this)
+ : $this;
$this->timeout = $timeout;
@@ -1256,7 +1281,7 @@ class SSH2
return;
}
- if (is_string($host)) {
+ if (Strings::is_stringable($host)) {
$this->host = $host;
$this->port = $port;
}
@@ -1266,14 +1291,13 @@ class SSH2
* Set Crypto Engine Mode
*
* Possible $engine values:
- * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT
+ * OpenSSL, mcrypt, Eval, PHP
*
* @param int $engine
- * @access public
*/
- function setCryptoEngine($engine)
+ public static function setCryptoEngine($engine)
{
- $this->crypto_engine = $engine;
+ self::$crypto_engine = $engine;
}
/**
@@ -1283,9 +1307,8 @@ class SSH2
* both sides MUST send an identification string". It does not say which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
- * @access public
*/
- function sendIdentificationStringFirst()
+ public function sendIdentificationStringFirst()
{
$this->send_id_string_first = true;
}
@@ -1297,9 +1320,8 @@ class SSH2
* both sides MUST send an identification string". It does not say which side sends it first. In
* theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
- * @access public
*/
- function sendIdentificationStringLast()
+ public function sendIdentificationStringLast()
{
$this->send_id_string_first = false;
}
@@ -1311,9 +1333,8 @@ class SSH2
* sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
- * @access public
*/
- function sendKEXINITFirst()
+ public function sendKEXINITFirst()
{
$this->send_kex_first = true;
}
@@ -1325,31 +1346,54 @@ class SSH2
* sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory
* it shouldn't matter but it is a fact of life that some SSH servers are simply buggy
*
- * @access public
*/
- function sendKEXINITLast()
+ public function sendKEXINITLast()
{
$this->send_kex_first = false;
}
/**
+ * stream_select wrapper
+ *
+ * Quoting https://stackoverflow.com/a/14262151/569976,
+ * "The general approach to `EINTR` is to simply handle the error and retry the operation again"
+ *
+ * This wrapper does that loop
+ */
+ private static function stream_select(&$read, &$write, &$except, $seconds, $microseconds = null)
+ {
+ $remaining = $seconds + $microseconds / 1000000;
+ $start = microtime(true);
+ while (true) {
+ $result = @stream_select($read, $write, $except, $seconds, $microseconds);
+ if ($result !== false) {
+ return $result;
+ }
+ $elapsed = microtime(true) - $start;
+ $seconds = (int) ($remaining - floor($elapsed));
+ $microseconds = (int) (1000000 * ($remaining - $seconds));
+ if ($elapsed >= $remaining) {
+ return false;
+ }
+ }
+ }
+
+ /**
* Connect to an SSHv2 server
*
- * @return bool
- * @access private
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @throws \RuntimeException on other errors
*/
- function _connect()
+ private function connect()
{
if ($this->bitmap & self::MASK_CONSTRUCTOR) {
- return false;
+ return;
}
$this->bitmap |= self::MASK_CONSTRUCTOR;
$this->curTimeout = $this->timeout;
- $this->last_packet = microtime(true);
-
if (!is_resource($this->fsock)) {
$start = microtime(true);
// with stream_select a timeout of 0 means that no timeout takes place;
@@ -1358,24 +1402,31 @@ class SSH2
$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout);
if (!$this->fsock) {
$host = $this->host . ':' . $this->port;
- user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
- return false;
+ throw new UnableToConnectException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
}
$elapsed = microtime(true) - $start;
if ($this->curTimeout) {
- $this->curTimeout-= $elapsed;
+ $this->curTimeout -= $elapsed;
if ($this->curTimeout < 0) {
- $this->is_timeout = true;
- return false;
+ throw new \RuntimeException('Connection timed out whilst attempting to open socket connection');
}
}
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->append_log('(fsockopen took ' . round($elapsed, 4) . 's)', '');
+ }
}
- $this->identifier = $this->_generate_identifier();
+ $this->identifier = $this->generate_identifier();
if ($this->send_id_string_first) {
+ $start = microtime(true);
fputs($this->fsock, $this->identifier . "\r\n");
+ $elapsed = round(microtime(true) - $start, 4);
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->append_log("-> (network: $elapsed)", $this->identifier . "\r\n");
+ }
}
/* According to the SSH2 specs,
@@ -1386,33 +1437,30 @@ class SSH2
in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
MUST be able to process such lines." */
$data = '';
+ $totalElapsed = 0;
while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) {
$line = '';
while (true) {
if ($this->curTimeout) {
if ($this->curTimeout < 0) {
- $this->is_timeout = true;
- return false;
+ throw new \RuntimeException('Connection timed out whilst receiving server identification string');
}
- $read = array($this->fsock);
+ $read = [$this->fsock];
$write = $except = null;
$start = microtime(true);
$sec = (int) floor($this->curTimeout);
$usec = (int) (1000000 * ($this->curTimeout - $sec));
- // on windows this returns a "Warning: Invalid CRT parameters detected" error
- // the !count() is done as a workaround for <https://bugs.php.net/42682>
- if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
- $this->is_timeout = true;
- return false;
+ if (static::stream_select($read, $write, $except, $sec, $usec) === false) {
+ throw new \RuntimeException('Connection timed out whilst receiving server identification string');
}
$elapsed = microtime(true) - $start;
- $this->curTimeout-= $elapsed;
+ $totalElapsed += $elapsed;
+ $this->curTimeout -= $elapsed;
}
$temp = stream_get_line($this->fsock, 255, "\n");
-
if ($temp === false) {
- return false;
+ throw new \RuntimeException('Error reading SSH identification string; are you sure you\'re connecting to an SSH server?');
}
$line .= $temp;
@@ -1425,60 +1473,69 @@ class SSH2
break;
}
- $data.= $line;
+ $data .= $line;
+ }
+
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->append_log('<- (network: ' . round($totalElapsed, 4) . ')', $data);
}
if (feof($this->fsock)) {
$this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
+ throw new ConnectionClosedException('Connection closed by server; are you sure you\'re connected to an SSH server?');
}
$extra = $matches[1];
- if (defined('NET_SSH2_LOGGING')) {
- $this->_append_log('<-', $matches[0]);
- $this->_append_log('->', $this->identifier . "\r\n");
- }
-
- $this->server_identifier = trim($data, "\r\n");
-
+ // earlier the SSH specs were quoted.
+ // "The server MAY send other lines of data before sending the version string." they said.
+ // the implication of this is that the lines of data before the server string are *not* a part of it
+ // getting this right is important because the correct server identifier needs to be fed into the
+ // exchange hash for the shared keys to be calculated correctly
+ $data = explode("\r\n", trim($data, "\r\n"));
+ $this->server_identifier = $data[count($data) - 1];
if (strlen($extra)) {
$this->errors[] = $data;
}
if (version_compare($matches[3], '1.99', '<')) {
- user_error("Cannot connect to SSH $matches[3] servers");
- return false;
+ $this->bitmap = 0;
+ throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
}
+ // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see
+ // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info.
+ // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses
+ // when consolekit was incorporated.
+ // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the
+ // issues with how Ubuntu incorporated consolekit
+ $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#';
+ $match = preg_match($pattern, $this->server_identifier, $matches);
+ $match = $match && version_compare('5.8', $matches[1], '<=');
+ $match = $match && version_compare('6.9', $matches[1], '>=');
+ $this->errorOnMultipleChannels = $match;
+
if (!$this->send_id_string_first) {
+ $start = microtime(true);
fputs($this->fsock, $this->identifier . "\r\n");
- }
-
- if (!$this->send_kex_first) {
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
+ $elapsed = round(microtime(true) - $start, 4);
+ if (defined('NET_SSH2_LOGGING')) {
+ $this->append_log("-> (network: $elapsed)", $this->identifier . "\r\n");
}
+ }
- if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
- user_error('Expected SSH_MSG_KEXINIT');
- return false;
- }
+ $this->last_packet = microtime(true);
- if (!$this->_key_exchange($response)) {
- return false;
- }
+ if (!$this->send_kex_first) {
+ $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_KEXINIT);
+ $this->key_exchange($response);
}
- if ($this->send_kex_first && !$this->_key_exchange()) {
- return false;
+ if ($this->send_kex_first) {
+ $this->key_exchange();
}
- $this->bitmap|= self::MASK_CONNECTED;
+ $this->bitmap |= self::MASK_CONNECTED;
return true;
}
@@ -1488,15 +1545,14 @@ class SSH2
*
* You should overwrite this method in your own class if you want to use another identifier
*
- * @access protected
* @return string
*/
- function _generate_identifier()
+ private function generate_identifier()
{
- $identifier = 'SSH-2.0-phpseclib_2.0';
+ $identifier = 'SSH-2.0-phpseclib_3.0';
- $ext = array();
- if (function_exists('sodium_crypto_box_publickey_from_secretkey')) {
+ $ext = [];
+ if (extension_loaded('sodium')) {
$ext[] = 'libsodium';
}
@@ -1522,10 +1578,13 @@ class SSH2
/**
* Key Exchange
*
- * @param string $kexinit_payload_server optional
- * @access private
+ * @return bool
+ * @param string|bool $kexinit_payload_server optional
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @throws \RuntimeException on other errors
+ * @throws NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
*/
- function _key_exchange($kexinit_payload_server = false)
+ private function key_exchange($kexinit_payload_server = false)
{
$this->bytesTransferredSinceLastKEX = 0;
@@ -1537,30 +1596,30 @@ class SSH2
$kex_algorithms = isset($preferred['kex']) ?
$preferred['kex'] :
- $this->getSupportedKEXAlgorithms();
+ SSH2::getSupportedKEXAlgorithms();
$server_host_key_algorithms = isset($preferred['hostkey']) ?
$preferred['hostkey'] :
- $this->getSupportedHostKeyAlgorithms();
+ SSH2::getSupportedHostKeyAlgorithms();
$s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
$preferred['server_to_client']['crypt'] :
- $this->getSupportedEncryptionAlgorithms();
+ SSH2::getSupportedEncryptionAlgorithms();
$c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
$preferred['client_to_server']['crypt'] :
- $this->getSupportedEncryptionAlgorithms();
+ SSH2::getSupportedEncryptionAlgorithms();
$s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
$preferred['server_to_client']['mac'] :
- $this->getSupportedMACAlgorithms();
+ SSH2::getSupportedMACAlgorithms();
$c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
$preferred['client_to_server']['mac'] :
- $this->getSupportedMACAlgorithms();
+ SSH2::getSupportedMACAlgorithms();
$s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
$preferred['server_to_client']['comp'] :
- $this->getSupportedCompressionAlgorithms();
+ SSH2::getSupportedCompressionAlgorithms();
$c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
$preferred['client_to_server']['comp'] :
- $this->getSupportedCompressionAlgorithms();
+ SSH2::getSupportedCompressionAlgorithms();
- $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c', 'kex-strict-c-v00@openssh.com'));
+ $kex_algorithms = array_merge($kex_algorithms, ['ext-info-c', 'kex-strict-c-v00@openssh.com']);
// some SSH servers have buggy implementations of some of the above algorithms
switch (true) {
@@ -1569,78 +1628,61 @@ class SSH2
if (!isset($preferred['server_to_client']['mac'])) {
$s2c_mac_algorithms = array_values(array_diff(
$s2c_mac_algorithms,
- array('hmac-sha1-96', 'hmac-md5-96')
+ ['hmac-sha1-96', 'hmac-md5-96']
));
}
if (!isset($preferred['client_to_server']['mac'])) {
$c2s_mac_algorithms = array_values(array_diff(
$c2s_mac_algorithms,
- array('hmac-sha1-96', 'hmac-md5-96')
+ ['hmac-sha1-96', 'hmac-md5-96']
+ ));
+ }
+ break;
+ case substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_':
+ if (!isset($preferred['server_to_client']['crypt'])) {
+ $s2c_encryption_algorithms = array_values(array_diff(
+ $s2c_encryption_algorithms,
+ ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com']
+ ));
+ }
+ if (!isset($preferred['client_to_server']['crypt'])) {
+ $c2s_encryption_algorithms = array_values(array_diff(
+ $c2s_encryption_algorithms,
+ ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com']
));
}
}
- $str_kex_algorithms = implode(',', $kex_algorithms);
- $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
- $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
- $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
- $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
- $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
- $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
- $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
-
$client_cookie = Random::string(16);
- $kexinit_payload_client = pack(
- 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
- NET_SSH2_MSG_KEXINIT,
- $client_cookie,
- strlen($str_kex_algorithms),
- $str_kex_algorithms,
- strlen($str_server_host_key_algorithms),
- $str_server_host_key_algorithms,
- strlen($encryption_algorithms_client_to_server),
- $encryption_algorithms_client_to_server,
- strlen($encryption_algorithms_server_to_client),
- $encryption_algorithms_server_to_client,
- strlen($mac_algorithms_client_to_server),
- $mac_algorithms_client_to_server,
- strlen($mac_algorithms_server_to_client),
- $mac_algorithms_server_to_client,
- strlen($compression_algorithms_client_to_server),
- $compression_algorithms_client_to_server,
- strlen($compression_algorithms_server_to_client),
- $compression_algorithms_server_to_client,
- 0,
- '',
- 0,
- '',
- 0,
- 0
+ $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie);
+ $kexinit_payload_client .= Strings::packSSH2(
+ 'L10bN',
+ $kex_algorithms,
+ $server_host_key_algorithms,
+ $c2s_encryption_algorithms,
+ $s2c_encryption_algorithms,
+ $c2s_mac_algorithms,
+ $s2c_mac_algorithms,
+ $c2s_compression_algorithms,
+ $s2c_compression_algorithms,
+ [], // language, client to server
+ [], // language, server to client
+ false, // first_kex_packet_follows
+ 0 // reserved for future extension
);
if ($kexinit_payload_server === false && $send_kex) {
- if (!$this->_send_binary_packet($kexinit_payload_client)) {
- return false;
- }
+ $this->send_binary_packet($kexinit_payload_client);
while (true) {
- $kexinit_payload_server = $this->_get_binary_packet();
- if ($kexinit_payload_server === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
-
- if (strlen($kexinit_payload_server)) {
- switch (ord($kexinit_payload_server[0])) {
- case NET_SSH2_MSG_KEXINIT:
- break 2;
- case NET_SSH2_MSG_DISCONNECT:
- return $this->_handleDisconnect($kexinit_payload_server);
- }
+ $kexinit_payload_server = $this->get_binary_packet();
+ switch (ord($kexinit_payload_server[0])) {
+ case NET_SSH2_MSG_KEXINIT:
+ break 2;
+ case NET_SSH2_MSG_DISCONNECT:
+ return $this->handleDisconnect($kexinit_payload_server);
}
-
$this->kex_buffer[] = $kexinit_payload_server;
}
@@ -1648,159 +1690,136 @@ class SSH2
}
$response = $kexinit_payload_server;
- $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
- $server_cookie = $this->_string_shift($response, 16);
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
+ Strings::shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
+ $server_cookie = Strings::shift($response, 16);
+
+ list(
+ $this->kex_algorithms,
+ $this->server_host_key_algorithms,
+ $this->encryption_algorithms_client_to_server,
+ $this->encryption_algorithms_server_to_client,
+ $this->mac_algorithms_client_to_server,
+ $this->mac_algorithms_server_to_client,
+ $this->compression_algorithms_client_to_server,
+ $this->compression_algorithms_server_to_client,
+ $this->languages_client_to_server,
+ $this->languages_server_to_client,
+ $first_kex_packet_follows
+ ) = Strings::unpackSSH2('L10C', $response);
if (in_array('kex-strict-s-v00@openssh.com', $this->kex_algorithms)) {
if ($this->session_id === false) {
// [kex-strict-s-v00@openssh.com is] only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored
// if [it is] present in subsequent SSH2_MSG_KEXINIT packets
$this->strict_kex_flag = true;
if (count($this->kex_buffer)) {
- user_error('Possible Terrapin Attack detected');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new \UnexpectedValueException('Possible Terrapin Attack detected');
}
}
}
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
-
$this->supported_private_key_algorithms = $this->server_host_key_algorithms;
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
-
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
- $first_kex_packet_follows = $first_kex_packet_follows != 0;
-
- if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) {
- return false;
+ if ($send_kex) {
+ $this->send_binary_packet($kexinit_payload_client);
}
// we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
+
// we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
// diffie-hellman key exchange as fast as possible
- $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
- $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
- if ($decryptKeyLength === null) {
- user_error('No compatible server to client encryption algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
+ if (!$decrypt || ($decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt)) === null) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
}
- $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
- $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
- if ($encryptKeyLength === null) {
- user_error('No compatible client to server encryption algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
+ if (!$encrypt || ($encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt)) === null) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
}
// through diffie-hellman key exchange a symmetric key is obtained
- $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
- if ($kex_algorithm === false) {
- user_error('No compatible key exchange algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms);
+ if ($this->kex_algorithm === false) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
}
- $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
+ $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
if ($server_host_key_algorithm === false) {
- user_error('No compatible server host key algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
}
- $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
+ $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
+ if ($mac_algorithm_out === false) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
+ }
+
+ $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
if ($mac_algorithm_in === false) {
- user_error('No compatible server to client message authentication algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
}
- $compression_map = array(
+ $compression_map = [
'none' => self::NET_SSH2_COMPRESSION_NONE,
'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
- );
+ ];
+
+ $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
+ if ($compression_algorithm_in === false) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
+ }
+ $this->decompress = $compression_map[$compression_algorithm_in];
- $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
+ $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm_out === false) {
- user_error('No compatible client to server compression algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
}
$this->compress = $compression_map[$compression_algorithm_out];
- $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
- if ($compression_algorithm_in === false) {
- user_error('No compatible server to client compression algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ switch ($this->kex_algorithm) {
+ case 'diffie-hellman-group15-sha512':
+ case 'diffie-hellman-group16-sha512':
+ case 'diffie-hellman-group17-sha512':
+ case 'diffie-hellman-group18-sha512':
+ case 'ecdh-sha2-nistp521':
+ $kexHash = new Hash('sha512');
+ break;
+ case 'ecdh-sha2-nistp384':
+ $kexHash = new Hash('sha384');
+ break;
+ case 'diffie-hellman-group-exchange-sha256':
+ case 'diffie-hellman-group14-sha256':
+ case 'ecdh-sha2-nistp256':
+ case 'curve25519-sha256@libssh.org':
+ case 'curve25519-sha256':
+ $kexHash = new Hash('sha256');
+ break;
+ default:
+ $kexHash = new Hash('sha1');
}
- $this->decompress = $compression_map[$compression_algorithm_in];
// Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
+
$exchange_hash_rfc4419 = '';
- if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
- $x = Random::string(32);
- $eBytes = sodium_crypto_box_publickey_from_secretkey($x);
+ if (strpos($this->kex_algorithm, 'curve25519-sha256') === 0 || strpos($this->kex_algorithm, 'ecdh-sha2-nistp') === 0) {
+ $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ?
+ 'Curve25519' :
+ substr($this->kex_algorithm, 10);
+ $ourPrivate = EC::createKey($curve);
+ $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates();
$clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY';
- $kexHash = new Hash('sha256');
} else {
- if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
+ if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
$dh_group_sizes_packed = pack(
'NNN',
$this->kex_dh_group_size_min,
@@ -1812,210 +1831,94 @@ class SSH2
NET_SSH2_MSG_KEXDH_GEX_REQUEST,
$dh_group_sizes_packed
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
- $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
+ $this->send_binary_packet($packet);
+ $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST');
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
- if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
- user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
- return false;
- }
- $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
-
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
- $primeBytes = $this->_string_shift($response, $primeLength);
+ $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_KEXDH_GEX_GROUP);
+ list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response);
+ $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP');
$prime = new BigInteger($primeBytes, -256);
-
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('NgLength', $this->_string_shift($response, 4)));
- $gBytes = $this->_string_shift($response, $gLength);
$g = new BigInteger($gBytes, -256);
- $exchange_hash_rfc4419 = pack(
- 'a*Na*Na*',
- $dh_group_sizes_packed,
- $primeLength,
+ $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2(
+ 'ss',
$primeBytes,
- $gLength,
$gBytes
);
+ $params = DH::createParameters($prime, $g);
$clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY';
} else {
- switch ($kex_algorithm) {
- // see http://tools.ietf.org/html/rfc2409#section-6.2 and
- // http://tools.ietf.org/html/rfc2412, appendex E
- case 'diffie-hellman-group1-sha1':
- $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
- '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
- '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
- 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
- break;
- // see http://tools.ietf.org/html/rfc3526#section-3
- case 'diffie-hellman-group14-sha1':
- $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
- '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
- '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
- 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
- '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
- '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
- 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
- '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
- break;
- }
- // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
- // the generator field element is 2 (decimal) and the hash function is sha1.
- $g = new BigInteger(2);
- $prime = new BigInteger($prime, 16);
+ $params = DH::createParameters($this->kex_algorithm);
$clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT';
$serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY';
}
- switch ($kex_algorithm) {
- case 'diffie-hellman-group-exchange-sha256':
- $kexHash = new Hash('sha256');
- break;
- default:
- $kexHash = new Hash('sha1');
- }
-
- /* To increase the speed of the key exchange, both client and server may
- reduce the size of their private exponents. It should be at least
- twice as long as the key material that is generated from the shared
- secret. For more details, see the paper by van Oorschot and Wiener
- [VAN-OORSCHOT].
+ $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength));
- -- http://tools.ietf.org/html/rfc4419#section-6.2 */
- $one = new BigInteger(1);
- $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
- $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
- $max = $max->subtract($one);
+ $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength
+ $ourPublic = $ourPrivate->getPublicKey()->toBigInteger();
+ $ourPublicBytes = $ourPublic->toBytes(true);
+ }
- $x = $one->random($one, $max);
- $e = $g->modPow($x, $prime);
+ $data = pack('CNa*', constant($clientKexInitMessage), strlen($ourPublicBytes), $ourPublicBytes);
- $eBytes = $e->toBytes(true);
- }
- $data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes);
+ $this->send_binary_packet($data);
- if (!$this->_send_binary_packet($data)) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
switch ($clientKexInitMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_INIT':
- $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
+ $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_INIT':
- $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
+ $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT');
}
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ $response = $this->get_binary_packet_or_close(constant($serverKexReplyMessage));
+
+ list(
+ $type,
+ $server_public_host_key,
+ $theirPublicBytes,
+ $this->signature
+ ) = Strings::unpackSSH2('Csss', $response);
- if ($type != constant($serverKexReplyMessage)) {
- user_error("Expected $serverKexReplyMessage");
- return false;
- }
switch ($serverKexReplyMessage) {
case 'NET_SSH2_MSG_KEX_ECDH_REPLY':
- $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
+ $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY');
break;
case 'NET_SSH2_MSG_KEXDH_GEX_REPLY':
- $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
- }
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
-
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $fBytes = $this->_string_shift($response, $temp['length']);
-
- if (strlen($response) < 4) {
- return false;
+ $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY');
}
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->signature = $this->_string_shift($response, $temp['length']);
+ $this->server_public_host_key = $server_public_host_key;
+ list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key);
if (strlen($this->signature) < 4) {
- return false;
+ throw new \LengthException('The signature needs at least four bytes');
}
- $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
- $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
+ $temp = unpack('Nlength', substr($this->signature, 0, 4));
+ $this->signature_format = substr($this->signature, 4, $temp['length']);
- if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
- if (strlen($fBytes) !== 32) {
- user_error('Received curve25519 public key of invalid length.');
- return false;
- }
- $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256);
- // sodium_compat doesn't emulate sodium_memzero
- // also, with v1 of libsodium API the extension identifies itself as
- // libsodium whereas v2 of the libsodium API (what PHP 7.2+ includes)
- // identifies itself as sodium. sodium_compat uses the v1 API to
- // emulate the v2 API if it's the v1 API that's available
- if (extension_loaded('sodium') || extension_loaded('libsodium')) {
- sodium_memzero($x);
- }
- } else {
- $f = new BigInteger($fBytes, -256);
- $key = $f->modPow($x, $prime);
+ $keyBytes = DH::computeSecret($ourPrivate, $theirPublicBytes);
+ if (($keyBytes & "\xFF\x80") === "\x00\x00") {
+ $keyBytes = substr($keyBytes, 1);
+ } elseif (($keyBytes[0] & "\x80") === "\x80") {
+ $keyBytes = "\0$keyBytes";
}
- $keyBytes = $key->toBytes(true);
- $this->exchange_hash = pack(
- 'Na*Na*Na*Na*Na*a*Na*Na*Na*',
- strlen($this->identifier),
+ $this->exchange_hash = Strings::packSSH2(
+ 's5',
$this->identifier,
- strlen($this->server_identifier),
$this->server_identifier,
- strlen($kexinit_payload_client),
$kexinit_payload_client,
- strlen($kexinit_payload_server),
$kexinit_payload_server,
- strlen($this->server_public_host_key),
- $this->server_public_host_key,
- $exchange_hash_rfc4419,
- strlen($eBytes),
- $eBytes,
- strlen($fBytes),
- $fBytes,
- strlen($keyBytes),
+ $this->server_public_host_key
+ );
+ $this->exchange_hash .= $exchange_hash_rfc4419;
+ $this->exchange_hash .= Strings::packSSH2(
+ 's3',
+ $ourPublicBytes,
+ $theirPublicBytes,
$keyBytes
);
@@ -2026,52 +1929,27 @@ class SSH2
}
switch ($server_host_key_algorithm) {
- case 'ssh-dss':
- $expected_key_format = 'ssh-dss';
- break;
- //case 'rsa-sha2-256':
- //case 'rsa-sha2-512':
+ case 'rsa-sha2-256':
+ case 'rsa-sha2-512':
//case 'ssh-rsa':
- default:
$expected_key_format = 'ssh-rsa';
+ break;
+ default:
+ $expected_key_format = $server_host_key_algorithm;
}
-
if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) {
switch (true) {
case $this->signature_format == $server_host_key_algorithm:
case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512':
case $this->signature_format != 'ssh-rsa':
- user_error('Server Host Key Algorithm Mismatch');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')');
}
}
- $packet = pack(
- 'C',
- NET_SSH2_MSG_NEWKEYS
- );
-
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $response = $this->_get_binary_packet();
-
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
-
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
-
- if ($type != NET_SSH2_MSG_NEWKEYS) {
- user_error('Expected SSH_MSG_NEWKEYS');
- return false;
- }
+ $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
+ $this->send_binary_packet($packet);
+ $this->get_binary_packet_or_close(NET_SSH2_MSG_NEWKEYS);
$this->keyExchangeInProgress = false;
@@ -2081,63 +1959,94 @@ class SSH2
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
- $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
+ $this->encrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
if ($this->encrypt) {
- if ($this->crypto_engine) {
- $this->encrypt->setPreferredEngine($this->crypto_engine);
+ if (self::$crypto_engine) {
+ $this->encrypt->setPreferredEngine(self::$crypto_engine);
}
- if ($this->encrypt->block_size) {
- $this->encrypt_block_size = $this->encrypt->block_size;
+ if ($this->encrypt->getBlockLengthInBytes()) {
+ $this->encrypt_block_size = $this->encrypt->getBlockLengthInBytes();
}
- $this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding();
- if ($this->encrypt->getBlockLength()) {
- $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
+ if ($this->encrypt->usesIV()) {
+ $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
+ while ($this->encrypt_block_size > strlen($iv)) {
+ $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
+ }
+ $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
}
- $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
- while ($this->encrypt_block_size > strlen($iv)) {
- $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
+ switch ($encrypt) {
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
+ $this->encryptFixedPart = substr($nonce, 0, 4);
+ $this->encryptInvocationCounter = substr($nonce, 4, 8);
+ // fall-through
+ case 'chacha20-poly1305@openssh.com':
+ break;
+ default:
+ $this->encrypt->enableContinuousBuffer();
}
- $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
while ($encryptKeyLength > strlen($key)) {
- $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ }
+ switch ($encrypt) {
+ case 'chacha20-poly1305@openssh.com':
+ $encryptKeyLength = 32;
+ $this->lengthEncrypt = self::encryption_algorithm_to_crypt_instance($encrypt);
+ $this->lengthEncrypt->setKey(substr($key, 32, 32));
}
$this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
-
$this->encryptName = $encrypt;
}
- $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
+ $this->decrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
if ($this->decrypt) {
- if ($this->crypto_engine) {
- $this->decrypt->setPreferredEngine($this->crypto_engine);
+ if (self::$crypto_engine) {
+ $this->decrypt->setPreferredEngine(self::$crypto_engine);
}
- if ($this->decrypt->block_size) {
- $this->decrypt_block_size = $this->decrypt->block_size;
+ if ($this->decrypt->getBlockLengthInBytes()) {
+ $this->decrypt_block_size = $this->decrypt->getBlockLengthInBytes();
}
- $this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding();
- if ($this->decrypt->getBlockLength()) {
- $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
+ if ($this->decrypt->usesIV()) {
+ $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
+ while ($this->decrypt_block_size > strlen($iv)) {
+ $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
+ }
+ $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
}
- $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
- while ($this->decrypt_block_size > strlen($iv)) {
- $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
+ switch ($decrypt) {
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ // see https://tools.ietf.org/html/rfc5647#section-7.1
+ $nonce = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
+ $this->decryptFixedPart = substr($nonce, 0, 4);
+ $this->decryptInvocationCounter = substr($nonce, 4, 8);
+ // fall-through
+ case 'chacha20-poly1305@openssh.com':
+ break;
+ default:
+ $this->decrypt->enableContinuousBuffer();
}
- $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
while ($decryptKeyLength > strlen($key)) {
- $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ }
+ switch ($decrypt) {
+ case 'chacha20-poly1305@openssh.com':
+ $decryptKeyLength = 32;
+ $this->lengthDecrypt = self::encryption_algorithm_to_crypt_instance($decrypt);
+ $this->lengthDecrypt->setKey(substr($key, 32, 32));
}
$this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
-
$this->decryptName = $decrypt;
}
@@ -2155,77 +2064,45 @@ class SSH2
$this->decrypt->decrypt(str_repeat("\0", 1536));
}
- $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
- if ($mac_algorithm_out === false) {
- user_error('No compatible client to server message authentication algorithms found');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
- }
-
- $createKeyLength = 0; // ie. $mac_algorithm == 'none'
- switch ($mac_algorithm_out) {
- case 'hmac-sha2-256':
- $this->hmac_create = new Hash('sha256');
- $createKeyLength = 32;
- break;
- case 'hmac-sha1':
- $this->hmac_create = new Hash('sha1');
- $createKeyLength = 20;
- break;
- case 'hmac-sha1-96':
- $this->hmac_create = new Hash('sha1-96');
- $createKeyLength = 20;
- break;
- case 'hmac-md5':
- $this->hmac_create = new Hash('md5');
- $createKeyLength = 16;
- break;
- case 'hmac-md5-96':
- $this->hmac_create = new Hash('md5-96');
- $createKeyLength = 16;
+ if (!$this->encrypt->usesNonce()) {
+ list($this->hmac_create, $createKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_out);
+ } else {
+ $this->hmac_create = new \stdClass();
+ $this->hmac_create_name = $mac_algorithm_out;
+ //$mac_algorithm_out = 'none';
+ $createKeyLength = 0;
}
- $this->hmac_create_name = $mac_algorithm_out;
- $checkKeyLength = 0;
- $this->hmac_size = 0;
- switch ($mac_algorithm_in) {
- case 'hmac-sha2-256':
- $this->hmac_check = new Hash('sha256');
- $checkKeyLength = 32;
- $this->hmac_size = 32;
- break;
- case 'hmac-sha1':
- $this->hmac_check = new Hash('sha1');
- $checkKeyLength = 20;
- $this->hmac_size = 20;
- break;
- case 'hmac-sha1-96':
- $this->hmac_check = new Hash('sha1-96');
- $checkKeyLength = 20;
- $this->hmac_size = 12;
- break;
- case 'hmac-md5':
- $this->hmac_check = new Hash('md5');
- $checkKeyLength = 16;
- $this->hmac_size = 16;
- break;
- case 'hmac-md5-96':
- $this->hmac_check = new Hash('md5-96');
- $checkKeyLength = 16;
- $this->hmac_size = 12;
+ if ($this->hmac_create instanceof Hash) {
+ $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
+ while ($createKeyLength > strlen($key)) {
+ $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ }
+ $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
+ $this->hmac_create_name = $mac_algorithm_out;
+ $this->hmac_create_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_out);
}
- $this->hmac_check_name = $mac_algorithm_in;
- $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
- while ($createKeyLength > strlen($key)) {
- $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ if (!$this->decrypt->usesNonce()) {
+ list($this->hmac_check, $checkKeyLength) = self::mac_algorithm_to_hash_instance($mac_algorithm_in);
+ $this->hmac_size = $this->hmac_check->getLengthInBytes();
+ } else {
+ $this->hmac_check = new \stdClass();
+ $this->hmac_check_name = $mac_algorithm_in;
+ //$mac_algorithm_in = 'none';
+ $checkKeyLength = 0;
+ $this->hmac_size = 0;
}
- $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
- $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
- while ($checkKeyLength > strlen($key)) {
- $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ if ($this->hmac_check instanceof Hash) {
+ $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
+ while ($checkKeyLength > strlen($key)) {
+ $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
+ }
+ $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
+ $this->hmac_check_name = $mac_algorithm_in;
+ $this->hmac_check_etm = preg_match('#-etm@openssh\.com$#', $mac_algorithm_in);
}
- $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
$this->regenerate_compression_context = $this->regenerate_decompression_context = true;
@@ -2237,17 +2114,17 @@ class SSH2
*
* @param string $algorithm Name of the encryption algorithm
* @return int|null Number of bytes as an integer or null for unknown
- * @access private
*/
- function _encryption_algorithm_to_key_size($algorithm)
+ private function encryption_algorithm_to_key_size($algorithm)
{
- if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) {
+ if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) {
return 16;
}
switch ($algorithm) {
case 'none':
return 0;
+ case 'aes128-gcm@openssh.com':
case 'aes128-cbc':
case 'aes128-ctr':
case 'arcfour':
@@ -2264,6 +2141,7 @@ class SSH2
case 'twofish192-cbc':
case 'twofish192-ctr':
return 24;
+ case 'aes256-gcm@openssh.com':
case 'aes256-cbc':
case 'aes256-ctr':
case 'arcfour256':
@@ -2271,64 +2149,103 @@ class SSH2
case 'twofish256-cbc':
case 'twofish256-ctr':
return 32;
+ case 'chacha20-poly1305@openssh.com':
+ return 64;
}
return null;
}
/**
* Maps an encryption algorithm name to an instance of a subclass of
- * \phpseclib\Crypt\Base.
+ * \phpseclib3\Crypt\Common\SymmetricKey.
*
* @param string $algorithm Name of the encryption algorithm
- * @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
- * @access private
+ * @return SymmetricKey|null
*/
- function _encryption_algorithm_to_crypt_instance($algorithm)
+ private static function encryption_algorithm_to_crypt_instance($algorithm)
{
switch ($algorithm) {
case '3des-cbc':
- return new TripleDES();
+ return new TripleDES('cbc');
case '3des-ctr':
- return new TripleDES(Base::MODE_CTR);
+ return new TripleDES('ctr');
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
- return new Rijndael();
+ return new Rijndael('cbc');
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
- return new Rijndael(Base::MODE_CTR);
+ return new Rijndael('ctr');
case 'blowfish-cbc':
- return new Blowfish();
+ return new Blowfish('cbc');
case 'blowfish-ctr':
- return new Blowfish(Base::MODE_CTR);
+ return new Blowfish('ctr');
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
- return new Twofish();
+ return new Twofish('cbc');
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
- return new Twofish(Base::MODE_CTR);
+ return new Twofish('ctr');
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
return new RC4();
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ return new Rijndael('gcm');
+ case 'chacha20-poly1305@openssh.com':
+ return new ChaCha20();
}
return null;
}
/**
+ * Maps an encryption algorithm name to an instance of a subclass of
+ * \phpseclib3\Crypt\Hash.
+ *
+ * @param string $algorithm Name of the encryption algorithm
+ * @return array{Hash, int}|null
+ */
+ private static function mac_algorithm_to_hash_instance($algorithm)
+ {
+ switch ($algorithm) {
+ case 'umac-64@openssh.com':
+ case 'umac-64-etm@openssh.com':
+ return [new Hash('umac-64'), 16];
+ case 'umac-128@openssh.com':
+ case 'umac-128-etm@openssh.com':
+ return [new Hash('umac-128'), 16];
+ case 'hmac-sha2-512':
+ case 'hmac-sha2-512-etm@openssh.com':
+ return [new Hash('sha512'), 64];
+ case 'hmac-sha2-256':
+ case 'hmac-sha2-256-etm@openssh.com':
+ return [new Hash('sha256'), 32];
+ case 'hmac-sha1':
+ case 'hmac-sha1-etm@openssh.com':
+ return [new Hash('sha1'), 20];
+ case 'hmac-sha1-96':
+ return [new Hash('sha1-96'), 20];
+ case 'hmac-md5':
+ return [new Hash('md5'), 16];
+ case 'hmac-md5-96':
+ return [new Hash('md5-96'), 16];
+ }
+ }
+
+ /**
* Tests whether or not proposed algorithm has a potential for issues
*
* @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html
* @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291
* @param string $algorithm Name of the encryption algorithm
* @return bool
- * @access private
*/
- function _bad_algorithm_candidate($algorithm)
+ private static function bad_algorithm_candidate($algorithm)
{
switch ($algorithm) {
case 'arcfour256':
@@ -2343,65 +2260,75 @@ class SSH2
/**
* Login
*
- * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array
+ * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA|EC|DSA object, a \phpseclib3\System\SSH\Agent object or an array
*
* @param string $username
+ * @param string|PrivateKey|array[]|Agent|null ...$args
* @return bool
* @see self::_login()
- * @access public
*/
- function login($username)
+ public function login($username, ...$args)
{
- $args = func_get_args();
if (!$this->login_credentials_finalized) {
- $this->auth[] = $args;
+ $this->auth[] = func_get_args();
}
// try logging with 'none' as an authentication method first since that's what
// PuTTY does
if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
- if ($this->_login($username)) {
+ if ($this->sublogin($username)) {
return true;
}
- if (count($args) == 1) {
+ if (!count($args)) {
return false;
}
}
- return call_user_func_array(array(&$this, '_login'), $args);
+ return $this->sublogin($username, ...$args);
}
/**
* Login Helper
*
* @param string $username
+ * @param string|PrivateKey|array[]|Agent|null ...$args
* @return bool
* @see self::_login_helper()
- * @access private
*/
- function _login($username)
+ protected function sublogin($username, ...$args)
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
- if (!$this->_connect()) {
- return false;
- }
+ $this->connect();
}
- $args = array_slice(func_get_args(), 1);
if (empty($args)) {
- return $this->_login_helper($username);
+ return $this->login_helper($username);
+ }
+
+ foreach ($args as $arg) {
+ switch (true) {
+ case $arg instanceof PublicKey:
+ throw new \UnexpectedValueException('A PublicKey object was passed to the login method instead of a PrivateKey object');
+ case $arg instanceof PrivateKey:
+ case $arg instanceof Agent:
+ case is_array($arg):
+ case Strings::is_stringable($arg):
+ break;
+ default:
+ throw new \UnexpectedValueException('$password needs to either be an instance of \phpseclib3\Crypt\Common\PrivateKey, \System\SSH\Agent, an array or a string');
+ }
}
while (count($args)) {
if (!$this->auth_methods_to_continue || !$this->smartMFA) {
$newargs = $args;
- $args = array();
+ $args = [];
} else {
- $newargs = array();
+ $newargs = [];
foreach ($this->auth_methods_to_continue as $method) {
switch ($method) {
case 'publickey':
foreach ($args as $key => $arg) {
- if (is_object($arg)) {
+ if ($arg instanceof PrivateKey || $arg instanceof Agent) {
$newargs[] = $arg;
unset($args[$key]);
break;
@@ -2415,7 +2342,7 @@ class SSH2
$hasArray = true;
break;
}
- if ($hasString || is_string($arg)) {
+ if ($hasString || Strings::is_stringable($arg)) {
$hasString = true;
break;
}
@@ -2428,6 +2355,7 @@ class SSH2
}
}
}
+ // fall-through
case 'password':
foreach ($args as $key => $arg) {
$newargs[] = $arg;
@@ -2443,7 +2371,7 @@ class SSH2
}
foreach ($newargs as $arg) {
- if ($this->_login_helper($username, $arg)) {
+ if ($this->login_helper($username, $arg)) {
$this->login_credentials_finalized = true;
return true;
}
@@ -2455,102 +2383,63 @@ class SSH2
/**
* Login Helper
*
+ * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
+ * by sending dummy SSH_MSG_IGNORE messages.}
+ *
* @param string $username
- * @param string $password
+ * @param string|AsymmetricKey|array[]|Agent|null ...$args
* @return bool
- * @access private
- * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
- * by sending dummy SSH_MSG_IGNORE messages.
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @throws \RuntimeException on other errors
*/
- function _login_helper($username, $password = null)
+ private function login_helper($username, $password = null)
{
if (!($this->bitmap & self::MASK_CONNECTED)) {
return false;
}
if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
- $packet = pack(
- 'CNa*',
- NET_SSH2_MSG_SERVICE_REQUEST,
- strlen('ssh-userauth'),
- 'ssh-userauth'
- );
-
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $bad_key_size_fix = $this->bad_key_size_fix;
- $response = $this->_get_binary_packet();
- if ($response === false) {
- // bad_key_size_fix is only ever re-assigned to true
- // under certain conditions. when it's newly set we'll
- // retry the connection with that new setting but we'll
- // only try it once.
- if ($bad_key_size_fix != $this->bad_key_size_fix) {
- if (!$this->_connect()) {
- return false;
- }
- return $this->_login_helper($username, $password);
+ $packet = Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth');
+ $this->send_binary_packet($packet);
+
+ try {
+ $response = $this->get_binary_packet_or_close(NET_SSH2_MSG_SERVICE_ACCEPT);
+ } catch (InvalidPacketLengthException $e) {
+ // the first opportunity to encounter the "bad key size" error
+ if (!$this->bad_key_size_fix && $this->decryptName != null && self::bad_algorithm_candidate($this->decryptName)) {
+ // bad_key_size_fix is only ever re-assigned to true here
+ // retry the connection with that new setting but we'll
+ // only try it once.
+ $this->bad_key_size_fix = true;
+ return $this->reconnect();
}
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
+ throw $e;
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ list($type) = Strings::unpackSSH2('C', $response);
+ list($service) = Strings::unpackSSH2('s', $response);
- if ($type == NET_SSH2_MSG_EXT_INFO) {
- if (strlen($response) < 4) {
- return false;
- }
- $nr_extensions = unpack('Nlength', $this->_string_shift($response, 4));
- for ($i = 0; $i < $nr_extensions['length']; $i++) {
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $extension_name = $this->_string_shift($response, $temp['length']);
- if ($extension_name == 'server-sig-algs') {
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($response, 4));
- $this->supported_private_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
- }
- }
-
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
- }
-
- if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
- user_error('Expected SSH_MSG_SERVICE_ACCEPT');
- return false;
+ if ($service != 'ssh-userauth') {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
+ throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT');
}
$this->bitmap |= self::MASK_LOGIN_REQ;
}
if (strlen($this->last_interactive_response)) {
- return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
+ return !Strings::is_stringable($password) && !is_array($password) ? false : $this->keyboard_interactive_process($password);
+ }
+
+ if ($password instanceof PrivateKey) {
+ return $this->privatekey_login($username, $password);
}
- if ($password instanceof RSA) {
- return $this->_privatekey_login($username, $password);
- } elseif ($password instanceof Agent) {
- return $this->_ssh_agent_login($username, $password);
+ if ($password instanceof Agent) {
+ return $this->ssh_agent_login($username, $password);
}
if (is_array($password)) {
- if ($this->_keyboard_interactive_login($username, $password)) {
+ if ($this->keyboard_interactive_login($username, $password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
@@ -2558,56 +2447,39 @@ class SSH2
}
if (!isset($password)) {
- $packet = pack(
- 'CNa*Na*Na*',
+ $packet = Strings::packSSH2(
+ 'Cs3',
NET_SSH2_MSG_USERAUTH_REQUEST,
- strlen($username),
$username,
- strlen('ssh-connection'),
'ssh-connection',
- strlen('none'),
'none'
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
+ $this->send_binary_packet($packet);
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ $response = $this->get_binary_packet_or_close();
+ list($type) = Strings::unpackSSH2('C', $response);
switch ($type) {
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
- extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
- $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
+ list($auth_methods) = Strings::unpackSSH2('L', $response);
+ $this->auth_methods_to_continue = $auth_methods;
+ // fall-through
default:
return false;
}
}
- $packet = pack(
- 'CNa*Na*Na*CNa*',
+ $packet = Strings::packSSH2(
+ 'Cs3bs',
NET_SSH2_MSG_USERAUTH_REQUEST,
- strlen($username),
$username,
- strlen('ssh-connection'),
'ssh-connection',
- strlen('password'),
'password',
- 0,
- strlen($password),
+ false,
$password
);
@@ -2615,63 +2487,36 @@ class SSH2
if (!defined('NET_SSH2_LOGGING')) {
$logged = null;
} else {
- $logged = pack(
- 'CNa*Na*Na*CNa*',
+ $logged = Strings::packSSH2(
+ 'Cs3bs',
NET_SSH2_MSG_USERAUTH_REQUEST,
- strlen('username'),
- 'username',
- strlen('ssh-connection'),
+ $username,
'ssh-connection',
- strlen('password'),
'password',
- 0,
- strlen('password'),
+ false,
'password'
);
}
- if (!$this->_send_binary_packet($packet, $logged)) {
- return false;
- }
-
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
-
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ $this->send_binary_packet($packet, $logged);
+ $response = $this->get_binary_packet_or_close();
+ list($type) = Strings::unpackSSH2('C', $response);
switch ($type) {
case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
- $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length);
- return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
+ $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ');
+
+ list($message) = Strings::unpackSSH2('s', $response);
+ $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message;
+
+ return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE:
// can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
// multi-factor authentication
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $auth_methods = explode(',', $this->_string_shift($response, $length));
+ list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response);
$this->auth_methods_to_continue = $auth_methods;
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
- $partial_success = $partial_success != 0;
-
if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
- if ($this->_keyboard_interactive_login($username, $password)) {
+ if ($this->keyboard_interactive_login($username, $password)) {
$this->bitmap |= self::MASK_LOGIN;
return true;
}
@@ -2692,81 +2537,49 @@ class SSH2
* See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
*
* @param string $username
- * @param string $password
+ * @param string|array $password
* @return bool
- * @access private
*/
- function _keyboard_interactive_login($username, $password)
+ private function keyboard_interactive_login($username, $password)
{
- $packet = pack(
- 'CNa*Na*Na*Na*Na*',
+ $packet = Strings::packSSH2(
+ 'Cs5',
NET_SSH2_MSG_USERAUTH_REQUEST,
- strlen($username),
$username,
- strlen('ssh-connection'),
'ssh-connection',
- strlen('keyboard-interactive'),
'keyboard-interactive',
- 0,
- '',
- 0,
- ''
+ '', // language tag
+ '' // submethods
);
+ $this->send_binary_packet($packet);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- return $this->_keyboard_interactive_process($password);
+ return $this->keyboard_interactive_process($password);
}
/**
* Handle the keyboard-interactive requests / responses.
*
+ * @param string|array ...$responses
* @return bool
- * @access private
+ * @throws \RuntimeException on connection error
*/
- function _keyboard_interactive_process()
+ private function keyboard_interactive_process(...$responses)
{
- $responses = func_get_args();
-
if (strlen($this->last_interactive_response)) {
$response = $this->last_interactive_response;
} else {
- $orig = $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
+ $orig = $response = $this->get_binary_packet_or_close();
}
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
-
+ list($type) = Strings::unpackSSH2('C', $response);
switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->_string_shift($response, $length); // name; may be empty
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->_string_shift($response, $length); // instruction; may be empty
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->_string_shift($response, $length); // language tag; may be empty
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
+ list(
+ , // name; may be empty
+ , // instruction; may be empty
+ , // language tag; may be empty
+ $num_prompts
+ ) = Strings::unpackSSH2('s3N', $response);
for ($i = 0; $i < count($responses); $i++) {
if (is_array($responses[$i])) {
@@ -2780,13 +2593,10 @@ class SSH2
if (isset($this->keyboard_requests_responses)) {
for ($i = 0; $i < $num_prompts; $i++) {
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- // prompt - ie. "Password: "; must not be empty
- $prompt = $this->_string_shift($response, $length);
- //$echo = $this->_string_shift($response) != chr(0);
+ list(
+ $prompt, // prompt - ie. "Password: "; must not be empty
+ // echo
+ ) = Strings::unpackSSH2('sC', $response);
foreach ($this->keyboard_requests_responses as $key => $value) {
if (substr($prompt, 0, strlen($key)) == $key) {
$responses[] = $value;
@@ -2800,7 +2610,7 @@ class SSH2
if (strlen($this->last_interactive_response)) {
$this->last_interactive_response = '';
} else {
- $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
+ $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST');
}
if (!count($responses) && $num_prompts) {
@@ -2815,15 +2625,13 @@ class SSH2
// see http://tools.ietf.org/html/rfc4256#section-3.4
$packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
for ($i = 0; $i < count($responses); $i++) {
- $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
- $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
+ $packet .= Strings::packSSH2('s', $responses[$i]);
+ $logged .= Strings::packSSH2('s', 'dummy-answer');
}
- if (!$this->_send_binary_packet($packet, $logged)) {
- return false;
- }
+ $this->send_binary_packet($packet, $logged);
- $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
+ $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE');
/*
After receiving the response, the server MUST send either an
@@ -2832,12 +2640,12 @@ class SSH2
*/
// maybe phpseclib should force close the connection after x request / responses? unless something like that is done
// there could be an infinite loop of request / responses.
- return $this->_keyboard_interactive_process();
+ return $this->keyboard_interactive_process();
case NET_SSH2_MSG_USERAUTH_SUCCESS:
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
- extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
- $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
+ list($auth_methods) = Strings::unpackSSH2('L', $response);
+ $this->auth_methods_to_continue = $auth_methods;
return false;
}
@@ -2848,17 +2656,16 @@ class SSH2
* Login with an ssh-agent provided key
*
* @param string $username
- * @param \phpseclib\System\SSH\Agent $agent
+ * @param Agent $agent
* @return bool
- * @access private
*/
- function _ssh_agent_login($username, $agent)
+ private function ssh_agent_login($username, Agent $agent)
{
$this->agent = $agent;
$keys = $agent->requestIdentities();
$orig_algorithms = $this->supported_private_key_algorithms;
foreach ($keys as $key) {
- if ($this->_privatekey_login($username, $key)) {
+ if ($this->privatekey_login($username, $key)) {
return true;
}
$this->supported_private_key_algorithms = $orig_algorithms;
@@ -2870,95 +2677,100 @@ class SSH2
/**
* Login with an RSA private key
*
+ * {@internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
+ * by sending dummy SSH_MSG_IGNORE messages.}
+ *
* @param string $username
- * @param \phpseclib\Crypt\RSA $privatekey
+ * @param PrivateKey $privatekey
* @return bool
- * @access private
- * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
- * by sending dummy SSH_MSG_IGNORE messages.
+ * @throws \RuntimeException on connection error
*/
- function _privatekey_login($username, $privatekey)
+ private function privatekey_login($username, PrivateKey $privatekey)
{
- // see http://tools.ietf.org/html/rfc4253#page-15
- $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
- if ($publickey === false) {
- return false;
- }
+ $publickey = $privatekey->getPublicKey();
- $publickey = array(
- 'e' => $publickey['e']->toBytes(true),
- 'n' => $publickey['n']->toBytes(true)
- );
- $publickey = pack(
- 'Na*Na*Na*',
- strlen('ssh-rsa'),
- 'ssh-rsa',
- strlen($publickey['e']),
- $publickey['e'],
- strlen($publickey['n']),
- $publickey['n']
- );
-
- $algos = array('rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa');
- if (isset($this->preferred['hostkey'])) {
- $algos = array_intersect($algos, $this->preferred['hostkey']);
+ if ($publickey instanceof RSA) {
+ $privatekey = $privatekey->withPadding(RSA::SIGNATURE_PKCS1);
+ $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa'];
+ if (isset($this->preferred['hostkey'])) {
+ $algos = array_intersect($algos, $this->preferred['hostkey']);
+ }
+ $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms);
+ switch ($algo) {
+ case 'rsa-sha2-512':
+ $hash = 'sha512';
+ $signatureType = 'rsa-sha2-512';
+ break;
+ case 'rsa-sha2-256':
+ $hash = 'sha256';
+ $signatureType = 'rsa-sha2-256';
+ break;
+ //case 'ssh-rsa':
+ default:
+ $hash = 'sha1';
+ $signatureType = 'ssh-rsa';
+ }
+ } elseif ($publickey instanceof EC) {
+ $privatekey = $privatekey->withSignatureFormat('SSH2');
+ $curveName = $privatekey->getCurve();
+ switch ($curveName) {
+ case 'Ed25519':
+ $hash = 'sha512';
+ $signatureType = 'ssh-ed25519';
+ break;
+ case 'secp256r1': // nistp256
+ $hash = 'sha256';
+ $signatureType = 'ecdsa-sha2-nistp256';
+ break;
+ case 'secp384r1': // nistp384
+ $hash = 'sha384';
+ $signatureType = 'ecdsa-sha2-nistp384';
+ break;
+ case 'secp521r1': // nistp521
+ $hash = 'sha512';
+ $signatureType = 'ecdsa-sha2-nistp521';
+ break;
+ default:
+ if (is_array($curveName)) {
+ throw new UnsupportedCurveException('Specified Curves are not supported by SSH2');
+ }
+ throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported by phpseclib3\'s SSH2 implementation');
+ }
+ } elseif ($publickey instanceof DSA) {
+ $privatekey = $privatekey->withSignatureFormat('SSH2');
+ $hash = 'sha1';
+ $signatureType = 'ssh-dss';
+ } else {
+ throw new UnsupportedAlgorithmException('Please use either an RSA key, an EC one or a DSA key');
}
- $algo = $this->_array_intersect_first($algos, $this->supported_private_key_algorithms);
- switch ($algo) {
- case 'rsa-sha2-512':
- $hash = 'sha512';
- $signatureType = 'rsa-sha2-512';
- break;
- case 'rsa-sha2-256':
- $hash = 'sha256';
- $signatureType = 'rsa-sha2-256';
- break;
- //case 'ssh-rsa':
- default:
- $hash = 'sha1';
- $signatureType = 'ssh-rsa';
- }
+ $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]);
- $part1 = pack(
- 'CNa*Na*Na*',
+ $part1 = Strings::packSSH2(
+ 'Csss',
NET_SSH2_MSG_USERAUTH_REQUEST,
- strlen($username),
$username,
- strlen('ssh-connection'),
'ssh-connection',
- strlen('publickey'),
'publickey'
);
- $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey);
+ $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr);
$packet = $part1 . chr(0) . $part2;
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
+ $this->send_binary_packet($packet);
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ $response = $this->get_binary_packet_or_close(
+ NET_SSH2_MSG_USERAUTH_SUCCESS,
+ NET_SSH2_MSG_USERAUTH_FAILURE,
+ NET_SSH2_MSG_USERAUTH_PK_OK
+ );
+ list($type) = Strings::unpackSSH2('C', $response);
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
- $auth_methods = explode(',', $this->_string_shift($response, $methodlistlen));
+ list($auth_methods) = Strings::unpackSSH2('L', $response);
if (in_array('publickey', $auth_methods) && substr($signatureType, 0, 9) == 'rsa-sha2-') {
- $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, array('rsa-sha2-256', 'rsa-sha2-512'));
- return $this->_privatekey_login($username, $privatekey);
+ $this->supported_private_key_algorithms = array_diff($this->supported_private_key_algorithms, ['rsa-sha2-256', 'rsa-sha2-512']);
+ return $this->privatekey_login($username, $privatekey);
}
$this->auth_methods_to_continue = $auth_methods;
$this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
@@ -2966,52 +2778,39 @@ class SSH2
case NET_SSH2_MSG_USERAUTH_PK_OK:
// we'll just take it on faith that the public key blob and the public key algorithm name are as
// they should be
- $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
+ $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
break;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
- default:
- user_error('Unexpected response to publickey authentication pt 1');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = $part1 . chr(1) . $part2;
- $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
- $privatekey->setHash($hash);
- $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
- $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature);
- $packet.= pack('Na*', strlen($signature), $signature);
-
- if (!$this->_send_binary_packet($packet)) {
- return false;
+ $privatekey = $privatekey->withHash($hash);
+ $signature = $privatekey->sign(Strings::packSSH2('s', $this->session_id) . $packet);
+ if ($publickey instanceof RSA) {
+ $signature = Strings::packSSH2('ss', $signatureType, $signature);
}
+ $packet .= Strings::packSSH2('s', $signature);
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
+ $this->send_binary_packet($packet);
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
+ $response = $this->get_binary_packet_or_close(
+ NET_SSH2_MSG_USERAUTH_SUCCESS,
+ NET_SSH2_MSG_USERAUTH_FAILURE
+ );
+ list($type) = Strings::unpackSSH2('C', $response);
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employs multi-factor authentication
- extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
- $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
+ list($auth_methods) = Strings::unpackSSH2('L', $response);
+ $this->auth_methods_to_continue = $auth_methods;
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
-
- user_error('Unexpected response to publickey authentication pt 2');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
/**
@@ -3019,7 +2818,7 @@ class SSH2
*
* @return int
*/
- function getTimeout()
+ public function getTimeout()
{
return $this->timeout;
}
@@ -3028,12 +2827,11 @@ class SSH2
* Set Timeout
*
* $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout.
- * Setting $timeout to false or 0 will mean there is no timeout.
+ * Setting $timeout to false or 0 will revert to the default socket timeout.
*
* @param mixed $timeout
- * @access public
*/
- function setTimeout($timeout)
+ public function setTimeout($timeout)
{
$this->timeout = $this->curTimeout = $timeout;
}
@@ -3044,9 +2842,8 @@ class SSH2
* Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
*
* @param int $interval
- * @access public
*/
- function setKeepAlive($interval)
+ public function setKeepAlive($interval)
{
$this->keepAlive = $interval;
}
@@ -3054,9 +2851,8 @@ class SSH2
/**
* Get the output from stdError
*
- * @access public
*/
- function getStdError()
+ public function getStdError()
{
return $this->stdErrorLog;
}
@@ -3064,15 +2860,16 @@ class SSH2
/**
* Execute Command
*
- * If $callback is set to false then \phpseclib\Net\SSH2::_get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
+ * If $callback is set to false then \phpseclib3\Net\SSH2::get_channel_packet(self::CHANNEL_EXEC) will need to be called manually.
* In all likelihood, this is not a feature you want to be taking advantage of.
*
* @param string $command
- * @param Callback $callback
- * @return string
- * @access public
+ * @param callable $callback
+ * @return string|bool
+ * @psalm-return ($callback is callable ? bool : string|bool)
+ * @throws \RuntimeException on connection error
*/
- function exec($command, $callback = null)
+ public function exec($command, $callback = null)
{
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
@@ -3082,75 +2879,39 @@ class SSH2
return false;
}
- if ($this->in_request_pty_exec) {
- user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
- return false;
- }
+ //if ($this->isPTYOpen()) {
+ // throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
+ //}
- // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
- // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
- // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
- // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
- $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
- // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
- // uses 0x4000, that's what will be used here, as well.
- $packet_size = 0x4000;
-
- $packet = pack(
- 'CNa*N3',
- NET_SSH2_MSG_CHANNEL_OPEN,
- strlen('session'),
- 'session',
- self::CHANNEL_EXEC,
- $this->window_size_server_to_client[self::CHANNEL_EXEC],
- $packet_size
- );
-
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
-
- $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
- if ($response === false) {
- return false;
- }
+ $this->open_channel(self::CHANNEL_EXEC);
if ($this->request_pty === true) {
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
- $packet = pack(
- 'CNNa*CNa*N5a*',
+ $packet = Strings::packSSH2(
+ 'CNsCsN4s',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
- strlen('pty-req'),
'pty-req',
1,
- strlen('vt100'),
- 'vt100',
+ $this->term,
$this->windowColumns,
$this->windowRows,
0,
0,
- strlen($terminal_modes),
$terminal_modes
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
- if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) {
- user_error('Unable to request pseudo-terminal');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new \RuntimeException('Unable to request pseudo-terminal');
}
-
- $this->in_request_pty_exec = true;
}
// sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
- // down. the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &').
+ // down. the one place where it might be desirable is if you're doing something like \phpseclib3\Net\SSH2::exec('ping localhost &').
// with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
// then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
// neither will your script.
@@ -3158,36 +2919,32 @@ class SSH2
// although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
// SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
// "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
- $packet = pack(
- 'CNNa*CNa*',
+ $packet = Strings::packSSH2(
+ 'CNsCs',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_EXEC],
- strlen('exec'),
'exec',
1,
- strlen($command),
$command
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
- $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
- if ($response === false) {
+ if (!$this->get_channel_packet(self::CHANNEL_EXEC)) {
return false;
}
$this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
- if ($callback === false || $this->in_request_pty_exec) {
+ if ($this->request_pty === true) {
+ $this->channel_id_last_interactive = self::CHANNEL_EXEC;
return true;
}
$output = '';
while (true) {
- $temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
+ $temp = $this->get_channel_packet(self::CHANNEL_EXEC);
switch (true) {
case $temp === true:
return is_callable($callback) ? true : $output;
@@ -3195,122 +2952,154 @@ class SSH2
return false;
default:
if (is_callable($callback)) {
- if (call_user_func($callback, $temp) === true) {
- $this->_close_channel(self::CHANNEL_EXEC);
+ if ($callback($temp) === true) {
+ $this->close_channel(self::CHANNEL_EXEC);
return true;
}
} else {
- $output.= $temp;
+ $output .= $temp;
}
}
}
}
/**
- * Creates an interactive shell
+ * How many channels are currently open?
*
- * @see self::read()
- * @see self::write()
+ * @return int
+ */
+ public function getOpenChannelCount()
+ {
+ return $this->channelCount;
+ }
+
+ /**
+ * Opens a channel
+ *
+ * @param string $channel
+ * @param bool $skip_extended
* @return bool
- * @access private
*/
- function _initShell()
+ protected function open_channel($channel, $skip_extended = false)
{
- if ($this->in_request_pty_exec === true) {
- return true;
+ if (isset($this->channel_status[$channel])) {
+ throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again');
}
- $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
+ $this->channelCount++;
+
+ if ($this->channelCount > 1 && $this->errorOnMultipleChannels) {
+ throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels");
+ }
+
+ // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
+ // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
+ // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
+ // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
+ $this->window_size_server_to_client[$channel] = $this->window_size;
+ // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
+ // uses 0x4000, that's what will be used here, as well.
$packet_size = 0x4000;
- $packet = pack(
- 'CNa*N3',
+ $packet = Strings::packSSH2(
+ 'CsN3',
NET_SSH2_MSG_CHANNEL_OPEN,
- strlen('session'),
'session',
- self::CHANNEL_SHELL,
- $this->window_size_server_to_client[self::CHANNEL_SHELL],
+ $channel,
+ $this->window_size_server_to_client[$channel],
$packet_size
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
- $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
+ $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN;
- $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
- if ($response === false) {
- return false;
+ return $this->get_channel_packet($channel, $skip_extended);
+ }
+
+ /**
+ * Creates an interactive shell
+ *
+ * Returns bool(true) if the shell was opened.
+ * Returns bool(false) if the shell was already open.
+ *
+ * @see self::isShellOpen()
+ * @see self::read()
+ * @see self::write()
+ * @return bool
+ * @throws InsufficientSetupException if not authenticated
+ * @throws \UnexpectedValueException on receipt of unexpected packets
+ * @throws \RuntimeException on other errors
+ */
+ public function openShell()
+ {
+ if (!$this->isAuthenticated()) {
+ throw new InsufficientSetupException('Operation disallowed prior to login()');
}
+ $this->open_channel(self::CHANNEL_SHELL);
+
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
- $packet = pack(
- 'CNNa*CNa*N5a*',
+ $packet = Strings::packSSH2(
+ 'CNsbsN4s',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
- strlen('pty-req'),
'pty-req',
- 1,
- strlen('vt100'),
- 'vt100',
+ true, // want reply
+ $this->term,
$this->windowColumns,
$this->windowRows,
0,
0,
- strlen($terminal_modes),
$terminal_modes
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
- if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) {
- user_error('Unable to request pseudo-terminal');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ if (!$this->get_channel_packet(self::CHANNEL_SHELL)) {
+ throw new \RuntimeException('Unable to request pty');
}
- $packet = pack(
- 'CNNa*C',
+ $packet = Strings::packSSH2(
+ 'CNsb',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SHELL],
- strlen('shell'),
'shell',
- 1
+ true // want reply
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
- $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
+ $response = $this->get_channel_packet(self::CHANNEL_SHELL);
if ($response === false) {
- return false;
+ throw new \RuntimeException('Unable to request shell');
}
$this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
+ $this->channel_id_last_interactive = self::CHANNEL_SHELL;
+
$this->bitmap |= self::MASK_SHELL;
return true;
}
/**
- * Return the channel to be used with read() / write()
- *
+ * Return the channel to be used with read(), write(), and reset(), if none were specified
+ * @deprecated for lack of transparency in intended channel target, to be potentially replaced
+ * with method which guarantees open-ness of all yielded channels and throws
+ * error for multiple open channels
* @see self::read()
* @see self::write()
* @return int
- * @access public
*/
- function _get_interactive_channel()
+ private function get_interactive_channel()
{
switch (true) {
- case $this->in_subsystem:
+ case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM):
return self::CHANNEL_SUBSYSTEM;
- case $this->in_request_pty_exec:
+ case $this->is_channel_status_data(self::CHANNEL_EXEC):
return self::CHANNEL_EXEC;
default:
return self::CHANNEL_SHELL;
@@ -3318,12 +3107,22 @@ class SSH2
}
/**
+ * Indicates the DATA status on the given channel
+ *
+ * @param int $channel The channel number to evaluate
+ * @return bool
+ */
+ private function is_channel_status_data($channel)
+ {
+ return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA;
+ }
+
+ /**
* Return an available open channel
*
* @return int
- * @access public
*/
- function _get_open_channel()
+ private function get_open_channel()
{
$channel = self::CHANNEL_EXEC;
do {
@@ -3336,36 +3135,82 @@ class SSH2
}
/**
+ * Request agent forwarding of remote server
+ *
+ * @return bool
+ */
+ public function requestAgentForwarding()
+ {
+ $request_channel = $this->get_open_channel();
+ if ($request_channel === false) {
+ return false;
+ }
+
+ $packet = Strings::packSSH2(
+ 'CNsC',
+ NET_SSH2_MSG_CHANNEL_REQUEST,
+ $this->server_channels[$request_channel],
+ 'auth-agent-req@openssh.com',
+ 1
+ );
+
+ $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;
+
+ $this->send_binary_packet($packet);
+
+ if (!$this->get_channel_packet($request_channel)) {
+ return false;
+ }
+
+ $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
+
+ return true;
+ }
+
+ /**
* Returns the output of an interactive shell
*
* Returns when there's a match for $expect, which can take the form of a string literal or,
* if $mode == self::READ_REGEX, a regular expression.
*
+ * If not specifying a channel, an open interactive channel will be selected, or, if there are
+ * no open channels, an interactive shell will be created. If there are multiple open
+ * interactive channels, a legacy behavior will apply in which channel selection prioritizes
+ * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive
+ * channels, callers are discouraged from relying on this legacy behavior and should specify
+ * the intended channel.
+ *
* @see self::write()
* @param string $expect
- * @param int $mode
- * @return string|bool
- * @access public
+ * @param int $mode One of the self::READ_* constants
+ * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
+ * @return string|bool|null
+ * @throws \RuntimeException on connection error
+ * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
*/
- function read($expect = '', $mode = self::READ_SIMPLE)
+ public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null)
{
+ if (!$this->isAuthenticated()) {
+ throw new InsufficientSetupException('Operation disallowed prior to login()');
+ }
+
$this->curTimeout = $this->timeout;
$this->is_timeout = false;
- if (!$this->isAuthenticated()) {
- user_error('Operation disallowed prior to login()');
- return false;
+ if ($channel === null) {
+ $channel = $this->get_interactive_channel();
}
- if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
- user_error('Unable to initiate an interactive shell session');
- return false;
+ if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) {
+ if ($channel != self::CHANNEL_SHELL) {
+ throw new InsufficientSetupException('Data is not available on channel');
+ } elseif (!$this->openShell()) {
+ throw new \RuntimeException('Unable to initiate an interactive shell session');
+ }
}
- $channel = $this->_get_interactive_channel();
-
if ($mode == self::READ_NEXT) {
- return $this->_get_channel_packet($channel);
+ return $this->get_channel_packet($channel);
}
$match = $expect;
@@ -3376,39 +3221,56 @@ class SSH2
}
$pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
if ($pos !== false) {
- return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
+ return Strings::shift($this->interactiveBuffer, $pos + strlen($match));
}
- $response = $this->_get_channel_packet($channel);
- if (is_bool($response)) {
- $this->in_request_pty_exec = false;
- return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
+ $response = $this->get_channel_packet($channel);
+ if ($response === true) {
+ return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer));
}
- $this->interactiveBuffer.= $response;
+ $this->interactiveBuffer .= $response;
}
}
/**
* Inputs a command into an interactive shell.
*
- * @see self::read()
+ * If not specifying a channel, an open interactive channel will be selected, or, if there are
+ * no open channels, an interactive shell will be created. If there are multiple open
+ * interactive channels, a legacy behavior will apply in which channel selection prioritizes
+ * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive
+ * channels, callers are discouraged from relying on this legacy behavior and should specify
+ * the intended channel.
+ *
+ * @see SSH2::read()
* @param string $cmd
- * @return bool
- * @access public
+ * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
+ * @return void
+ * @throws \RuntimeException on connection error
+ * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
+ * @throws TimeoutException if the write could not be completed within the requested self::setTimeout()
*/
- function write($cmd)
+ public function write($cmd, $channel = null)
{
if (!$this->isAuthenticated()) {
- user_error('Operation disallowed prior to login()');
- return false;
+ throw new InsufficientSetupException('Operation disallowed prior to login()');
}
- if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
- user_error('Unable to initiate an interactive shell session');
- return false;
+ if ($channel === null) {
+ $channel = $this->get_interactive_channel();
+ }
+
+ if (!$this->is_channel_status_data($channel)) {
+ if ($channel != self::CHANNEL_SHELL) {
+ throw new InsufficientSetupException('Data is not available on channel');
+ } elseif (!$this->openShell()) {
+ throw new \RuntimeException('Unable to initiate an interactive shell session');
+ }
}
- return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
+ $this->curTimeout = $this->timeout;
+ $this->is_timeout = false;
+ $this->send_channel_packet($channel, $cmd);
}
/**
@@ -3423,59 +3285,30 @@ class SSH2
* @see self::stopSubsystem()
* @param string $subsystem
* @return bool
- * @access public
*/
- function startSubsystem($subsystem)
+ public function startSubsystem($subsystem)
{
- $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
-
- $packet = pack(
- 'CNa*N3',
- NET_SSH2_MSG_CHANNEL_OPEN,
- strlen('session'),
- 'session',
- self::CHANNEL_SUBSYSTEM,
- $this->window_size,
- 0x4000
- );
+ $this->open_channel(self::CHANNEL_SUBSYSTEM);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
-
- $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
-
- $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
- if ($response === false) {
- return false;
- }
-
- $packet = pack(
- 'CNNa*CNa*',
+ $packet = Strings::packSSH2(
+ 'CNsCs',
NET_SSH2_MSG_CHANNEL_REQUEST,
$this->server_channels[self::CHANNEL_SUBSYSTEM],
- strlen('subsystem'),
'subsystem',
1,
- strlen($subsystem),
$subsystem
);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
$this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
- $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
-
- if ($response === false) {
+ if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) {
return false;
}
$this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
- $this->bitmap |= self::MASK_SHELL;
- $this->in_subsystem = true;
+ $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM;
return true;
}
@@ -3485,12 +3318,12 @@ class SSH2
*
* @see self::startSubsystem()
* @return bool
- * @access public
*/
- function stopSubsystem()
+ public function stopSubsystem()
{
- $this->in_subsystem = false;
- $this->_close_channel(self::CHANNEL_SUBSYSTEM);
+ if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) {
+ $this->close_channel(self::CHANNEL_SUBSYSTEM);
+ }
return true;
}
@@ -3499,11 +3332,44 @@ class SSH2
*
* If read() timed out you might want to just close the channel and have it auto-restart on the next read() call
*
- * @access public
+ * If not specifying a channel, an open interactive channel will be selected. If there are
+ * multiple open interactive channels, a legacy behavior will apply in which channel selection
+ * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple
+ * interactive channels, callers are discouraged from relying on this legacy behavior and
+ * should specify the intended channel.
+ *
+ * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
+ * @return void
*/
- function reset()
+ public function reset($channel = null)
{
- $this->_close_channel($this->_get_interactive_channel());
+ if ($channel === null) {
+ $channel = $this->get_interactive_channel();
+ }
+ if ($this->isInteractiveChannelOpen($channel)) {
+ $this->close_channel($channel);
+ }
+ }
+
+ /**
+ * Send EOF on a channel
+ *
+ * Sends an EOF to the stream; this is typically used to close standard
+ * input, while keeping output and error alive.
+ *
+ * @param int|null $channel Channel id returned by self::getInteractiveChannelId()
+ * @return void
+ */
+ public function sendEOF($channel = null)
+ {
+ if ($channel === null) {
+ $channel = $this->get_interactive_channel();
+ }
+
+ $excludeStatuses = [NET_SSH2_MSG_CHANNEL_EOF, NET_SSH2_MSG_CHANNEL_CLOSE];
+ if (isset($this->channel_status[$channel]) && !in_array($this->channel_status[$channel], $excludeStatuses)) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$channel]));
+ }
}
/**
@@ -3511,9 +3377,8 @@ class SSH2
*
* Did exec() or read() return because they timed out or because they encountered the end?
*
- * @access public
*/
- function isTimeout()
+ public function isTimeout()
{
return $this->is_timeout;
}
@@ -3521,14 +3386,14 @@ class SSH2
/**
* Disconnect
*
- * @access public
*/
- function disconnect()
+ public function disconnect()
{
- $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
fclose($this->realtime_log_file);
}
+ unset(self::$connections[$this->getResourceId()]);
}
/**
@@ -3537,9 +3402,8 @@ class SSH2
* Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
* disconnect().
*
- * @access public
*/
- function __destruct()
+ public function __destruct()
{
$this->disconnect();
}
@@ -3547,102 +3411,201 @@ class SSH2
/**
* Is the connection still active?
*
+ * $level has 3x possible values:
+ * 0 (default): phpseclib takes a passive approach to see if the connection is still active by calling feof()
+ * on the socket
+ * 1: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_IGNORE
+ * packet that doesn't require a response
+ * 2: phpseclib takes an active approach to see if the connection is still active by sending an SSH_MSG_CHANNEL_OPEN
+ * packet and imediately trying to close that channel. some routers, in particular, however, will only let you
+ * open one channel, so this approach could yield false positives
+ *
+ * @param int $level
* @return bool
- * @access public
*/
- function isConnected()
+ public function isConnected($level = 0)
{
- return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock);
+ if (!is_int($level) || $level < 0 || $level > 2) {
+ throw new \InvalidArgumentException('$level must be 0, 1 or 2');
+ }
+
+ if ($level == 0) {
+ return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock);
+ }
+ try {
+ if ($level == 1) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
+ } else {
+ $this->open_channel(self::CHANNEL_KEEP_ALIVE);
+ $this->close_channel(self::CHANNEL_KEEP_ALIVE);
+ }
+ return true;
+ } catch (\Exception $e) {
+ return false;
+ }
}
/**
* Have you successfully been logged in?
*
* @return bool
- * @access public
*/
- function isAuthenticated()
+ public function isAuthenticated()
{
return (bool) ($this->bitmap & self::MASK_LOGIN);
}
/**
+ * Is the interactive shell active?
+ *
+ * @return bool
+ */
+ public function isShellOpen()
+ {
+ return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL);
+ }
+
+ /**
+ * Is the exec pty active?
+ *
+ * @return bool
+ */
+ public function isPTYOpen()
+ {
+ return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC);
+ }
+
+ /**
+ * Is the given interactive channel active?
+ *
+ * @param int $channel Channel id returned by self::getInteractiveChannelId()
+ * @return bool
+ */
+ public function isInteractiveChannelOpen($channel)
+ {
+ return $this->isAuthenticated() && $this->is_channel_status_data($channel);
+ }
+
+ /**
+ * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status.
+ * Returns 0 if no interactive channel has been opened.
+ *
+ * @see self::isInteractiveChannelOpen()
+ * @return int
+ */
+ public function getInteractiveChannelId()
+ {
+ return $this->channel_id_last_interactive;
+ }
+
+ /**
* Pings a server connection, or tries to reconnect if the connection has gone down
*
* Inspired by http://php.net/manual/en/mysqli.ping.php
*
* @return bool
- * @access public
*/
- function ping()
+ public function ping()
{
if (!$this->isAuthenticated()) {
if (!empty($this->auth)) {
- return $this->_reconnect();
+ return $this->reconnect();
}
return false;
}
- $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
- $packet_size = 0x4000;
- $packet = pack(
- 'CNa*N3',
- NET_SSH2_MSG_CHANNEL_OPEN,
- strlen('session'),
- 'session',
- self::CHANNEL_KEEP_ALIVE,
- $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
- $packet_size
- );
-
- if (!@$this->_send_binary_packet($packet)) {
- return $this->_reconnect();
- }
-
- $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
-
- $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE);
- if ($response !== false) {
- $this->_close_channel(self::CHANNEL_KEEP_ALIVE);
- return true;
+ try {
+ $this->open_channel(self::CHANNEL_KEEP_ALIVE);
+ } catch (\RuntimeException $e) {
+ return $this->reconnect();
}
- return $this->_reconnect();
+ $this->close_channel(self::CHANNEL_KEEP_ALIVE);
+ return true;
}
/**
* In situ reconnect method
*
* @return boolean
- * @access private
*/
- function _reconnect()
+ private function reconnect()
{
- $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
- if (!$this->_connect()) {
- return false;
- }
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->connect();
foreach ($this->auth as $auth) {
- $result = call_user_func_array(array(&$this, 'login'), $auth);
+ $result = $this->login(...$auth);
}
return $result;
}
/**
* Resets a connection for re-use
- *
- * @param int $reason
- * @access private
*/
- function _reset_connection($reason)
+ protected function reset_connection()
{
- $this->_disconnect($reason);
+ if (is_resource($this->fsock) && get_resource_type($this->fsock) === 'stream') {
+ fclose($this->fsock);
+ }
+ $this->fsock = null;
+ $this->bitmap = 0;
+ $this->binary_packet_buffer = null;
$this->decrypt = $this->encrypt = false;
$this->decrypt_block_size = $this->encrypt_block_size = 8;
$this->hmac_check = $this->hmac_create = false;
$this->hmac_size = false;
$this->session_id = false;
+ $this->last_packet = null;
$this->get_seq_no = $this->send_seq_no = 0;
+ $this->channel_status = [];
+ $this->channel_id_last_interactive = 0;
+ $this->channel_buffers = [];
+ $this->channel_buffers_write = [];
+ }
+
+ /**
+ * @return int[] second and microsecond stream timeout options based on user-requested timeout and keep-alive, or the default socket timeout by default, which mirrors PHP socket streams.
+ */
+ private function get_stream_timeout()
+ {
+ $sec = ini_get('default_socket_timeout');
+ $usec = 0;
+ if ($this->curTimeout > 0) {
+ $sec = (int) floor($this->curTimeout);
+ $usec = (int) (1000000 * ($this->curTimeout - $sec));
+ }
+ if ($this->keepAlive > 0) {
+ $elapsed = microtime(true) - $this->last_packet;
+ $timeout = max($this->keepAlive - $elapsed, 0);
+ if (!$this->curTimeout || $timeout < $this->curTimeout) {
+ $sec = (int) floor($timeout);
+ $usec = (int) (1000000 * ($timeout - $sec));
+ }
+ }
+ return [$sec, $usec];
+ }
+
+ /**
+ * Retrieves the next packet with added timeout and type handling
+ *
+ * @param string $message_types Message types to enforce in response, closing if not met
+ * @return string
+ * @throws ConnectionClosedException If an error has occurred preventing read of the next packet
+ */
+ private function get_binary_packet_or_close(...$message_types)
+ {
+ try {
+ $packet = $this->get_binary_packet();
+ if (count($message_types) > 0 && !in_array(ord($packet[0]), $message_types)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
+ throw new ConnectionClosedException('Bad message type. Expected: #'
+ . implode(', #', $message_types) . '. Got: #' . ord($packet[0]));
+ }
+ return $packet;
+ } catch (TimeoutException $e) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new ConnectionClosedException('Connection closed due to timeout');
+ }
}
/**
@@ -3652,145 +3615,151 @@ class SSH2
*
* @see self::_send_binary_packet()
* @return string
- * @access private
+ * @throws TimeoutException If user requested timeout was reached while waiting for next packet
+ * @throws ConnectionClosedException If an error has occurred preventing read of the next packet
*/
- function _get_binary_packet($skip_channel_filter = false)
+ private function get_binary_packet()
{
- if (!$this->keyExchangeInProgress && count($this->kex_buffer)) {
- return $this->_filter(array_shift($this->kex_buffer), $skip_channel_filter);
+ if (!is_resource($this->fsock)) {
+ throw new \InvalidArgumentException('fsock is not a resource.');
}
-
- if ($skip_channel_filter) {
- $read = array($this->fsock);
- $write = $except = null;
-
- if (!$this->curTimeout) {
- if ($this->keepAlive <= 0) {
- @stream_select($read, $write, $except, null);
- } else {
- if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
- return $this->_get_binary_packet(true);
- }
- }
- } else {
- if ($this->curTimeout < 0) {
- $this->is_timeout = true;
- return true;
- }
-
- $read = array($this->fsock);
- $write = $except = null;
-
- $start = microtime(true);
-
- if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
- if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
- $elapsed = microtime(true) - $start;
- $this->curTimeout-= $elapsed;
- return $this->_get_binary_packet(true);
- }
- $elapsed = microtime(true) - $start;
- $this->curTimeout-= $elapsed;
- }
-
- $sec = (int)floor($this->curTimeout);
- $usec = (int)(1000000 * ($this->curTimeout - $sec));
-
- // on windows this returns a "Warning: Invalid CRT parameters detected" error
- if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
- $this->is_timeout = true;
- return true;
- }
- $elapsed = microtime(true) - $start;
- $this->curTimeout-= $elapsed;
+ if (!$this->keyExchangeInProgress && count($this->kex_buffer)) {
+ return $this->filter(array_shift($this->kex_buffer));
+ }
+ if ($this->binary_packet_buffer == null) {
+ // buffer the packet to permit continued reads across timeouts
+ $this->binary_packet_buffer = (object) [
+ 'read_time' => 0, // the time to read the packet from the socket
+ 'raw' => '', // the raw payload read from the socket
+ 'plain' => '', // the packet in plain text, excluding packet_length header
+ 'packet_length' => null, // the packet_length value pulled from the payload
+ 'size' => $this->decrypt_block_size, // the total size of this packet to be read from the socket
+ // initialize to read single block until packet_length is available
+ ];
+ }
+ $packet = $this->binary_packet_buffer;
+ while (strlen($packet->raw) < $packet->size) {
+ if (feof($this->fsock)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
+ throw new ConnectionClosedException('Connection closed by server');
}
- }
-
- if (!is_resource($this->fsock) || feof($this->fsock)) {
- $this->bitmap = 0;
- $str = 'Connection closed (by server) prematurely';
- if (isset($elapsed)) {
- $str.= ' ' . $elapsed . 's';
+ if ($this->curTimeout < 0) {
+ $this->is_timeout = true;
+ throw new TimeoutException('Timed out waiting for server');
}
- user_error($str);
- return false;
- }
-
- $start = microtime(true);
- $sec = (int) floor($this->curTimeout);
- $usec = (int) (1000000 * ($this->curTimeout - $sec));
- stream_set_timeout($this->fsock, $sec, $usec);
- $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
-
- if (!strlen($raw)) {
- user_error('No data received from server');
- return false;
- }
+ $this->send_keep_alive();
- if ($this->decrypt !== false) {
- $raw = $this->decrypt->decrypt($raw);
- }
- if ($raw === false) {
- user_error('Unable to decrypt content');
- return false;
+ list($sec, $usec) = $this->get_stream_timeout();
+ stream_set_timeout($this->fsock, $sec, $usec);
+ $start = microtime(true);
+ $raw = stream_get_contents($this->fsock, $packet->size - strlen($packet->raw));
+ $elapsed = microtime(true) - $start;
+ $packet->read_time += $elapsed;
+ if ($this->curTimeout > 0) {
+ $this->curTimeout -= $elapsed;
+ }
+ if ($raw === false) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
+ throw new ConnectionClosedException('Connection closed by server');
+ } elseif (!strlen($raw)) {
+ continue;
+ }
+ $packet->raw .= $raw;
+ if (!$packet->packet_length) {
+ $this->get_binary_packet_size($packet);
+ }
}
- if (strlen($raw) < 5) {
- return false;
+ if (strlen($packet->raw) != $packet->size) {
+ throw new \RuntimeException('Size of packet was not expected length');
}
- extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
-
- $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
-
- if (!$this->keyExchangeInProgress) {
- $this->bytesTransferredSinceLastKEX+= $packet_length + $padding_length + 5;
+ // destroy buffer as packet represents the entire payload and should be processed in full
+ $this->binary_packet_buffer = null;
+ // copy the raw payload, so as not to destroy original
+ $raw = $packet->raw;
+ if ($this->hmac_check instanceof Hash) {
+ $hmac = Strings::pop($raw, $this->hmac_size);
}
-
- // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
- // "implementations SHOULD check that the packet length is reasonable"
- // PuTTY uses 0x9000 as the actual max packet size and so, too, shall we
- if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
- if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decryptName) && !($this->bitmap & SSH2::MASK_LOGIN)) {
- $this->bad_key_size_fix = true;
- $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
- return false;
+ $packet_length_header_size = 4;
+ if ($this->decrypt) {
+ switch ($this->decryptName) {
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ $this->decrypt->setNonce(
+ $this->decryptFixedPart .
+ $this->decryptInvocationCounter
+ );
+ Strings::increment_str($this->decryptInvocationCounter);
+ $this->decrypt->setAAD(Strings::shift($raw, $packet_length_header_size));
+ $this->decrypt->setTag(Strings::pop($raw, $this->decrypt_block_size));
+ $packet->plain = $this->decrypt->decrypt($raw);
+ break;
+ case 'chacha20-poly1305@openssh.com':
+ // This should be impossible, but we are checking anyway to narrow the type for Psalm.
+ if (!($this->decrypt instanceof ChaCha20)) {
+ throw new \LogicException('$this->decrypt is not a ' . ChaCha20::class);
+ }
+ $this->decrypt->setNonce(pack('N2', 0, $this->get_seq_no));
+ $this->decrypt->setCounter(0);
+ // this is the same approach that's implemented in Salsa20::createPoly1305Key()
+ // but we don't want to use the same AEAD construction that RFC8439 describes
+ // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
+ $this->decrypt->setPoly1305Key(
+ $this->decrypt->encrypt(str_repeat("\0", 32))
+ );
+ $this->decrypt->setAAD(Strings::shift($raw, $packet_length_header_size));
+ $this->decrypt->setCounter(1);
+ $this->decrypt->setTag(Strings::pop($raw, 16));
+ $packet->plain = $this->decrypt->decrypt($raw);
+ break;
+ default:
+ if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) {
+ // first block was already decrypted for contained packet_length header
+ Strings::shift($raw, $this->decrypt_block_size);
+ if (strlen($raw) > 0) {
+ $packet->plain .= $this->decrypt->decrypt($raw);
+ }
+ } else {
+ Strings::shift($raw, $packet_length_header_size);
+ $packet->plain = $this->decrypt->decrypt($raw);
+ }
+ break;
}
- user_error('Invalid size');
- return false;
- }
-
- $buffer = '';
- while ($remaining_length > 0) {
- $temp = stream_get_contents($this->fsock, $remaining_length);
- if ($temp === false || feof($this->fsock)) {
- $this->bitmap = 0;
- user_error('Error reading from socket');
- return false;
+ } else {
+ Strings::shift($raw, $packet_length_header_size);
+ $packet->plain = $raw;
+ }
+ if ($this->hmac_check instanceof Hash) {
+ $reconstructed = !$this->hmac_check_etm ?
+ pack('Na*', $packet->packet_length, $packet->plain) :
+ substr($packet->raw, 0, -$this->hmac_size);
+ if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
+ $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
+ if ($hmac != $this->hmac_check->hash($reconstructed)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
+ throw new ConnectionClosedException('Invalid UMAC');
+ }
+ } else {
+ if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
+ throw new ConnectionClosedException('Invalid HMAC');
+ }
}
- $buffer.= $temp;
- $remaining_length-= strlen($temp);
}
-
- $stop = microtime(true);
- if (strlen($buffer)) {
- $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
+ $padding_length = 0;
+ $payload = $packet->plain;
+ $padding_length = unpack('Cpadding_length', Strings::shift($payload, 1))['padding_length'];
+ if ($padding_length > 0) {
+ Strings::pop($payload, $padding_length);
}
- $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
- $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
+ if (!$this->keyExchangeInProgress) {
+ $this->bytesTransferredSinceLastKEX += $packet->packet_length + $padding_length + 5;
+ }
- if ($this->hmac_check !== false) {
- $hmac = stream_get_contents($this->fsock, $this->hmac_size);
- if ($hmac === false || strlen($hmac) != $this->hmac_size) {
- $this->bitmap = 0;
- user_error('Error reading socket');
- return false;
- } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
- user_error('Invalid HMAC');
- return false;
- }
+ if (empty($payload)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
+ throw new ConnectionClosedException('Plaintext is too short');
}
switch ($this->decompress) {
@@ -3798,6 +3767,7 @@ class SSH2
if (!$this->isAuthenticated()) {
break;
}
+ // fall-through
case self::NET_SSH2_COMPRESSION_ZLIB:
if ($this->regenerate_decompression_context) {
$this->regenerate_decompression_context = false;
@@ -3805,23 +3775,23 @@ class SSH2
$cmf = ord($payload[0]);
$cm = $cmf & 0x0F;
if ($cm != 8) { // deflate
- user_error("Only CM = 8 ('deflate') is supported ($cm)");
+ throw new UnsupportedAlgorithmException("Only CM = 8 ('deflate') is supported ($cm)");
}
$cinfo = ($cmf & 0xF0) >> 4;
if ($cinfo > 7) {
- user_error("CINFO above 7 is not allowed ($cinfo)");
+ throw new \RuntimeException("CINFO above 7 is not allowed ($cinfo)");
}
$windowSize = 1 << ($cinfo + 8);
$flg = ord($payload[1]);
//$fcheck = $flg && 0x0F;
if ((($cmf << 8) | $flg) % 31) {
- user_error('fcheck failed');
+ throw new \RuntimeException('fcheck failed');
}
$fdict = boolval($flg & 0x20);
$flevel = ($flg & 0xC0) >> 6;
- $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
+ $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, ['window' => $cinfo + 8]);
$payload = substr($payload, 2);
}
if ($this->decompress_context) {
@@ -3833,19 +3803,80 @@ class SSH2
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
- $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
+ $message_number = isset(self::$message_numbers[ord($payload[0])]) ? self::$message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
$message_number = '<- ' . $message_number .
- ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
- $this->_append_log($message_number, $payload);
- $this->last_packet = $current;
+ ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($packet->read_time, 4) . 's)';
+ $this->append_log($message_number, $payload);
}
+ $this->last_packet = microtime(true);
if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) {
- $this->_key_exchange();
+ $this->key_exchange();
}
- // don't filter if we're in the middle of a key exchange (since _filter might send out packets)
- return $this->keyExchangeInProgress ? $payload : $this->_filter($payload, $skip_channel_filter);
+ return $this->filter($payload);
+ }
+
+ /**
+ * @param object $packet The packet object being constructed, passed by reference
+ * The size, packet_length, and plain properties of this object may be modified in processing
+ * @throws InvalidPacketLengthException if the packet length header is invalid
+ */
+ private function get_binary_packet_size(&$packet)
+ {
+ $packet_length_header_size = 4;
+ if (strlen($packet->raw) < $packet_length_header_size) {
+ return;
+ }
+ $packet_length = 0;
+ $added_validation_length = 0; // indicates when the packet length header is included when validating packet length against block size
+ if ($this->decrypt) {
+ switch ($this->decryptName) {
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ $packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
+ $packet->size = $packet_length_header_size + $packet_length + $this->decrypt_block_size; // expect tag
+ break;
+ case 'chacha20-poly1305@openssh.com':
+ $this->lengthDecrypt->setNonce(pack('N2', 0, $this->get_seq_no));
+ $packet_length_header = $this->lengthDecrypt->decrypt(substr($packet->raw, 0, $packet_length_header_size));
+ $packet_length = unpack('Npacket_length', $packet_length_header)['packet_length'];
+ $packet->size = $packet_length_header_size + $packet_length + 16; // expect tag
+ break;
+ default:
+ if (!$this->hmac_check instanceof Hash || !$this->hmac_check_etm) {
+ if (strlen($packet->raw) < $this->decrypt_block_size) {
+ return;
+ }
+ $packet->plain = $this->decrypt->decrypt(substr($packet->raw, 0, $this->decrypt_block_size));
+ $packet_length = unpack('Npacket_length', Strings::shift($packet->plain, $packet_length_header_size))['packet_length'];
+ $packet->size = $packet_length_header_size + $packet_length;
+ $added_validation_length = $packet_length_header_size;
+ } else {
+ $packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
+ $packet->size = $packet_length_header_size + $packet_length;
+ }
+ break;
+ }
+ } else {
+ $packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
+ $packet->size = $packet_length_header_size + $packet_length;
+ $added_validation_length = $packet_length_header_size;
+ }
+ // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
+ // "implementations SHOULD check that the packet length is reasonable"
+ // PuTTY uses 0x9000 as the actual max packet size and so to shall we
+ if (
+ $packet_length <= 0 || $packet_length > 0x9000
+ || ($packet_length + $added_validation_length) % $this->decrypt_block_size != 0
+ ) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR);
+ throw new InvalidPacketLengthException('Invalid packet length');
+ }
+ if ($this->hmac_check instanceof Hash) {
+ $packet->size += $this->hmac_size;
+ }
+ $packet->packet_length = $packet_length;
}
/**
@@ -3853,21 +3884,18 @@ class SSH2
*
* Because some binary packets need to be ignored...
*
- * @see self::_filter()
- * @see self::_key_exchange
+ * @see self::filter()
+ * @see self::key_exchange()
* @return boolean
* @access private
*/
- function _handleDisconnect($payload)
+ private function handleDisconnect($payload)
{
- $this->_string_shift($payload, 1);
- if (strlen($payload) < 8) {
- return false;
- }
- extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
- $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length);
- $this->bitmap = 0;
- return false;
+ Strings::shift($payload, 1);
+ list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload);
+ $this->errors[] = 'SSH_MSG_DISCONNECT: ' . self::$disconnect_reasons[$reason_code] . "\r\n$message";
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
+ throw new ConnectionClosedException('Connection closed by server');
}
/**
@@ -3876,136 +3904,117 @@ class SSH2
* Because some binary packets need to be ignored...
*
* @see self::_get_binary_packet()
+ * @param string $payload
* @return string
- * @access private
*/
- function _filter($payload, $skip_channel_filter)
+ private function filter($payload)
{
+ if (ord($payload[0]) == NET_SSH2_MSG_DISCONNECT) {
+ return $this->handleDisconnect($payload);
+ }
+
+ if ($this->session_id === false && $this->keyExchangeInProgress) {
+ return $payload;
+ }
+
switch (ord($payload[0])) {
- case NET_SSH2_MSG_DISCONNECT:
- return $this->_handleDisconnect($payload);
case NET_SSH2_MSG_IGNORE:
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ $payload = $this->get_binary_packet();
break;
case NET_SSH2_MSG_DEBUG:
- $this->_string_shift($payload, 2);
- if (strlen($payload) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($payload, 4)));
- $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length);
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ Strings::shift($payload, 2); // second byte is "always_display"
+ list($message) = Strings::unpackSSH2('s', $payload);
+ $this->errors[] = "SSH_MSG_DEBUG: $message";
+ $payload = $this->get_binary_packet();
break;
case NET_SSH2_MSG_UNIMPLEMENTED:
- return false;
+ break; // return payload
case NET_SSH2_MSG_KEXINIT:
// this is here for server initiated key re-exchanges after the initial key exchange
- if ($this->session_id !== false) {
- $this->send_kex_first = false;
- if (!$this->_key_exchange($payload)) {
- $this->bitmap = 0;
- return false;
+ if (!$this->keyExchangeInProgress && $this->session_id !== false) {
+ if (!$this->key_exchange($payload)) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ throw new ConnectionClosedException('Key exchange failed');
}
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ $payload = $this->get_binary_packet();
}
break;
case NET_SSH2_MSG_EXT_INFO:
- $this->_string_shift($payload, 1);
- if (strlen($payload) < 4) {
- return false;
- }
- $nr_extensions = unpack('Nlength', $this->_string_shift($payload, 4));
- for ($i = 0; $i < $nr_extensions['length']; $i++) {
- if (strlen($payload) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($payload, 4));
- $extension_name = $this->_string_shift($payload, $temp['length']);
+ Strings::shift($payload, 1);
+ list($nr_extensions) = Strings::unpackSSH2('N', $payload);
+ for ($i = 0; $i < $nr_extensions; $i++) {
+ list($extension_name, $extension_value) = Strings::unpackSSH2('ss', $payload);
if ($extension_name == 'server-sig-algs') {
- if (strlen($payload) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($payload, 4));
- $this->supported_private_key_algorithms = explode(',', $this->_string_shift($payload, $temp['length']));
+ $this->supported_private_key_algorithms = explode(',', $extension_value);
}
}
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ $payload = $this->get_binary_packet();
+ }
+
+ /*
+ Once a party has sent a SSH_MSG_KEXINIT message for key exchange or
+ re-exchange, until it has sent a SSH_MSG_NEWKEYS message (Section
+ 7.3), it MUST NOT send any messages other than:
+
+ o Transport layer generic messages (1 to 19) (but
+ SSH_MSG_SERVICE_REQUEST and SSH_MSG_SERVICE_ACCEPT MUST NOT be
+ sent);
+
+ o Algorithm negotiation messages (20 to 29) (but further
+ SSH_MSG_KEXINIT messages MUST NOT be sent);
+
+ o Specific key exchange method messages (30 to 49).
+
+ -- https://www.rfc-editor.org/rfc/rfc4253#section-7.1
+ */
+ if ($this->keyExchangeInProgress) {
+ return $payload;
}
// see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
- $this->_string_shift($payload, 1);
- if (strlen($payload) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($payload, 4)));
- $this->banner_message = $this->_string_shift($payload, $length);
- $payload = $this->_get_binary_packet();
+ Strings::shift($payload, 1);
+ list($this->banner_message) = Strings::unpackSSH2('s', $payload);
+ $payload = $this->get_binary_packet();
}
// only called when we've already logged in
if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
- if (is_bool($payload)) {
- return $payload;
- }
-
switch (ord($payload[0])) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
if (strlen($payload) == 31) {
- extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
+ $unpacked = unpack('cpacket_type/Nchannel/Nlength', $payload);
+ $packet_type = $unpacked['packet_type'];
+ $channel = $unpacked['channel'];
+ $length = $unpacked['length'];
if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
if (ord(substr($payload, 9 + $length))) { // want reply
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
}
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ $payload = $this->get_binary_packet();
}
}
break;
- case NET_SSH2_MSG_CHANNEL_DATA:
- case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
- case NET_SSH2_MSG_CHANNEL_CLOSE:
- case NET_SSH2_MSG_CHANNEL_EOF:
- if (!$skip_channel_filter && !empty($this->server_channels)) {
- $this->binary_packet_buffer = $payload;
- $this->_get_channel_packet(true);
- $payload = $this->_get_binary_packet();
- }
- break;
case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
- if (strlen($payload) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($payload, 4)));
- $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
-
- if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
- }
-
- $payload = $this->_get_binary_packet($skip_channel_filter);
+ Strings::shift($payload, 1);
+ list($request_name) = Strings::unpackSSH2('s', $payload);
+ $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name";
+ $this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE));
+ $payload = $this->get_binary_packet();
break;
case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
- $this->_string_shift($payload, 1);
- if (strlen($payload) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($payload, 4)));
- $data = $this->_string_shift($payload, $length);
- if (strlen($payload) < 4) {
- return false;
- }
- extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
+ Strings::shift($payload, 1);
+ list($data, $server_channel) = Strings::unpackSSH2('sN', $payload);
switch ($data) {
case 'auth-agent':
case 'auth-agent@openssh.com':
if (isset($this->agent)) {
$new_channel = self::CHANNEL_AGENT_FORWARD;
- if (strlen($payload) < 8) {
- return false;
- }
- extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
- extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
+ list(
+ $remote_window_size,
+ $remote_maximum_packet_size
+ ) = Strings::unpackSSH2('NN', $payload);
$this->packet_size_client_to_server[$new_channel] = $remote_window_size;
$this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
@@ -4024,39 +4033,23 @@ class SSH2
$this->server_channels[$new_channel] = $server_channel;
$this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->send_binary_packet($packet);
}
break;
default:
- $packet = pack(
- 'CN3a*Na*',
- NET_SSH2_MSG_REQUEST_FAILURE,
+ $packet = Strings::packSSH2(
+ 'CN2ss',
+ NET_SSH2_MSG_CHANNEL_OPEN_FAILURE,
$server_channel,
NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
- 0,
- '',
- 0,
- ''
+ '', // description
+ '' // language tag
);
-
- if (!$this->_send_binary_packet($packet)) {
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
- }
- }
- $payload = $this->_get_binary_packet($skip_channel_filter);
- break;
- case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
- $this->_string_shift($payload, 1);
- if (strlen($payload) < 8) {
- return false;
+ $this->send_binary_packet($packet);
}
- extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
- extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
- $this->window_size_client_to_server[$channel]+= $window_size;
- $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter);
+ $payload = $this->get_binary_packet();
+ break;
}
}
@@ -4068,9 +4061,8 @@ class SSH2
*
* Suppress stderr from output
*
- * @access public
*/
- function enableQuietMode()
+ public function enableQuietMode()
{
$this->quiet_mode = true;
}
@@ -4080,9 +4072,8 @@ class SSH2
*
* Show stderr in output
*
- * @access public
*/
- function disableQuietMode()
+ public function disableQuietMode()
{
$this->quiet_mode = false;
}
@@ -4092,10 +4083,9 @@ class SSH2
*
* @see self::enableQuietMode()
* @see self::disableQuietMode()
- * @access public
* @return bool
*/
- function isQuietModeEnabled()
+ public function isQuietModeEnabled()
{
return $this->quiet_mode;
}
@@ -4103,9 +4093,8 @@ class SSH2
/**
* Enable request-pty when using exec()
*
- * @access public
*/
- function enablePTY()
+ public function enablePTY()
{
$this->request_pty = true;
}
@@ -4113,13 +4102,11 @@ class SSH2
/**
* Disable request-pty when using exec()
*
- * @access public
*/
- function disablePTY()
+ public function disablePTY()
{
- if ($this->in_request_pty_exec) {
- $this->_close_channel(self::CHANNEL_EXEC);
- $this->in_request_pty_exec = false;
+ if ($this->isPTYOpen()) {
+ $this->close_channel(self::CHANNEL_EXEC);
}
$this->request_pty = false;
}
@@ -4129,10 +4116,9 @@ class SSH2
*
* @see self::enablePTY()
* @see self::disablePTY()
- * @access public
* @return bool
*/
- function isPTYEnabled()
+ public function isPTYEnabled()
{
return $this->request_pty;
}
@@ -4140,17 +4126,32 @@ class SSH2
/**
* Gets channel data
*
- * Returns the data as a string if it's available and false if not.
+ * Returns the data as a string. bool(true) is returned if:
*
- * @param int $client_channel
+ * - the server closes the channel
+ * - if the connection times out
+ * - if a window adjust packet is received on the given negated client channel
+ * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION
+ * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS
+ * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE
+ *
+ * bool(false) is returned if:
+ *
+ * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_FAILURE
+ *
+ * @param int $client_channel Specifies the channel to return data for, and data received
+ * on other channels is buffered. The respective negative value of a channel is
+ * also supported for the case that the caller is awaiting adjustment of the data
+ * window, and where data received on that respective channel is also buffered.
* @param bool $skip_extended
- * @return mixed|bool
- * @access private
+ * @return mixed
+ * @throws \RuntimeException on connection error
*/
- function _get_channel_packet($client_channel, $skip_extended = false)
+ protected function get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
- switch ($this->channel_status[$client_channel]) {
+ // in phpseclib 4.0 this should be changed to $this->channel_status[$client_channel] ?? null
+ switch (isset($this->channel_status[$client_channel]) ? $this->channel_status[$client_channel] : null) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
switch (ord($packet[0])) {
@@ -4167,67 +4168,47 @@ class SSH2
}
while (true) {
- if ($this->binary_packet_buffer !== false) {
- $response = $this->binary_packet_buffer;
- $this->binary_packet_buffer = false;
- } else {
- $response = $this->_get_binary_packet(true);
- if ($response === true && $this->is_timeout) {
- return true;
- }
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
- }
-
- if ($client_channel == -1 && $response === true) {
+ try {
+ $response = $this->get_binary_packet();
+ } catch (TimeoutException $e) {
return true;
}
- if (!strlen($response)) {
- return false;
- }
- extract(unpack('Ctype', $this->_string_shift($response, 1)));
-
- if (strlen($response) < 4) {
- return false;
- }
- if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- } else {
- extract(unpack('Nchannel', $this->_string_shift($response, 4)));
+ list($type) = Strings::unpackSSH2('C', $response);
+ if (strlen($response) >= 4) {
+ list($channel) = Strings::unpackSSH2('N', $response);
}
// will not be setup yet on incoming channel open request
if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
- $this->window_size_server_to_client[$channel]-= strlen($response);
+ $this->window_size_server_to_client[$channel] -= strlen($response);
// resize the window, if appropriate
if ($this->window_size_server_to_client[$channel] < 0) {
// PuTTY does something more analogous to the following:
//if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) {
$packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
- $this->window_size_server_to_client[$channel]+= $this->window_resize;
+ $this->send_binary_packet($packet);
+ $this->window_size_server_to_client[$channel] += $this->window_resize;
}
switch ($type) {
+ case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ list($window_size) = Strings::unpackSSH2('N', $response);
+ $this->window_size_client_to_server[$channel] += $window_size;
+ if ($channel == -$client_channel) {
+ return true;
+ }
+
+ continue 2;
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
/*
if ($client_channel == self::CHANNEL_EXEC) {
- $this->_send_channel_packet($client_channel, chr(0));
+ $this->send_channel_packet($client_channel, chr(0));
}
*/
// currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
- if (strlen($response) < 8) {
- return false;
- }
- extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
- $data = $this->_string_shift($response, $length);
- $this->stdErrorLog.= $data;
+ list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response);
+ $this->stdErrorLog .= $data;
if ($skip_extended || $this->quiet_mode) {
continue 2;
}
@@ -4238,43 +4219,36 @@ class SSH2
continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
- if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
+ if (!isset($this->channel_status[$channel])) {
continue 2;
}
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $value = $this->_string_shift($response, $length);
+ list($value) = Strings::unpackSSH2('s', $response);
switch ($value) {
case 'exit-signal':
- $this->_string_shift($response, 1);
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
- $this->_string_shift($response, 1);
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- if ($length) {
- $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
+ list(
+ , // FALSE
+ $signal_name,
+ , // core dumped
+ $error_message
+ ) = Strings::unpackSSH2('bsbs', $response);
+
+ $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name";
+ if (strlen($error_message)) {
+ $this->errors[count($this->errors) - 1] .= "\r\n$error_message";
}
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
+ if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
+ if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$channel]));
+ }
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
- $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
+ $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
+ }
continue 3;
case 'exit-status':
- if (strlen($response) < 5) {
- return false;
- }
- extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
- $this->exit_status = $exit_status;
+ list(, $this->exit_status) = Strings::unpackSSH2('CN', $response);
// "The client MAY ignore these messages."
// -- http://tools.ietf.org/html/rfc4254#section-6.10
@@ -4291,37 +4265,29 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_OPEN:
switch ($type) {
case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
- $this->server_channels[$channel] = $server_channel;
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
+ list(
+ $this->server_channels[$channel],
+ $window_size,
+ $this->packet_size_client_to_server[$channel]
+ ) = Strings::unpackSSH2('NNN', $response);
+
if ($window_size < 0) {
- $window_size&= 0x7FFFFFFF;
- $window_size+= 0x80000000;
+ $window_size &= 0x7FFFFFFF;
+ $window_size += 0x80000000;
}
$this->window_size_client_to_server[$channel] = $window_size;
- if (strlen($response) < 4) {
- return false;
- }
- $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
- $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
- $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
- $this->_on_channel_open();
+ $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended);
+ $this->on_channel_open();
return $result;
case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
- user_error('Unable to open channel');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new \RuntimeException('Unable to open channel');
default:
if ($client_channel == $channel) {
- user_error('Unexpected response to open request');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new \RuntimeException('Unexpected response to open request');
}
- return $this->_get_channel_packet($client_channel, $skip_extended);
+ return $this->get_channel_packet($client_channel, $skip_extended);
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -4331,19 +4297,18 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_FAILURE:
return false;
case NET_SSH2_MSG_CHANNEL_DATA:
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $data = $this->_string_shift($response, $length);
+ list($data) = Strings::unpackSSH2('s', $response);
$this->channel_buffers[$channel][] = chr($type) . $data;
- return $this->_get_channel_packet($client_channel, $skip_extended);
+ return $this->get_channel_packet($client_channel, $skip_extended);
default:
- user_error('Unable to fulfill channel request');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new \RuntimeException('Unable to fulfill channel request');
}
case NET_SSH2_MSG_CHANNEL_CLOSE:
- return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
+ if ($client_channel == $channel && $type == NET_SSH2_MSG_CHANNEL_CLOSE) {
+ return true;
+ }
+ return $this->get_channel_packet($client_channel, $skip_extended);
}
}
@@ -4357,19 +4322,15 @@ class SSH2
// this actually seems to make things twice as fast. more to the point, the message right after
// SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
// in OpenSSH it slows things down but only by a couple thousandths of a second.
- $this->_send_channel_packet($channel, chr(0));
+ $this->send_channel_packet($channel, chr(0));
}
*/
- if (strlen($response) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $data = $this->_string_shift($response, $length);
+ list($data) = Strings::unpackSSH2('s', $response);
if ($channel == self::CHANNEL_AGENT_FORWARD) {
- $agent_response = $this->agent->_forward_data($data);
+ $agent_response = $this->agent->forwardData($data);
if (!is_bool($agent_response)) {
- $this->_send_channel_packet($channel, $agent_response);
+ $this->send_channel_packet($channel, $agent_response);
}
break;
}
@@ -4382,22 +4343,24 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_CLOSE:
$this->curTimeout = 5;
- if ($this->bitmap & self::MASK_SHELL) {
- $this->bitmap&= ~self::MASK_SHELL;
- }
- if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
+ $this->close_channel_bitmap($channel);
+
+ if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
}
- $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
+ unset($this->channel_status[$channel]);
+ $this->channelCount--;
+
if ($client_channel == $channel) {
return true;
}
+ // fall-through
case NET_SSH2_MSG_CHANNEL_EOF:
break;
default:
- user_error("Error reading channel data ($type)");
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ throw new \RuntimeException("Error reading channel data ($type)");
}
}
}
@@ -4410,15 +4373,13 @@ class SSH2
* @param string $data
* @param string $logged
* @see self::_get_binary_packet()
- * @return bool
- * @access private
+ * @return void
*/
- function _send_binary_packet($data, $logged = null)
+ protected function send_binary_packet($data, $logged = null)
{
if (!is_resource($this->fsock) || feof($this->fsock)) {
- $this->bitmap = 0;
- user_error('Connection closed prematurely');
- return false;
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST);
+ throw new ConnectionClosedException('Connection closed prematurely');
}
if (!isset($logged)) {
@@ -4430,12 +4391,13 @@ class SSH2
if (!$this->isAuthenticated()) {
break;
}
+ // fall-through
case self::NET_SSH2_COMPRESSION_ZLIB:
if (!$this->regenerate_compression_context) {
$header = '';
} else {
$this->regenerate_compression_context = false;
- $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15));
+ $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]);
$header = "\x78\x9C";
}
if ($this->compress_context) {
@@ -4445,46 +4407,132 @@ class SSH2
// 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
$packet_length = strlen($data) + 9;
+ if ($this->encrypt && $this->encrypt->usesNonce()) {
+ $packet_length -= 4;
+ }
// round up to the nearest $this->encrypt_block_size
- $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
+ $packet_length += (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
// subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
$padding_length = $packet_length - strlen($data) - 5;
+ switch (true) {
+ case $this->encrypt && $this->encrypt->usesNonce():
+ case $this->hmac_create instanceof Hash && $this->hmac_create_etm:
+ $padding_length += 4;
+ $packet_length += 4;
+ }
+
$padding = Random::string($padding_length);
// we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
$packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
- $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
- $this->send_seq_no++;
+ $hmac = '';
+ if ($this->hmac_create instanceof Hash && !$this->hmac_create_etm) {
+ if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
+ $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
+ $hmac = $this->hmac_create->hash($packet);
+ } else {
+ $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
+ }
+ }
+
+ if ($this->encrypt) {
+ switch ($this->encryptName) {
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ $this->encrypt->setNonce(
+ $this->encryptFixedPart .
+ $this->encryptInvocationCounter
+ );
+ Strings::increment_str($this->encryptInvocationCounter);
+ $this->encrypt->setAAD($temp = ($packet & "\xFF\xFF\xFF\xFF"));
+ $packet = $temp . $this->encrypt->encrypt(substr($packet, 4));
+ break;
+ case 'chacha20-poly1305@openssh.com':
+ // This should be impossible, but we are checking anyway to narrow the type for Psalm.
+ if (!($this->encrypt instanceof ChaCha20)) {
+ throw new \LogicException('$this->encrypt is not a ' . ChaCha20::class);
+ }
- if ($this->encrypt !== false) {
- $packet = $this->encrypt->encrypt($packet);
+ $nonce = pack('N2', 0, $this->send_seq_no);
+
+ $this->encrypt->setNonce($nonce);
+ $this->lengthEncrypt->setNonce($nonce);
+
+ $length = $this->lengthEncrypt->encrypt($packet & "\xFF\xFF\xFF\xFF");
+
+ $this->encrypt->setCounter(0);
+ // this is the same approach that's implemented in Salsa20::createPoly1305Key()
+ // but we don't want to use the same AEAD construction that RFC8439 describes
+ // for ChaCha20-Poly1305 so we won't rely on it (see Salsa20::poly1305())
+ $this->encrypt->setPoly1305Key(
+ $this->encrypt->encrypt(str_repeat("\0", 32))
+ );
+ $this->encrypt->setAAD($length);
+ $this->encrypt->setCounter(1);
+ $packet = $length . $this->encrypt->encrypt(substr($packet, 4));
+ break;
+ default:
+ $packet = $this->hmac_create instanceof Hash && $this->hmac_create_etm ?
+ ($packet & "\xFF\xFF\xFF\xFF") . $this->encrypt->encrypt(substr($packet, 4)) :
+ $this->encrypt->encrypt($packet);
+ }
}
- $packet.= $hmac;
+ if ($this->hmac_create instanceof Hash && $this->hmac_create_etm) {
+ if (($this->hmac_create->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
+ $this->hmac_create->setNonce("\0\0\0\0" . pack('N', $this->send_seq_no));
+ $hmac = $this->hmac_create->hash($packet);
+ } else {
+ $hmac = $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet));
+ }
+ }
+
+ $this->send_seq_no++;
+
+ $packet .= $this->encrypt && $this->encrypt->usesNonce() ? $this->encrypt->getTag() : $hmac;
if (!$this->keyExchangeInProgress) {
- $this->bytesTransferredSinceLastKEX+= strlen($packet);
+ $this->bytesTransferredSinceLastKEX += strlen($packet);
}
-
+
$start = microtime(true);
- $result = strlen($packet) == @fputs($this->fsock, $packet);
+ $sent = @fputs($this->fsock, $packet);
$stop = microtime(true);
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
- $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
+ $message_number = isset(self::$message_numbers[ord($logged[0])]) ? self::$message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
$message_number = '-> ' . $message_number .
' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
- $this->_append_log($message_number, $logged);
- $this->last_packet = $current;
+ $this->append_log($message_number, $logged);
+ }
+ $this->last_packet = microtime(true);
+
+ if (strlen($packet) != $sent) {
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ $message = $sent === false ?
+ 'Unable to write ' . strlen($packet) . ' bytes' :
+ "Only $sent of " . strlen($packet) . " bytes were sent";
+ throw new \RuntimeException($message);
}
if ($this->bytesTransferredSinceLastKEX > $this->doKeyReexchangeAfterXBytes) {
- $this->_key_exchange();
+ $this->key_exchange();
}
+ }
- return $result;
+ /**
+ * Sends a keep-alive message, if keep-alive is enabled and interval is met
+ */
+ private function send_keep_alive()
+ {
+ if ($this->bitmap & self::MASK_CONNECTED) {
+ $elapsed = microtime(true) - $this->last_packet;
+ if ($this->keepAlive > 0 && $elapsed >= $this->keepAlive) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
+ }
+ }
}
/**
@@ -4494,28 +4542,61 @@ class SSH2
*
* @param string $message_number
* @param string $message
- * @access private
*/
- function _append_log($message_number, $message)
+ private function append_log($message_number, $message)
+ {
+ $this->append_log_helper(
+ NET_SSH2_LOGGING,
+ $message_number,
+ $message,
+ $this->message_number_log,
+ $this->message_log,
+ $this->log_size,
+ $this->realtime_log_file,
+ $this->realtime_log_wrap,
+ $this->realtime_log_size
+ );
+ }
+
+ /**
+ * Logs data packet helper
+ *
+ * @param int $constant
+ * @param string $message_number
+ * @param string $message
+ * @param array &$message_number_log
+ * @param array &$message_log
+ * @param int &$log_size
+ * @param resource &$realtime_log_file
+ * @param bool &$realtime_log_wrap
+ * @param int &$realtime_log_size
+ */
+ protected function append_log_helper($constant, $message_number, $message, array &$message_number_log, array &$message_log, &$log_size, &$realtime_log_file, &$realtime_log_wrap, &$realtime_log_size)
{
// remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
- if (strlen($message_number) > 2) {
- $this->_string_shift($message);
+ if (!in_array(substr($message_number, 0, 4), ['<- (', '-> (']) && strlen($message_number) > 2) {
+ Strings::shift($message);
}
- switch (NET_SSH2_LOGGING) {
+ switch ($constant) {
// useful for benchmarks
case self::LOG_SIMPLE:
- $this->message_number_log[] = $message_number;
+ $message_number_log[] = $message_number;
+ break;
+ case self::LOG_SIMPLE_REALTIME:
+ echo $message_number;
+ echo PHP_SAPI == 'cli' ? "\r\n" : '<br>';
+ @flush();
+ @ob_flush();
break;
// the most useful log for SSH2
case self::LOG_COMPLEX:
- $this->message_number_log[] = $message_number;
- $this->log_size+= strlen($message);
- $this->message_log[] = $message;
- while ($this->log_size > self::LOG_MAX_SIZE) {
- $this->log_size-= strlen(array_shift($this->message_log));
- array_shift($this->message_number_log);
+ $message_number_log[] = $message_number;
+ $log_size += strlen($message);
+ $message_log[] = $message;
+ while ($log_size > self::LOG_MAX_SIZE) {
+ $log_size -= strlen(array_shift($message_log));
+ array_shift($message_number_log);
}
break;
// dump the output out realtime; packets may be interspersed with non packets,
@@ -4530,39 +4611,39 @@ class SSH2
$start = '<pre>';
$stop = '</pre>';
}
- echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
+ echo $start . $this->format_log([$message], [$message_number]) . $stop;
@flush();
@ob_flush();
break;
- // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
+ // basically the same thing as self::LOG_REALTIME with the caveat that NET_SSH2_LOG_REALTIME_FILENAME
// needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
// the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
// at the beginning of the file
case self::LOG_REALTIME_FILE:
- if (!isset($this->realtime_log_file)) {
+ if (!isset($realtime_log_file)) {
// PHP doesn't seem to like using constants in fopen()
- $filename = self::LOG_REALTIME_FILENAME;
+ $filename = NET_SSH2_LOG_REALTIME_FILENAME;
$fp = fopen($filename, 'w');
- $this->realtime_log_file = $fp;
+ $realtime_log_file = $fp;
}
- if (!is_resource($this->realtime_log_file)) {
+ if (!is_resource($realtime_log_file)) {
break;
}
- $entry = $this->_format_log(array($message), array($message_number));
- if ($this->realtime_log_wrap) {
+ $entry = $this->format_log([$message], [$message_number]);
+ if ($realtime_log_wrap) {
$temp = "<<< START >>>\r\n";
- $entry.= $temp;
- fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
+ $entry .= $temp;
+ fseek($realtime_log_file, ftell($realtime_log_file) - strlen($temp));
}
- $this->realtime_log_size+= strlen($entry);
- if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
- fseek($this->realtime_log_file, 0);
- $this->realtime_log_size = strlen($entry);
- $this->realtime_log_wrap = true;
+ $realtime_log_size += strlen($entry);
+ if ($realtime_log_size > self::LOG_MAX_SIZE) {
+ fseek($realtime_log_file, 0);
+ $realtime_log_size = strlen($entry);
+ $realtime_log_wrap = true;
}
- fputs($this->realtime_log_file, $entry);
+ fputs($realtime_log_file, $entry);
break;
- case NET_SSH2_LOG_REALTIME_SIMPLE:
+ case self::LOG_REALTIME_SIMPLE:
echo $message_number;
echo PHP_SAPI == 'cli' ? "\r\n" : '<br>';
}
@@ -4575,17 +4656,28 @@ class SSH2
*
* @param int $client_channel
* @param string $data
- * @return bool
- * @access private
+ * @return void
*/
- function _send_channel_packet($client_channel, $data)
+ protected function send_channel_packet($client_channel, $data)
{
+ if (
+ isset($this->channel_buffers_write[$client_channel])
+ && strpos($data, $this->channel_buffers_write[$client_channel]) === 0
+ ) {
+ // if buffer holds identical initial data content, resume send from the unmatched data portion
+ $data = substr($data, strlen($this->channel_buffers_write[$client_channel]));
+ } else {
+ $this->channel_buffers_write[$client_channel] = '';
+ }
while (strlen($data)) {
if (!$this->window_size_client_to_server[$client_channel]) {
- $this->bitmap^= self::MASK_WINDOW_ADJUST;
// using an invalid channel will let the buffers be built up for the valid channels
- $this->_get_channel_packet(-1);
- $this->bitmap^= self::MASK_WINDOW_ADJUST;
+ $this->get_channel_packet(-$client_channel);
+ if ($this->isTimeout()) {
+ throw new TimeoutException('Timed out waiting for server');
+ } elseif (!$this->window_size_client_to_server[$client_channel]) {
+ throw new \RuntimeException('Data window was not adjusted');
+ }
}
/* The maximum amount of data allowed is determined by the maximum
@@ -4597,62 +4689,69 @@ class SSH2
$this->window_size_client_to_server[$client_channel]
);
- $temp = $this->_string_shift($data, $max_size);
- $packet = pack(
- 'CN2a*',
+ $temp = Strings::shift($data, $max_size);
+ $packet = Strings::packSSH2(
+ 'CNs',
NET_SSH2_MSG_CHANNEL_DATA,
$this->server_channels[$client_channel],
- strlen($temp),
$temp
);
- $this->window_size_client_to_server[$client_channel]-= strlen($temp);
- if (!$this->_send_binary_packet($packet)) {
- return false;
- }
+ $this->window_size_client_to_server[$client_channel] -= strlen($temp);
+ $this->send_binary_packet($packet);
+ $this->channel_buffers_write[$client_channel] .= $temp;
}
-
- return true;
+ unset($this->channel_buffers_write[$client_channel]);
}
/**
* Closes and flushes a channel
*
- * \phpseclib\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
+ * \phpseclib3\Net\SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
* and for SFTP channels are presumably closed when the client disconnects. This functions is intended
* for SCP more than anything.
*
* @param int $client_channel
* @param bool $want_reply
- * @return bool
- * @access private
+ * @return void
*/
- function _close_channel($client_channel, $want_reply = false)
+ private function close_channel($client_channel)
{
// see http://tools.ietf.org/html/rfc4254#section-5.3
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
-
- if (!$want_reply) {
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
+ if ($this->channel_status[$client_channel] != NET_SSH2_MSG_CHANNEL_EOF) {
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
}
+ $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
$this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
- $this->curTimeout = 5;
+ $this->channelCount--;
- while (!is_bool($this->_get_channel_packet($client_channel))) {
+ $this->curTimeout = 5;
+ while (!is_bool($this->get_channel_packet($client_channel))) {
}
- if ($this->is_timeout) {
- $this->disconnect();
- }
+ unset($this->channel_status[$client_channel]);
- if ($want_reply) {
- $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
- }
+ $this->close_channel_bitmap($client_channel);
+ }
- if ($this->bitmap & self::MASK_SHELL) {
- $this->bitmap&= ~self::MASK_SHELL;
+ /**
+ * Maintains execution state bitmap in response to channel closure
+ *
+ * @param int $client_channel The channel number to maintain closure status of
+ * @return void
+ */
+ private function close_channel_bitmap($client_channel)
+ {
+ switch ($client_channel) {
+ case self::CHANNEL_SHELL:
+ // Shell status has been maintained in the bitmap for backwards
+ // compatibility sake, but can be removed going forward
+ if ($this->bitmap & self::MASK_SHELL) {
+ $this->bitmap &= ~self::MASK_SHELL;
+ }
+ break;
}
}
@@ -4660,39 +4759,26 @@ class SSH2
* Disconnect
*
* @param int $reason
- * @return bool
- * @access private
+ * @return false
*/
- function _disconnect($reason)
+ protected function disconnect_helper($reason)
{
- if ($this->bitmap & self::MASK_CONNECTED) {
- $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
- $this->_send_binary_packet($data);
+ if ($this->bitmap & self::MASK_DISCONNECT) {
+ // Disregard subsequent disconnect requests
+ return false;
}
-
- $this->bitmap = 0;
- if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
- fclose($this->fsock);
+ $this->bitmap |= self::MASK_DISCONNECT;
+ if ($this->isConnected()) {
+ $data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', '');
+ try {
+ $this->send_binary_packet($data);
+ } catch (\Exception $e) {
+ }
}
- return false;
- }
+ $this->reset_connection();
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
- * @return string
- * @access private
- */
- function _string_shift(&$string, $index = 1)
- {
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
+ return false;
}
/**
@@ -4702,11 +4788,11 @@ class SSH2
* named constants from it, using the value as the name of the constant and the index as the value of the constant.
* If any of the constants that would be defined already exists, none of the constants will be defined.
*
- * @access private
+ * @param mixed[] ...$args
+ * @access protected
*/
- function _define_array()
+ protected static function define_array(...$args)
{
- $args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key => $value) {
if (!defined($value)) {
@@ -4723,10 +4809,9 @@ class SSH2
*
* Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
*
- * @access public
* @return array|false|string
*/
- function getLog()
+ public function getLog()
{
if (!defined('NET_SSH2_LOGGING')) {
return false;
@@ -4736,7 +4821,7 @@ class SSH2
case self::LOG_SIMPLE:
return $this->message_number_log;
case self::LOG_COMPLEX:
- $log = $this->_format_log($this->message_log, $this->message_number_log);
+ $log = $this->format_log($this->message_log, $this->message_number_log);
return PHP_SAPI == 'cli' ? $log : '<pre>' . $log . '</pre>';
default:
return false;
@@ -4748,62 +4833,51 @@ class SSH2
*
* @param array $message_log
* @param array $message_number_log
- * @access private
* @return string
*/
- function _format_log($message_log, $message_number_log)
+ protected function format_log(array $message_log, array $message_number_log)
{
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
- $output.= $message_number_log[$i] . "\r\n";
+ $output .= $message_number_log[$i];
$current_log = $message_log[$i];
$j = 0;
+ if (strlen($current_log)) {
+ $output .= "\r\n";
+ }
do {
if (strlen($current_log)) {
- $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
+ $output .= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
}
- $fragment = $this->_string_shift($current_log, $this->log_short_width);
- $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
+ $fragment = Strings::shift($current_log, $this->log_short_width);
+ $hex = substr(preg_replace_callback('#.#s', function ($matches) {
+ return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
+ }, $fragment), strlen($this->log_boundary));
// replace non ASCII printable characters with dots
// http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
- $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
+ $output .= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
$j++;
} while (strlen($current_log));
- $output.= "\r\n";
+ $output .= "\r\n";
}
return $output;
}
/**
- * Helper function for _format_log
- *
- * For use with preg_replace_callback()
- *
- * @param array $matches
- * @access private
- * @return string
- */
- function _format_log_helper($matches)
- {
- return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
- }
-
- /**
- * Helper function for agent->_on_channel_open()
+ * Helper function for agent->on_channel_open()
*
* Used when channels are created to inform agent
* of said channel opening. Must be called after
* channel open confirmation received
*
- * @access private
*/
- function _on_channel_open()
+ private function on_channel_open()
{
if (isset($this->agent)) {
- $this->agent->_on_channel_open($this);
+ $this->agent->registerChannelOpen($this);
}
}
@@ -4814,9 +4888,8 @@ class SSH2
* @param array $array1
* @param array $array2
* @return mixed False if intersection is empty, else intersected value.
- * @access private
*/
- function _array_intersect_first($array1, $array2)
+ private static function array_intersect_first(array $array1, array $array2)
{
foreach ($array1 as $value) {
if (in_array($value, $array2)) {
@@ -4832,9 +4905,8 @@ class SSH2
* If you are looking for messages from the SFTP layer, please see SFTP::getSFTPErrors()
*
* @return string[]
- * @access public
*/
- function getErrors()
+ public function getErrors()
{
return $this->errors;
}
@@ -4845,9 +4917,8 @@ class SSH2
* If you are looking for messages from the SFTP layer, please see SFTP::getLastSFTPError()
*
* @return string
- * @access public
*/
- function getLastError()
+ public function getLastError()
{
$count = count($this->errors);
@@ -4859,203 +4930,74 @@ class SSH2
/**
* Return the server identification.
*
- * @return string
- * @access public
+ * @return string|false
*/
- function getServerIdentification()
+ public function getServerIdentification()
{
- $this->_connect();
+ $this->connect();
return $this->server_identifier;
}
/**
- * Return a list of the key exchange algorithms the server supports.
- *
- * @return array
- * @access public
- */
- function getKexAlgorithms()
- {
- $this->_connect();
-
- return $this->kex_algorithms;
- }
-
- /**
- * Return a list of the host key (public key) algorithms the server supports.
- *
- * @return array
- * @access public
- */
- function getServerHostKeyAlgorithms()
- {
- $this->_connect();
-
- return $this->server_host_key_algorithms;
- }
-
- /**
- * Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff from the client.
- *
- * @return array
- * @access public
- */
- function getEncryptionAlgorithmsClient2Server()
- {
- $this->_connect();
-
- return $this->encryption_algorithms_client_to_server;
- }
-
- /**
- * Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to the client.
- *
- * @return array
- * @access public
- */
- function getEncryptionAlgorithmsServer2Client()
- {
- $this->_connect();
-
- return $this->encryption_algorithms_server_to_client;
- }
-
- /**
- * Return a list of the MAC algorithms the server supports, when receiving stuff from the client.
- *
- * @return array
- * @access public
- */
- function getMACAlgorithmsClient2Server()
- {
- $this->_connect();
-
- return $this->mac_algorithms_client_to_server;
- }
-
- /**
- * Return a list of the MAC algorithms the server supports, when sending stuff to the client.
- *
- * @return array
- * @access public
- */
- function getMACAlgorithmsServer2Client()
- {
- $this->_connect();
-
- return $this->mac_algorithms_server_to_client;
- }
-
- /**
- * Return a list of the compression algorithms the server supports, when receiving stuff from the client.
- *
- * @return array
- * @access public
- */
- function getCompressionAlgorithmsClient2Server()
- {
- $this->_connect();
-
- return $this->compression_algorithms_client_to_server;
- }
-
- /**
- * Return a list of the compression algorithms the server supports, when sending stuff to the client.
- *
- * @return array
- * @access public
- */
- function getCompressionAlgorithmsServer2Client()
- {
- $this->_connect();
-
- return $this->compression_algorithms_server_to_client;
- }
-
- /**
- * Return a list of the languages the server supports, when sending stuff to the client.
- *
- * @return array
- * @access public
- */
- function getLanguagesServer2Client()
- {
- $this->_connect();
-
- return $this->languages_server_to_client;
- }
-
- /**
- * Return a list of the languages the server supports, when receiving stuff from the client.
- *
- * @return array
- * @access public
- */
- function getLanguagesClient2Server()
- {
- $this->_connect();
-
- return $this->languages_client_to_server;
- }
-
- /**
* Returns a list of algorithms the server supports
*
* @return array
- * @access public
*/
- function getServerAlgorithms()
+ public function getServerAlgorithms()
{
- $this->_connect();
+ $this->connect();
- return array(
+ return [
'kex' => $this->kex_algorithms,
'hostkey' => $this->server_host_key_algorithms,
- 'client_to_server' => array(
+ 'client_to_server' => [
'crypt' => $this->encryption_algorithms_client_to_server,
'mac' => $this->mac_algorithms_client_to_server,
'comp' => $this->compression_algorithms_client_to_server,
'lang' => $this->languages_client_to_server
- ),
- 'server_to_client' => array(
+ ],
+ 'server_to_client' => [
'crypt' => $this->encryption_algorithms_server_to_client,
'mac' => $this->mac_algorithms_server_to_client,
'comp' => $this->compression_algorithms_server_to_client,
'lang' => $this->languages_server_to_client
- )
- );
+ ]
+ ];
}
/**
* Returns a list of KEX algorithms that phpseclib supports
*
* @return array
- * @access public
*/
- function getSupportedKEXAlgorithms()
+ public static function getSupportedKEXAlgorithms()
{
- $kex_algorithms = array(
+ $kex_algorithms = [
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
// Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
// libssh repository for more information.
+ 'curve25519-sha256',
'curve25519-sha256@libssh.org',
+ 'ecdh-sha2-nistp256', // RFC 5656
+ 'ecdh-sha2-nistp384', // RFC 5656
+ 'ecdh-sha2-nistp521', // RFC 5656
+
'diffie-hellman-group-exchange-sha256',// RFC 4419
'diffie-hellman-group-exchange-sha1', // RFC 4419
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
// groups.
+ 'diffie-hellman-group14-sha256',
'diffie-hellman-group14-sha1', // REQUIRED
- 'diffie-hellman-group1-sha1', // REQUIRED
- );
+ 'diffie-hellman-group15-sha512',
+ 'diffie-hellman-group16-sha512',
+ 'diffie-hellman-group17-sha512',
+ 'diffie-hellman-group18-sha512',
- if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
- $kex_algorithms = array_diff(
- $kex_algorithms,
- array('curve25519-sha256@libssh.org')
- );
- }
+ 'diffie-hellman-group1-sha1', // REQUIRED
+ ];
return $kex_algorithms;
}
@@ -5064,27 +5006,33 @@ class SSH2
* Returns a list of host key algorithms that phpseclib supports
*
* @return array
- * @access public
*/
- function getSupportedHostKeyAlgorithms()
+ public static function getSupportedHostKeyAlgorithms()
{
- return array(
+ return [
+ 'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02
+ 'ecdsa-sha2-nistp256', // RFC 5656
+ 'ecdsa-sha2-nistp384', // RFC 5656
+ 'ecdsa-sha2-nistp521', // RFC 5656
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
- );
+ ];
}
/**
* Returns a list of symmetric key algorithms that phpseclib supports
*
* @return array
- * @access public
*/
- function getSupportedEncryptionAlgorithms()
+ public static function getSupportedEncryptionAlgorithms()
{
- $algos = array(
+ $algos = [
+ // from <https://tools.ietf.org/html/rfc5647>:
+ 'aes128-gcm@openssh.com',
+ 'aes256-gcm@openssh.com',
+
// from <http://tools.ietf.org/html/rfc4345#section-4>:
'arcfour256',
'arcfour128',
@@ -5096,6 +5044,16 @@ class SSH2
'aes192-ctr', // RECOMMENDED AES with 192-bit key
'aes256-ctr', // RECOMMENDED AES with 256-bit key
+ // from <https://github.com/openssh/openssh-portable/blob/001aa55/PROTOCOL.chacha20poly1305>:
+ // one of the big benefits of chacha20-poly1305 is speed. the problem is...
+ // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even
+ // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20
+ // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down.
+ // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC
+ // (which is always gonna be super fast to compute thanks to the hash extension, which
+ // "is bundled and compiled into PHP by default")
+ 'chacha20-poly1305@openssh.com',
+
'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
@@ -5119,34 +5077,66 @@ class SSH2
'3des-cbc', // REQUIRED three-key 3DES in CBC mode
//'none' // OPTIONAL no encryption; NOT RECOMMENDED
- );
+ ];
- if ($this->crypto_engine) {
- $engines = array($this->crypto_engine);
+ if (self::$crypto_engine) {
+ $engines = [self::$crypto_engine];
} else {
- $engines = array(
- Base::ENGINE_OPENSSL,
- Base::ENGINE_MCRYPT,
- Base::ENGINE_INTERNAL
- );
+ $engines = [
+ 'libsodium',
+ 'OpenSSL (GCM)',
+ 'OpenSSL',
+ 'mcrypt',
+ 'Eval',
+ 'PHP'
+ ];
}
- $ciphers = array();
+ $ciphers = [];
+
foreach ($engines as $engine) {
foreach ($algos as $algo) {
- $obj = $this->_encryption_algorithm_to_crypt_instance($algo);
+ $obj = self::encryption_algorithm_to_crypt_instance($algo);
if ($obj instanceof Rijndael) {
$obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
}
switch ($algo) {
+ // Eval engines do not exist for ChaCha20 or RC4 because they would not benefit from one.
+ // to benefit from an Eval engine they'd need to loop a variable amount of times, they'd
+ // need to do table lookups (eg. sbox subsitutions). ChaCha20 doesn't do either because
+ // it's a so-called ARX cipher, meaning that the only operations it does are add (A), rotate (R)
+ // and XOR (X). RC4 does do table lookups but being a stream cipher it works differently than
+ // block ciphers. with RC4 you XOR the plaintext against a keystream and the keystream changes
+ // as you encrypt stuff. the only table lookups are made against this keystream and thus table
+ // lookups are kinda unavoidable. with AES and DES, however, the table lookups that are done
+ // are done against substitution boxes (sboxes), which are invariant.
+
+ // OpenSSL can't be used as an engine, either, because OpenSSL doesn't support continuous buffers
+ // as SSH2 uses and altho you can emulate a continuous buffer with block ciphers you can't do so
+ // with stream ciphers. As for ChaCha20... for the ChaCha20 part OpenSSL could prob be used but
+ // the big slow down isn't with ChaCha20 - it's with Poly1305. SSH constructs the key for that
+ // differently than how OpenSSL does it (OpenSSL does it as the RFC describes, SSH doesn't).
+
+ // libsodium can't be used because it doesn't support RC4 and it doesn't construct the Poly1305
+ // keys in the same way that SSH does
+
+ // mcrypt could prob be used for RC4 but mcrypt hasn't been included in PHP core for yearss
+ case 'chacha20-poly1305@openssh.com':
case 'arcfour128':
case 'arcfour256':
- if ($engine != Base::ENGINE_INTERNAL) {
+ if ($engine != 'PHP') {
+ continue 2;
+ }
+ break;
+ case 'aes128-gcm@openssh.com':
+ case 'aes256-gcm@openssh.com':
+ if ($engine == 'OpenSSL') {
continue 2;
}
+ $obj->setNonce('dummydummydu');
}
if ($obj->isValidEngine($engine)) {
- $algos = array_diff($algos, array($algo));
+ $algos = array_diff($algos, [$algo]);
$ciphers[] = $algo;
}
}
@@ -5159,31 +5149,42 @@ class SSH2
* Returns a list of MAC algorithms that phpseclib supports
*
* @return array
- * @access public
*/
- function getSupportedMACAlgorithms()
+ public static function getSupportedMACAlgorithms()
{
- return array(
+ return [
+ 'hmac-sha2-256-etm@openssh.com',
+ 'hmac-sha2-512-etm@openssh.com',
+ 'hmac-sha1-etm@openssh.com',
+
// from <http://www.ietf.org/rfc/rfc6668.txt>:
'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
+ 'hmac-sha2-512',// OPTIONAL HMAC-SHA512 (digest length = key length = 64)
'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
+
+ 'umac-64-etm@openssh.com',
+ 'umac-128-etm@openssh.com',
+
+ // from <https://tools.ietf.org/html/draft-miller-secsh-umac-01>:
+ 'umac-64@openssh.com',
+ 'umac-128@openssh.com',
+
//'none' // OPTIONAL no MAC; NOT RECOMMENDED
- );
+ ];
}
/**
* Returns a list of compression algorithms that phpseclib supports
*
* @return array
- * @access public
*/
- function getSupportedCompressionAlgorithms()
+ public static function getSupportedCompressionAlgorithms()
{
- $algos = array('none'); // REQUIRED no compression
+ $algos = ['none']; // REQUIRED no compression
if (function_exists('deflate_init')) {
$algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
$algos[] = 'zlib';
@@ -5197,32 +5198,49 @@ class SSH2
* Uses the same format as https://www.php.net/ssh2-methods-negotiated
*
* @return array
- * @access public
*/
- function getAlgorithmsNegotiated()
+ public function getAlgorithmsNegotiated()
{
- $this->_connect();
+ $this->connect();
- $compression_map = array(
+ $compression_map = [
self::NET_SSH2_COMPRESSION_NONE => 'none',
self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
- );
+ ];
- return array(
+ return [
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
- 'client_to_server' => array(
+ 'client_to_server' => [
'crypt' => $this->encryptName,
'mac' => $this->hmac_create_name,
'comp' => $compression_map[$this->compress],
- ),
- 'server_to_client' => array(
+ ],
+ 'server_to_client' => [
'crypt' => $this->decryptName,
'mac' => $this->hmac_check_name,
'comp' => $compression_map[$this->decompress],
- )
- );
+ ]
+ ];
+ }
+
+ /**
+ * Force multiple channels (even if phpseclib has decided to disable them)
+ */
+ public function forceMultipleChannels()
+ {
+ $this->errorOnMultipleChannels = false;
+ }
+
+ /**
+ * Allows you to set the terminal
+ *
+ * @param string $term
+ */
+ public function setTerminal($term)
+ {
+ $this->term = $term;
}
/**
@@ -5230,11 +5248,10 @@ class SSH2
* <https://www.php.net/manual/en/function.ssh2-connect.php>
*
* @param array $methods
- * @access public
*/
- function setPreferredAlgorithms($methods)
+ public function setPreferredAlgorithms(array $methods)
{
- $keys = array('client_to_server', 'server_to_client');
+ $keys = ['client_to_server', 'server_to_client'];
if (isset($methods['kex']) && is_string($methods['kex'])) {
$methods['kex'] = explode(',', $methods['kex']);
@@ -5299,7 +5316,7 @@ class SSH2
}
}
- $keys = array(
+ $keys = [
'kex',
'hostkey',
'client_to_server/crypt',
@@ -5308,7 +5325,7 @@ class SSH2
'server_to_client/crypt',
'server_to_client/comp',
'server_to_client/mac',
- );
+ ];
foreach ($keys as $key) {
$p = $preferred;
$m = $methods;
@@ -5327,8 +5344,7 @@ class SSH2
$msg = count($diff) == 1 ?
' is not a supported algorithm' :
' are not supported algorithms';
- user_error(implode(', ', $diff) . $msg);
- return false;
+ throw new UnsupportedAlgorithmException(implode(', ', $diff) . $msg);
}
}
@@ -5342,9 +5358,8 @@ class SSH2
* authentication may be relevant for getting legal protection."
*
* @return string
- * @access public
*/
- function getBannerMessage()
+ public function getBannerMessage()
{
return $this->banner_message;
}
@@ -5355,170 +5370,64 @@ class SSH2
* Caching this the first time you connect to a server and checking the result on subsequent connections
* is recommended. Returns false if the server signature is not signed correctly with the public host key.
*
- * @return mixed
- * @access public
+ * @return string|false
+ * @throws \RuntimeException on badly formatted keys
+ * @throws NoSupportedAlgorithmsException when the key isn't in a supported format
*/
- function getServerPublicHostKey()
+ public function getServerPublicHostKey()
{
if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
- if (!$this->_connect()) {
- return false;
- }
+ $this->connect();
}
$signature = $this->signature;
- $server_public_host_key = $this->server_public_host_key;
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
- $this->_string_shift($server_public_host_key, $length);
+ $server_public_host_key = base64_encode($this->server_public_host_key);
if ($this->signature_validated) {
return $this->bitmap ?
- $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
+ $this->signature_format . ' ' . $server_public_host_key :
false;
}
$this->signature_validated = true;
switch ($this->signature_format) {
- case 'ssh-dss':
- $zero = new BigInteger();
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
-
- /* The value for 'dss_signature_blob' is encoded as a string containing
- r, followed by s (which are 160-bit integers, without lengths or
- padding, unsigned, and in network byte order). */
- $temp = unpack('Nlength', $this->_string_shift($signature, 4));
- if ($temp['length'] != 40) {
- user_error('Invalid signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
- }
-
- $r = new BigInteger($this->_string_shift($signature, 20), 256);
- $s = new BigInteger($this->_string_shift($signature, 20), 256);
-
- switch (true) {
- case $r->equals($zero):
- case $r->compare($q) >= 0:
- case $s->equals($zero):
- case $s->compare($q) >= 0:
- user_error('Invalid signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
- }
-
- $w = $s->modInverse($q);
-
- $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16));
- list(, $u1) = $u1->divide($q);
-
- $u2 = $w->multiply($r);
- list(, $u2) = $u2->divide($q);
-
- $g = $g->modPow($u1, $p);
- $y = $y->modPow($u2, $p);
-
- $v = $g->multiply($y);
- list(, $v) = $v->divide($p);
- list(, $v) = $v->divide($q);
-
- if (!$v->equals($r)) {
- user_error('Bad server signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
- }
-
- break;
- case 'ssh-rsa':
- case 'rsa-sha2-256':
- case 'rsa-sha2-512':
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
-
- if (strlen($server_public_host_key) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
- $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
- $n = new BigInteger($rawN, -256);
- $nLength = strlen(ltrim($rawN, "\0"));
-
- /*
- if (strlen($signature) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($signature, 4));
- $signature = $this->_string_shift($signature, $temp['length']);
-
- $rsa = new RSA();
+ case 'ssh-ed25519':
+ case 'ecdsa-sha2-nistp256':
+ case 'ecdsa-sha2-nistp384':
+ case 'ecdsa-sha2-nistp521':
+ $key = EC::loadFormat('OpenSSH', $server_public_host_key)
+ ->withSignatureFormat('SSH2');
switch ($this->signature_format) {
- case 'rsa-sha2-512':
+ case 'ssh-ed25519':
$hash = 'sha512';
break;
- case 'rsa-sha2-256':
+ case 'ecdsa-sha2-nistp256':
$hash = 'sha256';
break;
- //case 'ssh-rsa':
- default:
- $hash = 'sha1';
- }
- $rsa->setHash($hash);
- $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
- $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW);
-
- if (!$rsa->verify($this->exchange_hash, $signature)) {
- user_error('Bad server signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
- }
- */
-
- if (strlen($signature) < 4) {
- return false;
- }
- $temp = unpack('Nlength', $this->_string_shift($signature, 4));
- $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256);
-
- // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
- // following URL:
- // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
-
- // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
-
- if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) {
- user_error('Invalid signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+ case 'ecdsa-sha2-nistp384':
+ $hash = 'sha384';
+ break;
+ case 'ecdsa-sha2-nistp521':
+ $hash = 'sha512';
}
+ $key = $key->withHash($hash);
+ break;
+ case 'ssh-dss':
+ $key = DSA::loadFormat('OpenSSH', $server_public_host_key)
+ ->withSignatureFormat('SSH2')
+ ->withHash('sha1');
+ break;
+ case 'ssh-rsa':
+ case 'rsa-sha2-256':
+ case 'rsa-sha2-512':
+ // could be ssh-rsa, rsa-sha2-256, rsa-sha2-512
+ // we don't check here because we already checked in key_exchange
+ // some signatures have the type embedded within the message and some don't
+ list(, $signature) = Strings::unpackSSH2('ss', $signature);
- $s = $s->modPow($e, $n);
- $s = $s->toBytes();
-
+ $key = RSA::loadFormat('OpenSSH', $server_public_host_key)
+ ->withPadding(RSA::SIGNATURE_PKCS1);
switch ($this->signature_format) {
case 'rsa-sha2-512':
$hash = 'sha512';
@@ -5530,41 +5439,26 @@ class SSH2
default:
$hash = 'sha1';
}
- $hashObj = new Hash($hash);
- switch ($this->signature_format) {
- case 'rsa-sha2-512':
- $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash));
- break;
- case 'rsa-sha2-256':
- $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash));
- break;
- //case 'ssh-rsa':
- default:
- $hash = 'sha1';
- $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash));
- }
- $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
-
- if ($s != $h) {
- user_error('Bad server signature');
- return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
- }
+ $key = $key->withHash($hash);
break;
default:
- user_error('Unsupported signature format');
- return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ throw new NoSupportedAlgorithmsException('Unsupported signature format');
}
- return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
+ if (!$key->verify($this->exchange_hash, $signature)) {
+ return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
+ };
+
+ return $this->signature_format . ' ' . $server_public_host_key;
}
/**
* Returns the exit status of an SSH command or false.
*
* @return false|int
- * @access public
*/
- function getExitStatus()
+ public function getExitStatus()
{
if (is_null($this->exit_status)) {
return false;
@@ -5576,9 +5470,8 @@ class SSH2
* Returns the number of columns for the terminal window size.
*
* @return int
- * @access public
*/
- function getWindowColumns()
+ public function getWindowColumns()
{
return $this->windowColumns;
}
@@ -5587,9 +5480,8 @@ class SSH2
* Returns the number of rows for the terminal window size.
*
* @return int
- * @access public
*/
- function getWindowRows()
+ public function getWindowRows()
{
return $this->windowRows;
}
@@ -5598,9 +5490,8 @@ class SSH2
* Sets the number of columns for the terminal window size.
*
* @param int $value
- * @access public
*/
- function setWindowColumns($value)
+ public function setWindowColumns($value)
{
$this->windowColumns = $value;
}
@@ -5609,9 +5500,8 @@ class SSH2
* Sets the number of rows for the terminal window size.
*
* @param int $value
- * @access public
*/
- function setWindowRows($value)
+ public function setWindowRows($value)
{
$this->windowRows = $value;
}
@@ -5621,22 +5511,79 @@ class SSH2
*
* @param int $columns
* @param int $rows
- * @access public
*/
- function setWindowSize($columns = 80, $rows = 24)
+ public function setWindowSize($columns = 80, $rows = 24)
{
$this->windowColumns = $columns;
$this->windowRows = $rows;
}
/**
+ * To String Magic Method
+ *
+ * @return string
+ */
+ #[\ReturnTypeWillChange]
+ public function __toString()
+ {
+ return $this->getResourceId();
+ }
+
+ /**
+ * Get Resource ID
+ *
+ * We use {} because that symbols should not be in URL according to
+ * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}.
+ * It will safe us from any conflicts, because otherwise regexp will
+ * match all alphanumeric domains.
+ *
+ * @return string
+ */
+ public function getResourceId()
+ {
+ return '{' . spl_object_hash($this) . '}';
+ }
+
+ /**
+ * Return existing connection
+ *
+ * @param string $id
+ *
+ * @return bool|SSH2 will return false if no such connection
+ */
+ public static function getConnectionByResourceId($id)
+ {
+ if (isset(self::$connections[$id])) {
+ return self::$connections[$id] instanceof \WeakReference ? self::$connections[$id]->get() : self::$connections[$id];
+ }
+ return false;
+ }
+
+ /**
+ * Return all excising connections
+ *
+ * @return array<string, SSH2>
+ */
+ public static function getConnections()
+ {
+ if (!class_exists('WeakReference')) {
+ /** @var array<string, SSH2> */
+ return self::$connections;
+ }
+ $temp = [];
+ foreach (self::$connections as $key => $ref) {
+ $temp[$key] = $ref->get();
+ }
+ return $temp;
+ }
+
+ /*
* Update packet types in log history
*
* @param string $old
* @param string $new
- * @access private
*/
- function _updateLogHistory($old, $new)
+ private function updateLogHistory($old, $new)
{
if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
@@ -5653,7 +5600,7 @@ class SSH2
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @return array|null
*/
- function getAuthMethodsToContinue()
+ public function getAuthMethodsToContinue()
{
return $this->auth_methods_to_continue;
}
@@ -5661,7 +5608,7 @@ class SSH2
/**
* Enables "smart" multi-factor authentication (MFA)
*/
- function enableSmartMFA()
+ public function enableSmartMFA()
{
$this->smartMFA = true;
}
@@ -5669,15 +5616,17 @@ class SSH2
/**
* Disables "smart" multi-factor authentication (MFA)
*/
- function disableSmartMFA()
+ public function disableSmartMFA()
{
$this->smartMFA = false;
}
/**
* How many bytes until the next key re-exchange?
+ *
+ * @param int $bytes
*/
- function bytesUntilKeyReexchange($bytes)
+ public function bytesUntilKeyReexchange($bytes)
{
$this->doKeyReexchangeAfterXBytes = $bytes;
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
index ec1d9773e..376d77bfe 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
@@ -3,6 +3,8 @@
/**
* Pure-PHP ssh-agent client.
*
+ * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent}
+ *
* PHP version 5
*
* Here are some examples of how to use this library:
@@ -10,9 +12,9 @@
* <?php
* include 'vendor/autoload.php';
*
- * $agent = new \phpseclib\System\SSH\Agent();
+ * $agent = new \phpseclib3\System\SSH\Agent();
*
- * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
+ * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $agent)) {
* exit('Login Failed');
* }
@@ -22,36 +24,35 @@
* ?>
* </code>
*
- * @category System
- * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2014 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
- * @internal See http://api.libssh.org/rfc/PROTOCOL.agent
*/
-namespace phpseclib\System\SSH;
+namespace phpseclib3\System\SSH;
-use phpseclib\Crypt\RSA;
-use phpseclib\System\SSH\Agent\Identity;
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Exception\BadConfigurationException;
+use phpseclib3\Net\SSH2;
+use phpseclib3\System\SSH\Agent\Identity;
/**
* Pure-PHP ssh-agent client identity factory
*
- * requestIdentities() method pumps out \phpseclib\System\SSH\Agent\Identity objects
+ * requestIdentities() method pumps out \phpseclib3\System\SSH\Agent\Identity objects
*
- * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
- * @access public
*/
class Agent
{
- /**#@+
- * Message numbers
- *
- * @access private
- */
+ use Common\Traits\ReadBytes;
+
+ // Message numbers
+
// to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
const SSH_AGENTC_REQUEST_IDENTITIES = 11;
// this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
@@ -60,20 +61,15 @@ class Agent
const SSH_AGENTC_SIGN_REQUEST = 13;
// the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
const SSH_AGENT_SIGN_RESPONSE = 14;
- /**#@-*/
- /**@+
- * Agent forwarding status
- *
- * @access private
- */
+ // Agent forwarding status
+
// no forwarding requested and not active
const FORWARD_NONE = 0;
// request agent forwarding when opportune
const FORWARD_REQUEST = 1;
// forwarding has been request and is active
const FORWARD_ACTIVE = 2;
- /**#@-*/
/**
* Unused
@@ -84,40 +80,42 @@ class Agent
* Socket Resource
*
* @var resource
- * @access private
*/
- var $fsock;
+ private $fsock;
/**
* Agent forwarding status
*
- * @access private
+ * @var int
*/
- var $forward_status = self::FORWARD_NONE;
+ private $forward_status = self::FORWARD_NONE;
/**
* Buffer for accumulating forwarded authentication
* agent data arriving on SSH data channel destined
* for agent unix socket
*
- * @access private
+ * @var string
*/
- var $socket_buffer = '';
+ private $socket_buffer = '';
/**
* Tracking the number of bytes we are expecting
* to arrive for the agent socket on the SSH data
* channel
+ *
+ * @var int
*/
- var $expected_bytes = 0;
+ private $expected_bytes = 0;
/**
* Default Constructor
*
- * @return \phpseclib\System\SSH\Agent
- * @access public
+ * @return Agent
+ * @throws BadConfigurationException if SSH_AUTH_SOCK cannot be found
+ * @throws \RuntimeException on connection errors
*/
- function __construct($address = null)
+ public function __construct($address = null)
{
if (!$address) {
switch (true) {
@@ -128,24 +126,23 @@ class Agent
$address = $_ENV['SSH_AUTH_SOCK'];
break;
default:
- user_error('SSH_AUTH_SOCK not found');
- return false;
+ throw new BadConfigurationException('SSH_AUTH_SOCK not found');
}
}
if (in_array('unix', stream_get_transports())) {
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
if (!$this->fsock) {
- user_error("Unable to connect to ssh-agent (Error $errno: $errstr)");
+ throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)");
}
} else {
if (substr($address, 0, 9) != '\\\\.\\pipe\\' || strpos(substr($address, 9), '\\') !== false) {
- user_error('Address is not formatted as a named pipe should be');
- } else {
- $this->fsock = fopen($address, 'r+b');
- if (!$this->fsock) {
- user_error('Unable to open address');
- }
+ throw new \RuntimeException('Address is not formatted as a named pipe should be');
+ }
+
+ $this->fsock = fopen($address, 'r+b');
+ if (!$this->fsock) {
+ throw new \RuntimeException('Unable to open address');
}
}
}
@@ -154,85 +151,50 @@ class Agent
* Request Identities
*
* See "2.5.2 Requesting a list of protocol 2 keys"
- * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects
+ * Returns an array containing zero or more \phpseclib3\System\SSH\Agent\Identity objects
*
* @return array
- * @access public
+ * @throws \RuntimeException on receipt of unexpected packets
*/
- function requestIdentities()
+ public function requestIdentities()
{
if (!$this->fsock) {
- return array();
+ return [];
}
$packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) {
- user_error('Connection closed while requesting identities');
- return array();
+ throw new \RuntimeException('Connection closed while requesting identities');
}
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $length = current(unpack('N', $temp));
- $type = ord(fread($this->fsock, 1));
+ $length = current(unpack('N', $this->readBytes(4)));
+ $packet = $this->readBytes($length);
+
+ list($type, $keyCount) = Strings::unpackSSH2('CN', $packet);
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
- user_error('Unable to request identities');
- return array();
+ throw new \RuntimeException('Unable to request identities');
}
- $identities = array();
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $keyCount = current(unpack('N', $temp));
+ $identities = [];
for ($i = 0; $i < $keyCount; $i++) {
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $length = current(unpack('N', $temp));
- $key_blob = fread($this->fsock, $length);
- if (strlen($key_blob) != $length) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $key_str = 'ssh-rsa ' . base64_encode($key_blob);
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $length = current(unpack('N', $temp));
- if ($length) {
- $temp = fread($this->fsock, $length);
- if (strlen($temp) != $length) {
- user_error('Connection closed while requesting identities');
- return array();
- }
- $key_str.= ' ' . $temp;
- }
- $length = current(unpack('N', substr($key_blob, 0, 4)));
- $key_type = substr($key_blob, 4, $length);
+ list($key_blob, $comment) = Strings::unpackSSH2('ss', $packet);
+ $temp = $key_blob;
+ list($key_type) = Strings::unpackSSH2('s', $temp);
switch ($key_type) {
case 'ssh-rsa':
- $key = new RSA();
- $key->loadKey($key_str);
- break;
case 'ssh-dss':
- // not currently supported
- break;
+ case 'ssh-ed25519':
+ case 'ecdsa-sha2-nistp256':
+ case 'ecdsa-sha2-nistp384':
+ case 'ecdsa-sha2-nistp521':
+ $key = PublicKeyLoader::load($key_type . ' ' . base64_encode($key_blob));
}
// resources are passed by reference by default
if (isset($key)) {
- $identity = new Identity($this->fsock);
- $identity->setPublicKey($key);
- $identity->setPublicKeyBlob($key_blob);
+ $identity = (new Identity($this->fsock))
+ ->withPublicKey($key)
+ ->withPublicKeyBlob($key_blob)
+ ->withComment($comment);
$identities[] = $identity;
unset($key);
}
@@ -242,13 +204,30 @@ class Agent
}
/**
+ * Returns the SSH Agent identity matching a given public key or null if no identity is found
+ *
+ * @return ?Identity
+ */
+ public function findIdentityByPublicKey(PublicKey $key)
+ {
+ $identities = $this->requestIdentities();
+ $key = (string) $key;
+ foreach ($identities as $identity) {
+ if (((string) $identity->getPublicKey()) == $key) {
+ return $identity;
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Signal that agent forwarding should
* be requested when a channel is opened
*
- * @return bool
- * @access public
+ * @return void
*/
- function startSSHForwarding()
+ public function startSSHForwarding()
{
if ($this->forward_status == self::FORWARD_NONE) {
$this->forward_status = self::FORWARD_REQUEST;
@@ -258,38 +237,15 @@ class Agent
/**
* Request agent forwarding of remote server
*
- * @param Net_SSH2 $ssh
+ * @param SSH2 $ssh
* @return bool
- * @access private
*/
- function _request_forwarding($ssh)
+ private function request_forwarding(SSH2 $ssh)
{
- $request_channel = $ssh->_get_open_channel();
- if ($request_channel === false) {
- return false;
- }
-
- $packet = pack(
- 'CNNa*C',
- NET_SSH2_MSG_CHANNEL_REQUEST,
- $ssh->server_channels[$request_channel],
- strlen('auth-agent-req@openssh.com'),
- 'auth-agent-req@openssh.com',
- 1
- );
-
- $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST;
-
- if (!$ssh->_send_binary_packet($packet)) {
- return false;
- }
-
- $response = $ssh->_get_channel_packet($request_channel);
- if ($response === false) {
+ if (!$ssh->requestAgentForwarding()) {
return false;
}
- $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN;
$this->forward_status = self::FORWARD_ACTIVE;
return true;
@@ -302,13 +258,12 @@ class Agent
* open to give the SSH Agent an opportunity
* to take further action. i.e. request agent forwarding
*
- * @param Net_SSH2 $ssh
- * @access private
+ * @param SSH2 $ssh
*/
- function _on_channel_open($ssh)
+ public function registerChannelOpen(SSH2 $ssh)
{
if ($this->forward_status == self::FORWARD_REQUEST) {
- $this->_request_forwarding($ssh);
+ $this->request_forwarding($ssh);
}
}
@@ -316,13 +271,13 @@ class Agent
* Forward data to SSH Agent and return data reply
*
* @param string $data
- * @return data from SSH Agent
- * @access private
+ * @return string Data from SSH Agent
+ * @throws \RuntimeException on connection errors
*/
- function _forward_data($data)
+ public function forwardData($data)
{
if ($this->expected_bytes > 0) {
- $this->socket_buffer.= $data;
+ $this->socket_buffer .= $data;
$this->expected_bytes -= strlen($data);
} else {
$agent_data_bytes = current(unpack('N', $data));
@@ -335,25 +290,15 @@ class Agent
}
if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
- user_error('Connection closed attempting to forward data to SSH agent');
- return false;
+ throw new \RuntimeException('Connection closed attempting to forward data to SSH agent');
}
$this->socket_buffer = '';
$this->expected_bytes = 0;
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed while reading data response');
- return false;
- }
- $agent_reply_bytes = current(unpack('N', $temp));
+ $agent_reply_bytes = current(unpack('N', $this->readBytes(4)));
- $agent_reply_data = fread($this->fsock, $agent_reply_bytes);
- if (strlen($agent_reply_data) != $agent_reply_bytes) {
- user_error('Connection closed while reading data response');
- return false;
- }
+ $agent_reply_data = $this->readBytes($agent_reply_bytes);
$agent_reply_data = current(unpack('a*', $agent_reply_data));
return pack('Na*', $agent_reply_bytes, $agent_reply_data);
diff --git a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php
index 68b6bfdfa..06a4bafd1 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php
@@ -1,93 +1,109 @@
<?php
+
/**
* Pure-PHP ssh-agent client.
*
+ * {@internal See http://api.libssh.org/rfc/PROTOCOL.agent}
+ *
* PHP version 5
*
- * @category System
- * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
- * @internal See http://api.libssh.org/rfc/PROTOCOL.agent
*/
-namespace phpseclib\System\SSH\Agent;
+namespace phpseclib3\System\SSH\Agent;
-use phpseclib\System\SSH\Agent;
+use phpseclib3\Common\Functions\Strings;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Crypt\DSA;
+use phpseclib3\Crypt\EC;
+use phpseclib3\Crypt\RSA;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\System\SSH\Agent;
+use phpseclib3\System\SSH\Common\Traits\ReadBytes;
/**
* Pure-PHP ssh-agent client identity object
*
- * Instantiation should only be performed by \phpseclib\System\SSH\Agent class.
- * This could be thought of as implementing an interface that phpseclib\Crypt\RSA
+ * Instantiation should only be performed by \phpseclib3\System\SSH\Agent class.
+ * This could be thought of as implementing an interface that phpseclib3\Crypt\RSA
* implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
* The methods in this interface would be getPublicKey and sign since those are the
* methods phpseclib looks for to perform public key authentication.
*
- * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
- * @access internal
+ * @internal
*/
-class Identity
+class Identity implements PrivateKey
{
- /**@+
- * Signature Flags
- *
- * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
- *
- * @access private
- */
+ use ReadBytes;
+
+ // Signature Flags
+ // See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;
- /**#@-*/
/**
* Key Object
*
- * @var \phpseclib\Crypt\RSA
- * @access private
+ * @var PublicKey
* @see self::getPublicKey()
*/
- var $key;
+ private $key;
/**
* Key Blob
*
* @var string
- * @access private
* @see self::sign()
*/
- var $key_blob;
+ private $key_blob;
/**
* Socket Resource
*
* @var resource
- * @access private
* @see self::sign()
*/
- var $fsock;
+ private $fsock;
/**
* Signature flags
*
* @var int
- * @access private
* @see self::sign()
* @see self::setHash()
*/
- var $flags = 0;
+ private $flags = 0;
+
+ /**
+ * Comment
+ *
+ * @var null|string
+ */
+ private $comment;
+
+ /**
+ * Curve Aliases
+ *
+ * @var array
+ */
+ private static $curveAliases = [
+ 'secp256r1' => 'nistp256',
+ 'secp384r1' => 'nistp384',
+ 'secp521r1' => 'nistp521',
+ 'Ed25519' => 'Ed25519'
+ ];
/**
* Default Constructor.
*
* @param resource $fsock
- * @return \phpseclib\System\SSH\Agent\Identity
- * @access private
*/
- function __construct($fsock)
+ public function __construct($fsock)
{
$this->fsock = $fsock;
}
@@ -95,29 +111,36 @@ class Identity
/**
* Set Public Key
*
- * Called by \phpseclib\System\SSH\Agent::requestIdentities()
+ * Called by \phpseclib3\System\SSH\Agent::requestIdentities()
*
- * @param \phpseclib\Crypt\RSA $key
- * @access private
+ * @param PublicKey $key
*/
- function setPublicKey($key)
+ public function withPublicKey(PublicKey $key)
{
- $this->key = $key;
- $this->key->setPublicKey();
+ if ($key instanceof EC) {
+ if (is_array($key->getCurve()) || !isset(self::$curveAliases[$key->getCurve()])) {
+ throw new UnsupportedAlgorithmException('The only supported curves are nistp256, nistp384, nistp512 and Ed25519');
+ }
+ }
+
+ $new = clone $this;
+ $new->key = $key;
+ return $new;
}
/**
* Set Public Key
*
- * Called by \phpseclib\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key
+ * Called by \phpseclib3\System\SSH\Agent::requestIdentities(). The key blob could be extracted from $this->key
* but this saves a small amount of computation.
*
* @param string $key_blob
- * @access private
*/
- function setPublicKeyBlob($key_blob)
+ public function withPublicKeyBlob($key_blob)
{
- $this->key_blob = $key_blob;
+ $new = clone $this;
+ $new->key_blob = $key_blob;
+ return $new;
}
/**
@@ -125,51 +148,115 @@ class Identity
*
* Wrapper for $this->key->getPublicKey()
*
- * @param int $format optional
* @return mixed
- * @access public
*/
- function getPublicKey($format = null)
+ public function getPublicKey()
{
- return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
+ return $this->key;
}
/**
- * Set Signature Mode
+ * Sets the hash
*
- * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie.
- * ssh-agent's only supported mode is \phpseclib\Crypt\RSA::SIGNATURE_PKCS1
+ * @param string $hash
+ */
+ public function withHash($hash)
+ {
+ $new = clone $this;
+
+ $hash = strtolower($hash);
+
+ if ($this->key instanceof RSA) {
+ $new->flags = 0;
+ switch ($hash) {
+ case 'sha1':
+ break;
+ case 'sha256':
+ $new->flags = self::SSH_AGENT_RSA2_256;
+ break;
+ case 'sha512':
+ $new->flags = self::SSH_AGENT_RSA2_512;
+ break;
+ default:
+ throw new UnsupportedAlgorithmException('The only supported hashes for RSA are sha1, sha256 and sha512');
+ }
+ }
+ if ($this->key instanceof EC) {
+ switch ($this->key->getCurve()) {
+ case 'secp256r1':
+ $expectedHash = 'sha256';
+ break;
+ case 'secp384r1':
+ $expectedHash = 'sha384';
+ break;
+ //case 'secp521r1':
+ //case 'Ed25519':
+ default:
+ $expectedHash = 'sha512';
+ }
+ if ($hash != $expectedHash) {
+ throw new UnsupportedAlgorithmException('The only supported hash for ' . self::$curveAliases[$this->key->getCurve()] . ' is ' . $expectedHash);
+ }
+ }
+ if ($this->key instanceof DSA) {
+ if ($hash != 'sha1') {
+ throw new UnsupportedAlgorithmException('The only supported hash for DSA is sha1');
+ }
+ }
+ return $new;
+ }
+
+ /**
+ * Sets the padding
*
- * @param int $mode
- * @access public
+ * Only PKCS1 padding is supported
+ *
+ * @param string $padding
*/
- function setSignatureMode($mode)
+ public function withPadding($padding)
{
+ if (!$this->key instanceof RSA) {
+ throw new UnsupportedAlgorithmException('Only RSA keys support padding');
+ }
+ if ($padding != RSA::SIGNATURE_PKCS1 && $padding != RSA::SIGNATURE_RELAXED_PKCS1) {
+ throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures');
+ }
+ return $this;
}
/**
- * Set Hash
+ * Determines the signature padding mode
*
- * ssh-agent doesn't support using hashes for RSA other than SHA1
+ * Valid values are: ASN1, SSH2, Raw
*
- * @param string $hash
- * @access public
+ * @param string $format
*/
- function setHash($hash)
+ public function withSignatureFormat($format)
{
- $this->flags = 0;
- switch ($hash) {
- case 'sha1':
- break;
- case 'sha256':
- $this->flags = self::SSH_AGENT_RSA2_256;
- break;
- case 'sha512':
- $this->flags = self::SSH_AGENT_RSA2_512;
- break;
- default:
- user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
+ if ($this->key instanceof RSA) {
+ throw new UnsupportedAlgorithmException('Only DSA and EC keys support signature format setting');
}
+ if ($format != 'SSH2') {
+ throw new UnsupportedAlgorithmException('Only SSH2-formatted signatures are currently supported');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the curve
+ *
+ * Returns a string if it's a named curve, an array if not
+ *
+ * @return string|array
+ */
+ public function getCurve()
+ {
+ if (!$this->key instanceof EC) {
+ throw new UnsupportedAlgorithmException('Only EC keys have curves');
+ }
+
+ return $this->key->getCurve();
}
/**
@@ -179,63 +266,81 @@ class Identity
*
* @param string $message
* @return string
- * @access public
+ * @throws \RuntimeException on connection errors
+ * @throws UnsupportedAlgorithmException if the algorithm is unsupported
*/
- function sign($message)
+ public function sign($message)
{
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
- $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags);
- $packet = pack('Na*', strlen($packet), $packet);
+ $packet = Strings::packSSH2(
+ 'CssN',
+ Agent::SSH_AGENTC_SIGN_REQUEST,
+ $this->key_blob,
+ $message,
+ $this->flags
+ );
+ $packet = Strings::packSSH2('s', $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
- user_error('Connection closed during signing');
- return false;
+ throw new \RuntimeException('Connection closed during signing');
}
- $temp = fread($this->fsock, 4);
- if (strlen($temp) != 4) {
- user_error('Connection closed during signing');
- return false;
- }
- $length = current(unpack('N', $temp));
- $type = ord(fread($this->fsock, 1));
+ $length = current(unpack('N', $this->readBytes(4)));
+ $packet = $this->readBytes($length);
+
+ list($type, $signature_blob) = Strings::unpackSSH2('Cs', $packet);
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
- user_error('Unable to retrieve signature');
- return false;
+ throw new \RuntimeException('Unable to retrieve signature');
}
- $signature_blob = fread($this->fsock, $length - 1);
- if (strlen($signature_blob) != $length - 1) {
- user_error('Connection closed during signing');
- return false;
- }
- $length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
- if ($length != strlen($signature_blob)) {
- user_error('Malformed signature blob');
+ if (!$this->key instanceof RSA) {
+ return $signature_blob;
}
- $length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
- if ($length > strlen($signature_blob) + 4) {
- user_error('Malformed signature blob');
- }
- $type = $this->_string_shift($signature_blob, $length);
- $this->_string_shift($signature_blob, 4);
+
+ list($type, $signature_blob) = Strings::unpackSSH2('ss', $signature_blob);
return $signature_blob;
}
/**
- * String Shift
+ * Returns the private key
*
- * Inspired by array_shift
- *
- * @param string $string
- * @param int $index
+ * @param string $type
+ * @param array $options optional
* @return string
- * @access private
*/
- function _string_shift(&$string, $index = 1)
+ public function toString($type, array $options = [])
+ {
+ throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
+ }
+
+ /**
+ * Sets the password
+ *
+ * @param string|bool $password
+ * @return never
+ */
+ public function withPassword($password = false)
+ {
+ throw new \RuntimeException('ssh-agent does not provide a mechanism to get the private key');
+ }
+
+ /**
+ * Sets the comment
+ */
+ public function withComment($comment = null)
+ {
+ $new = clone $this;
+ $new->comment = $comment;
+ return $new;
+ }
+
+ /**
+ * Returns the comment
+ *
+ * @return null|string
+ */
+ public function getComment()
{
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
+ return $this->comment;
}
}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php
new file mode 100644
index 000000000..6fd032bd4
--- /dev/null
+++ b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Common/Traits/ReadBytes.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * ReadBytes trait
+ *
+ * PHP version 5
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2015 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib3\System\SSH\Common\Traits;
+
+/**
+ * ReadBytes trait
+ *
+ * @author Jim Wigginton <terrafrost@php.net>
+ */
+trait ReadBytes
+{
+ /**
+ * Read data
+ *
+ * @param int $length
+ * @throws \RuntimeException on connection errors
+ */
+ public function readBytes($length)
+ {
+ $temp = fread($this->fsock, $length);
+ if (strlen($temp) != $length) {
+ throw new \RuntimeException("Expected $length bytes; got " . strlen($temp));
+ }
+ return $temp;
+ }
+}
diff --git a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php
index 547688f9f..517106c3a 100644
--- a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php
+++ b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php
@@ -1,7 +1,12 @@
<?php
+
/**
* Bootstrapping File for phpseclib
*
+ * composer isn't a requirement for phpseclib 2.0 but this file isn't really required
+ * either. it's a bonus for those using composer but if you're not phpseclib will
+ * still work
+ *
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
@@ -9,7 +14,7 @@ if (extension_loaded('mbstring')) {
// 2 - MB_OVERLOAD_STRING
// mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0.
if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
- throw new \UnexpectedValueException(
+ throw new UnexpectedValueException(
'Overloading of string functions using mbstring.func_overload ' .
'is not supported by phpseclib.'
);
diff --git a/vendor/phpseclib/phpseclib2_compat/.github/workflows/ci.yml b/vendor/phpseclib/phpseclib2_compat/.github/workflows/ci.yml
new file mode 100644
index 000000000..4d19f96c9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/.github/workflows/ci.yml
@@ -0,0 +1,61 @@
+name: CI
+on: [push, pull_request]
+
+permissions:
+ contents: read # to fetch code (actions/checkout)
+
+jobs:
+ tests:
+ name: Tests
+ timeout-minutes: 10
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php-version }}
+ - name: Clone phpseclib v2 and update tests
+ run: |
+ git clone -b 2.0 --single-branch https://github.com/phpseclib/phpseclib.git temp
+ mv temp/tests tests
+ rm tests/Functional/Net/SCPSSH2UserStoryTest.php
+ rm tests/Unit/Net/SSH1Test.php
+ rm tests/Unit/Net/SSH2UnitTest.php
+ rm tests/Unit/Math/BigInteger/InternalTest.php
+ php .github/workflows/update.php
+ - name: Composer Install
+ run: composer install --no-interaction --no-cache
+ - name: Make Tests Compatiable With PHPUnit 9+
+ if: matrix.php-version != '5.6' && matrix.php-version != '7.0' && matrix.php-version != '7.1' && matrix.php-version != '7.2'
+ run: php tests/make_compatible_with_phpunit9.php
+ - name: Setup Secure Shell Functional Tests
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ PHPSECLIB_SSH_USERNAME='phpseclib'
+ PHPSECLIB_SSH_PASSWORD='EePoov8po1aethu2kied1ne0'
+
+ sudo useradd --create-home --base-dir /home "$PHPSECLIB_SSH_USERNAME"
+ echo "$PHPSECLIB_SSH_USERNAME:$PHPSECLIB_SSH_PASSWORD" | sudo chpasswd
+ ssh-keygen -t rsa -b 1024 -f "$HOME/.ssh/id_rsa" -q -N ""
+ eval `ssh-agent -s`
+ ssh-add "$HOME/.ssh/id_rsa"
+ sudo mkdir -p "/home/$PHPSECLIB_SSH_USERNAME/.ssh/"
+ sudo cp "$HOME/.ssh/id_rsa.pub" "/home/$PHPSECLIB_SSH_USERNAME/.ssh/authorized_keys"
+ sudo ssh-keyscan -t rsa localhost > "/tmp/known_hosts"
+ sudo cp "/tmp/known_hosts" "/home/$PHPSECLIB_SSH_USERNAME/.ssh/known_hosts"
+ sudo chown "$PHPSECLIB_SSH_USERNAME:$PHPSECLIB_SSH_USERNAME" "/home/$PHPSECLIB_SSH_USERNAME/.ssh/" -R
+
+ echo "PHPSECLIB_SSH_HOSTNAME=localhost" >> $GITHUB_ENV
+ echo "PHPSECLIB_SSH_USERNAME=$PHPSECLIB_SSH_USERNAME" >> $GITHUB_ENV
+ echo "PHPSECLIB_SSH_PASSWORD=$PHPSECLIB_SSH_PASSWORD" >> $GITHUB_ENV
+ echo "PHPSECLIB_SSH_HOME=/home/phpseclib" >> $GITHUB_ENV
+ echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV
+ - name: PHPUnit
+ run: vendor/bin/phpunit
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
diff --git a/vendor/phpseclib/phpseclib2_compat/.github/workflows/update.php b/vendor/phpseclib/phpseclib2_compat/.github/workflows/update.php
new file mode 100644
index 000000000..61cd00641
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/.github/workflows/update.php
@@ -0,0 +1,12 @@
+<?php
+
+function replaceFileContents($fileName, $search, $replace)
+{
+ $fileContents = file_get_contents($fileName);
+ $updatedFileContents = preg_replace($search, $replace, $fileContents);
+ file_put_contents($fileName, $updatedFileContents);
+}
+
+replaceFileContents('tests/PhpseclibTestCase.php', '~%s/../phpseclib/%s~', '%s/../src/%s');
+replaceFileContents('tests/Unit/Crypt/RSA/LoadKeyTest.php', '~ public function testSetPrivate\(\)~', ' private function skiptestSetPrivate()');
+replaceFileContents('tests/Unit/Crypt/RSA/ModeTest.php', '~ public function testOAEPWithLabel\(\)~', ' private function skiptestOAEPWithLabel()'); \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/LICENSE b/vendor/phpseclib/phpseclib2_compat/LICENSE
new file mode 100644
index 000000000..39a347e41
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 phpseclib
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/phpseclib/phpseclib2_compat/README.md b/vendor/phpseclib/phpseclib2_compat/README.md
new file mode 100644
index 000000000..df4739d34
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/README.md
@@ -0,0 +1,92 @@
+# phpseclib2_compat
+
+[![CI Status](https://github.com/phpseclib/phpseclib2_compat/actions/workflows/ci.yml/badge.svg?branch=1.0&event=push "CI Status")](https://github.com/phpseclib/phpseclib2_compat/actions/workflows/ci.yml?query=branch%3A1.0)
+
+phpseclib 2.0 polyfill built with phpseclib 3.0
+
+## Overview
+
+phpseclib 3.0 breaks backwards compatability with phpseclib 2.0. Most notably, public keys work completely differently. So let's say you wanted to use phpseclib 3.0 whilst some of your other dependencies still use phpseclib 2.0. What would you do in that instance?
+
+That's where phpseclib2_compat comes into play. Require phpseclib/phpseclib:~3.0 and phpseclib/phpseclib2_compat:~1.0 and your dependencies will magically start using phpseclib 3.0 even if they don't know it.
+
+Using phpseclib2_compat will actually bring a few enhancements to your dependencies. For example, while phpseclib 2.0 only supports RSA keys phpseclib2_compat sports support for ECDSA / DSA / Ed25519 / Ed449 keys.
+
+Consider this code sample:
+
+```php
+use phpseclib\Crypt\RSA;
+
+$rsa = new RSA;
+$rsa->loadKey('ecdsa private key');
+
+$ssh = new SSH2('website.com');
+$ssh->login('username', $rsa);
+```
+That'll work with phpseclib2_compat, even with an ECDSA private key, whereas in phpseclib 2.0 it would not work.
+
+SSH1 and SCP are not supported but those were likely never frequently used anyway.
+
+## Using the old cipher suite
+
+phpseclib 3.0 uses a different cipher suite (an expanded one) than 2.0. If this causes you issues you can use the 2.0 ciphersuite by doing this prior to calling `$ssh->login()`:
+
+```php
+$methods = [
+ 'crypt' => array_intersect([
+ 'arcfour256',
+ 'arcfour128',
+ 'aes128-ctr',
+ 'aes192-ctr',
+ 'aes256-ctr',
+ 'twofish128-ctr',
+ 'twofish192-ctr',
+ 'twofish256-ctr',
+ 'aes128-cbc',
+ 'aes192-cbc',
+ 'aes256-cbc',
+ 'twofish128-cbc',
+ 'twofish192-cbc',
+ 'twofish256-cbc',
+ 'twofish-cbc',
+ 'blowfish-ctr',
+ 'blowfish-cbc',
+ '3des-ctr',
+ '3des-cbc'
+ ], $ssh->getSupportedEncryptionAlgorithms()),
+ 'mac' => [
+ 'hmac-sha2-256',
+ 'hmac-sha1-96',
+ 'hmac-sha1',
+ 'hmac-md5-96',
+ 'hmac-md5'
+ ],
+ 'comp' => ['none']
+];
+
+$ssh->setPreferredAlgorithms([
+ 'kex' => [
+ 'curve25519-sha256@libssh.org',
+ 'diffie-hellman-group-exchange-sha256',
+ 'diffie-hellman-group-exchange-sha1',
+ 'diffie-hellman-group14-sha1',
+ 'diffie-hellman-group14-sha256'
+ ],
+ 'hostkey' => [
+ 'rsa-sha2-256',
+ 'rsa-sha2-512',
+ 'ssh-rsa',
+ 'ssh-dss'
+ ],
+ 'client_to_server' => $methods,
+ 'server_to_client' => $methods
+]);
+```
+
+## Installation
+
+With [Composer](https://getcomposer.org/):
+
+```
+composer require phpseclib/phpseclib2_compat:~1.0
+```
diff --git a/vendor/phpseclib/phpseclib2_compat/composer.json b/vendor/phpseclib/phpseclib2_compat/composer.json
new file mode 100644
index 000000000..f32ef85e8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/composer.json
@@ -0,0 +1,30 @@
+{
+ "name": "phpseclib/phpseclib2_compat",
+ "description": "phpseclib 2.0 polyfill built with phpseclib 3.0",
+ "type": "library",
+ "homepage": "https://github.com/phpseclib/phpseclib2_compat",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/phpseclib/phpseclib2_compat/issues",
+ "source": "https://github.com/phpseclib/phpseclib2_compat"
+ },
+ "provide": {
+ "phpseclib/phpseclib": "2.0.47"
+ },
+ "require": {
+ "phpseclib/phpseclib": "^3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7|^6.0|^9.4"
+ },
+ "autoload": {
+ "psr-4": {"phpseclib\\": "src/"}
+ }
+}
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/AES.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/AES.php
new file mode 100644
index 000000000..0e4da3543
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/AES.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Pure-PHP implementation of AES.
+ *
+ * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * NOTE: Since AES.php is (for compatibility and phpseclib-historical reasons) virtually
+ * just a wrapper to Rijndael.php you may consider using Rijndael.php instead of
+ * to save one include_once().
+ *
+ * If {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
+ * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
+ * it'll be null-padded to 192-bits and 192 bits will be the key length until {@link self::setKey() setKey()}
+ * is called, again, at which point, it'll be recalculated.
+ *
+ * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, some functions are available to be called that, in the context of AES, don't
+ * make a whole lot of sense. {@link self::setBlockLength() setBlockLength()}, for instance. Calling that function,
+ * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $aes = new \phpseclib\Crypt\AES();
+ *
+ * $aes->setKey('abcdefghijklmnop');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $aes->decrypt($aes->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package AES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2008 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of AES.
+ *
+ * @package AES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class AES extends Rijndael
+{
+ /**
+ * Dummy function
+ *
+ * Since \phpseclib\Crypt\AES extends \phpseclib\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
+ *
+ * @see \phpseclib\Crypt\Rijndael::setBlockLength()
+ * @access public
+ * @param int $length
+ */
+ function setBlockLength($length)
+ {
+ return;
+ }
+
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length <= 128:
+ return 128;
+ case $length <= 192:
+ return 192;
+ default:
+ return 256;
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Base.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Base.php
new file mode 100644
index 000000000..fad69c023
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Base.php
@@ -0,0 +1,585 @@
+<?php
+
+/**
+ * Base Class for all \phpseclib\Crypt\* cipher classes
+ *
+ * PHP version 5
+ *
+ * @category Crypt
+ * @package Base
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+use phpseclib3\Exception\BadDecryptionException;
+use phpseclib3\Exception\InconsistentSetupException;
+
+/**
+ * Base Class for all \phpseclib\Crypt\* cipher classes
+ *
+ * @package Base
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ */
+abstract class Base
+{
+ /**#@+
+ * @access public
+ * @see \phpseclib\Crypt\Base::encrypt()
+ * @see \phpseclib\Crypt\Base::decrypt()
+ */
+ /**
+ * Encrypt / decrypt using the Counter mode.
+ *
+ * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
+ */
+ const MODE_CTR = -1;
+ /**
+ * Encrypt / decrypt using the Electronic Code Book mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
+ */
+ const MODE_ECB = 1;
+ /**
+ * Encrypt / decrypt using the Code Book Chaining mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
+ */
+ const MODE_CBC = 2;
+ /**
+ * Encrypt / decrypt using the Cipher Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
+ */
+ const MODE_CFB = 3;
+ /**
+ * Encrypt / decrypt using the Cipher Feedback mode (8bit)
+ */
+ const MODE_CFB8 = 38;
+ /**
+ * Encrypt / decrypt using the Output Feedback mode (8bit)
+ */
+ const MODE_OFB8 = 7;
+ /**
+ * Encrypt / decrypt using the Output Feedback mode.
+ *
+ * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
+ */
+ const MODE_OFB = 4;
+ /**
+ * Encrypt / decrypt using streaming mode.
+ */
+ const MODE_STREAM = 5;
+ /**
+ * Encrypt / decrypt using Galois/Counter mode.
+ *
+ * @link https://en.wikipedia.org/wiki/Galois/Counter_Mode
+ */
+ const MODE_GCM = 6;
+ /**#@-*/
+
+ /**#@+
+ * @access private
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
+ */
+ /**
+ * Base value for the internal implementation $engine switch
+ */
+ const ENGINE_INTERNAL = 1;
+ /**
+ * Base value for the eval() implementation $engine switch
+ */
+ const ENGINE_EVAL = 4;
+ /**
+ * Base value for the mcrypt implementation $engine switch
+ */
+ const ENGINE_MCRYPT = 2;
+ /**
+ * Base value for the openssl implementation $engine switch
+ */
+ const ENGINE_OPENSSL = 3;
+ /**
+ * Base value for the libsodium implementation $engine switch
+ */
+ const ENGINE_LIBSODIUM = 5;
+ /**
+ * Base value for the openssl / gcm implementation $engine switch
+ */
+ const ENGINE_OPENSSL_GCM = 6;
+ /**#@-*/
+
+ /**
+ * Engine Map
+ *
+ * @access private
+ * @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine()
+ */
+ const ENGINE_MAP = [
+ self::ENGINE_INTERNAL => 'PHP',
+ self::ENGINE_EVAL => 'Eval',
+ self::ENGINE_MCRYPT => 'mcrypt',
+ self::ENGINE_OPENSSL => 'OpenSSL',
+ self::ENGINE_LIBSODIUM => 'libsodium',
+ self::ENGINE_OPENSSL_GCM => 'OpenSSL (GCM)'
+ ];
+
+ /**
+ * The Cipher
+ *
+ * @var \phpseclib3\Crypt\Common\SymmetricKey
+ * @access private
+ */
+ protected $cipher;
+
+ /**
+ * The Key
+ *
+ * @see self::setKey()
+ * @var string
+ * @access private
+ */
+ protected $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+ /**
+ * Password Parameters
+ *
+ * @see self::setPassword()
+ * @var array
+ * @access private
+ */
+ protected $password = [];
+
+ /**
+ * The Key Length (in bytes)
+ *
+ * @see self::setKeyLength()
+ * @var int
+ * @access private
+ */
+ protected $key_length = 128;
+
+ /**
+ * Does internal cipher state need to be (re)initialized?
+ *
+ * @see self::setKey()
+ * @var bool
+ * @access private
+ */
+ private $changed = true;
+
+ /**
+ * Has the IV been set?
+ *
+ * @var bool
+ * @access private
+ */
+ protected $ivSet = false;
+
+ /**
+ * Has the key length been explictly set?
+ *
+ * @var bool
+ * @access private
+ */
+ protected $explicit_key_length = false;
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * $mode could be:
+ *
+ * - self::MODE_ECB
+ *
+ * - self::MODE_CBC
+ *
+ * - self::MODE_CTR
+ *
+ * - self::MODE_CFB
+ *
+ * - self::MODE_OFB
+ *
+ * If not explicitly set, self::MODE_CBC will be used.
+ *
+ * @param int $mode
+ * @access public
+ */
+ public function __construct($mode = self::MODE_CBC)
+ {
+ $map = [
+ self::MODE_CTR => 'ctr',
+ self::MODE_ECB => 'ecb',
+ self::MODE_CBC => 'cbc',
+ self::MODE_CFB => 'cfb',
+ self::MODE_CFB8 => 'cfb8',
+ self::MODE_OFB => 'ofb',
+ self::MODE_OFB8 => 'ofb8',
+ self::MODE_GCM => 'gcm',
+ self::MODE_STREAM => 'stream'
+ ];
+ if (!isset($map[$mode])) {
+ $mode = self::MODE_CBC;
+ }
+ $class = new \ReflectionClass(static::class);
+ $class = "phpseclib3\\Crypt\\" . $class->getShortName();
+ $this->cipher = new $class($map[$mode]);
+ $this->key_length = $this->cipher->getKeyLength();
+ }
+
+ /**
+ * Sets the initialization vector. (optional)
+ *
+ * SetIV is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set, it'll be assumed
+ * to be all zero's.
+ *
+ * @access public
+ * @param string $iv
+ * @internal Can be overwritten by a sub class, but does not have to be
+ */
+ public function setIV($iv)
+ {
+ $this->ivSet = true;
+
+ if (!$this->cipher->usesIV()) {
+ return;
+ }
+
+ $length = $this->cipher->getBlockLengthInBytes();
+ $iv = str_pad(substr($iv, 0, $length), $length, "\0");
+
+ try {
+ $this->cipher->setIV($iv);
+ } catch (\Exception $e) {}
+ }
+
+ /**
+ * Sets the key length.
+ *
+ * Keys with explicitly set lengths need to be treated accordingly
+ *
+ * @access public
+ * @param int $length
+ */
+ public function setKeyLength($length)
+ {
+ // algorithms that have a fixed key length should override this with a method that does nothing
+ $this->changed = true;
+ $this->key_length = static::calculateNewKeyLength($length);
+ $this->explicit_key_length = true;
+ }
+
+ /**
+ * Returns the current key length in bits
+ *
+ * @access public
+ * @return int
+ */
+ public function getKeyLength()
+ {
+ return $this->key_length;
+ }
+
+ /**
+ * Returns the current block length in bits
+ *
+ * @access public
+ * @return int
+ */
+ public function getBlockLength()
+ {
+ return $this->cipher->getBlockLength();
+ }
+
+ /**
+ * Sets the key.
+ *
+ * The min/max length(s) of the key depends on the cipher which is used.
+ * If the key not fits the length(s) of the cipher it will paded with null bytes
+ * up to the closest valid key length. If the key is more than max length,
+ * we trim the excess bits.
+ *
+ * If the key is not explicitly set, it'll be assumed to be all null bytes.
+ *
+ * @access public
+ * @param string $key
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function setKey($key)
+ {
+ $this->key = $key;
+ $this->password = [];
+ if (!$this->explicit_key_length) {
+ $this->key_length = static::calculateNewKeyLength(strlen($key) << 3);
+ }
+ $this->changed = true;
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1:
+ * $hash, $salt, $count, $dkLen
+ *
+ * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
+ *
+ * @see Crypt/Hash.php
+ * @param string $password
+ * @param string $method
+ * @return bool
+ * @access public
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function setPassword($password, $method = 'pbkdf2')
+ {
+ $this->password = func_get_args();
+ $this->cipher->setKeyLength($this->key_length);
+ $this->cipher->setPassword(...func_get_args());
+ }
+
+ /**
+ * Encrypts a message.
+ *
+ * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher
+ * implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's
+ * necessary are discussed in the following
+ * URL:
+ *
+ * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
+ *
+ * An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
+ * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that
+ * length.
+ *
+ * @see self::decrypt()
+ * @access public
+ * @param string $plaintext
+ * @return string $ciphertext
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function encrypt($plaintext)
+ {
+ if ($this->changed) {
+ $this->setup();
+ }
+
+ try {
+ return $this->cipher->encrypt($plaintext);
+ } catch (\LengthException $e) {
+ user_error($e->getMessage());
+ $this->cipher->enablePadding();
+ return $this->cipher->encrypt($plaintext);
+ }
+ }
+
+ /**
+ * Decrypts a message.
+ *
+ * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
+ * it is.
+ *
+ * @see self::encrypt()
+ * @access public
+ * @param string $ciphertext
+ * @return string $plaintext
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function decrypt($ciphertext)
+ {
+ if ($this->changed) {
+ $this->setup();
+ }
+
+ try {
+ return $this->cipher->decrypt($ciphertext);
+ } catch (\LengthException $e) {
+ $len = strlen($ciphertext);
+ $block_size = $this->cipher->getBlockLengthInBytes();
+ $ciphertext = str_pad($ciphertext, $len + ($block_size - $len % $block_size) % $block_size, chr(0));
+ try {
+ return $this->cipher->decrypt($ciphertext);
+ } catch (BadDecryptionException $e) {
+ return false;
+ }
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Setup IV and key
+ */
+ protected function setup()
+ {
+ // we set this just in case it was already set to anything via setPassword()
+ $temp = $this->explicit_key_length;
+ $this->setKeyLength($this->key_length);
+ $this->explicit_key_length = $temp;
+ if ($this->explicit_key_length) {
+ $this->cipher->setKeyLength($this->key_length);
+ }
+ if (empty($this->password)) {
+ $key_length = $this->key_length >> 3;
+ $key = str_pad(substr($this->key, 0, $key_length), $key_length, "\0");
+ $this->cipher->setKey($key);
+ } else {
+ $this->cipher->setPassword(...$this->password);
+ }
+ if (!$this->ivSet) {
+ $this->setIV('');
+ }
+ $this->changed = false;
+ }
+
+ /**
+ * Pad "packets".
+ *
+ * Block ciphers working by encrypting between their specified [$this->]block_size at a time
+ * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
+ * pad the input so that it is of the proper length.
+ *
+ * Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH,
+ * where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
+ * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
+ * transmitted separately)
+ *
+ * @see self::disablePadding()
+ * @access public
+ */
+ public function enablePadding()
+ {
+ $this->cipher->enablePadding();
+ }
+
+ /**
+ * Do not pad packets.
+ *
+ * @see self::enablePadding()
+ * @access public
+ */
+ public function disablePadding()
+ {
+ $this->cipher->disablePadding();
+ }
+
+ /**
+ * Treat consecutive "packets" as if they are a continuous buffer.
+ *
+ * Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets
+ * will yield different outputs:
+ *
+ * <code>
+ * echo $rijndael->encrypt(substr($plaintext, 0, 16));
+ * echo $rijndael->encrypt(substr($plaintext, 16, 16));
+ * </code>
+ * <code>
+ * echo $rijndael->encrypt($plaintext);
+ * </code>
+ *
+ * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
+ * another, as demonstrated with the following:
+ *
+ * <code>
+ * $rijndael->encrypt(substr($plaintext, 0, 16));
+ * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
+ * </code>
+ * <code>
+ * echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
+ * </code>
+ *
+ * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
+ * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
+ * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
+ *
+ * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each
+ * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
+ * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
+ * however, they are also less intuitive and more likely to cause you problems.
+ *
+ * @see self::disableContinuousBuffer()
+ * @access public
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function enableContinuousBuffer()
+ {
+ try {
+ $this->cipher->enableContinuousBuffer();
+ } catch (\BadMethodCallException $e) {
+ user_error($e->getMessage());
+ }
+ }
+
+ /**
+ * Treat consecutive packets as if they are a discontinuous buffer.
+ *
+ * The default behavior.
+ *
+ * @see self::enableContinuousBuffer()
+ * @access public
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function disableContinuousBuffer()
+ {
+ $this->cipher->disableContinuousBuffer();
+ }
+
+ /**
+ * Test for engine validity
+ *
+ * @see self::__construct()
+ * @param int $engine
+ * @access public
+ * @return bool
+ */
+ public function isValidEngine($engine)
+ {
+ $map = self::ENGINE_MAP;
+ return $this->cipher->isValidEngine($map[$engine]);
+ }
+
+ /**
+ * Sets the preferred crypt engine
+ *
+ * Currently, $engine could be:
+ *
+ * - \phpseclib\Crypt\Base::ENGINE_OPENSSL [very fast]
+ *
+ * - \phpseclib\Crypt\Base::ENGINE_MCRYPT [fast]
+ *
+ * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow]
+ *
+ * If the preferred crypt engine is not available the fastest available one will be used
+ *
+ * @see self::__construct()
+ * @param int $engine
+ * @access public
+ */
+ public function setPreferredEngine($engine)
+ {
+ $map = self::ENGINE_MAP;
+ $this->cipher->setPreferredEngine($map[$engine]);
+ }
+
+ /**
+ * Returns the engine currently being utilized
+ *
+ * @see self::_setEngine()
+ * @access public
+ */
+ public function getEngine()
+ {
+ static $reverseMap;
+ if (!isset($reverseMap)) {
+ $reverseMap = array_flip(self::ENGINE_MAP);
+ }
+ return $reverseMap[$this->cipher->getEngine()];
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php
new file mode 100644
index 000000000..f2cb68ee8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Blowfish.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * Pure-PHP implementation of Blowfish.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $blowfish = new \phpseclib\Crypt\Blowfish();
+ *
+ * $blowfish->setKey('12345678901234567890123456789012');
+ *
+ * $plaintext = str_repeat('a', 1024);
+ *
+ * echo $blowfish->decrypt($blowfish->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package Blowfish
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of Blowfish.
+ *
+ * @package Blowfish
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Blowfish extends Base
+{
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length < 32:
+ return 32;
+ case $length > 448:
+ return 448;
+ }
+ return $length;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/DES.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/DES.php
new file mode 100644
index 000000000..512c59a27
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/DES.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Pure-PHP implementation of DES.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
+ * - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
+ * - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $des = new \phpseclib\Crypt\DES();
+ *
+ * $des->setKey('abcdefgh');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $des->decrypt($des->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package DES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of DES.
+ *
+ * @package DES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class DES extends Base
+{
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ return 64;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Hash.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Hash.php
new file mode 100644
index 000000000..de7942705
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Hash.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ *
+ * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
+ *
+ * md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96
+ *
+ * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will return the HMAC as opposed to
+ * the hash. If no valid algorithm is provided, sha1 will be used.
+ *
+ * PHP version 5
+ *
+ * {@internal The variable names are the same as those in
+ * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $hash = new \phpseclib\Crypt\Hash('sha1');
+ *
+ * $hash->setKey('abcdefg');
+ *
+ * echo base64_encode($hash->hash('abcdefg'));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package Hash
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ *
+ * @package Hash
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Hash
+{
+ /**#@+
+ * @access private
+ * @see \phpseclib\Crypt\Hash::__construct()
+ */
+ /**
+ * Toggles the internal implementation
+ */
+ const MODE_INTERNAL = 1;
+ /**#@-*/
+
+ /**
+ * Hash Object
+ *
+ * @see self::setHash()
+ * @var null|\phpseclib3\Crypt\Hash
+ * @access private
+ */
+ private $hash;
+
+ /**
+ * Default Constructor.
+ *
+ * @param string $hash
+ * @return \phpseclib\Crypt\Hash
+ * @access public
+ */
+ public function __construct($hash = 'sha1')
+ {
+ $this->setHash($hash);
+ }
+
+ /**
+ * Sets the key for HMACs
+ *
+ * Keys can be of any length.
+ *
+ * @access public
+ * @param string $key
+ */
+ public function setKey($key = false)
+ {
+ $this->hash->setKey($key);
+ }
+
+ /**
+ * Gets the hash function.
+ *
+ * As set by the constructor or by the setHash() method.
+ *
+ * @access public
+ * @return string
+ */
+ public function getHash()
+ {
+ return $this->hash->getHash();
+ }
+
+ /**
+ * Sets the hash function.
+ *
+ * @access public
+ * @param string $hash
+ */
+ public function setHash($hash)
+ {
+ $this->hash = new \phpseclib3\Crypt\Hash;
+ try {
+ $this->hash->setHash($hash);
+ } catch (\phpseclib3\Exception\UnsupportedAlgorithmException $e) {
+ $this->hash->setHash('sha1');
+ }
+ }
+
+ /**
+ * Compute the HMAC.
+ *
+ * @access public
+ * @param string $text
+ * @return string
+ */
+ public function hash($text)
+ {
+ return $this->hash->hash($text);
+ }
+
+ /**
+ * Returns the hash length (in bytes)
+ *
+ * @access public
+ * @return int
+ */
+ public function getLength()
+ {
+ return $this->hash->getLengthInBytes();
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC2.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC2.php
new file mode 100644
index 000000000..b23f73bca
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC2.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * Pure-PHP implementation of RC2.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://tools.ietf.org/html/rfc2268}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $rc2 = new \phpseclib\Crypt\RC2();
+ *
+ * $rc2->setKey('abcdefgh');
+ *
+ * $plaintext = str_repeat('a', 1024);
+ *
+ * echo $rc2->decrypt($rc2->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package RC2
+ * @author Patrick Monnerat <pm@datasphere.ch>
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of RC2.
+ *
+ * @package RC2
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class RC2 extends Base
+{
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * $mode could be:
+ *
+ * - self::MODE_ECB
+ *
+ * - self::MODE_CBC
+ *
+ * - self::MODE_CTR
+ *
+ * - self::MODE_CFB
+ *
+ * - self::MODE_OFB
+ *
+ * If not explicitly set, self::MODE_CBC will be used.
+ *
+ * @param int $mode
+ * @access public
+ */
+ public function __construct($mode = self::MODE_CBC)
+ {
+ parent::__construct($mode);
+ $this->key_length = 8;
+ }
+
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length < 8:
+ return 8;
+ case $length > 1024:
+ return 1024;
+ }
+
+ return $length;
+ }
+
+ /**
+ * Setup IV and key
+ */
+ protected function setup()
+ {
+ if ($this->explicit_key_length) {
+ $this->cipher->setKeyLength($this->key_length);
+ }
+ $this->cipher->setKey($this->key);
+ if (!$this->ivSet) {
+ $this->setIV('');
+ }
+ $this->changed = false;
+ }
+
+ /**
+ * Sets the key.
+ *
+ * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg.
+ * strlen($key) <= 128), however, we only use the first 128 bytes if $key
+ * has more then 128 bytes in it, and set $key to a single null byte if
+ * it is empty.
+ *
+ * If the key is not explicitly set, it'll be assumed to be a single
+ * null byte.
+ *
+ * @see \phpseclib\Crypt\Base::setKey()
+ * @access public
+ * @param string $key
+ * @param int $t1 optional Effective key length in bits.
+ */
+ function setKey($key, $t1 = 0)
+ {
+ parent::setKey($key);
+ if ($t1) {
+ $this->setKeyLength($t1);
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC4.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC4.php
new file mode 100644
index 000000000..78781f553
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RC4.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Pure-PHP implementation of RC4.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
+ * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
+ *
+ * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
+ * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification.
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $rc4 = new \phpseclib\Crypt\RC4();
+ *
+ * $rc4->setKey('abcdefgh');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $rc4->decrypt($rc4->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package RC4
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of RC4.
+ *
+ * @package RC4
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class RC4 extends Base
+{
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length < 8:
+ return 8;
+ case $length > 2048:
+ return 2048;
+ }
+
+ return $length;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/RSA.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RSA.php
new file mode 100644
index 000000000..7a45a0bb5
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/RSA.php
@@ -0,0 +1,948 @@
+<?php
+
+/**
+ * Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
+ *
+ * PHP version 5
+ *
+ * Here's an example of how to encrypt and decrypt text with this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $rsa = new \phpseclib\Crypt\RSA();
+ * extract($rsa->createKey());
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $rsa->loadKey($privatekey);
+ * $ciphertext = $rsa->encrypt($plaintext);
+ *
+ * $rsa->loadKey($publickey);
+ * echo $rsa->decrypt($ciphertext);
+ * ?>
+ * </code>
+ *
+ * Here's an example of how to create signatures and verify signatures with this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $rsa = new \phpseclib\Crypt\RSA();
+ * extract($rsa->createKey());
+ *
+ * $plaintext = 'terrafrost';
+ *
+ * $rsa->loadKey($privatekey);
+ * $signature = $rsa->sign($plaintext);
+ *
+ * $rsa->loadKey($publickey);
+ * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package RSA
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+use phpseclib3\Crypt\RSA as RSA2;
+use phpseclib3\Crypt\PublicKeyLoader;
+use phpseclib3\Crypt\Common\AsymmetricKey;
+use phpseclib3\Crypt\Common\PublicKey;
+use phpseclib3\Crypt\Common\PrivateKey;
+use phpseclib3\Exception\UnsupportedAlgorithmException;
+use phpseclib3\Exception\UnsupportedFormatException;
+use phpseclib3\Exception\NoKeyLoadedException;
+use phpseclib3\Crypt\Common\Formats\Keys\PuTTY;
+use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH;
+use phpseclib3\Math\BigInteger;
+use phpseclib\Math\BigInteger as BigInteger2;
+
+/**
+ * Pure-PHP PKCS#1 compliant implementation of RSA.
+ *
+ * @package RSA
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class RSA
+{
+ /**#@+
+ * @access public
+ * @see self::encrypt()
+ * @see self::decrypt()
+ */
+ /**
+ * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
+ * (OAEP) for encryption / decryption.
+ *
+ * Uses sha1 by default.
+ *
+ * @see self::setHash()
+ * @see self::setMGFHash()
+ */
+ const ENCRYPTION_OAEP = 1;
+ /**
+ * Use PKCS#1 padding.
+ *
+ * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
+ * compatibility with protocols (like SSH-1) written before OAEP's introduction.
+ */
+ const ENCRYPTION_PKCS1 = 2;
+ /**
+ * Do not use any padding
+ *
+ * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
+ * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
+ */
+ const ENCRYPTION_NONE = 3;
+ /**#@-*/
+
+ /**#@+
+ * @access public
+ * @see self::sign()
+ * @see self::verify()
+ * @see self::setHash()
+ */
+ /**
+ * Use the Probabilistic Signature Scheme for signing
+ *
+ * Uses sha1 by default.
+ *
+ * @see self::setSaltLength()
+ * @see self::setMGFHash()
+ */
+ const SIGNATURE_PSS = 1;
+ /**
+ * Use the PKCS#1 scheme by default.
+ *
+ * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
+ * compatibility with protocols (like SSH-2) written before PSS's introduction.
+ */
+ const SIGNATURE_PKCS1 = 2;
+ /**#@-*/
+
+ /**#@+
+ * @access public
+ * @see \phpseclib\Crypt\RSA::createKey()
+ * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat()
+ */
+ /**
+ * PKCS#1 formatted private key
+ *
+ * Used by OpenSSH
+ */
+ const PRIVATE_FORMAT_PKCS1 = 0;
+ /**
+ * PuTTY formatted private key
+ */
+ const PRIVATE_FORMAT_PUTTY = 1;
+ /**
+ * XML formatted private key
+ */
+ const PRIVATE_FORMAT_XML = 2;
+ /**
+ * PKCS#8 formatted private key
+ */
+ const PRIVATE_FORMAT_PKCS8 = 8;
+ /**
+ * OpenSSH formatted private key
+ */
+ const PRIVATE_FORMAT_OPENSSH = 9;
+ /**#@-*/
+
+ /**#@+
+ * @access public
+ * @see \phpseclib\Crypt\RSA::createKey()
+ * @see \phpseclib\Crypt\RSA::setPublicKeyFormat()
+ */
+ /**
+ * Raw public key
+ *
+ * An array containing two \phpseclib\Math\BigInteger objects.
+ *
+ * The exponent can be indexed with any of the following:
+ *
+ * 0, e, exponent, publicExponent
+ *
+ * The modulus can be indexed with any of the following:
+ *
+ * 1, n, modulo, modulus
+ */
+ const PUBLIC_FORMAT_RAW = 3;
+ /**
+ * PKCS#1 formatted public key (raw)
+ *
+ * Used by File/X509.php
+ *
+ * Has the following header:
+ *
+ * -----BEGIN RSA PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pem format (as specified by -m)
+ */
+ const PUBLIC_FORMAT_PKCS1 = 4;
+ const PUBLIC_FORMAT_PKCS1_RAW = 4;
+ /**
+ * XML formatted public key
+ */
+ const PUBLIC_FORMAT_XML = 5;
+ /**
+ * OpenSSH formatted public key
+ *
+ * Place in $HOME/.ssh/authorized_keys
+ */
+ const PUBLIC_FORMAT_OPENSSH = 6;
+ /**
+ * PKCS#1 formatted public key (encapsulated)
+ *
+ * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set)
+ *
+ * Has the following header:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ *
+ * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
+ * is specific to private keys it's basically creating a DER-encoded wrapper
+ * for keys. This just extends that same concept to public keys (much like ssh-keygen)
+ */
+ const PUBLIC_FORMAT_PKCS8 = 7;
+ /**#@-*/
+
+ /**
+ * The Original Key
+ *
+ * @see self::getComment()
+ * @var string
+ * @access private
+ */
+ private $origKey = null;
+
+ /**
+ * The Key
+ *
+ * @var \phpseclib3\Crypt\Common\AsymmetricKey
+ * @access private
+ */
+ private $key = null;
+
+ /**
+ * Password
+ *
+ * @var string
+ * @access private
+ */
+ private $password = false;
+
+ /**
+ * Private Key Format
+ *
+ * @var int
+ * @access private
+ */
+ private $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1;
+
+ /**
+ * Public Key Format
+ *
+ * @var int
+ * @access public
+ */
+ private $publicKeyFormat = self::PUBLIC_FORMAT_PKCS1;
+
+ /**
+ * Public key comment field.
+ *
+ * @var string
+ * @access private
+ */
+ private $comment = 'phpseclib-generated-key';
+
+ /**
+ * Encryption mode
+ *
+ * @var int
+ * @access private
+ */
+ private $encryptionMode = self::ENCRYPTION_OAEP;
+
+ /**
+ * Signature mode
+ *
+ * @var int
+ * @access private
+ */
+ private $signatureMode = self::SIGNATURE_PSS;
+
+ /**
+ * Hash name
+ *
+ * @var string
+ * @access private
+ */
+ private $hash = 'sha1';
+
+ /**
+ * Hash function for the Mask Generation Function
+ *
+ * @var string
+ * @access private
+ */
+ private $mgfHash = 'sha1';
+
+ /**
+ * Length of salt
+ *
+ * @var int
+ * @access private
+ */
+ private $sLen;
+
+ /**
+ * The constructor
+ *
+ * @return \phpseclib\Crypt\RSA
+ * @access public
+ */
+ public function __construct()
+ {
+ // don't do anything
+ }
+
+ /**
+ * Create public / private key pair
+ *
+ * Returns an array with the following three elements:
+ * - 'privatekey': The private key.
+ * - 'publickey': The public key.
+ * - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
+ * Will need to be passed back to \phpseclib\Crypt\RSA::createKey() as the third parameter for further processing.
+ *
+ * @access public
+ * @param int $bits
+ */
+ public function createKey($bits = 1024)
+ {
+ $privatekey = RSA2::createKey($bits);
+
+ return [
+ 'privatekey' => $privatekey,
+ 'publickey' => $privatekey->getPublicKey(),
+ 'partialkey' => false
+ ];
+ }
+
+ /**
+ * Returns the key size
+ *
+ * More specifically, this returns the size of the modulo in bits.
+ *
+ * @access public
+ * @return int
+ */
+ public function getSize()
+ {
+ // for EC and RSA keys this'll return an integer
+ // for DSA keys this'll return an array (L + N)
+ return isset($this->key) ? $this->key->getLength() : 0;
+ }
+
+ /**
+ * Sets the password
+ *
+ * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
+ * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
+ *
+ * @see self::createKey()
+ * @see self::loadKey()
+ * @access public
+ * @param string $password
+ */
+ public function setPassword($password = false)
+ {
+ $this->password = $password;
+ }
+
+ /**
+ * Loads a public or private key
+ *
+ * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
+ *
+ * @access public
+ * @param string|RSA|array $key
+ * @param bool|int $type optional
+ * @return bool
+ */
+ public function loadKey($key)
+ {
+ if ($key instanceof AsymmetricKey) {
+ $this->key = $key;
+ } else if ($key instanceof RSA) {
+ $this->key = $key->key;
+ } else {
+ try {
+ if (is_array($key)) {
+ foreach ($key as &$value) {
+ if ($value instanceof BigInteger2) {
+ $value = new BigInteger($value->toBytes(true), -256);
+ }
+ }
+ }
+ $this->key = PublicKeyLoader::load($key, $this->password);
+ } catch (NoKeyLoadedException $e) {
+ $this->key = $this->origKey = null;
+ return false;
+ }
+ $this->origKey = $key;
+ }
+
+ // with phpseclib 2.0 loading a key does not reset any of the following
+ // so we'll need to preserve the old settings whenever a new key is loaded
+ // with this shim
+ $this->setEncryptionMode($this->encryptionMode);
+ //$this->setSignatureMode($this->signatureMode);
+ $this->setHash($this->hash);
+ $this->setMGFHash($this->mgfHash);
+ $this->setSaltLength($this->sLen);
+
+ return true;
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * @access public
+ * @return string
+ */
+ public function __toString()
+ {
+ PuTTY::setComment($this->comment);
+ OpenSSH::setComment($this->comment);
+
+ if ($this->key instanceof PublicKey) {
+ return $this->key->toString(self::const2str($this->publicKeyFormat));
+ }
+
+ if ($this->key instanceof PrivateKey) {
+ try {
+ return $this->key->toString(self::const2str($this->privateKeyFormat));
+ } catch (UnsupportedFormatException $e) {
+ if ($this->password) {
+ return $this->key->withPassword()->toString(self::const2str($this->privateKeyFormat));
+ }
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Defines the public key
+ *
+ * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
+ * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
+ * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
+ * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
+ * exponent this won't work unless you manually add the public exponent. phpseclib tries to guess if the key being used
+ * is the public key but in the event that it guesses incorrectly you might still want to explicitly set the key as being
+ * public.
+ *
+ * Do note that when a new key is loaded the index will be cleared.
+ *
+ * Returns true on success, false on failure
+ *
+ * @see self::getPublicKey()
+ * @access public
+ * @param string $key optional
+ * @param int $type optional
+ * @return bool
+ */
+ public function setPublicKey()
+ {
+ return false;
+ }
+
+ /**
+ * Defines the private key
+ *
+ * If phpseclib guessed a private key was a public key and loaded it as such it might be desirable to force
+ * phpseclib to treat the key as a private key. This function will do that.
+ *
+ * Do note that when a new key is loaded the index will be cleared.
+ *
+ * Returns true on success, false on failure
+ *
+ * @see self::getPublicKey()
+ * @access public
+ * @param string $key optional
+ * @param int $type optional
+ * @return bool
+ */
+ public function setPrivateKey($key = false)
+ {
+ if ($key === false && $this->key instanceof RSA2) {
+ $this->key = $this->key->asPrivateKey();
+ }
+
+ try {
+ $key = PublicKeyLoader::load($key);
+ } catch (NoKeyLoadedException $e) {
+ return false;
+ }
+ if ($key instanceof RSA2) {
+ $this->key = $key instanceof PublicKey ? $key->asPrivateKey() : $key;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the public key
+ *
+ * The public key is only returned under two circumstances - if the private key had the public key embedded within it
+ * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
+ * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
+ *
+ * @see self::getPublicKey()
+ * @access public
+ * @param string $key
+ * @param int $type optional
+ */
+ public function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
+ {
+ PuTTY::setComment($this->comment);
+ OpenSSH::setComment($this->comment);
+
+ if ($this->key instanceof PrivateKey) {
+ return $this->key->getPublicKey()->toString(self::const2str($type));
+ }
+
+ if ($this->key instanceof PublicKey) {
+ return $this->key->toString(self::const2str($type));
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the public key's fingerprint
+ *
+ * The public key's fingerprint is returned, which is equivalent to running `ssh-keygen -lf rsa.pub`. If there is
+ * no public key currently loaded, false is returned.
+ * Example output (md5): "c1:b1:30:29:d7:b8:de:6c:97:77:10:d7:46:41:63:87" (as specified by RFC 4716)
+ *
+ * @access public
+ * @param string $algorithm The hashing algorithm to be used. Valid options are 'md5' and 'sha256'. False is returned
+ * for invalid values.
+ * @return mixed
+ */
+ public function getPublicKeyFingerprint($algorithm = 'md5')
+ {
+ if ($this->key instanceof PublicKey) {
+ return $this->key->getFingerprint($algorithm);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the private key
+ *
+ * The private key is only returned if the currently loaded key contains the constituent prime numbers.
+ *
+ * @see self::getPublicKey()
+ * @access public
+ * @param string $key
+ * @param int $type optional
+ * @return mixed
+ */
+ public function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1)
+ {
+ PuTTY::setComment($this->comment);
+ OpenSSH::setComment($this->comment);
+
+ if ($this->key instanceof PrivateKey) {
+ try {
+ return $this->key->toString(self::const2str($this->privateKeyFormat));
+ } catch (UnsupportedFormatException $e) {
+ if ($this->password) {
+ return $this->key->withPassword()->toString(self::const2str($this->privateKeyFormat));
+ }
+ }
+
+ }
+
+ return false;
+ }
+
+ /**
+ * __clone() magic method
+ *
+ * @access public
+ * @return Crypt_RSA
+ */
+ public function __clone()
+ {
+ $key = new RSA();
+ $key->loadKey($this);
+ return $key;
+ }
+
+ /**
+ * Convert phpseclib 2.0 style constants to phpseclib 3.0 style strings
+ *
+ * @param int $const
+ * @access private
+ * @return string
+ */
+ private static function const2str($const)
+ {
+ switch ($const) {
+ case self::PRIVATE_FORMAT_PKCS1:
+ case self::PUBLIC_FORMAT_PKCS1:
+ return 'PKCS1';
+ case self::PRIVATE_FORMAT_PUTTY:
+ return 'PuTTY';
+ case self::PRIVATE_FORMAT_XML:
+ case self::PUBLIC_FORMAT_XML:
+ return 'XML';
+ case self::PRIVATE_FORMAT_PKCS8:
+ case self::PUBLIC_FORMAT_PKCS8:
+ return 'PKCS8';
+ case self::PRIVATE_FORMAT_OPENSSH:
+ case self::PUBLIC_FORMAT_OPENSSH:
+ return 'OpenSSH';
+ }
+ }
+
+ /**
+ * Determines the private key format
+ *
+ * @see self::createKey()
+ * @access public
+ * @param int $format
+ */
+ public function setPrivateKeyFormat($format)
+ {
+ $this->privateKeyFormat = $format;
+ }
+
+ /**
+ * Determines the public key format
+ *
+ * @see self::createKey()
+ * @access public
+ * @param int $format
+ */
+ public function setPublicKeyFormat($format)
+ {
+ $this->publicKeyFormat = $format;
+ }
+
+ /**
+ * Determines which hashing function should be used
+ *
+ * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and
+ * decryption. If $hash isn't supported, sha1 is used.
+ *
+ * @access public
+ * @param string $hash
+ */
+ public function setHash($hash)
+ {
+ $this->hash = $hash;
+ if ($this->key instanceof AsymmetricKey) {
+ try {
+ $this->key = $this->key->withHash($hash);
+ } catch (UnsupportedAlgorithmException $e) {
+ $this->key = $this->key->withHash('sha1');
+ }
+ }
+ }
+
+ /**
+ * Determines which hashing function should be used for the mask generation function
+ *
+ * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's
+ * best if Hash and MGFHash are set to the same thing this is not a requirement.
+ *
+ * @access public
+ * @param string $hash
+ */
+ public function setMGFHash($hash)
+ {
+ $this->mgfHash = $hash;
+ if ($this->key instanceof RSA2) {
+ try {
+ $this->key = $this->key->withMGFHash($hash);
+ } catch (UnsupportedAlgorithmException $e) {
+ $this->key = $this->key->withMGFHash('sha1');
+ }
+ }
+ }
+
+ /**
+ * Determines the salt length
+ *
+ * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
+ *
+ * Typical salt lengths in octets are hLen (the length of the output
+ * of the hash function Hash) and 0.
+ *
+ * @access public
+ * @param int $format
+ */
+ public function setSaltLength($sLen)
+ {
+ $this->sLen = $sLen;
+ if ($this->key instanceof RSA2) {
+ $this->key = $this->key->withSaltLength($sLen);
+ }
+ }
+
+ /**
+ * Set Encryption Mode
+ *
+ * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
+ *
+ * @access public
+ * @param int $mode
+ */
+ public function setEncryptionMode($mode)
+ {
+ $this->encryptionMode = $mode;
+ if ($this->key instanceof RSA2) {
+ $this->key = $this->key->withPadding(
+ self::enc2pad($this->encryptionMode) |
+ self::sig2pad($this->signatureMode)
+ );
+ }
+ }
+
+ /**
+ * Set Signature Mode
+ *
+ * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
+ *
+ * @access public
+ * @param int $mode
+ */
+ public function setSignatureMode($mode)
+ {
+ $this->signatureMode = $mode;
+ if ($this->key instanceof RSA2) {
+ $this->key = $this->key->withPadding(
+ self::enc2pad($this->encryptionMode) |
+ self::sig2pad($this->signatureMode)
+ );
+ }
+ }
+
+ /**
+ * Convert phpseclib 2.0 style constants to phpseclib 3.0 style constants
+ *
+ * @param int $mode
+ * @access private
+ * @return int
+ */
+ private function enc2pad($mode)
+ {
+ switch ($mode) {
+ case self::ENCRYPTION_PKCS1:
+ return RSA2::ENCRYPTION_PKCS1;
+ case self::ENCRYPTION_NONE:
+ return RSA2::ENCRYPTION_NONE;
+ //case self::ENCRYPTION_OAEP:
+ default:
+ return RSA2::ENCRYPTION_OAEP;
+ }
+ }
+
+ /**
+ * Convert phpseclib 2.0 style constants to phpseclib 3.0 style constants
+ *
+ * @param int $mode
+ * @access private
+ * @return int
+ */
+ private function sig2pad($mode)
+ {
+ switch ($mode) {
+ case self::SIGNATURE_PKCS1:
+ return RSA2::SIGNATURE_PKCS1;
+ //case self::SIGNATURE_PSS:
+ default:
+ return RSA2::SIGNATURE_PSS;
+ }
+ }
+
+ /**
+ * Set public key comment.
+ *
+ * @access public
+ * @param string $comment
+ */
+ public function setComment($comment)
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * Get public key comment.
+ *
+ * @access public
+ * @return string
+ */
+ public function getComment()
+ {
+ // we'd need to make the load method in the parent PuTTY and OpenSSH classes public instead of protected
+ // for this to work
+ try {
+ $key = PuTTY::load($this->origKey);
+ return $key['comment'];
+ } catch (\Exception $e) {}
+
+ try {
+ $key = OpenSSH::load($this->origKey);
+ return $key['comment'];
+ } catch (\Exception $e) {}
+
+ return '';
+ }
+
+ /**
+ * Encryption
+ *
+ * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
+ * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
+ * be concatenated together.
+ *
+ * @see self::decrypt()
+ * @access public
+ * @param string $plaintext
+ * @return string
+ */
+ public function encrypt($plaintext)
+ {
+ if (!$this->key instanceof RSA2) {
+ return false;
+ }
+ $key = $this->key;
+ if ($key instanceof PrivateKey) {
+ $key = $key->toString('Raw');
+ $temp = new static();
+ $temp->loadKey(['e' => $key['d'], 'n' => $key['n']]);
+ $key = $temp->key;
+ }
+ if ($key instanceof PublicKey) {
+ switch ($this->encryptionMode) {
+ case self::ENCRYPTION_PKCS1:
+ $len = ($key->getLength() - 88) >> 3;
+ break;
+ case self::ENCRYPTION_NONE:
+ $len = $key->getLength() >> 3;
+ break;
+ //case self::ENCRYPTION_OAEP:
+ default:
+ $len = ($key->getLength() - 2 * $key->getHash()->getLength() - 16) >> 3;
+ }
+ $plaintext = str_split($plaintext, $len);
+ $ciphertext = '';
+ foreach ($plaintext as $m) {
+ $ciphertext.= $key->encrypt($m);
+ }
+ return $ciphertext;
+ }
+
+ return false;
+ }
+
+ /**
+ * Decryption
+ *
+ * @see self::encrypt()
+ * @access public
+ * @param string $plaintext
+ * @return string
+ */
+ public function decrypt($ciphertext)
+ {
+ if (!$this->key instanceof RSA2) {
+ return false;
+ }
+ $key = $this->key;
+ if ($key instanceof PublicKey) {
+ $key = $key->asPrivateKey();
+ }
+ if ($key instanceof PrivateKey) {
+ $len = $key->getLength() >> 3;
+ $ciphertext = str_split($ciphertext, $len);
+ $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $len, chr(0), STR_PAD_LEFT);
+
+ $plaintext = '';
+ foreach ($ciphertext as $c) {
+ try {
+ $plaintext.= $key->decrypt($c);
+ } catch (\Exception $e) {
+ return false;
+ }
+ }
+ return $plaintext;
+ }
+
+ return false;
+ }
+
+ /**
+ * Create a signature
+ *
+ * @see self::verify()
+ * @access public
+ * @param string $message
+ * @return string
+ */
+ public function sign($message)
+ {
+ if ($this->key instanceof PrivateKey) {
+ return $this->key->sign($message);
+ }
+
+ return false;
+ }
+
+ /**
+ * Verifies a signature
+ *
+ * @see self::sign()
+ * @access public
+ * @param string $message
+ * @param string $signature
+ * @return bool
+ */
+ public function verify($message, $signature)
+ {
+ if ($this->key instanceof PublicKey) {
+ return $this->key->verify($message, $signature);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a public key object
+ *
+ * @access public
+ * @return AsymmetricKey|false
+ */
+ public function getKeyObject()
+ {
+ return $this->key;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Random.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Random.php
new file mode 100644
index 000000000..49a281b3d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Random.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Random Number Generator
+ *
+ * PHP version 5
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * echo bin2hex(\phpseclib\Crypt\Random::string(8));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package Random
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP Random Number Generator
+ *
+ * @package Random
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Random extends \phpseclib3\Crypt\Random
+{
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php
new file mode 100644
index 000000000..f3b9a7df8
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Rijndael.php
@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * Pure-PHP implementation of Rijndael.
+ *
+ * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
+ * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
+ * {@link self::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
+ * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
+ * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
+ *
+ * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
+ * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
+ * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
+ * are first defined as valid key / block lengths in
+ * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
+ * Extensions: Other block and Cipher Key lengths.
+ * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
+ *
+ * {@internal The variable names are the same as those in
+ * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $rijndael = new \phpseclib\Crypt\Rijndael();
+ *
+ * $rijndael->setKey('abcdefghijklmnop');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $rijndael->decrypt($rijndael->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package Rijndael
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2008 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of Rijndael.
+ *
+ * @package Rijndael
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Rijndael extends Base
+{
+ /**
+ * Sets the block length
+ *
+ * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
+ * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
+ *
+ * @access public
+ * @param int $length
+ */
+ public function setBlockLength($length)
+ {
+ $length >>= 5;
+ if ($length > 8) {
+ $length = 8;
+ } elseif ($length < 4) {
+ $length = 4;
+ }
+ $this->cipher->setBlockLength($length);
+ }
+
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length <= 128:
+ return 128;
+ case $length <= 160:
+ return 160;
+ case $length <= 192:
+ return 192;
+ case $length <= 224:
+ return 224;
+ default:
+ return 256;
+ }
+ }
+
+ /**
+ * Sets the password.
+ *
+ * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
+ * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1:
+ * $hash, $salt, $count, $dkLen
+ *
+ * Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
+ *
+ * @see Crypt/Hash.php
+ * @param string $password
+ * @param string $method
+ * @return bool
+ * @access public
+ * @internal Could, but not must, extend by the child Crypt_* class
+ */
+ public function setPassword($password, $method = 'pbkdf2')
+ {
+ $this->cipher->setKeyLength($this->key_length);
+ $args = func_get_args();
+ if (in_array($method, ['pbkdf1', 'pbkdf2']) && !isset($args[3])) {
+ $args[1] = $method;
+ $args[2] = isset($args[2]) ? $args[2] : 'sha1';
+ $args[3] = 'phpseclib';
+ }
+ $this->password = $args;
+ $this->cipher->setPassword(...$args);
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php
new file mode 100644
index 000000000..d66dc74d6
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/TripleDES.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Pure-PHP implementation of Triple DES.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
+ *
+ * PHP version 5
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $des = new \phpseclib\Crypt\TripleDES();
+ *
+ * $des->setKey('abcdefghijklmnopqrstuvwx');
+ *
+ * $size = 10 * 1024;
+ * $plaintext = '';
+ * for ($i = 0; $i < $size; $i++) {
+ * $plaintext.= 'a';
+ * }
+ *
+ * echo $des->decrypt($des->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package TripleDES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of Triple DES.
+ *
+ * @package TripleDES
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class TripleDES extends Base
+{
+ /**
+ * Encrypt / decrypt using inner chaining
+ *
+ * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (self::MODE_CBC3).
+ */
+ const MODE_3CBC = -2;
+
+ /**
+ * Encrypt / decrypt using outer chaining
+ *
+ * Outer chaining is used by SSH-2 and when the mode is set to \phpseclib\Crypt\Base::MODE_CBC.
+ */
+ const MODE_CBC3 = Base::MODE_CBC;
+
+ /**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * $mode could be:
+ *
+ * - \phpseclib\Crypt\Base::MODE_ECB
+ *
+ * - \phpseclib\Crypt\Base::MODE_CBC
+ *
+ * - \phpseclib\Crypt\Base::MODE_CTR
+ *
+ * - \phpseclib\Crypt\Base::MODE_CFB
+ *
+ * - \phpseclib\Crypt\Base::MODE_OFB
+ *
+ * - \phpseclib\Crypt\TripleDES::MODE_3CBC
+ *
+ * If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
+ *
+ * @see \phpseclib\Crypt\DES::__construct()
+ * @see \phpseclib\Crypt\Base::__construct()
+ * @param int $mode
+ * @access public
+ */
+ public function __construct($mode = self::MODE_CBC)
+ {
+ if ($mode == self::MODE_3CBC) {
+ $this->cipher = new \phpseclib3\Crypt\TripleDES('3cbc');
+ $this->key_length = $this->cipher->getKeyLength();
+ return;
+ }
+ parent::__construct($mode);
+ }
+
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length <= 64:
+ return 64;
+ case $length <= 128:
+ return 128;
+ default:
+ return 192;
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php
new file mode 100644
index 000000000..f57c54c9d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Crypt/Twofish.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Pure-PHP implementation of Twofish.
+ *
+ * Uses mcrypt, if available, and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://en.wikipedia.org/wiki/Twofish Wikipedia description of Twofish}
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $twofish = new \phpseclib\Crypt\Twofish();
+ *
+ * $twofish->setKey('12345678901234567890123456789012');
+ *
+ * $plaintext = str_repeat('a', 1024);
+ *
+ * echo $twofish->decrypt($twofish->encrypt($plaintext));
+ * ?>
+ * </code>
+ *
+ * @category Crypt
+ * @package Twofish
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @author Hans-Juergen Petrich <petrich@tronic-media.com>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Crypt;
+
+/**
+ * Pure-PHP implementation of Twofish.
+ *
+ * @package Twofish
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Twofish extends Base
+{
+ /**
+ * Turns key lengths, be they valid or invalid, to valid key lengths
+ *
+ * @param int $length
+ * @access private
+ * @return int
+ */
+ protected function calculateNewKeyLength($length)
+ {
+ switch (true) {
+ case $length <= 128:
+ return 128;
+ case $length <= 192:
+ return 192;
+ default:
+ return 256;
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/File/ANSI.php b/vendor/phpseclib/phpseclib2_compat/src/File/ANSI.php
new file mode 100644
index 000000000..73bbb8a24
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/File/ANSI.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * Pure-PHP ANSI Decoder
+ *
+ * PHP version 5
+ *
+ * If you call read() in \phpseclib\Net\SSH2 you may get {@link http://en.wikipedia.org/wiki/ANSI_escape_code ANSI escape codes} back.
+ * They'd look like chr(0x1B) . '[00m' or whatever (0x1B = ESC). They tell a
+ * {@link http://en.wikipedia.org/wiki/Terminal_emulator terminal emulator} how to format the characters, what
+ * color to display them in, etc. \phpseclib\File\ANSI is a {@link http://en.wikipedia.org/wiki/VT100 VT100} terminal emulator.
+ *
+ * @category File
+ * @package ANSI
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2012 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\File;
+
+/**
+ * Pure-PHP ANSI Decoder
+ *
+ * @package ANSI
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class ANSI extends \phpseclib3\File\ANSI
+{
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/File/ASN1.php b/vendor/phpseclib/phpseclib2_compat/src/File/ASN1.php
new file mode 100644
index 000000000..ceecf7c0e
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/File/ASN1.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * Pure-PHP ASN.1 Parser
+ *
+ * PHP version 5
+ *
+ * ASN.1 provides the semantics for data encoded using various schemes. The most commonly
+ * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded
+ * DER blobs.
+ *
+ * \phpseclib\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
+ *
+ * Uses the 1988 ASN.1 syntax.
+ *
+ * @category File
+ * @package ASN1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2012 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\File;
+
+/**
+ * Pure-PHP ASN.1 Parser
+ *
+ * @package ASN1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class ASN1 extends \phpseclib3\File\ASN1
+{
+ /**
+ * Parse BER-encoding
+ *
+ * Serves a similar purpose to openssl's asn1parse
+ *
+ * @param string $encoded
+ * @return array
+ * @access public
+ */
+ public static function decodeBER($encoded)
+ {
+ $decoded = parent::decodeBER($encoded);
+ if ($decoded === null) {
+ return [false];
+ }
+ return $decoded;
+ }
+
+ /**
+ * BER-decode the OID
+ *
+ * Called by _decode_ber()
+ *
+ * @access private
+ * @param string $content
+ * @return string
+ */
+ public function _decodeOID($content)
+ {
+ return $this->decodeOID($content);
+ }
+
+ /**
+ * DER-encode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
+ *
+ * @access private
+ * @param int $length
+ * @return string
+ */
+ public function _encodeLength($length)
+ {
+ return $this->encodeLength($length);
+ }
+
+ /**
+ * DER-encode the OID
+ *
+ * Called by _encode_der()
+ *
+ * @access private
+ * @param string $content
+ * @return string
+ */
+ public function _encodeOID($source)
+ {
+ return $this->encodeOID($source);
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php b/vendor/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php
new file mode 100644
index 000000000..fe1871eb3
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/File/ASN1/Element.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Pure-PHP ASN.1 Parser
+ *
+ * PHP version 5
+ *
+ * @category File
+ * @package ASN1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2012 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\File\ASN1;
+
+/**
+ * ASN.1 Element
+ *
+ * Bypass normal encoding rules in phpseclib\File\ASN1::encodeDER()
+ *
+ * @package ASN1
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Element extends \phpseclib3\File\ASN1\Element
+{
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/File/X509.php b/vendor/phpseclib/phpseclib2_compat/src/File/X509.php
new file mode 100644
index 000000000..e285ab0b9
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/File/X509.php
@@ -0,0 +1,407 @@
+<?php
+
+/**
+ * Pure-PHP X.509 Parser
+ *
+ * PHP version 5
+ *
+ * Encode and decode X.509 certificates.
+ *
+ * The extensions are from {@link http://tools.ietf.org/html/rfc5280 RFC5280} and
+ * {@link http://web.archive.org/web/19961027104704/http://www3.netscape.com/eng/security/cert-exts.html Netscape Certificate Extensions}.
+ *
+ * Note that loading an X.509 certificate and resaving it may invalidate the signature. The reason being that the signature is based on a
+ * portion of the certificate that contains optional parameters with default values. ie. if the parameter isn't there the default value is
+ * used. Problem is, if the parameter is there and it just so happens to have the default value there are two ways that that parameter can
+ * be encoded. It can be encoded explicitly or left out all together. This would effect the signature value and thus may invalidate the
+ * the certificate all together unless the certificate is re-signed.
+ *
+ * @category File
+ * @package X509
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2012 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\File;
+
+use phpseclib\Crypt\RSA;
+use phpseclib3\File\ASN1\Element as NewElement;
+use phpseclib\File\ASN1\Element as OldElement;
+
+/**
+ * Pure-PHP X.509 Parser
+ *
+ * @package X509
+ * @method bool|mixed[] loadX509(string $cert, int $mode = X509::FORMAT_AUTO_DETECT)
+ * @method string|false saveX509(mixed[] $cert, int $format = X509::FORMAT_PEM)
+ * @method bool loadCA(string $cert)
+ * @method bool validateURL(string $url)
+ * @method bool validateDate(\DateTimeInterface|string $date = null)
+ * @method ?bool validateSignature(bool $caonly = true)
+ * @method static void setRecurLimit(int $count)
+ * @method static void disableURLFetch()
+ * @method static void enableURLFetch()
+ * @method static string decodeIP(string $ip)
+ * @method static array{bool|string, bool|string} decodeNameConstraintIP(string $ip)
+ * @method static string encodeIP(string|array{string, string} $ip)
+ * @method bool setDNProp(string $propName, mixed $propValue, string $type = 'utf8String')
+ * @method void removeDNProp(string $propName)
+ * @method mixed[] getDNProp(string $propName, mixed[] $dn, bool $withType = false)
+ * @method bool setDN(mixed $dn, bool $merge = false, string $type = 'utf8String')
+ * @method mixed getDN(int $format = X509::DN_ARRAY, mixed[] $dn = null)
+ * @method mixed getIssuerDN(int $format = X509::DN_ARRAY)
+ * @method mixed getSubjectDN(int $format = X509::DN_ARRAY)
+ * @method mixed getIssuerDNProp(string $propName, bool $withType = false)
+ * @method mixed getSubjectDNProp(string $propName, bool $withType = false)
+ * @method mixed[] getChain()
+ * @method bool|mixed[] getCurrentCert()
+ * @method void setChallenge(string $challenge)
+ * @method PublicKey|false getPublicKey()
+ * @method bool|mixed[] loadCSR(string $csr, int $mode = X509::FORMAT_AUTO_DETECT)
+ * @method string|false saveCSR(array $csr, int $format = X509::FORMAT_PEM)
+ * @method bool|mixed[] loadSPKAC(string $spkac)
+ * @method string|false saveSPKAC(array $spkac, int $format = X509::FORMAT_PEM)
+ * @method bool|mixed[] loadCRL(string $crl, int $mode = X509::FORMAT_AUTO_DETECT)
+ * @method string|false saveCRL(array $crl, int $format = X509::FORMAT_PEM)
+ * @method bool|mixed[] sign(X509 $issuer, X509 $subject)
+ * @method bool|mixed[] signCSR()
+ * @method bool|mixed[] signSPKAC()
+ * @method bool|mixed[] signCRL(X509 $issuer, X509 $crl)
+ * @method void setStartDate(\DateTimeInterface|string $date)
+ * @method void setEndDate(\DateTimeInterface|string $date)
+ * @method void setSerialNumber(string $serial, int $base = -256)
+ * @method void makeCA()
+ * @method bool removeExtension(string $id)
+ * @method mixed getExtension(string $id, mixed[] $cert = null, string $path = null)
+ * @method mixed[] getExtension(mixed[] $cert = null, string $path = null)
+ * @method bool setExtension(mixed[] $cert = null, mixed $value, string $path = null)
+ * @method bool removeAttribute(string $id, int $disposition = X509::ATTR_ALL)
+ * @method mixed getAttribute(string $id, int $disposition = X509::ATTR_ALL, array $csr = null)
+ * @method mixed[] getAttributes(mixed[] $csr = null)
+ * @method void setKeyIdentifier(string $value)
+ * @method mixed computeKeyIdentifier(mixed $key = null, int $method = 1)
+ * @method void setDomain(string ...$domains)
+ * @method void setIPAddress(mixed ...$ipAddresses)
+ * @method bool revoke(string $serial, string $date = null)
+ * @method bool unrevoke(string $serial)
+ * @method mixed getRevoked(string $serial)
+ * @method mixed[] listRevoked(mixed[] $crl = null)
+ * @method bool removeRevokedCertificateExtension(string $serial, string $id)
+ * @method mixed getRevokedCertificateExtension(string $serial, string $id, mixed[] $crl = null)
+ * @method bool|mixed[] getRevokedCertificateExtensions(string $serial, mixed[] $crl = null)
+ * @method bool setRevokedCertificateExtension(string $serial, string $id, $value, bool $critical = false, bool $replace = true)
+ * @method static void registerExtension(string $id, mixed[] $mapping)
+ * @method static ?mixed[] getRegisteredExtension(string $id)
+ * @method static void setExtensionValue(string $id, $value, bool $critical = false, bool $replace = false)
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class X509
+{
+ /**
+ * Flag to only accept signatures signed by certificate authorities
+ *
+ * Not really used anymore but retained all the same to suppress E_NOTICEs from old installs
+ *
+ * @access public
+ */
+ const VALIDATE_SIGNATURE_BY_CA = 1;
+
+ /**#@+
+ * @access public
+ * @see \phpseclib3\File\X509::getDN()
+ */
+ /**
+ * Return internal array representation
+ */
+ const DN_ARRAY = 0;
+ /**
+ * Return string
+ */
+ const DN_STRING = 1;
+ /**
+ * Return ASN.1 name string
+ */
+ const DN_ASN1 = 2;
+ /**
+ * Return OpenSSL compatible array
+ */
+ const DN_OPENSSL = 3;
+ /**
+ * Return canonical ASN.1 RDNs string
+ */
+ const DN_CANON = 4;
+ /**
+ * Return name hash for file indexing
+ */
+ const DN_HASH = 5;
+ /**#@-*/
+
+ /**#@+
+ * @access public
+ * @see \phpseclib3\File\X509::saveX509()
+ * @see \phpseclib3\File\X509::saveCSR()
+ * @see \phpseclib3\File\X509::saveCRL()
+ */
+ /**
+ * Save as PEM
+ *
+ * ie. a base64-encoded PEM with a header and a footer
+ */
+ const FORMAT_PEM = 0;
+ /**
+ * Save as DER
+ */
+ const FORMAT_DER = 1;
+ /**
+ * Save as a SPKAC
+ *
+ * Only works on CSRs. Not currently supported.
+ */
+ const FORMAT_SPKAC = 2;
+ /**
+ * Auto-detect the format
+ *
+ * Used only by the load*() functions
+ */
+ const FORMAT_AUTO_DETECT = 3;
+ /**#@-*/
+
+ /**
+ * Attribute value disposition.
+ * If disposition is >= 0, this is the index of the target value.
+ */
+ const ATTR_ALL = -1; // All attribute values (array).
+ const ATTR_APPEND = -2; // Add a value.
+ const ATTR_REPLACE = -3; // Clear first, then add a value.
+
+ /**
+ * The X509 object
+ *
+ * @var \phpseclib3\File\X509
+ * @access private
+ */
+ private $x509;
+
+ /**
+ * Default Constructor.
+ *
+ * @return \phpseclib\File\X509
+ * @access public
+ */
+ public function __construct()
+ {
+ // we don't extend phpseclib3\File\X509 because the setPublicKey() and setPrivateKey() methods
+ // have different method signatures
+ $this->x509 = new \phpseclib3\File\X509();
+ }
+
+ /**
+ * __call() magic method
+ *
+ * @access public
+ */
+ public function __call($name, $args)
+ {
+ foreach ($args as &$arg) {
+ if ($arg instanceof \phpseclib\File\X509) {
+ $arg = $arg->x509;
+ }
+ }
+
+ switch ($name) {
+ case 'loadX509':
+ case 'saveX509':
+ case 'sign':
+ $part1 = 'tbsCertificate';
+ $part2 = 'extensions';
+ break;
+ case 'loadCRL':
+ case 'saveCRL':
+ case 'signCRL':
+ $part1 = 'tbsCertList';
+ $part2 = 'crlExtensions';
+ break;
+ case 'loadCSR':
+ case 'saveCSR':
+ case 'signCSR':
+ $part1 = 'certificationRequestInfo';
+ $part2 = 'attributes';
+ }
+
+ if (isset($args[0])) {
+ switch ($name) {
+ case 'saveX509':
+ case 'saveCRL':
+ case 'saveCSR':
+ if (isset($args[0][$part1][$part2])) {
+ $arr = &$args[0][$part1][$part2];
+ if ($part2 == 'attributes') {
+ foreach ($arr as &$attr) {
+ if (isset($attr['type']) && $attr['type'] == 'pkcs-9-at-extensionRequest') {
+ $arr = $attr['value'][0];
+ break;
+ }
+ }
+ }
+ foreach ($arr as &$extension) {
+ if ($extension instanceof NewElement || !is_array($extension)) {
+ continue;
+ }
+ if (is_string($extension['extnValue'])) {
+ $extension['extnValue'] = base64_decode($extension['extnValue']);
+ }
+ }
+ }
+
+ if (isset($args[0]['signature'])) {
+ $args[0]['signature'] = base64_decode($args[0]['signature']);
+ }
+ }
+ }
+
+ $result = $this->x509->$name(...$args);
+ if ($result instanceof \phpseclib3\File\X509) {
+ $temp = new static;
+ $temp->x509 = $result;
+ return $temp;
+ }
+
+ if (!is_array($result)) {
+ return $result;
+ }
+
+ $result = self::replaceNewElements($result);
+
+ if (!isset($part1)) {
+ return $result;
+ }
+
+ if (isset($result[$part1][$part2])) {
+ $arr = &$result[$part1][$part2];
+ if ($part2 == 'attributes') {
+ foreach ($arr as &$attr) {
+ if (isset($attr['type']) && $attr['type'] == 'pkcs-9-at-extensionRequest') {
+ $arr = $attr['value'][0];
+ break;
+ }
+ }
+ }
+ foreach ($arr as &$extension) {
+ if ($extension instanceof NewElement || !is_array($extension)) {
+ continue;
+ }
+ if (is_string($extension['extnValue'])) {
+ $extension['extnValue'] = base64_encode($extension['extnValue']);
+ }
+ }
+ }
+
+ if (isset($result['signature'])) {
+ $result['signature'] = base64_encode($result['signature']);
+ }
+
+ return $result;
+ }
+
+ /**
+ * __callStatic() magic method
+ *
+ * @access public
+ */
+ public static function __callStatic($name, $args)
+ {
+ return \phpseclib3\File\X509::$name(...$args);
+ }
+
+ /**
+ * Set public key
+ *
+ * Key needs to be a \phpseclib\Crypt\RSA object
+ *
+ * @param object $key
+ * @access public
+ * @return bool
+ */
+ public function setPublicKey($key)
+ {
+ if (!$key instanceof RSA) {
+ return;
+ }
+ $key = $key->getKeyObject();
+ if ($key instanceof \phpseclib3\Crypt\Common\PublicKey) {
+ if ($key instanceof \phpseclib3\Crypt\RSA) {
+ $key = $key->withPadding(\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
+ }
+ $this->x509->setPublicKey($key);
+ }
+ }
+
+ /**
+ * Set private key
+ *
+ * Key needs to be a \phpseclib\Crypt\RSA object
+ *
+ * @param object $key
+ * @access public
+ */
+ public function setPrivateKey($key)
+ {
+ if (!$key instanceof RSA) {
+ return;
+ }
+ $key = $key->getKeyObject();
+ if ($key instanceof \phpseclib3\Crypt\Common\PrivateKey) {
+ if ($key instanceof \phpseclib3\Crypt\RSA) {
+ $key = $key->withPadding(\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1);
+ }
+ $this->x509->setPrivateKey($key);
+ }
+ }
+
+ /**
+ * Returns the OID corresponding to a name
+ *
+ * What's returned in the associative array returned by loadX509() (or load*()) is either a name or an OID if
+ * no OID to name mapping is available. The problem with this is that what may be an unmapped OID in one version
+ * of phpseclib may not be unmapped in the next version, so apps that are looking at this OID may not be able
+ * to work from version to version.
+ *
+ * This method will return the OID if a name is passed to it and if no mapping is avialable it'll assume that
+ * what's being passed to it already is an OID and return that instead. A few examples.
+ *
+ * getOID('2.16.840.1.101.3.4.2.1') == '2.16.840.1.101.3.4.2.1'
+ * getOID('id-sha256') == '2.16.840.1.101.3.4.2.1'
+ * getOID('zzz') == 'zzz'
+ *
+ * @access public
+ * @return string
+ */
+ public function getOID($name)
+ {
+ return \phpseclib3\File\ASN1::getOID($name);
+ }
+
+ /**
+ * Replaces \phpseclib3\File\ASN1\Element with \phpseclib\File\ASN1\Element
+ *
+ * @return array
+ */
+ private static function replaceNewElements($el)
+ {
+ switch (true) {
+ case $el instanceof NewElement:
+ return new OldElement($el->element);
+ case !is_array($el):
+ return $el;
+ }
+
+ foreach ($el as &$val) {
+ $val = self::replaceNewElements($val);
+ }
+
+ return $el;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Math/BigInteger.php b/vendor/phpseclib/phpseclib2_compat/src/Math/BigInteger.php
new file mode 100644
index 000000000..905d8fe65
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Math/BigInteger.php
@@ -0,0 +1,308 @@
+<?php
+
+/**
+ * Pure-PHP arbitrary precision integer arithmetic library.
+ *
+ * Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
+ * and an internal implementation, otherwise.
+ *
+ * PHP version 5
+ *
+ * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
+ * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
+ *
+ * BigInteger uses base-2**26 to perform operations such as multiplication and division and
+ * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
+ * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
+ * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
+ * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
+ * which only supports integers. Although this fact will slow this library down, the fact that such a high
+ * base is being used should more than compensate.
+ *
+ * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
+ * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = array(0, 1)
+ *
+ * Useful resources are as follows:
+ *
+ * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
+ * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
+ * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
+ *
+ * Here's an example of how to use this library:
+ * <code>
+ * <?php
+ * $a = new \phpseclib\Math\BigInteger(2);
+ * $b = new \phpseclib\Math\BigInteger(3);
+ *
+ * $c = $a->add($b);
+ *
+ * echo $c->toString(); // outputs 5
+ * ?>
+ * </code>
+ *
+ * @category Math
+ * @package BigInteger
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2006 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+namespace phpseclib\Math;
+
+use phpseclib3\Math\BigInteger as BigInteger2;
+
+/**
+ * Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
+ * numbers.
+ *
+ * @package BigInteger
+ * @method string toString()
+ * @method string toBytes()
+ * @method string toHex()
+ * @method string toBits()
+ * @method BigInteger add(BigInteger $y)
+ * @method BigInteger subtract(BigInteger $y)
+ * @method BigInteger multiply(BigInteger $x)
+ * @method array{BigInteger, BigInteger} divide(BigInteger $x)
+ * @method BigInteger modInverse(BigInteger $n)
+ * @method {'gcd': BigInteger, 'x': BigInteger, 'y': BigInteger} extendedGCD(BigInteger $n)
+ * @method BigInteger gcd(BigInteger $n)
+ * @method BigInteger abs()
+ * @method void setPrecision(int $bits)
+ * @method int|bool getPrecision()
+ * @method BigInteger powMod(BigInteger $e, BigInteger $n)
+ * @method BigInteger modPow(BigInteger $e, BigInteger $n)
+ * @method int compare(BigInteger $y)
+ * @method bool equals(BigInteger $x)
+ * @method BigInteger bitwise_not()
+ * @method BigInteger bitwise_and(BigInteger $x)
+ * @method BigInteger bitwise_or(BigInteger $x)
+ * @method BigInteger bitwise_rightShift($shift)
+ * @method BigInteger bitwise_leftShift($shift)
+ * @method BigInteger bitwise_leftRotate($shift)
+ * @method BigInteger bitwise_rightRotate($shift)
+ * @method {'min': BigInteger, 'max': BigInteger} minMaxBits($bits)
+ * @method int getLength()
+ * @method int getLengthInBytes()
+ * @method bool isPrime(int|bool $t = false)
+ * @method BigInteger root(int $n = 2)
+ * @method BigInteger pow(BigInteger $n)
+ * @method static BigInteger min(BigInteger ...$nums)
+ * @method static BigInteger max(BigInteger ...$nums)
+ * @method bool between(BigInteger $min, BigInteger $max)
+ * @method bool isOdd()
+ * @method bool testBit(int $x)
+ * @method bool isNegative()
+ * @method BigInteger negate()
+ * @method callable createRecurringModuloFunction()
+ * @method BigInteger[] bitwise_split(int $split)
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class BigInteger
+{
+ /**#@+
+ * Array constants
+ *
+ * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
+ *
+ * @access private
+ */
+ /**
+ * $result[self::VALUE] contains the value.
+ */
+ const VALUE = 0;
+ /**
+ * $result[self::SIGN] contains the sign.
+ */
+ const SIGN = 1;
+ /**#@-*/
+
+ /**#@+
+ * @access private
+ * @see BigInteger::_montgomery()
+ * @see BigInteger::_barrett()
+ */
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ */
+ const DATA = 1;
+ /**#@-*/
+
+ /**#@+
+ * Mode constants.
+ *
+ * @access private
+ * @see BigInteger::__construct()
+ */
+ /**
+ * To use the pure-PHP implementation
+ */
+ const MODE_INTERNAL = 1;
+ /**
+ * To use the BCMath library
+ *
+ * (if enabled; otherwise, the internal implementation will be used)
+ */
+ const MODE_BCMATH = 2;
+ /**
+ * To use the GMP library
+ *
+ * (if present; otherwise, either the BCMath or the internal implementation will be used)
+ */
+ const MODE_GMP = 3;
+ /**#@-*/
+
+ /**
+ * The BigInteger object
+ *
+ * @var \phpseclib3\Math\BigInteger
+ * @access private
+ */
+ private $bigint;
+
+ /**
+ * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
+ *
+ * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
+ * two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
+ *
+ * Here's an example:
+ * <code>
+ * <?php
+ * $a = new \phpseclib\Math\BigInteger('0x32', 16); // 50 in base-16
+ *
+ * echo $a->toString(); // outputs 50
+ * ?>
+ * </code>
+ *
+ * @param $x base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @return \phpseclib\Math\BigInteger
+ * @access public
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ $this->bigint = new BigInteger2($x, $base);
+ }
+
+ /**
+ * __call() magic method
+ *
+ * @access public
+ */
+ public function __call($name, $args)
+ {
+ foreach ($args as &$arg) {
+ if ($arg instanceof BigInteger) {
+ $arg = $arg->bigint;
+ }
+ }
+ $result = $this->bigint->$name(...$args);
+ if (!$result instanceof BigInteger2) {
+ return $result;
+ }
+
+ $temp = new static;
+ $temp->bigint = $result;
+
+ return $temp;
+ }
+
+ /**
+ * __toString() magic method
+ *
+ * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
+ * toString().
+ *
+ * @access public
+ * @internal Implemented per a suggestion by Techie-Michael - thanks!
+ */
+ public function __toString()
+ {
+ return $this->bigint->__toString();
+ }
+
+ /**
+ * __debugInfo() magic method
+ *
+ * Will be called, automatically, when print_r() or var_dump() are called
+ *
+ * @access public
+ */
+ public function __debugInfo()
+ {
+ return $this->bigint->__debugInfo();
+ }
+
+ /**
+ * Generate a random number
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * $min->random($max)
+ * $max->random($min)
+ *
+ * @param \phpseclib\Math\BigInteger $arg1
+ * @param \phpseclib\Math\BigInteger $arg2
+ * @return \phpseclib\Math\BigInteger
+ * @access public
+ * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object.
+ * That method is still supported for BC purposes.
+ */
+ public function random($arg1, $arg2 = false)
+ {
+ $temp = new static;
+ $temp->bigint = BigInteger2::randomRange(
+ $arg1->bigint,
+ $arg2 instanceof BigInteger ? $arg2->bigint : $this->bigint
+ );
+ return $temp;
+ }
+
+ /**
+ * Generate a random prime number.
+ *
+ * If there's not a prime within the given range, false will be returned.
+ * If more than $timeout seconds have elapsed, give up and return false.
+ *
+ * @param \phpseclib\Math\BigInteger $arg1
+ * @param \phpseclib\Math\BigInteger $arg2
+ * @return Math_BigInteger|false
+ * @access public
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
+ */
+ public function randomPrime($arg1, $arg2 = false)
+ {
+ $temp = new static;
+ $temp->bigint = BigInteger2::randomRange(
+ $arg1->bigint,
+ $arg2 instanceof BigInteger ? $arg2->bigint : $this->bigint
+ );
+ return $temp;
+ }
+
+ /**
+ * Logical Exclusive-Or
+ *
+ * See https://github.com/phpseclib/phpseclib/issues/1245 for more context
+ *
+ * @param \phpseclib\Math\BigInteger $x
+ * @access public
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function bitwise_xor($x)
+ {
+ $temp = new static;
+ $temp->bigint = $this->bigint->abs()->bitwise_xor($x->bigint->abs());
+ return $temp;
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP.php b/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP.php
new file mode 100644
index 000000000..fab1f7219
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP.php
@@ -0,0 +1,276 @@
+<?php
+
+/**
+ * Pure-PHP implementation of SFTP.
+ *
+ * PHP version 5
+ *
+ * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
+ * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
+ * to an SFTPv4/5/6 server.
+ *
+ * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
+ *
+ * Here's a short example of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $sftp = new \phpseclib\Net\SFTP('www.domain.tld');
+ * if (!$sftp->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $sftp->pwd() . "\r\n";
+ * $sftp->put('filename.ext', 'hello, world!');
+ * print_r($sftp->nlist());
+ * ?>
+ * </code>
+ *
+ * @category Net
+ * @package SFTP
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Net;
+
+use phpseclib\Crypt\RSA;
+
+/**
+ * Pure-PHP implementation of SFTP.
+ *
+ * @package SFTP
+ * @method void disableStatCache()
+ * @method void enableStatCache()
+ * @method void clearStatCache()
+ * @method void enablePathCanonicalization()
+ * @method void disablePathCanonicalization()
+ * @method void enableArbitraryLengthPackets()
+ * @method void disableArbitraryLengthPackets()
+ * @method string|false pwd()
+ * @method string|false realpath(string $path)
+ * @method bool chdir(string $dir)
+ * @method string[]|false nlist(string $dir = '.', bool $recursive = false)
+ * @method mixed[]|false rawlist(string $dir = '.', bool $recursive = false)
+ * @method void setListOrder(mixed ...$args)
+ * @method mixed[]|false stat(string $filename)
+ * @method mixed[]|false lstat(string $filename)
+ * @method bool truncate(string $filename, int $new_size)
+ * @method bool touch(string $filename, int $time = null, int $atime = null)
+ * @method bool chown(string $filename, int|string $uid, bool $recursive = false)
+ * @method bool chgrp(string $filename, int|string $gid, bool $recursive = false)
+ * @method bool chmod(int $mode, string $filename, bool $recursive = false)
+ * @method mixed readlink(string $link)
+ * @method bool symlink(string $target, string $link)
+ * @method bool mkdir(string $dir, int $mode = -1, bool $recursive = false)
+ * @method bool rmdir(string $dir)
+ * @method bool put(string $remote_file, string $data, int $mode = SFTP::SOURCE_STRING, int $start = -1, int $local_start = -1, ?callable $progressCallback = null)
+ * @method string|bool get(string $remote_file, string $local_file = false, int $offset = 0, int $length = -1, ?callable $progressCallback = null)
+ * @method bool delete(string $path, bool $recursive = true)
+ * @method bool file_exists(string $path)
+ * @method bool is_dir(string $path)
+ * @method bool is_file(string $path)
+ * @method bool is_link(string $path)
+ * @method bool is_readable(string $path)
+ * @method bool is_writable(string $path)
+ * @method bool is_writeable(string $path)
+ * @method int|float|false fileatime(string $path)
+ * @method int|float|false filemtime(string $path)
+ * @method int|false fileperms(string $path)
+ * @method int|false fileowner(string $path)
+ * @method int|false filegroup(string $path)
+ * @method int|float|false filesize(string $path)
+ * @method string|false filetype(string $path)
+ * @method bool rename(string $oldname, string $newname)
+ * @method string[]|string getSFTPLog()
+ * @method string[] getSFTPErrors()
+ * @method string getLastSFTPError()
+ * @method mixed[]|false getSupportedVersions()
+ * @method int|false getNegotiatedVersion()
+ * @method void setPreferredVersion(int $version)
+ * @method void enableDatePreservation()
+ * @method void disableDatePreservation()
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class SFTP
+{
+ /**#@+
+ * @access public
+ * @see \phpseclib\Net\SFTP::put()
+ */
+ /**
+ * Reads data from a local file.
+ */
+ const SOURCE_LOCAL_FILE = 1;
+ /**
+ * Reads data from a string.
+ */
+ // this value isn't really used anymore but i'm keeping it reserved for historical reasons
+ const SOURCE_STRING = 2;
+ /**
+ * Reads data from callback:
+ * function callback($length) returns string to proceed, null for EOF
+ */
+ const SOURCE_CALLBACK = 16;
+ /**
+ * Resumes an upload
+ */
+ const RESUME = 4;
+ /**
+ * Append a local file to an already existing remote file
+ */
+ const RESUME_START = 8;
+ /**#@-*/
+
+ /**
+ * The SFTP object
+ *
+ * @var \phpseclib3\File\SFTP
+ * @access private
+ */
+ private $sftp = null;
+
+ /**
+ * Default Constructor.
+ *
+ * Connects to an SFTP server
+ *
+ * @param string $host
+ * @param int $port
+ * @param int $timeout
+ * @return \phpseclib\Net\SFTP
+ * @access public
+ */
+ function __construct($host, $port = 22, $timeout = 10)
+ {
+ $this->sftp = new \phpseclib3\Net\SFTP($host, $port, $timeout);
+ }
+
+ /**
+ * Login
+ *
+ * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA object or an array
+ *
+ * @param string $username
+ * @param $args[] param mixed $password
+ * @return bool
+ * @see self::_login()
+ * @access public
+ */
+ public function login($username, ...$args)
+ {
+ foreach ($args as &$arg) {
+ if ($arg instanceof RSA) {
+ $arg = $arg->getKeyObject();
+ if (!$arg instanceof \phpseclib3\Crypt\Common\PrivateKey) {
+ return false;
+ }
+ }
+ }
+
+ try {
+ return $this->sftp->login($username, ...$args);
+ } catch (\Exception $e) {
+ user_error($e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Parse Attributes
+ *
+ * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
+ *
+ * @param string $response
+ * @return array
+ * @access private
+ */
+ protected function parseAttributes(&$response)
+ {
+ $r = $this->sftp->parseAttributes($response);
+ if (isset($r['mode'])) {
+ $r['permissions'] = $r['mode'];
+ }
+ return $r;
+ }
+
+ /**
+ * Defines how nlist() and rawlist() will be sorted - if at all.
+ *
+ * If sorting is enabled directories and files will be sorted independently with
+ * directories appearing before files in the resultant array that is returned.
+ *
+ * Any parameter returned by stat is a valid sort parameter for this function.
+ * Filename comparisons are case insensitive.
+ *
+ * Examples:
+ *
+ * $sftp->setListOrder('filename', SORT_ASC);
+ * $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
+ * $sftp->setListOrder(true);
+ * Separates directories from files but doesn't do any sorting beyond that
+ * $sftp->setListOrder();
+ * Don't do any sort of sorting
+ *
+ * @param $args[]
+ * @access public
+ */
+ public function setListOrder(...$args)
+ {
+ $sortOptions = [];
+ if (empty($args)) {
+ return;
+ }
+ $len = count($args) & 0x7FFFFFFE;
+ for ($i = 0; $i < $len; $i+=2) {
+ if ($args[$i] == 'permissions') {
+ $args[$i] = 'mode';
+ }
+ $sortOptions[$args[$i]] = $args[$i + 1];
+ }
+ $this->sftp->setListOrder(...$args);
+ }
+
+ /**
+ * Returns the file size, in bytes, or false, on failure
+ *
+ * Files larger than 4GB will show up as being exactly 4GB.
+ *
+ * @param string $filename
+ * @return mixed
+ * @access public
+ */
+ public function size($filename)
+ {
+ return $this->sftp->filesize($filename);
+ }
+
+ /**
+ * Returns a public key object
+ *
+ * @access public
+ * @return SFTP|false
+ */
+ public function getSFTPObject()
+ {
+ return $this->sftp;
+ }
+
+ /**
+ * __call() magic method
+ *
+ * @access public
+ */
+ public function __call($name, $args)
+ {
+ try {
+ return $this->sftp->$name(...$args);
+ } catch (\Exception $e) {
+ user_error($e->getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php b/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php
new file mode 100644
index 000000000..c456990a7
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Net/SFTP/Stream.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * SFTP Stream Wrapper
+ *
+ * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
+ *
+ * PHP version 5
+ *
+ * @category Net
+ * @package SFTP
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2013 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+namespace phpseclib\Net\SFTP;
+
+use phpseclib\Crypt\RSA;
+use phpseclib\Net\SFTP;
+
+/**
+ * SFTP Stream Wrapper
+ *
+ * @package SFTP
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Stream extends \phpseclib3\Net\SFTP\Stream
+{
+ /**
+ * Path Parser
+ *
+ * Extract a path from a URI and actually connect to an SSH server if appropriate
+ *
+ * If "notification" is set as a context parameter the message code for successful login is
+ * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE.
+ *
+ * @param string $path
+ * @return string
+ * @access private
+ */
+ protected function parse_path($path)
+ {
+ $scheme = parse_url($path, PHP_URL_SCHEME);
+ if (isset($this->context)) {
+ $options = stream_context_get_options($this->context);
+ }
+ if (isset($options[$scheme]['privkey']) && $options[$scheme]['privkey'] instanceof RSA) {
+ stream_context_set_option($this->context, $scheme, 'privKey', $options[$scheme]['privkey']->getKeyObject());
+ }
+ if (isset($options[$scheme]['session']) && $options[$scheme]['session'] instanceof SFTP) {
+ stream_context_set_option($this->context, $scheme, 'session', $options[$scheme]['session']->getSFTPObject());
+ }
+ if (isset($options[$scheme]['sftp']) && $options[$scheme]['sftp'] instanceof SFTP) {
+ stream_context_set_option($this->context, $scheme, 'sftp', $options[$scheme]['sftp']->getSFTPObject());
+ }
+ return parent::parse_path($path);
+ }
+
+ /**
+ * __call() magic method
+ *
+ * @access public
+ */
+ public function __call($name, array $args)
+ {
+ try {
+ return parent::__call($name, $args);
+ } catch (\Exception $e) {
+ user_error($e->getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/Net/SSH2.php b/vendor/phpseclib/phpseclib2_compat/src/Net/SSH2.php
new file mode 100644
index 000000000..a78e7e87d
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/Net/SSH2.php
@@ -0,0 +1,233 @@
+<?php
+
+/**
+ * Pure-PHP implementation of SSHv2.
+ *
+ * PHP version 5
+ *
+ * Here are some examples of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
+ * if (!$ssh->login('username', 'password')) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->exec('pwd');
+ * echo $ssh->exec('ls -la');
+ * ?>
+ * </code>
+ *
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $key = new \phpseclib\Crypt\RSA();
+ * //$key->setPassword('whatever');
+ * $key->loadKey(file_get_contents('privatekey'));
+ *
+ * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
+ * if (!$ssh->login('username', $key)) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->read('username@username:~$');
+ * $ssh->write("ls -la\n");
+ * echo $ssh->read('username@username:~$');
+ * ?>
+ * </code>
+ *
+ * @category Net
+ * @package SSH2
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2007 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ */
+
+
+namespace phpseclib\Net;
+
+use phpseclib\Crypt\RSA;
+
+/**
+ * Pure-PHP implementation of SSHv2.
+ *
+ * @package SSHv2
+ * @method static void setCryptoEngine(string $engine)
+ * @method void sendIdentificationStringFirst()
+ * @method void sendIdentificationStringLast()
+ * @method void sendKEXINITFirst()
+ * @method void sendKEXINITLast()
+ * @method int|float getTimeout()
+ * @method void setTimeout(int|float $timeout)
+ * @method void setKeepAlive(int|float $interval)
+ * @method string getStdError()
+ * @method string|bool exec(string $command, callable ?$callback = null)
+ * @method bool requestAgentForwarding()
+ * @method string|bool|null read(string $expect = '', int $mode = SSH2::READ_SIMPLE)
+ * @method void write(string $cmd)
+ * @method bool startSubsystem(string $subsystem)
+ * @method bool stopSubsystem()
+ * @method void reset()
+ * @method bool isTimeout()
+ * @method void disconnect()
+ * @method bool isConnected()
+ * @method bool isAuthenticated()
+ * @method bool ping()
+ * @method void enableQuietMode()
+ * @method void disableQuietMode()
+ * @method bool isQuietModeEnabled()
+ * @method void enablePTY()
+ * @method void disablePTY()
+ * @method bool isPTYEnabled()
+ * @method array|false|string getLog()
+ * @method string[] getErrors()
+ * @method ?string getLastError()
+ * @method string|false getServerIdentification()
+ * @method mixed[] getServerAlgorithms()
+ * @method static string[] getSupportedKEXAlgorithms()
+ * @method static string[] getSupportedHostKeyAlgorithms()
+ * @method static string[] getSupportedEncryptionAlgorithms()
+ * @method static string[] getSupportedMACAlgorithms()
+ * @method static string[] getSupportedCompressionAlgorithms()
+ * @method mixed[] getAlgorithmsNegotiated()
+ * @method void setTerminal(string $term)
+ * @method void setPreferredAlgorithms(mixed[] $methods)
+ * @method string getBannerMessage()
+ * @method string|false getServerPublicHostKey()
+ * @method false|int getExitStatus()
+ * @method int getWindowColumns()
+ * @method int getWindowRows()
+ * @method setWindowColumns(int $value)
+ * @method setWindowRows(int $value)
+ * @method setWindowSize(int $columns = 80, int $rows = 24)
+ * @method string getResourceId()
+ * @method static bool|SSH2 getConnectionByResourceId(string $id)
+ * @method static array<string, SSH2> getConnections()
+ * @method ?mixed[] getAuthMethodsToContinue()
+ * @method void enableSmartMFA()
+ * @method void disableSmartMFA()
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class SSH2
+{
+ /**#@+
+ * @access public
+ * @see \phpseclib\Net\SSH2::getLog()
+ */
+ /**
+ * Returns the message numbers
+ */
+ const LOG_SIMPLE = 1;
+ /**
+ * Returns the message content
+ */
+ const LOG_COMPLEX = 2;
+ /**
+ * Outputs the content real-time
+ */
+ const LOG_REALTIME = 3;
+ /**
+ * Dumps the content real-time to a file
+ */
+ const LOG_REALTIME_FILE = 4;
+ /**
+ * Make sure that the log never gets larger than this
+ */
+ const LOG_MAX_SIZE = 1048576; // 1024 * 1024
+ /**#@-*/
+
+ /**#@+
+ * @access public
+ * @see \phpseclib\Net\SSH2::read()
+ */
+ /**
+ * Returns when a string matching $expect exactly is found
+ */
+ const READ_SIMPLE = 1;
+ /**
+ * Returns when a string matching the regular expression $expect is found
+ */
+ const READ_REGEX = 2;
+ /**
+ * Returns whenever a data packet is received.
+ *
+ * Some data packets may only contain a single character so it may be necessary
+ * to call read() multiple times when using this option
+ */
+ const READ_NEXT = 3;
+ /**#@-*/
+
+ /**
+ * The SSH2 object
+ *
+ * @var \phpseclib3\File\SSH2
+ * @access private
+ */
+ private $ssh;
+
+ /**
+ * Default Constructor.
+ *
+ * $host can either be a string, representing the host, or a stream resource.
+ *
+ * @param mixed $host
+ * @param int $port
+ * @param int $timeout
+ * @see self::login()
+ * @return \phpseclib\Net\SSH2
+ * @access public
+ */
+ function __construct($host, $port = 22, $timeout = 10)
+ {
+ $this->ssh = new \phpseclib3\Net\SSH2($host, $port, $timeout);
+ }
+
+ /**
+ * Login
+ *
+ * The $password parameter can be a plaintext password, a \phpseclib3\Crypt\RSA object or an array
+ *
+ * @param string $username
+ * @param $args[] param mixed $password
+ * @return bool
+ * @see self::_login()
+ * @access public
+ */
+ public function login($username, ...$args)
+ {
+ foreach ($args as &$arg) {
+ if ($arg instanceof RSA) {
+ $arg = $arg->getKeyObject();
+ if (!$arg) {
+ return false;
+ }
+ }
+ }
+
+ try {
+ return $this->ssh->login($username, ...$args);
+ } catch (\Exception $e) {
+ user_error($e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * __call() magic method
+ *
+ * @access public
+ */
+ public function __call($name, $args)
+ {
+ try {
+ return $this->ssh->$name(...$args);
+ } catch (\Exception $e) {
+ user_error($e->getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php b/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php
new file mode 100644
index 000000000..365353f67
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * Pure-PHP ssh-agent client.
+ *
+ * PHP version 5
+ *
+ * Here are some examples of how to use this library:
+ * <code>
+ * <?php
+ * include 'vendor/autoload.php';
+ *
+ * $agent = new \phpseclib3\System\SSH\Agent();
+ *
+ * $ssh = new \phpseclib3\Net\SSH2('www.domain.tld');
+ * if (!$ssh->login('username', $agent)) {
+ * exit('Login Failed');
+ * }
+ *
+ * echo $ssh->exec('pwd');
+ * echo $ssh->exec('ls -la');
+ * ?>
+ * </code>
+ *
+ * @category System
+ * @package SSH\Agent
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2014 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ * @internal See http://api.libssh.org/rfc/PROTOCOL.agent
+ */
+
+namespace phpseclib\System\SSH;
+
+/**
+ * Pure-PHP ssh-agent client identity factory
+ *
+ * requestIdentities() method pumps out \phpseclib3\System\SSH\Agent\Identity objects
+ *
+ * @package SSH\Agent
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access public
+ */
+class Agent extends \phpseclib3\System\SSH\Agent
+{
+} \ No newline at end of file
diff --git a/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php b/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php
new file mode 100644
index 000000000..c85f8330f
--- /dev/null
+++ b/vendor/phpseclib/phpseclib2_compat/src/System/SSH/Agent/Identity.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Pure-PHP ssh-agent client.
+ *
+ * PHP version 5
+ *
+ * @category System
+ * @package SSH\Agent
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @copyright 2009 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://phpseclib.sourceforge.net
+ * @internal See http://api.libssh.org/rfc/PROTOCOL.agent
+ */
+
+namespace phpseclib\System\SSH\Agent;
+
+/**
+ * Pure-PHP ssh-agent client identity object
+ *
+ * Instantiation should only be performed by \phpseclib3\System\SSH\Agent class.
+ * This could be thought of as implementing an interface that phpseclib3\Crypt\RSA
+ * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
+ * The methods in this interface would be getPublicKey and sign since those are the
+ * methods phpseclib looks for to perform public key authentication.
+ *
+ * @package SSH\Agent
+ * @author Jim Wigginton <terrafrost@php.net>
+ * @access internal
+ */
+class Identity extends \phpseclib3\System\SSH\Agent\Identity
+{
+} \ No newline at end of file
diff --git a/vendor/ralouphie/getallheaders/LICENSE b/vendor/ralouphie/getallheaders/LICENSE
new file mode 100644
index 000000000..be5540c2a
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ralph Khattar
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/ralouphie/getallheaders/README.md b/vendor/ralouphie/getallheaders/README.md
new file mode 100644
index 000000000..9430d76bb
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/README.md
@@ -0,0 +1,27 @@
+getallheaders
+=============
+
+PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3.
+
+[![Build Status](https://travis-ci.org/ralouphie/getallheaders.svg?branch=master)](https://travis-ci.org/ralouphie/getallheaders)
+[![Coverage Status](https://coveralls.io/repos/ralouphie/getallheaders/badge.png?branch=master)](https://coveralls.io/r/ralouphie/getallheaders?branch=master)
+[![Latest Stable Version](https://poser.pugx.org/ralouphie/getallheaders/v/stable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![Latest Unstable Version](https://poser.pugx.org/ralouphie/getallheaders/v/unstable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![License](https://poser.pugx.org/ralouphie/getallheaders/license.png)](https://packagist.org/packages/ralouphie/getallheaders)
+
+
+This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php).
+
+## Install
+
+For PHP version **`>= 5.6`**:
+
+```
+composer require ralouphie/getallheaders
+```
+
+For PHP version **`< 5.6`**:
+
+```
+composer require ralouphie/getallheaders "^2"
+```
diff --git a/vendor/ralouphie/getallheaders/composer.json b/vendor/ralouphie/getallheaders/composer.json
new file mode 100644
index 000000000..de8ce62e4
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "ralouphie/getallheaders",
+ "description": "A polyfill for getallheaders.",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5 || ^6.5",
+ "php-coveralls/php-coveralls": "^2.1"
+ },
+ "autoload": {
+ "files": ["src/getallheaders.php"]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "getallheaders\\Tests\\": "tests/"
+ }
+ }
+}
diff --git a/vendor/ralouphie/getallheaders/src/getallheaders.php b/vendor/ralouphie/getallheaders/src/getallheaders.php
new file mode 100644
index 000000000..c7285a5ba
--- /dev/null
+++ b/vendor/ralouphie/getallheaders/src/getallheaders.php
@@ -0,0 +1,46 @@
+<?php
+
+if (!function_exists('getallheaders')) {
+
+ /**
+ * Get all HTTP header key/values as an associative array for the current request.
+ *
+ * @return string[string] The HTTP header key/value pairs.
+ */
+ function getallheaders()
+ {
+ $headers = array();
+
+ $copy_server = array(
+ 'CONTENT_TYPE' => 'Content-Type',
+ 'CONTENT_LENGTH' => 'Content-Length',
+ 'CONTENT_MD5' => 'Content-Md5',
+ );
+
+ foreach ($_SERVER as $key => $value) {
+ if (substr($key, 0, 5) === 'HTTP_') {
+ $key = substr($key, 5);
+ if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
+ $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
+ $headers[$key] = $value;
+ }
+ } elseif (isset($copy_server[$key])) {
+ $headers[$copy_server[$key]] = $value;
+ }
+ }
+
+ if (!isset($headers['Authorization'])) {
+ if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+ } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
+ $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
+ $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
+ } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
+ $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
+ }
+ }
+
+ return $headers;
+ }
+
+}
diff --git a/view/css/conversation.css b/view/css/conversation.css
index d8a893b0e..38970bb45 100644
--- a/view/css/conversation.css
+++ b/view/css/conversation.css
@@ -193,18 +193,32 @@ a.wall-item-name-link {
color: var(--bs-primary);
}
-.item-highlight {
+.item-highlight,
+.item-indent,
+.wall-item-comment,
+.thread-wrapper {
position: relative;
}
-.item-highlight:after {
+.item-fade-in {
+ opacity: 0;
+ transform: scale(.7) translateY(-20px);
+ transition: opacity 0.3s ease-out, transform 0.3s ease-out;
+}
+.item-fade-in.show {
+ opacity: 1;
+ transform: scale(1) translateY(0);
+}
+
+.item-highlight::after {
content: '';
position: absolute;
pointer-events: none;
box-shadow: inset .15rem 0 0 0 var(--hz-item-highlight);
- height: 100%;
width: 100%;
top: 0;
+ bottom: 0;
+/* margin: var(--bs-border-radius) 0 var(--bs-border-radius) 0; */
}
.item-highlight-fade {
@@ -217,6 +231,82 @@ a.wall-item-name-link {
to { background-color: none; }
}
+.item-indent {
+ /* This must be equal to .item.indent:before width */
+ padding-left: .75rem;
+}
+
+.item-indent::before {
+ content: '';
+ position: absolute;
+ height: 100%;
+ width: .75rem;
+ top: 0;
+ left: 0;
+ border-color: var(--hz-item-indent, var(--bs-border-color));
+ border-radius: 0 1.5rem 0 0;
+ border-style: solid;
+ border-width: 1px 1px 0 0;
+}
+
+.wall-item-comment.expanded {
+ opacity: 0.5;
+}
+
+.wall-item-comment.collapsed::before {
+ content: '\2212';
+ position: absolute;
+ left: .85rem;
+ top: .45rem;
+}
+
+.wall-item-backdrop::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ backdrop-filter: saturate(0%) brightness(90%);
+ z-index: 1;
+}
+
+.wall-item-expanded {
+ position: relative;
+ border-radius: var(--bs-border-radius);
+ background-color: var(--bs-body-bg);
+ z-index: 2;
+}
+
+.wall-item-expanded::after {
+ content: '';
+ position: absolute;
+ pointer-events: none;
+ box-shadow: 0 0 0 1px var(--bs-border-color);
+ border-radius: var(--bs-border-radius);
+ width: 100%;
+ top: 0;
+ bottom: 0;
+}
+
+.wall-item-expanded::before {
+ content: var(--hz-wall-item-expanded-before-content, "");;
+ position: absolute;
+ top: 0;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: var(--bs-info-bg-subtle);
+ color: var(--bs-info-text-emphasis);
+ border-radius: var(--bs-border-radius-pill);
+ padding: 0.35em 0.65em;
+ font-size: 0.6em;
+ font-weight: 700;
+ z-index: 3;
+ text-transform: uppercase;
+}
+
+.load-more:hover .load-more-dots {
+ background: var(--bs-light-bg-subtle);
+ padding: 0 0.35em;
+}
+
/* comment_item */
diff --git a/view/js/main.js b/view/js/main.js
index 7043c8577..5bf7234aa 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -28,6 +28,8 @@ var expanded_items = [];
var updateTimeout = [];
const singlethread_modules = ['display', 'hq'];
const redirect_modules = ['display', 'notify'];
+let b64mids = [];
+
var page_cache = {};
@@ -87,6 +89,146 @@ $(document).ready(function() {
}
});
+ document.addEventListener('click', function(event) {
+ // Only handle clicks on .wall-item-reaction or its children
+ const target = event.target.closest('.wall-item-reaction');
+ if (!target) return;
+
+ let doRequest = true;
+ const isUserClick = event.isTrusted;
+
+ // Destructure relevant data attributes
+ const { itemId: id, itemMid: mid, itemParent: parentId, itemUuid: uuid, itemVerb: verb } = target.dataset;
+ const isCommentBtn = target.classList.contains('wall-item-comment');
+
+ if (isCommentBtn) {
+ if (id === parentId) {
+ // Handle blog mode
+ target.classList.add('disabled');
+ document.getElementById(`load-more-progress-wrapper-${id}`).classList.remove('d-none');
+ document.getElementById(`load-more-${id}`).classList.remove('d-none')
+ request(id, mid, 'load', parentId, uuid, isUserClick);
+ return;
+ }
+
+ // Get relevant DOM elements
+ const threadWrapper = document.getElementById(`thread-wrapper-${id}`);
+ const parentWrapper = document.getElementById(`thread-wrapper-${parentId}`);
+ const subThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`);
+ const parentSubThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${parentId}`);
+
+ // Query related sub-thread and highlight elements
+ const parentIndentedThreads = document.querySelectorAll(`#wall-item-sub-thread-wrapper-${parentId} .wall-item-sub-thread-wrapper.item-indent`);
+
+ let ancestorIds = [];
+
+ doRequest = !subThreadWrapper.children.length;
+
+ // Set visual styles using UUID
+ subThreadWrapper.style.setProperty('--hz-item-indent', stringToHslColor(uuid));
+ threadWrapper.style.setProperty('--hz-item-highlight', stringToHslColor(uuid));
+ threadWrapper.style.setProperty('--hz-wall-item-expanded-before-content', '"' + aStr.dblclick_to_exit_zoom + '"');
+
+ // Clear previous highlights
+ parentSubThreadWrapper.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => el.classList.remove('item-highlight'));
+
+ if (isUserClick && parentIndentedThreads.length === 0 && !subThreadWrapper.children.length) {
+ // Handle first-time expansion and highlighting but not for toplevels (blog mode)
+ threadWrapper.classList.add('item-highlight');
+ } else {
+ // Handle indentation and zooming
+ let ancestor = subThreadWrapper.parentElement;
+ ancestorIds.push(ancestor.id.slice(15)); // thread-wrapper-1234
+
+ while (ancestor) {
+ if (ancestor.classList.contains('item-indent') && ancestor.classList.contains('wall-item-sub-thread-wrapper')) {
+ ancestorIds.push(ancestor.parentElement.id.slice(15));
+ }
+ ancestor = ancestor.parentElement;
+ }
+
+ ancestorIds.reverse();
+
+ if (ancestorIds.length > 3) {
+ // Handle zooming in
+ let firstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[0]);
+ let firstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[0]);
+
+ firstWrapper.querySelector('.wall-item-comment').classList.remove('indented');
+ firstWrapper.classList.remove('wall-item-expanded', 'shadow');
+ firstSubWrapper.classList.remove('item-indent');
+
+ let newFirstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[1])
+ let newFirstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[1])
+
+ newFirstWrapper.classList.add('wall-item-expanded', 'shadow');
+ parentWrapper.classList.add('wall-item-backdrop');
+
+ // Exit zoom on double-click
+ newFirstWrapper.addEventListener('dblclick', function() {
+ parentWrapper.querySelectorAll('.wall-item-comment.indented').forEach(el => el.classList.remove('indented'));
+ parentWrapper.querySelectorAll('.wall-item-comment.collapsed').forEach(el => el.classList.remove('collapsed'));
+ parentWrapper.classList.remove('wall-item-backdrop');
+ parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => el.classList.remove('item-indent'));
+ parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.d-none').forEach(el => el.classList.remove('d-none'));
+ parentWrapper.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => el.classList.remove('wall-item-expanded', 'shadow'));
+ }, { once: true });
+ }
+
+ // Toggle sub-thread visibility if indented
+ if (isUserClick && target.classList.contains('indented')) {
+ doRequest = false;
+ subThreadWrapper.classList.toggle('d-none');
+ target.classList.toggle('collapsed');
+ }
+
+ // Indenting of already expanded but flattened items
+ if (isUserClick && subThreadWrapper.classList.contains('item-expanded') && !subThreadWrapper.classList.contains('item-indent')) {
+ doRequest = false;
+
+ threadWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-expanded').forEach(function (el, i) {
+ el.classList.add('item-indent');
+
+ el.querySelectorAll('.wall-item-comment.expanded').forEach(function (el, i) {
+ el.classList.add('collapsed', 'indented');
+ });
+
+ // Collapse everything below the first level
+ if (i > 0) {
+ el.classList.add('d-none');
+ }
+ });
+ }
+
+ // Indent the subthread
+ subThreadWrapper.classList.add('item-indent', 'item-expanded');
+
+ // Mark as indented after visibility toggle
+ target.classList.add('indented');
+ }
+
+ // Mark as expanded
+ target.classList.add('expanded');
+ }
+
+ if (doRequest) {
+ request(id, mid, verb, parentId, uuid, isUserClick);
+ }
+ });
+
+ document.addEventListener('click', function(event) {
+ const targetElement = event.target.closest('.dropdown-item-expand');
+ if (!targetElement) return;
+
+ event.preventDefault();
+
+ const id = targetElement.dataset.itemId;
+ const subWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`);
+
+ subWrapper.innerHTML = '';
+ autoExpand(id);
+ });
+
// @hilmar |->
if ( typeof(window.tao) == 'undefined' ) {
window.tao = {};
@@ -752,12 +894,22 @@ function updateConvItems(mode, data) {
let data_json = JSON.parse(elem.dataset.b64mids);
- // Also highlight the thread parent
- if (data_json.includes(bParam_mid) && elem.parentNode.classList.contains('wall-item-sub-thread-wrapper')) {
- if (!elem.parentNode.parentNode.classList.contains('toplevel_item')) {
+ if (elem.parentNode.classList.contains('wall-item-sub-thread-wrapper') && elem.parentNode.children.length) {
+ // Set the highlight state
+ if (data_json.includes(bParam_mid) && !elem.parentNode.parentNode.classList.contains('toplevel_item')) {
elem.parentNode.parentNode.classList.add('item-highlight');
- document.documentElement.style.setProperty('--hz-item-highlight', stringToHlsColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0]));
+ document.documentElement.style.setProperty('--hz-item-highlight', stringToHslColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0]));
}
+
+ let elemSubThreadWrapper = elem.querySelector('.wall-item-sub-thread-wrapper');
+ let elemCommentButton = elem.querySelector('.wall-item-comment');
+
+ // Set the button and sub-thread-wrapper state
+ if (elemCommentButton && elemSubThreadWrapper.children.length) {
+ elemCommentButton.classList.add('expanded');
+ }
+
+ elem.parentNode.classList.add('item-expanded');
}
b64mids.push(...data_json);
@@ -1292,27 +1444,64 @@ function justifyPhotosAjax(id) {
$('#' + id).justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; });
}
-function request(id, mid, verb, parent, uuid) {
+function request(id, mid, verb, parent, uuid, userClick) {
- const loading = document.getElementById('like-rotator-' + id);
- loading.style.display = 'block';
+ if (verb === 'load') {
+ const dots = document.getElementById('load-more-dots-' + parent);
+ dots.classList.add('jumping-dots');
- if (verb === 'comment') {
+ const parent_sub = document.getElementById('wall-item-sub-thread-wrapper-' + parent);
+ const offset = parent_sub.children.length;
- if (singlethread_modules.indexOf(module) !== -1) {
- let stateObj = { b64mid: uuid };
- history.pushState(stateObj, '', module + '/' + uuid);
- }
+ fetch('/request?offset=' + offset + '&verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module)
+ .then(response => response.json())
+ .then(obj => {
+ let parser = new DOMParser();
+ let doc = parser.parseFromString(obj.html, 'text/html');
+ let b64mids = [];
+
+ doc.querySelectorAll('.thread-wrapper').forEach(function (e) {
+ let data = JSON.parse(e.dataset.b64mids);
+ b64mids.push(...data);
+ });
+
+ imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () {
+ injectWithAnimation('wall-item-sub-thread-wrapper-' + parent, doc);
+ dots.classList.remove('jumping-dots');
+
+ const loadmore_progress = document.getElementById('load-more-progress-' + parent);
+ loadmore_progress.style.width = Math.round(100 * parent_sub.children.length / loadmore_progress.dataset.commentsTotal) + '%';
+
+ if (Number(parent_sub.children.length) === Number(loadmore_progress.dataset.commentsTotal)) {
+ const loadmore = document.getElementById('load-more-' + parent);
+ loadmore.remove();
+ }
- document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => {
- el.classList.remove('item-highlight');
- el.style.boxShadow = '';
+ updateRelativeTime('.autotime');
+ collapseHeight();
+
+ document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids }));
+ document.dispatchEvent(new Event('hz:sse_bs_counts'));
+ });
+
+ })
+ .catch(error => {
+ console.error('Error fetching data:', error);
});
- const wrapper = document.getElementById('thread-wrapper-' + id);
- if (!wrapper.classList.contains('toplevel_item')) {
- wrapper.classList.add('item-highlight');
- document.documentElement.style.setProperty('--hz-item-highlight', stringToHlsColor(uuid));
+ return;
+ }
+
+ const loading = document.getElementById('like-rotator-' + id);
+
+ if (userClick) {
+ loading.style.display = 'block';
+ }
+
+ if (verb === 'comment') {
+ if (userClick && singlethread_modules.indexOf(module) !== -1) {
+ let stateObj = { b64mid: uuid };
+ history.pushState(stateObj, '', module + '/' + uuid);
}
fetch('/request?verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module)
@@ -1320,7 +1509,6 @@ function request(id, mid, verb, parent, uuid) {
.then(obj => {
let parser = new DOMParser();
let doc = parser.parseFromString(obj.html, 'text/html');
- let b64mids = [];
doc.querySelectorAll('.thread-wrapper').forEach(function (e) {
let data = JSON.parse(e.dataset.b64mids);
@@ -1328,13 +1516,15 @@ function request(id, mid, verb, parent, uuid) {
});
imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () {
- injectWithAnimation('wall-item-sub-thread-wrapper-' + id, obj.html);
+ injectWithAnimation('wall-item-sub-thread-wrapper-' + id, doc, true);
updateRelativeTime('.autotime');
- loading.style.display = 'none';
collapseHeight();
- document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids }));
- document.dispatchEvent(new Event('hz:sse_bs_counts'));
+ if (userClick) {
+ loading.style.display = 'none';
+ document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids }));
+ document.dispatchEvent(new Event('hz:sse_bs_counts'));
+ }
});
})
@@ -1377,32 +1567,134 @@ function request(id, mid, verb, parent, uuid) {
}
-function injectWithAnimation(container, html) {
- const target = document.getElementById(container);
- target.innerHTML = html;
+function injectWithAnimation(containerId, parsedDoc, overwrite = false) {
+ const container = document.getElementById(containerId);
+ if (!container) return;
+ if (overwrite) container.innerHTML = '';
- target.animate([
- { opacity: 0, transform: 'translateY(-20px)' },
- { opacity: 1, transform: 'translateY(0)' }
- ], {
- duration: 300,
- easing: 'ease-out'
- });
+ const newElements = Array.from(parsedDoc.body.children);
- // For children animation
- Array.from(target.children).forEach((el, i) => {
- el.animate([
- { opacity: 0, transform: 'scale(.7)' },
- { opacity: 1, transform: 'scale(1)' }
- ], {
- duration: 300,
- delay: i * 50,
- fill: 'both',
- easing: 'ease-out'
- });
- });
+ for (let i = newElements.length - 1; i >= 0; i--) {
+ const el = newElements[i].cloneNode(true);
+ el.classList.add('item-fade-in');
+ container.insertBefore(el, container.firstChild);
+
+ // Remove classes after transition ends
+ const onTransitionEnd = (event) => {
+ el.classList.remove('item-fade-in', 'show');
+ el.removeEventListener('transitionend', onTransitionEnd);
+ };
+ el.addEventListener('transitionend', onTransitionEnd);
+
+ setTimeout(() => {
+ el.classList.add('show');
+ }, (newElements.length - 1 - i) * 30);
+ }
}
+const autoExpand = (function () {
+ const clickedElements = new Set(); // Stores clicked button references
+
+ // We wait 10 seconds for images. Set the timeout here slightly higher.
+ function waitForElement(selector, timeout = 11000) {
+ return new Promise((resolve, reject) => {
+ // Check if the element already exists
+ const element = document.querySelector(selector);
+
+ if (element) {
+ resolve(element);
+ return;
+ }
+
+ // Set up a timeout to reject the promise if the element doesn't appear in time
+ const timer = setTimeout(() => {
+ observer.disconnect();
+ reject(new Error(`Element "${selector}" not found within ${timeout}ms`));
+ }, timeout);
+
+ // Create a MutationObserver to watch for DOM changes
+ const observer = new MutationObserver(() => {
+ const el = document.querySelector(selector);
+ if (el) {
+ clearTimeout(timer);
+ observer.disconnect();
+ resolve(el);
+ }
+ });
+
+ // Start observing the document for changes
+ observer.observe(document.documentElement, {
+ childList: true,
+ subtree: true
+ });
+ });
+ }
+
+ async function autoExpand(id) {
+ const loading = document.getElementById('like-rotator-' + id);
+ let iteration = 0;
+ const maxIterations = 3;
+ clickedElements.clear();
+
+ try {
+ // Step 1: Ensure initial button is clicked
+ const initBtnSelector = '#wall-item-comment-' + id;
+ const initBtn = await waitForElement(initBtnSelector);
+
+ if (!clickedElements.has(initBtn)) {
+ initBtn.click();
+ clickedElements.add(initBtn);
+ iteration++;
+ }
+
+ // Step 2: Loop until no new buttons are found
+ let newButtonsFound;
+
+ const commentSelector = `#wall-item-sub-thread-wrapper-${id} .thread-wrapper`;
+ const commentBtnSelector = `#wall-item-sub-thread-wrapper-${id} .wall-item-comment`;
+
+ do {
+ newButtonsFound = false;
+
+ // Wait for any comment to appear
+ await waitForElement(commentSelector);
+
+ const expandButtons = document.querySelectorAll(commentBtnSelector);
+
+ for (const btn of expandButtons) {
+ if (!clickedElements.has(btn)) {
+ btn.click();
+ clickedElements.add(btn);
+ newButtonsFound = true;
+ // Optional: await waitForElement(...) to wait for new content
+ }
+ }
+
+ // Wait between iterations to allow UI to update
+ if (newButtonsFound) {
+ iteration++;
+ await new Promise(res => setTimeout(res, 700));
+ }
+
+ } while (newButtonsFound && iteration < maxIterations);
+
+ console.log('Replies loaded!');
+
+ loading.style.display = 'none';
+
+ document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids }));
+ document.dispatchEvent(new Event('hz:sse_bs_counts'));
+
+ } catch (error) {
+ loading.style.display = 'none';
+ console.error("autoExpand failed:", error.message);
+ }
+ }
+
+ return autoExpand;
+})();
+
+
function stringToHexColor(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
@@ -1416,11 +1708,11 @@ function stringToHexColor(str) {
return color;
}
-function stringToHlsColor(str) {
+function stringToHslColor(str) {
let stringUniqueHash = [...str].reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
- return `hsl(${stringUniqueHash % 360}, 95%, 70%)`;
+ return `hsl(${stringUniqueHash % 360}, 65%, 65%)`;
}
function dolike(ident, verb) {
diff --git a/view/nb/hmessages.po b/view/nb/hmessages.po
index 3d0fa3dca..4f1d47d8e 100644
--- a/view/nb/hmessages.po
+++ b/view/nb/hmessages.po
@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: 8.6RC1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-09-20 12:31+0200\n"
-"PO-Revision-Date: 2024-11-04 10:19+0100\n"
+"POT-Creation-Date: 2025-06-24 07:52+0000\n"
+"PO-Revision-Date: 2025-07-13 15:47+0200\n"
"Last-Translator: Harald Eilertsen <haraldei@anduin.net>\n"
"Language-Team: Norwegian Bokmål <l10n-no@lister.huftis.org>\n"
"Language: nb_NO\n"
@@ -18,90 +18,633 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
-"X-Generator: Lokalize 24.08.2\n"
+"X-Generator: Poedit 3.6\n"
-#: ../../include/bbcode.php:234 ../../include/bbcode.php:994
-#: ../../include/bbcode.php:1659 ../../include/bbcode.php:1667
-msgid "Image/photo"
-msgstr "Bilde/fotografi"
+#: ../../util/nconfig.php:34
+msgid "Source channel not found."
+msgstr "Fant ikke kildekanalen."
-#: ../../include/bbcode.php:286
-msgid "Encrypted content"
-msgstr "Kryptert innhold"
+#: ../../view/theme/redbasic/php/config.php:19 ../../include/text.php:3544
+#: ../../addon/cart/submodules/orderoptions.php:335
+#: ../../addon/cart/submodules/orderoptions.php:359
+#: ../../addon/cart/submodules/orderoptions.php:435
+#: ../../addon/cart/submodules/orderoptions.php:459
+#: ../../Zotlabs/Module/Admin/Site.php:233
+msgid "Default"
+msgstr "Standard"
-#: ../../include/bbcode.php:342
-#, php-format
-msgid "Install %1$s element %2$s"
+#: ../../view/theme/redbasic/php/config.php:20
+#: ../../view/theme/redbasic/php/config.php:23
+msgid "Focus (Hubzilla default)"
+msgstr "Focus (Hubzilla standard)"
+
+#: ../../view/theme/redbasic/php/config.php:193 ../../include/js_strings.php:21
+#: ../../include/conversation.php:1127 ../../addon/irc/irc.php:45
+#: ../../addon/socialauth/Mod_SocialAuth.php:341
+#: ../../addon/hubwall/hubwall.php:96
+#: ../../addon/openclipatar/openclipatar.php:54
+#: ../../addon/hzfiles/hzfiles.php:86 ../../addon/piwik/piwik.php:95
+#: ../../addon/redphotos/redphotos.php:136
+#: ../../addon/pageheader/Mod_Pageheader.php:52
+#: ../../addon/ijpost/Mod_Ijpost.php:72 ../../addon/redred/Mod_Redred.php:88
+#: ../../addon/startpage/Mod_Startpage.php:75
+#: ../../addon/libertree/Mod_Libertree.php:68 ../../addon/logrot/logrot.php:35
+#: ../../addon/dwpost/Mod_Dwpost.php:78 ../../addon/diaspora/diaspora.php:90
+#: ../../addon/diaspora/Mod_Diaspora.php:101
+#: ../../addon/fuzzloc/Mod_Fuzzloc.php:54 ../../addon/mailtest/mailtest.php:100
+#: ../../addon/nsfw/Mod_Nsfw.php:59 ../../addon/pumpio/Mod_Pumpio.php:113
+#: ../../addon/wppost/Mod_Wppost.php:107 ../../addon/wiki/Mod_Wiki.php:218
+#: ../../addon/wiki/Mod_Wiki.php:907 ../../addon/wiki/Widget/Wiki_pages.php:72
+#: ../../addon/xmpp/Mod_Xmpp.php:70 ../../addon/statusnet/statusnet.php:602
+#: ../../addon/statusnet/Mod_Statusnet.php:191
+#: ../../addon/statusnet/Mod_Statusnet.php:249
+#: ../../addon/statusnet/Mod_Statusnet.php:304
+#: ../../addon/ljpost/Mod_Ljpost.php:80 ../../addon/faces/Mod_Faces.php:141
+#: ../../addon/cart/submodules/hzservices.php:645
+#: ../../addon/cart/submodules/subscriptions.php:410
+#: ../../addon/cart/submodules/manualcat.php:248
+#: ../../addon/cart/submodules/orderoptions.php:312
+#: ../../addon/cart/submodules/orderoptions.php:412
+#: ../../addon/cart/cart.php:1424 ../../addon/cart/Settings/Cart.php:132
+#: ../../addon/cart/Settings/Cart.php:142
+#: ../../addon/twitter/Mod_Twitter.php:182
+#: ../../addon/redfiles/redfiles.php:124
+#: ../../addon/openstreetmap/openstreetmap.php:147
+#: ../../addon/workflow/workflow.php:1490
+#: ../../addon/workflow/workflow.php:1549
+#: ../../addon/workflow/workflow.php:1668
+#: ../../addon/workflow/workflow.php:2815
+#: ../../addon/workflow/Settings/Mod_WorkflowSettings.php:94
+#: ../../addon/skeleton/Mod_Skeleton.php:49
+#: ../../addon/content_import/Mod_content_import.php:140
+#: ../../addon/rtof/Mod_Rtof.php:70 ../../addon/nofed/Mod_Nofed.php:51
+#: ../../addon/photocache/Mod_Photocache.php:63
+#: ../../addon/likebanner/likebanner.php:57
+#: ../../Zotlabs/Lib/ThreadItem.php:809 ../../Zotlabs/Storage/Browser.php:390
+#: ../../Zotlabs/Module/Oauth.php:110 ../../Zotlabs/Module/Import_items.php:125
+#: ../../Zotlabs/Module/Thing.php:364 ../../Zotlabs/Module/Thing.php:414
+#: ../../Zotlabs/Module/Tokens.php:294 ../../Zotlabs/Module/Pdledit.php:137
+#: ../../Zotlabs/Module/Connect.php:107
+#: ../../Zotlabs/Module/Filestorage.php:208
+#: ../../Zotlabs/Module/Contactedit.php:415
+#: ../../Zotlabs/Module/Contactedit.php:448
+#: ../../Zotlabs/Module/Pconfig.php:117 ../../Zotlabs/Module/Appman.php:230
+#: ../../Zotlabs/Module/Connedit.php:714 ../../Zotlabs/Module/Defperms.php:262
+#: ../../Zotlabs/Module/Locs.php:125
+#: ../../Zotlabs/Module/Admin/Accounts.php:209
+#: ../../Zotlabs/Module/Admin/Themes.php:174
+#: ../../Zotlabs/Module/Admin/Addons.php:193
+#: ../../Zotlabs/Module/Admin/Features.php:67
+#: ../../Zotlabs/Module/Admin/Profs.php:179
+#: ../../Zotlabs/Module/Admin/Channels.php:169
+#: ../../Zotlabs/Module/Admin/Account_edit.php:78
+#: ../../Zotlabs/Module/Admin/Logs.php:85
+#: ../../Zotlabs/Module/Admin/Site.php:403
+#: ../../Zotlabs/Module/Admin/Security.php:130
+#: ../../Zotlabs/Module/Affinity.php:84 ../../Zotlabs/Module/Permcats.php:257
+#: ../../Zotlabs/Module/Xchan.php:15 ../../Zotlabs/Module/Group.php:151
+#: ../../Zotlabs/Module/Group.php:160 ../../Zotlabs/Module/Invite.php:547
+#: ../../Zotlabs/Module/Mitem.php:257 ../../Zotlabs/Module/Photos.php:1057
+#: ../../Zotlabs/Module/Photos.php:1096 ../../Zotlabs/Module/Photos.php:1197
+#: ../../Zotlabs/Module/Sources.php:123 ../../Zotlabs/Module/Sources.php:160
+#: ../../Zotlabs/Module/Profiles.php:738 ../../Zotlabs/Module/Chat.php:208
+#: ../../Zotlabs/Module/Chat.php:247 ../../Zotlabs/Module/Regate.php:408
+#: ../../Zotlabs/Module/Setup.php:322 ../../Zotlabs/Module/Setup.php:362
+#: ../../Zotlabs/Module/Editpost.php:88 ../../Zotlabs/Module/Oauth2.php:115
+#: ../../Zotlabs/Module/Settings/Display.php:187
+#: ../../Zotlabs/Module/Settings/Network.php:62
+#: ../../Zotlabs/Module/Settings/Channel_home.php:91
+#: ../../Zotlabs/Module/Settings/Account.php:109
+#: ../../Zotlabs/Module/Settings/Editor.php:42
+#: ../../Zotlabs/Module/Settings/Features.php:48
+#: ../../Zotlabs/Module/Settings/Directory.php:42
+#: ../../Zotlabs/Module/Settings/Manage.php:43
+#: ../../Zotlabs/Module/Settings/Privacy.php:123
+#: ../../Zotlabs/Module/Settings/Events.php:42
+#: ../../Zotlabs/Module/Settings/Photos.php:42
+#: ../../Zotlabs/Module/Settings/Conversation.php:44
+#: ../../Zotlabs/Module/Settings/Channel.php:230
+#: ../../Zotlabs/Module/Settings/Profiles.php:52
+#: ../../Zotlabs/Module/Settings/Connections.php:42
+#: ../../Zotlabs/Module/Settings/Calendar.php:42
+#: ../../Zotlabs/Module/Settings/Multifactor.php:85
+#: ../../Zotlabs/Module/Email_validation.php:41
+#: ../../Zotlabs/Module/Import.php:623
+msgid "Submit"
+msgstr "Lagre"
+
+#: ../../view/theme/redbasic/php/config.php:197
+msgid "Theme settings"
+msgstr "Instillinger for utseende"
+
+#: ../../view/theme/redbasic/php/config.php:198
+msgid "Dark style"
+msgstr "Mørk drakt"
+
+#: ../../view/theme/redbasic/php/config.php:199
+msgid "Light style"
+msgstr "Lys drakt"
+
+#: ../../view/theme/redbasic/php/config.php:200
+msgid "Common settings"
+msgstr "Felles innstillinger"
+
+#: ../../view/theme/redbasic/php/config.php:201
+msgid "Primary theme color"
+msgstr "Primærfarge for tema"
+
+#: ../../view/theme/redbasic/php/config.php:201
+#: ../../view/theme/redbasic/php/config.php:202
+#: ../../view/theme/redbasic/php/config.php:203
+#: ../../view/theme/redbasic/php/config.php:204
+#: ../../view/theme/redbasic/php/config.php:205
+msgid "Current color, leave empty for default"
+msgstr "Gjeldende farge, la feltet stå tomt for å bruke standardfarge"
+
+#: ../../view/theme/redbasic/php/config.php:202
+msgid "Success theme color"
+msgstr "Farge for vellykket utførelse"
+
+#: ../../view/theme/redbasic/php/config.php:203
+msgid "Info theme color"
+msgstr "Farge for informasjon"
+
+#: ../../view/theme/redbasic/php/config.php:204
+msgid "Warning theme color"
+msgstr "Farge for advarsler"
+
+#: ../../view/theme/redbasic/php/config.php:205
+msgid "Danger theme color"
+msgstr "Farge for fare"
+
+#: ../../view/theme/redbasic/php/config.php:206
+msgid "Default to dark mode"
+msgstr "Bruk mørk drakt som standard"
+
+#: ../../view/theme/redbasic/php/config.php:206
+#: ../../view/theme/redbasic/php/config.php:207
+#: ../../view/theme/redbasic/php/config.php:208
+#: ../../view/theme/redbasic/php/config.php:220
+#: ../../include/conversation.php:1159
+#: ../../addon/socialauth/Mod_SocialAuth.php:218
+#: ../../addon/ijpost/Mod_Ijpost.php:61 ../../addon/redred/Mod_Redred.php:61
+#: ../../addon/libertree/Mod_Libertree.php:57
+#: ../../addon/dwpost/Mod_Dwpost.php:59 ../../addon/dwpost/Mod_Dwpost.php:63
+#: ../../addon/diaspora/Mod_Diaspora.php:70
+#: ../../addon/pumpio/Mod_Pumpio.php:92 ../../addon/pumpio/Mod_Pumpio.php:96
+#: ../../addon/pumpio/Mod_Pumpio.php:100 ../../addon/wppost/Mod_Wppost.php:84
+#: ../../addon/wppost/Mod_Wppost.php:88 ../../addon/wppost/Mod_Wppost.php:92
+#: ../../addon/wiki/Mod_Wiki.php:230 ../../addon/wiki/Mod_Wiki.php:231
+#: ../../addon/statusnet/Mod_Statusnet.php:258
+#: ../../addon/statusnet/Mod_Statusnet.php:280
+#: ../../addon/statusnet/Mod_Statusnet.php:289
+#: ../../addon/ljpost/Mod_Ljpost.php:61 ../../addon/ljpost/Mod_Ljpost.php:65
+#: ../../addon/ljpost/Mod_Ljpost.php:69
+#: ../../addon/cart/submodules/hzservices.php:67
+#: ../../addon/cart/submodules/hzservices.php:651
+#: ../../addon/cart/submodules/hzservices.php:655
+#: ../../addon/cart/submodules/subscriptions.php:153
+#: ../../addon/cart/submodules/subscriptions.php:425
+#: ../../addon/cart/submodules/paypalbuttonV2.php:88
+#: ../../addon/cart/submodules/paypalbuttonV2.php:98
+#: ../../addon/cart/submodules/paypalbutton.php:87
+#: ../../addon/cart/submodules/paypalbutton.php:95
+#: ../../addon/cart/submodules/manualcat.php:63
+#: ../../addon/cart/submodules/manualcat.php:254
+#: ../../addon/cart/submodules/manualcat.php:258
+#: ../../addon/cart/submodules/orderoptions.php:72
+#: ../../addon/cart/submodules/orderoptions.php:338
+#: ../../addon/cart/submodules/orderoptions.php:362
+#: ../../addon/cart/submodules/orderoptions.php:438
+#: ../../addon/cart/submodules/orderoptions.php:462
+#: ../../addon/cart/cart.php:1418 ../../addon/cart/Settings/Cart.php:61
+#: ../../addon/cart/Settings/Cart.php:73
+#: ../../addon/twitter/Mod_Twitter.php:160
+#: ../../addon/twitter/Mod_Twitter.php:169
+#: ../../addon/content_import/Mod_content_import.php:135
+#: ../../addon/content_import/Mod_content_import.php:136
+#: ../../addon/rtof/Mod_Rtof.php:47 ../../addon/nofed/Mod_Nofed.php:40
+#: ../../boot.php:1766 ../../Zotlabs/Lib/Libzotdir.php:166
+#: ../../Zotlabs/Lib/Libzotdir.php:167 ../../Zotlabs/Lib/Libzotdir.php:169
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:315
+#: ../../Zotlabs/Storage/Browser.php:316 ../../Zotlabs/Storage/Browser.php:397
+#: ../../Zotlabs/Storage/Browser.php:399 ../../Zotlabs/Storage/Browser.php:562
+#: ../../Zotlabs/Module/Filestorage.php:203
+#: ../../Zotlabs/Module/Filestorage.php:211
+#: ../../Zotlabs/Module/Contactedit.php:270
+#: ../../Zotlabs/Module/Contactedit.php:315
+#: ../../Zotlabs/Module/Register.php:515 ../../Zotlabs/Module/Connedit.php:622
+#: ../../Zotlabs/Module/Defperms.php:195 ../../Zotlabs/Module/Api.php:101
+#: ../../Zotlabs/Module/Menu.php:163 ../../Zotlabs/Module/Menu.php:222
+#: ../../Zotlabs/Module/Admin/Site.php:307
+#: ../../Zotlabs/Module/Permcats.php:247 ../../Zotlabs/Module/Group.php:138
+#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:148
+#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
+#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Mitem.php:176
+#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:254
+#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:668
+#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
+#: ../../Zotlabs/Module/Profiles.php:674 ../../Zotlabs/Module/Profiles.php:684
+#: ../../Zotlabs/Module/Profiles.php:692 ../../Zotlabs/Module/Profiles.php:696
+#: ../../Zotlabs/Module/Settings/Display.php:87
+#: ../../Zotlabs/Module/Settings/Privacy.php:133
+#: ../../Zotlabs/Module/Settings/Privacy.php:134
+#: ../../Zotlabs/Module/Settings/Privacy.php:135
+#: ../../Zotlabs/Module/Settings/Privacy.php:136
+#: ../../Zotlabs/Module/Settings/Privacy.php:137
+#: ../../Zotlabs/Module/Settings/Privacy.php:138
+#: ../../Zotlabs/Module/Settings/Channel.php:225
+#: ../../Zotlabs/Module/Settings/Multifactor.php:82
+#: ../../Zotlabs/Module/Import.php:612 ../../Zotlabs/Module/Import.php:616
+#: ../../Zotlabs/Module/Import.php:617
+msgid "No"
+msgstr "Nei"
+
+#: ../../view/theme/redbasic/php/config.php:206
+#: ../../view/theme/redbasic/php/config.php:207
+#: ../../view/theme/redbasic/php/config.php:208
+#: ../../view/theme/redbasic/php/config.php:220
+#: ../../include/conversation.php:1159
+#: ../../addon/socialauth/Mod_SocialAuth.php:218
+#: ../../addon/ijpost/Mod_Ijpost.php:61 ../../addon/redred/Mod_Redred.php:61
+#: ../../addon/libertree/Mod_Libertree.php:57
+#: ../../addon/dwpost/Mod_Dwpost.php:59 ../../addon/dwpost/Mod_Dwpost.php:63
+#: ../../addon/diaspora/Mod_Diaspora.php:70
+#: ../../addon/pumpio/Mod_Pumpio.php:92 ../../addon/pumpio/Mod_Pumpio.php:96
+#: ../../addon/pumpio/Mod_Pumpio.php:100 ../../addon/wppost/Mod_Wppost.php:84
+#: ../../addon/wppost/Mod_Wppost.php:88 ../../addon/wppost/Mod_Wppost.php:92
+#: ../../addon/wiki/Mod_Wiki.php:230 ../../addon/wiki/Mod_Wiki.php:231
+#: ../../addon/statusnet/Mod_Statusnet.php:258
+#: ../../addon/statusnet/Mod_Statusnet.php:280
+#: ../../addon/statusnet/Mod_Statusnet.php:289
+#: ../../addon/ljpost/Mod_Ljpost.php:61 ../../addon/ljpost/Mod_Ljpost.php:65
+#: ../../addon/ljpost/Mod_Ljpost.php:69
+#: ../../addon/cart/submodules/hzservices.php:67
+#: ../../addon/cart/submodules/hzservices.php:651
+#: ../../addon/cart/submodules/hzservices.php:655
+#: ../../addon/cart/submodules/subscriptions.php:153
+#: ../../addon/cart/submodules/subscriptions.php:425
+#: ../../addon/cart/submodules/paypalbuttonV2.php:88
+#: ../../addon/cart/submodules/paypalbuttonV2.php:98
+#: ../../addon/cart/submodules/paypalbutton.php:87
+#: ../../addon/cart/submodules/paypalbutton.php:95
+#: ../../addon/cart/submodules/manualcat.php:63
+#: ../../addon/cart/submodules/manualcat.php:254
+#: ../../addon/cart/submodules/manualcat.php:258
+#: ../../addon/cart/submodules/orderoptions.php:72
+#: ../../addon/cart/submodules/orderoptions.php:337
+#: ../../addon/cart/submodules/orderoptions.php:361
+#: ../../addon/cart/submodules/orderoptions.php:437
+#: ../../addon/cart/submodules/orderoptions.php:461
+#: ../../addon/cart/cart.php:1418 ../../addon/cart/Settings/Cart.php:61
+#: ../../addon/cart/Settings/Cart.php:73
+#: ../../addon/twitter/Mod_Twitter.php:160
+#: ../../addon/twitter/Mod_Twitter.php:169
+#: ../../addon/content_import/Mod_content_import.php:135
+#: ../../addon/content_import/Mod_content_import.php:136
+#: ../../addon/rtof/Mod_Rtof.php:47 ../../addon/nofed/Mod_Nofed.php:40
+#: ../../boot.php:1766 ../../Zotlabs/Lib/Libzotdir.php:166
+#: ../../Zotlabs/Lib/Libzotdir.php:167 ../../Zotlabs/Lib/Libzotdir.php:169
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:315
+#: ../../Zotlabs/Storage/Browser.php:316 ../../Zotlabs/Storage/Browser.php:397
+#: ../../Zotlabs/Storage/Browser.php:399 ../../Zotlabs/Storage/Browser.php:562
+#: ../../Zotlabs/Module/Filestorage.php:203
+#: ../../Zotlabs/Module/Filestorage.php:211
+#: ../../Zotlabs/Module/Contactedit.php:270
+#: ../../Zotlabs/Module/Register.php:515 ../../Zotlabs/Module/Defperms.php:195
+#: ../../Zotlabs/Module/Api.php:100 ../../Zotlabs/Module/Menu.php:163
+#: ../../Zotlabs/Module/Menu.php:222 ../../Zotlabs/Module/Admin/Site.php:309
+#: ../../Zotlabs/Module/Permcats.php:247 ../../Zotlabs/Module/Group.php:138
+#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:148
+#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
+#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Mitem.php:176
+#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:254
+#: ../../Zotlabs/Module/Mitem.php:255 ../../Zotlabs/Module/Photos.php:668
+#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
+#: ../../Zotlabs/Module/Profiles.php:674 ../../Zotlabs/Module/Profiles.php:684
+#: ../../Zotlabs/Module/Profiles.php:692 ../../Zotlabs/Module/Profiles.php:696
+#: ../../Zotlabs/Module/Settings/Display.php:87
+#: ../../Zotlabs/Module/Settings/Privacy.php:133
+#: ../../Zotlabs/Module/Settings/Privacy.php:134
+#: ../../Zotlabs/Module/Settings/Privacy.php:135
+#: ../../Zotlabs/Module/Settings/Privacy.php:136
+#: ../../Zotlabs/Module/Settings/Privacy.php:137
+#: ../../Zotlabs/Module/Settings/Privacy.php:138
+#: ../../Zotlabs/Module/Settings/Channel.php:225
+#: ../../Zotlabs/Module/Settings/Multifactor.php:82
+#: ../../Zotlabs/Module/Import.php:612 ../../Zotlabs/Module/Import.php:616
+#: ../../Zotlabs/Module/Import.php:617
+msgid "Yes"
+msgstr "Ja"
+
+#: ../../view/theme/redbasic/php/config.php:207
+msgid "Always use light icons for navbar"
+msgstr "Alltid bruk lyse ikoner i navigasjonslinjen"
+
+#: ../../view/theme/redbasic/php/config.php:207
+msgid "Enable this option if you use a dark navbar color in light mode"
msgstr ""
+"Slå på dette valget om du bruker mørk farge for navigasjonslinjen med lys "
+"drakt"
-#: ../../include/bbcode.php:346
+#: ../../view/theme/redbasic/php/config.php:208
+msgid "Narrow navbar"
+msgstr "Smal navigasjonslinje"
+
+#: ../../view/theme/redbasic/php/config.php:209
+msgid "Navigation bar background color"
+msgstr "Navigasjonslinjens bakgrunnsfarge"
+
+#: ../../view/theme/redbasic/php/config.php:210
+msgid "Dark navigation bar background color"
+msgstr "Mørk bakgrunnsfarge for navigasjonslinjen"
+
+#: ../../view/theme/redbasic/php/config.php:211
+msgid "Set the background color"
+msgstr "Angi bakgrunnsfargen"
+
+#: ../../view/theme/redbasic/php/config.php:212
+msgid "Set the dark background color"
+msgstr "Angi bakgrunnsfargen for mørk drakt"
+
+#: ../../view/theme/redbasic/php/config.php:213
+msgid "Set the background image"
+msgstr "Angi bakgrunnsbilde"
+
+#: ../../view/theme/redbasic/php/config.php:214
+msgid "Set the dark background image"
+msgstr "Angi bakgrunnsbilde for mørk drakt"
+
+#: ../../view/theme/redbasic/php/config.php:215
+msgid "Set font-size for the entire application"
+msgstr "Angi skriftstørrelsen for hele programmet"
+
+#: ../../view/theme/redbasic/php/config.php:215
+msgid "Examples: 1rem, 100%, 16px"
+msgstr "For eksempel: 1rem, 100%, 16px"
+
+#: ../../view/theme/redbasic/php/config.php:216
+msgid "Set radius of corners in rem"
+msgstr "Angi hjørneradius i rem"
+
+#: ../../view/theme/redbasic/php/config.php:216
+msgid "Leave empty for default radius"
+msgstr "La feltet stå tomt for å bruke standard radius"
+
+#: ../../view/theme/redbasic/php/config.php:217
+msgid "Set maximum width of content region in rem"
+msgstr "Set maksbredde for hovedregionen i rem"
+
+#: ../../view/theme/redbasic/php/config.php:217
+msgid "Leave empty for default width"
+msgstr "La feltet stå tomt for å bruke standard bredde"
+
+#: ../../view/theme/redbasic/php/config.php:218
+msgid "Set size of conversation author photo"
+msgstr "Angi størrelsen for samtalens forfatterbilde"
+
+#: ../../view/theme/redbasic/php/config.php:218
+#: ../../view/theme/redbasic/php/config.php:219
+msgid "Leave empty for default size"
+msgstr "La feltet stå tomt for å bruke standard størrelse"
+
+#: ../../view/theme/redbasic/php/config.php:219
+msgid "Set size of followup author photos"
+msgstr "Angi størrelsen på forfatterbilder ved oppfølging"
+
+#: ../../view/theme/redbasic/php/config.php:220
+msgid "Show advanced settings"
+msgstr "Vis avanserte innstillinger"
+
+#: ../../include/bookmarks.php:34
#, php-format
-msgid ""
-"This post contains an installable %s element, however you lack permissions "
-"to install it on this site."
-msgstr ""
-"Dette innlegget inneholder det installerbare elementet %s, men du mangler "
-"tillatelse til å installere det på dette nettstedet."
+msgid "%1$s's bookmarks"
+msgstr "%1$s sine bokmerker"
-#: ../../include/bbcode.php:356 ../../Zotlabs/Module/Impel.php:47
-msgid "webpage"
-msgstr "nettside"
+#: ../../include/opengraph.php:56
+#, php-format
+msgid "This is the home page of %s."
+msgstr "Dette er hjemmesiden til %s."
-#: ../../include/bbcode.php:359 ../../Zotlabs/Module/Impel.php:57
-msgid "layout"
-msgstr "layout"
+#: ../../include/auth.php:231
+msgid "Delegation session ended."
+msgstr "Delegasjonsøkt avsluttet."
-#: ../../include/bbcode.php:362 ../../Zotlabs/Module/Impel.php:52
-msgid "block"
-msgstr "byggekloss"
+#: ../../include/auth.php:235
+msgid "Logged out."
+msgstr "Logget ut."
-#: ../../include/bbcode.php:365 ../../Zotlabs/Module/Impel.php:64
-msgid "menu"
-msgstr "meny"
+#: ../../include/auth.php:341
+msgid "Email validation is incomplete. Please check your email."
+msgstr "Verifisering via epost er ikke fullført. Sjekk eposten din."
-#: ../../include/bbcode.php:568
-msgid "card"
-msgstr ""
+#: ../../include/auth.php:357
+msgid "Failed authentication"
+msgstr "Mislykket autentisering"
-#: ../../include/bbcode.php:570
-msgid "article"
-msgstr ""
+#: ../../include/auth.php:367 ../../addon/openid/Mod_Openid.php:189
+msgid "Login failed."
+msgstr "Innlogging mislyktes."
-#: ../../include/bbcode.php:572 ../../include/conversation.php:180
-#: ../../include/text.php:2364 ../../include/markdown.php:208
-#: ../../Zotlabs/Module/Tagger.php:79
-msgid "post"
-msgstr "innlegg"
+#: ../../include/oembed.php:159
+msgid "View PDF"
+msgstr "Vis PDF"
+
+#: ../../include/oembed.php:394
+msgid " by "
+msgstr " av "
+
+#: ../../include/oembed.php:395
+msgid " on "
+msgstr "på"
+
+#: ../../include/oembed.php:428
+msgid "Embedded content"
+msgstr "Innebygget innhold"
+
+#: ../../include/oembed.php:437
+msgid "Embedding disabled"
+msgstr "Innbygging avskrudd"
+
+#: ../../include/event.php:35 ../../include/event.php:141
+msgid "l F d, Y \\@ g:i A"
+msgstr "l F d, Y \\@ g:i A"
+
+#: ../../include/event.php:43
+msgid "Starts:"
+msgstr "Starter:"
+
+#: ../../include/event.php:53
+msgid "Finishes:"
+msgstr "Slutter:"
+
+#: ../../include/event.php:67 ../../include/event.php:159
+#: ../../include/channel.php:1642 ../../Zotlabs/Module/Directory.php:354
+msgid "Location:"
+msgstr "Plassering:"
+
+#: ../../include/event.php:141
+msgid "l F d, Y"
+msgstr "l d. F, Y"
+
+#: ../../include/event.php:145
+msgid "Start:"
+msgstr "Start:"
+
+#: ../../include/event.php:149
+msgid "End:"
+msgstr "Slutt:"
+
+#: ../../include/event.php:154 ../../addon/openid/MysqlProvider.php:67
+msgid "Timezone"
+msgstr "Tidssone"
+
+#: ../../include/event.php:1246
+msgid "This event has been added to your calendar."
+msgstr "Denne hendelsen er lagt til i din kalender."
+
+#: ../../include/event.php:1366 ../../include/conversation.php:153
+#: ../../include/text.php:2380 ../../Zotlabs/Module/Tagger.php:77
+#: ../../Zotlabs/Module/Like.php:441
+#: ../../Zotlabs/Module/Channel_calendar.php:209
+msgid "event"
+msgstr "hendelse"
+
+#: ../../include/event.php:1450
+msgid "Not specified"
+msgstr "Ikke spesifisert"
+
+#: ../../include/event.php:1451
+msgid "Needs Action"
+msgstr "Trenger handling"
+
+#: ../../include/event.php:1452
+msgid "Completed"
+msgstr "Ferdig"
+
+#: ../../include/event.php:1453
+msgid "In Process"
+msgstr "Igang"
+
+#: ../../include/event.php:1454
+msgid "Cancelled"
+msgstr "Avbrutt"
+
+#: ../../include/event.php:1535 ../../include/connections.php:790
+#: ../../Zotlabs/Module/Connedit.php:741 ../../Zotlabs/Module/Cdav.php:1377
+msgid "Mobile"
+msgstr "Mobil"
+
+#: ../../include/event.php:1536 ../../include/connections.php:791
+#: ../../Zotlabs/Module/Connedit.php:742 ../../Zotlabs/Module/Cdav.php:1378
+msgid "Home"
+msgstr "Hjem"
+
+#: ../../include/event.php:1537 ../../include/connections.php:792
+msgid "Home, Voice"
+msgstr "Hjem, telefon"
+
+#: ../../include/event.php:1538 ../../include/connections.php:793
+msgid "Home, Fax"
+msgstr "Hjem, fax"
+
+#: ../../include/event.php:1539 ../../include/connections.php:794
+#: ../../Zotlabs/Module/Connedit.php:743 ../../Zotlabs/Module/Cdav.php:1379
+msgid "Work"
+msgstr "Arbeid"
+
+#: ../../include/event.php:1540 ../../include/connections.php:795
+msgid "Work, Voice"
+msgstr "Arbeid, telefon"
+
+#: ../../include/event.php:1541 ../../include/connections.php:796
+msgid "Work, Fax"
+msgstr "Arbeid, fax"
+
+#: ../../include/event.php:1542 ../../include/event.php:1549
+#: ../../include/selectors.php:64 ../../include/selectors.php:81
+#: ../../include/selectors.php:119 ../../include/selectors.php:155
+#: ../../include/connections.php:797 ../../include/connections.php:804
+#: ../../Zotlabs/Access/PermissionRoles.php:362
+#: ../../Zotlabs/Module/Connedit.php:744 ../../Zotlabs/Module/Cdav.php:1380
+msgid "Other"
+msgstr "Annen"
+
+#: ../../include/feedutils.php:863 ../../include/text.php:1602
+msgid "unknown"
+msgstr "ukjent"
+
+#: ../../include/items.php:484 ../../addon/hzfiles/hzfiles.php:75
+#: ../../addon/redphotos/redphotos.php:119
+#: ../../addon/redfiles/redfiles.php:109
+#: ../../Zotlabs/Module/Import_items.php:116
+#: ../../Zotlabs/Module/Dreport.php:10 ../../Zotlabs/Module/Dreport.php:60
+#: ../../Zotlabs/Module/Subthread.php:89 ../../Zotlabs/Module/Group.php:109
+#: ../../Zotlabs/Module/Like.php:332 ../../Zotlabs/Module/Profperm.php:29
+msgid "Permission denied"
+msgstr "Tillatelse avvist"
+
+#: ../../include/items.php:1278
+msgid "Visible to anybody on the internet."
+msgstr "Synlig for alle."
+
+#: ../../include/items.php:1280
+msgid "Visible to you only."
+msgstr "Synlig bare for deg."
-#: ../../include/bbcode.php:576 ../../include/markdown.php:206
+#: ../../include/items.php:1282
+msgid "Visible to anybody in this network."
+msgstr "Synlig for alle i dette nettverket."
+
+#: ../../include/items.php:1284
+msgid "Visible to anybody authenticated."
+msgstr "Synlig for alle som er autentisert."
+
+#: ../../include/items.php:1286
#, php-format
-msgid "%1$s wrote the following %2$s %3$s"
-msgstr "%1$s skrev følgende %2$s %3$s"
+msgid "Visible to anybody on %s."
+msgstr "Synlig for alle på %s."
-#: ../../include/bbcode.php:653 ../../include/bbcode.php:661
-msgid "Click to open/close"
-msgstr "Klikk for å åpne/lukke"
+#: ../../include/items.php:1288
+msgid "Visible to all connections."
+msgstr "Synlig for alle forbindelser."
-#: ../../include/bbcode.php:661 ../../include/markdown.php:278
-msgid "spoiler"
-msgstr ""
+#: ../../include/items.php:1290
+msgid "Visible to approved connections."
+msgstr "Synlig for godkjente forbindelser."
-#: ../../include/bbcode.php:674
-msgid "View article"
-msgstr ""
+#: ../../include/items.php:1292
+msgid "Visible to specific connections."
+msgstr "Synlig for spesifikke forbindelser."
-#: ../../include/bbcode.php:674
-msgid "View summary"
-msgstr ""
+#: ../../include/items.php:3502 ../../Zotlabs/Lib/Activity.php:2225
+#: ../../Zotlabs/Module/Share.php:124
+#, php-format
+msgid "&#x1f501; Repeated %1$s's %2$s"
+msgstr "&#x1f501; videreformidlet %2$s til %1$s "
-#: ../../include/bbcode.php:1127 ../../include/bbcode.php:1312
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:630
-msgid "Different viewers will see this text differently"
-msgstr "Denne teksten vil se forskjellig ut for ulike besøkende"
+#: ../../include/items.php:4579 ../../Zotlabs/Module/Group.php:63
+#: ../../Zotlabs/Module/Group.php:207
+msgid "Privacy group not found."
+msgstr "Personverngruppen ble ikke funnet."
-#: ../../include/bbcode.php:1635
-msgid "$1 wrote:"
-msgstr "$1 skrev:"
+#: ../../include/items.php:4595
+msgid "Privacy group is empty."
+msgstr "Personverngruppen er tom."
+
+#: ../../include/items.php:4602
+#, php-format
+msgid "Privacy group: %s"
+msgstr "Personverngruppe: %s"
+
+#: ../../include/items.php:4612
+#, php-format
+msgid "Connection: %s"
+msgstr "Forbindelse: %s"
+
+#: ../../include/items.php:4614
+msgid "Connection not found."
+msgstr "Forbindelsen ble ikke funnet."
#: ../../include/features.php:53 ../../Zotlabs/Module/Admin/Features.php:55
#: ../../Zotlabs/Module/Admin/Features.php:56
@@ -122,19 +665,19 @@ msgstr "Kalender"
#: ../../include/features.php:84
msgid "Start calendar week on Monday"
-msgstr ""
+msgstr "Mandag er ukestart"
#: ../../include/features.php:85
msgid "Default is Sunday"
-msgstr ""
+msgstr "Søndag er standard"
#: ../../include/features.php:92
msgid "Event Timezone Selection"
-msgstr ""
+msgstr "Tidssone for arrangementet"
#: ../../include/features.php:93
msgid "Allow event creation in timezones other than your own."
-msgstr ""
+msgstr "Tillat arrangementer i andre tidssoner enn din egen."
#: ../../include/features.php:102
msgid "Channel Home"
@@ -156,11 +699,11 @@ msgstr "Merkelappsky"
msgid "Provide a personal tag cloud on your channel page"
msgstr "Tilby en personlig merkelappsky på din kanalside"
-#: ../../include/features.php:122 ../../include/features.php:356
+#: ../../include/features.php:122 ../../include/features.php:364
msgid "Use blog/list mode"
msgstr "Bruk blogg/listemodus"
-#: ../../include/features.php:123 ../../include/features.php:357
+#: ../../include/features.php:123 ../../include/features.php:365
msgid "Comments will be displayed separately"
msgstr "Kommentarer blir vist separat fra innlegget"
@@ -180,15 +723,15 @@ msgstr ""
#: ../../include/features.php:144
msgid "Conversation"
-msgstr ""
+msgstr "Samtale"
#: ../../include/features.php:156
msgid "Emoji Reactions"
-msgstr ""
+msgstr "Hurtigsvar med Emojier"
#: ../../include/features.php:157
msgid "Add emoji reaction ability to posts"
-msgstr ""
+msgstr "Legger til en knapp for hurtige svar på innlegg med en emoji"
#: ../../include/features.php:164
msgid "Dislike Posts"
@@ -200,49 +743,57 @@ msgstr "Mulighet til å mislike innlegg/kommentarer"
#: ../../include/features.php:172
msgid "Star Posts"
-msgstr "Stjerneinnlegg"
+msgstr "Favorittmerk innlegg"
#: ../../include/features.php:173
-msgid "Ability to mark special posts with a star indicator"
-msgstr "Mulighet til å merke spesielle innlegg med en stjerne"
+msgid "Ability to mark conversations with a star"
+msgstr "Mulighet til å favorittmerke innlegg med en stjerne"
#: ../../include/features.php:180
-msgid "Reply on comment"
-msgstr ""
+msgid "File Posts"
+msgstr "Arkivér innlegg"
#: ../../include/features.php:181
+msgid "Ability to file posts"
+msgstr "Mulighet til å arkivere innlegg i mapper"
+
+#: ../../include/features.php:188
+msgid "Reply on comment"
+msgstr "Svar på kommentar"
+
+#: ../../include/features.php:189
msgid "Ability to reply on selected comment"
-msgstr ""
+msgstr "Mulighet for å svare på en valgt kommentar"
-#: ../../include/features.php:190 ../../Zotlabs/Lib/Apps.php:353
+#: ../../include/features.php:198 ../../Zotlabs/Lib/Apps.php:353
msgid "Directory"
msgstr "Katalog"
-#: ../../include/features.php:194
+#: ../../include/features.php:202
msgid "Advanced Directory Search"
msgstr "Avansert katalogsøk"
-#: ../../include/features.php:195
+#: ../../include/features.php:203
msgid "Allows creation of complex directory search queries"
msgstr "Gjør det mulig å bruke avanserte kriterier ved søk i katalogen"
-#: ../../include/features.php:204
+#: ../../include/features.php:212
msgid "Editor"
-msgstr ""
+msgstr "Innleggsredigering"
-#: ../../include/features.php:208
+#: ../../include/features.php:216
msgid "Post Categories"
msgstr "Innleggskategorier"
-#: ../../include/features.php:209
+#: ../../include/features.php:217
msgid "Add categories to your posts"
msgstr "Legg kategorier til dine innlegg"
-#: ../../include/features.php:216
+#: ../../include/features.php:224
msgid "Large Photos"
msgstr "Store bilder"
-#: ../../include/features.php:217
+#: ../../include/features.php:225
msgid ""
"Include large (1024px) photo thumbnails in posts. If not enabled, use small "
"(640px) photo thumbnails"
@@ -250,48 +801,48 @@ msgstr ""
"Inkluder store (1024px) småbilder i innlegg. Hvis denne ikke er påskrudd, "
"bruk små (640px) småbilder"
-#: ../../include/features.php:224
+#: ../../include/features.php:232
msgid "Even More Encryption"
msgstr "Enda mer kryptering"
-#: ../../include/features.php:225
+#: ../../include/features.php:233
msgid ""
"Allow optional encryption of content end-to-end with a shared secret key"
msgstr ""
"Tillat valgfri kryptering av innhold ende-til-ende via en delt hemmelig "
"nøkkel"
-#: ../../include/features.php:232
+#: ../../include/features.php:240
msgid "Disable Comments"
msgstr "Slå av kommentarer"
-#: ../../include/features.php:233
+#: ../../include/features.php:241
msgid "Provide the option to disable comments for a post"
-msgstr "Slår på valg for å ikke tillate kommentarer til innlegget"
+msgstr "Gjør det mulig å slå av kommentarer for et innlegg"
-#: ../../include/features.php:240
+#: ../../include/features.php:248
msgid "Delayed Posting"
msgstr "Tidfest publisering"
-#: ../../include/features.php:241
+#: ../../include/features.php:249
msgid "Allow posts to be published at a later date"
msgstr "Tillat innlegg å bli publisert på et senere tidspunkt"
-#: ../../include/features.php:248
+#: ../../include/features.php:256
msgid "Content Expiration"
msgstr "Innholdet utløper"
-#: ../../include/features.php:249
+#: ../../include/features.php:257
msgid "Remove posts/comments and/or private messages at a future time"
msgstr ""
"Fjern innlegg/kommentarer og/eller private meldinger på et angitt tidspunkt "
"i fremtiden"
-#: ../../include/features.php:256
+#: ../../include/features.php:264
msgid "Suppress Duplicate Posts/Comments"
msgstr "Forhindre duplikat av innlegg/kommentarer"
-#: ../../include/features.php:257
+#: ../../include/features.php:265
msgid ""
"Prevent posts with identical content to be published with less than two "
"minutes in between submissions."
@@ -299,11 +850,11 @@ msgstr ""
"Forhindre innlegg med identisk innhold fra å bli publisert hvis det er "
"mindre enn to minutter mellom innsendingene."
-#: ../../include/features.php:264
+#: ../../include/features.php:272
msgid "Auto-save drafts of posts and comments"
msgstr "Automatisk lagring av kladd for innlegg og kommentarer"
-#: ../../include/features.php:265
+#: ../../include/features.php:273
msgid ""
"Automatically saves post and comment drafts in local browser storage to help "
"prevent accidental loss of compositions"
@@ -311,62 +862,63 @@ msgstr ""
"Lagrer automatisk utkast til innlegg og kommentarer i nettleserens interne "
"lager for å unngå å tape innholet ved et uhell"
-#: ../../include/features.php:274
+#: ../../include/features.php:282
msgid "Manage"
-msgstr ""
+msgstr "Administrasjon"
-#: ../../include/features.php:278
+#: ../../include/features.php:286
msgid "Navigation Channel Select"
-msgstr "Navigasjon kanalvalg"
+msgstr "Enklere kanalvalg"
-#: ../../include/features.php:279
+#: ../../include/features.php:287
msgid "Change channels directly from within the navigation dropdown menu"
msgstr "Endre kanaler direkte fra navigasjonsmenyen"
-#: ../../include/features.php:288 ../../Zotlabs/Module/Connections.php:347
+#: ../../include/features.php:296 ../../Zotlabs/Lib/Apps.php:344
#: ../../Zotlabs/Widget/Notifications.php:23
+#: ../../Zotlabs/Module/Connections.php:347
msgid "Network"
msgstr "Nettverk"
-#: ../../include/features.php:292
+#: ../../include/features.php:300
msgid "Events Filter"
msgstr "Arrangementsfilter"
-#: ../../include/features.php:293
+#: ../../include/features.php:301
msgid "Ability to display only events"
msgstr "Mulighet for kun å vise arrangementer"
-#: ../../include/features.php:300
+#: ../../include/features.php:308
msgid "Polls Filter"
msgstr "Filer for Spørreskjema"
-#: ../../include/features.php:301
+#: ../../include/features.php:309
msgid "Ability to display only polls"
msgstr "Mulighet for å kun vise spørreskjema"
-#: ../../include/features.php:308 ../../Zotlabs/Widget/Savedsearch.php:89
+#: ../../include/features.php:316 ../../Zotlabs/Widget/Savedsearch.php:89
msgid "Saved Searches"
msgstr "Lagrede søk"
-#: ../../include/features.php:309
+#: ../../include/features.php:317
msgid "Save search terms for re-use"
msgstr "Lagre søkeuttrykk for senere bruk"
-#: ../../include/features.php:316 ../../include/contact_widgets.php:55
-#: ../../Zotlabs/Widget/Filer.php:33
+#: ../../include/features.php:324 ../../include/contact_widgets.php:55
#: ../../Zotlabs/Widget/Activity_filter.php:196
+#: ../../Zotlabs/Widget/Filer.php:33
msgid "Saved Folders"
msgstr "Lagrede mapper"
-#: ../../include/features.php:317
+#: ../../include/features.php:325
msgid "Ability to file posts under folders"
msgstr "Mulighet til å sortere innlegg i mapper"
-#: ../../include/features.php:324
+#: ../../include/features.php:332
msgid "Alternate Stream Order"
msgstr "Alternativer for sortering"
-#: ../../include/features.php:325
+#: ../../include/features.php:333
msgid ""
"Ability to order the stream by last post date, last comment date or "
"unthreaded activities"
@@ -374,814 +926,780 @@ msgstr ""
"Mulighet for å sortere tidslinjen etter dato for siste innlegg, siste "
"kommentar eller som separate aktiviteter"
-#: ../../include/features.php:332
+#: ../../include/features.php:340
msgid "Contact Filter"
msgstr "Kontaktfilter"
-#: ../../include/features.php:333
+#: ../../include/features.php:341
msgid "Ability to display only posts of a selected contact"
msgstr "Mulighet for å kun vise innlegg fra en bestemt kontakt"
-#: ../../include/features.php:340
+#: ../../include/features.php:348
msgid "Forum Filter"
msgstr "Forumfilter"
-#: ../../include/features.php:341
+#: ../../include/features.php:349
msgid "Ability to display only posts of a specific forum"
msgstr "Mulighet for å kun vise innlegg i et bestemt forum"
-#: ../../include/features.php:348
+#: ../../include/features.php:356
msgid "Personal Posts Filter"
msgstr "Filter for personlige innlegg"
-#: ../../include/features.php:349
+#: ../../include/features.php:357
msgid "Ability to display only posts that you've interacted on"
msgstr "Mulighet til å vise kun innlegg du har deltatt på"
-#: ../../include/features.php:366 ../../include/nav.php:470
-#: ../../Zotlabs/Lib/Apps.php:351 ../../Zotlabs/Module/Fbrowser.php:29
+#: ../../include/features.php:374 ../../include/nav.php:470
+#: ../../Zotlabs/Lib/Apps.php:351
#: ../../Zotlabs/Widget/Channel_activities.php:93
+#: ../../Zotlabs/Module/Fbrowser.php:31
msgid "Photos"
msgstr "Bilder"
-#: ../../include/features.php:370
+#: ../../include/features.php:378
msgid "Photo Location"
msgstr "Bildeplassering"
-#: ../../include/features.php:371
+#: ../../include/features.php:379
msgid "If location data is available on uploaded photos, link this to a map."
msgstr ""
"Hvis plasseringsdata er tilgjengelige i opplastede bilder, plasser dette på "
"et kart."
-#: ../../include/features.php:378
+#: ../../include/features.php:386
msgid "Flag Adult Photos"
-msgstr ""
+msgstr "Merk bilder med voksent innhold"
-#: ../../include/features.php:379
+#: ../../include/features.php:387
msgid ""
"Provide photo edit option to hide inappropriate photos from default album "
"view"
msgstr ""
+"Gir mulighet for å merke bilder med upassende innhold fra standard "
+"albumvisning"
-#: ../../include/features.php:388 ../../Zotlabs/Lib/Apps.php:367
+#: ../../include/features.php:396 ../../Zotlabs/Lib/Apps.php:367
#: ../../Zotlabs/Module/Contactedit.php:430
msgid "Profiles"
-msgstr ""
+msgstr "Profiler"
-#: ../../include/features.php:392
+#: ../../include/features.php:400
msgid "Advanced Profiles"
msgstr "Avanserte profiler"
-#: ../../include/features.php:393
+#: ../../include/features.php:401
msgid "Additional profile sections and selections"
msgstr "Ytterlige seksjoner og utvalg til profilen"
-#: ../../include/features.php:400
+#: ../../include/features.php:408
msgid "Profile Import/Export"
msgstr "Profil-import/-eksport"
-#: ../../include/features.php:401
+#: ../../include/features.php:409
msgid "Save and load profile details across sites/channels"
msgstr "Lagre og åpne profildetaljer på tvers av nettsteder/kanaler"
-#: ../../include/features.php:408
+#: ../../include/features.php:416
msgid "Multiple Profiles"
msgstr "Flere profiler"
-#: ../../include/features.php:409
+#: ../../include/features.php:417
msgid "Ability to create multiple profiles"
msgstr "Mulig å lage flere profiler"
-#: ../../include/bookmarks.php:34
-#, php-format
-msgid "%1$s's bookmarks"
-msgstr "%1$s sine bokmerker"
-
-#: ../../include/feedutils.php:851 ../../include/text.php:1580
-msgid "unknown"
-msgstr "ukjent"
-
-#: ../../include/attach.php:156 ../../include/attach.php:205
-#: ../../include/attach.php:278 ../../include/attach.php:329
-#: ../../include/attach.php:431 ../../include/attach.php:445
-#: ../../include/attach.php:452 ../../include/attach.php:534
-#: ../../include/attach.php:1106 ../../include/attach.php:1179
-#: ../../include/attach.php:1344 ../../include/photos.php:31
-#: ../../include/items.php:3928 ../../Zotlabs/Lib/Chatroom.php:135
-#: ../../Zotlabs/Web/WebServer.php:119 ../../Zotlabs/Module/Menu.php:130
-#: ../../Zotlabs/Module/Menu.php:141 ../../Zotlabs/Module/Editblock.php:67
-#: ../../Zotlabs/Module/Channel.php:234 ../../Zotlabs/Module/Channel.php:391
-#: ../../Zotlabs/Module/Channel.php:429
-#: ../../Zotlabs/Module/New_channel.php:106
-#: ../../Zotlabs/Module/New_channel.php:131 ../../Zotlabs/Module/Block.php:24
-#: ../../Zotlabs/Module/Block.php:74 ../../Zotlabs/Module/Setup.php:220
-#: ../../Zotlabs/Module/Bookmarks.php:70 ../../Zotlabs/Module/Api.php:26
-#: ../../Zotlabs/Module/Channel_calendar.php:232
-#: ../../Zotlabs/Module/Editlayout.php:67
-#: ../../Zotlabs/Module/Editlayout.php:90 ../../Zotlabs/Module/Photos.php:71
-#: ../../Zotlabs/Module/Viewconnections.php:28
-#: ../../Zotlabs/Module/Viewconnections.php:33
-#: ../../Zotlabs/Module/Editpost.php:17 ../../Zotlabs/Module/Common.php:38
-#: ../../Zotlabs/Module/Profiles.php:168 ../../Zotlabs/Module/Profiles.php:611
-#: ../../Zotlabs/Module/Network.php:18 ../../Zotlabs/Module/Authtest.php:13
-#: ../../Zotlabs/Module/Invite.php:65 ../../Zotlabs/Module/Invite.php:316
-#: ../../Zotlabs/Module/Sources.php:80 ../../Zotlabs/Module/Suggest.php:32
-#: ../../Zotlabs/Module/Group.php:15 ../../Zotlabs/Module/Group.php:31
-#: ../../Zotlabs/Module/Settings.php:59 ../../Zotlabs/Module/Layouts.php:71
-#: ../../Zotlabs/Module/Layouts.php:78 ../../Zotlabs/Module/Layouts.php:89
-#: ../../Zotlabs/Module/Viewsrc.php:19 ../../Zotlabs/Module/Cover_photo.php:299
-#: ../../Zotlabs/Module/Cover_photo.php:312 ../../Zotlabs/Module/Chat.php:111
-#: ../../Zotlabs/Module/Chat.php:116 ../../Zotlabs/Module/Achievements.php:34
-#: ../../Zotlabs/Module/Appman.php:163
-#: ../../Zotlabs/Module/Service_limits.php:11
-#: ../../Zotlabs/Module/Connedit.php:299
+#: ../../include/attach.php:157 ../../include/attach.php:206
+#: ../../include/attach.php:279 ../../include/attach.php:330
+#: ../../include/attach.php:432 ../../include/attach.php:446
+#: ../../include/attach.php:453 ../../include/attach.php:535
+#: ../../include/attach.php:1115 ../../include/attach.php:1188
+#: ../../include/attach.php:1359 ../../include/photos.php:32
+#: ../../addon/openid/Mod_Id.php:53 ../../addon/keepout/keepout.php:36
+#: ../../addon/cards/Mod_Cards.php:89 ../../addon/cards/Mod_Card_edit.php:51
+#: ../../addon/pumpio/pumpio.php:44
+#: ../../addon/articles/Mod_Article_edit.php:51
+#: ../../addon/articles/Mod_Articles.php:94 ../../addon/wiki/Mod_Wiki.php:63
+#: ../../addon/wiki/Mod_Wiki.php:288 ../../addon/wiki/Mod_Wiki.php:425
+#: ../../Zotlabs/Lib/Chatroom.php:135 ../../Zotlabs/Module/Page.php:34
+#: ../../Zotlabs/Module/Page.php:133 ../../Zotlabs/Module/Display.php:387
+#: ../../Zotlabs/Module/Cover_photo.php:299
+#: ../../Zotlabs/Module/Cover_photo.php:312
#: ../../Zotlabs/Module/Editwebpage.php:68
#: ../../Zotlabs/Module/Editwebpage.php:89
#: ../../Zotlabs/Module/Editwebpage.php:107
#: ../../Zotlabs/Module/Editwebpage.php:121
-#: ../../Zotlabs/Module/Register.php:201 ../../Zotlabs/Module/Thing.php:309
-#: ../../Zotlabs/Module/Thing.php:331 ../../Zotlabs/Module/Thing.php:372
-#: ../../Zotlabs/Module/Blocks.php:73 ../../Zotlabs/Module/Blocks.php:80
-#: ../../Zotlabs/Module/Defperms.php:181 ../../Zotlabs/Module/Locs.php:98
-#: ../../Zotlabs/Module/Page.php:34 ../../Zotlabs/Module/Page.php:133
-#: ../../Zotlabs/Module/Vote.php:19 ../../Zotlabs/Module/Profile_photo.php:390
-#: ../../Zotlabs/Module/Profile_photo.php:421
-#: ../../Zotlabs/Module/Webpages.php:131
-#: ../../Zotlabs/Module/Connections.php:32
+#: ../../Zotlabs/Module/Webpages.php:130 ../../Zotlabs/Module/Network.php:18
+#: ../../Zotlabs/Module/Service_limits.php:11
+#: ../../Zotlabs/Module/Thing.php:316 ../../Zotlabs/Module/Thing.php:338
+#: ../../Zotlabs/Module/Thing.php:377 ../../Zotlabs/Module/Pdledit.php:35
#: ../../Zotlabs/Module/Filestorage.php:20
#: ../../Zotlabs/Module/Filestorage.php:78
#: ../../Zotlabs/Module/Filestorage.php:96
#: ../../Zotlabs/Module/Filestorage.php:119
-#: ../../Zotlabs/Module/Filestorage.php:165 ../../Zotlabs/Module/Pdledit.php:35
-#: ../../Zotlabs/Module/Moderate.php:15 ../../Zotlabs/Module/Regmod.php:20
+#: ../../Zotlabs/Module/Filestorage.php:165
+#: ../../Zotlabs/Module/Register.php:201 ../../Zotlabs/Module/Appman.php:163
+#: ../../Zotlabs/Module/Viewsrc.php:19 ../../Zotlabs/Module/Item.php:289
+#: ../../Zotlabs/Module/Item.php:308 ../../Zotlabs/Module/Item.php:318
+#: ../../Zotlabs/Module/Item.php:1272 ../../Zotlabs/Module/Achievements.php:34
+#: ../../Zotlabs/Module/Connedit.php:299 ../../Zotlabs/Module/Defperms.php:181
+#: ../../Zotlabs/Module/Suggest.php:32 ../../Zotlabs/Module/Regmod.php:20
+#: ../../Zotlabs/Module/Profile_photo.php:390
+#: ../../Zotlabs/Module/Profile_photo.php:421
+#: ../../Zotlabs/Module/Viewconnections.php:28
+#: ../../Zotlabs/Module/Viewconnections.php:33
+#: ../../Zotlabs/Module/Block.php:24 ../../Zotlabs/Module/Block.php:74
+#: ../../Zotlabs/Module/Api.php:26 ../../Zotlabs/Module/Layouts.php:71
+#: ../../Zotlabs/Module/Layouts.php:78 ../../Zotlabs/Module/Layouts.php:89
+#: ../../Zotlabs/Module/Common.php:38 ../../Zotlabs/Module/Locs.php:98
+#: ../../Zotlabs/Module/Menu.php:130 ../../Zotlabs/Module/Menu.php:141
+#: ../../Zotlabs/Module/Manage.php:10 ../../Zotlabs/Module/Bookmarks.php:70
#: ../../Zotlabs/Module/Attach_edit.php:90
#: ../../Zotlabs/Module/Attach_edit.php:99
-#: ../../Zotlabs/Module/Attach_edit.php:106 ../../Zotlabs/Module/Item.php:512
-#: ../../Zotlabs/Module/Item.php:531 ../../Zotlabs/Module/Item.php:541
-#: ../../Zotlabs/Module/Item.php:1463 ../../Zotlabs/Module/Display.php:387
-#: ../../Zotlabs/Module/Mitem.php:129 ../../Zotlabs/Module/Sharedwithme.php:19
-#: ../../Zotlabs/Module/Like.php:242 ../../Zotlabs/Module/Manage.php:10
-#: ../../Zotlabs/Module/Profile.php:99 ../../Zotlabs/Module/Profile.php:114
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:51
-#: ../../extend/addon/hzaddons/articles/Mod_Articles.php:94
-#: ../../extend/addon/hzaddons/pumpio/pumpio.php:44
-#: ../../extend/addon/hzaddons/keepout/keepout.php:36
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:63
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:288
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:425
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:51
-#: ../../extend/addon/hzaddons/cards/Mod_Cards.php:89
-#: ../../extend/addon/hzaddons/openid/Mod_Id.php:53
+#: ../../Zotlabs/Module/Attach_edit.php:106 ../../Zotlabs/Module/Group.php:15
+#: ../../Zotlabs/Module/Group.php:31 ../../Zotlabs/Module/Invite.php:65
+#: ../../Zotlabs/Module/Invite.php:316 ../../Zotlabs/Module/Like.php:230
+#: ../../Zotlabs/Module/Blocks.php:73 ../../Zotlabs/Module/Blocks.php:80
+#: ../../Zotlabs/Module/Mitem.php:129 ../../Zotlabs/Module/New_channel.php:106
+#: ../../Zotlabs/Module/New_channel.php:131 ../../Zotlabs/Module/Photos.php:71
+#: ../../Zotlabs/Module/Channel.php:234 ../../Zotlabs/Module/Channel.php:395
+#: ../../Zotlabs/Module/Channel.php:434 ../../Zotlabs/Module/Profile.php:99
+#: ../../Zotlabs/Module/Profile.php:114 ../../Zotlabs/Module/Moderate.php:15
+#: ../../Zotlabs/Module/Sources.php:80 ../../Zotlabs/Module/Profiles.php:168
+#: ../../Zotlabs/Module/Profiles.php:611
+#: ../../Zotlabs/Module/Sharedwithme.php:19
+#: ../../Zotlabs/Module/Authtest.php:13
+#: ../../Zotlabs/Module/Channel_calendar.php:220
+#: ../../Zotlabs/Module/Chat.php:111 ../../Zotlabs/Module/Chat.php:116
+#: ../../Zotlabs/Module/Editlayout.php:67
+#: ../../Zotlabs/Module/Editlayout.php:90 ../../Zotlabs/Module/Settings.php:59
+#: ../../Zotlabs/Module/Setup.php:220 ../../Zotlabs/Module/Editpost.php:17
+#: ../../Zotlabs/Module/Connections.php:32
+#: ../../Zotlabs/Module/Editblock.php:67 ../../Zotlabs/Module/Vote.php:19
msgid "Permission denied."
msgstr "Tillatelse avslått."
-#: ../../include/attach.php:273 ../../include/attach.php:324
-#: ../../include/attach.php:426
+#: ../../include/attach.php:274 ../../include/attach.php:325
+#: ../../include/attach.php:427
msgid "Item was not found."
msgstr "Elementet ble ikke funnet."
-#: ../../include/attach.php:290
+#: ../../include/attach.php:291
msgid "Unknown error."
-msgstr ""
+msgstr "Ukjent feil."
-#: ../../include/attach.php:621
+#: ../../include/attach.php:622
msgid "No source file."
msgstr "Ingen kildefil."
-#: ../../include/attach.php:643
+#: ../../include/attach.php:644
msgid "Cannot locate file to replace"
msgstr "Kan ikke finne filen som skal byttes ut"
-#: ../../include/attach.php:662
+#: ../../include/attach.php:663
msgid "Cannot locate file to revise/update"
msgstr "Finner ikke filen som skal revideres/oppdateres"
-#: ../../include/attach.php:808
+#: ../../include/attach.php:804 ../../include/attach.php:2609
+msgid "Filename too long"
+msgstr "Filnavnet er for langt"
+
+#: ../../include/attach.php:817
#, php-format
msgid "File exceeds size limit of %d"
msgstr "Filens størrelse overgår grensen på %d"
-#: ../../include/attach.php:829
+#: ../../include/attach.php:838
#, php-format
msgid "You have reached your limit of %1$.0f Mbytes attachment storage."
msgstr "Du har nådd din lagringsgrense for vedlegg på %1$.0f Mbytes."
-#: ../../include/attach.php:1019
+#: ../../include/attach.php:1028
msgid "File upload failed. Possible system limit or action terminated."
msgstr ""
"Mislyktes med å laste opp filen. Mulig systemgrense eller handling avbrutt."
-#: ../../include/attach.php:1048
+#: ../../include/attach.php:1057
msgid "Stored file could not be verified. Upload failed."
msgstr "Lagret fil kunne ikke bekreftes. Opplasting mislyktes."
-#: ../../include/attach.php:1120 ../../include/attach.php:1136
+#: ../../include/attach.php:1129 ../../include/attach.php:1145
msgid "Path not available."
msgstr "Stien er ikke tilgjengelig."
-#: ../../include/attach.php:1184 ../../include/attach.php:1349
+#: ../../include/attach.php:1193 ../../include/attach.php:1364
msgid "Empty pathname"
msgstr "Tomt sti-navn"
-#: ../../include/attach.php:1210
+#: ../../include/attach.php:1199
+msgid "Pathname too long"
+msgstr "Filbanen er for lang"
+
+#: ../../include/attach.php:1225
msgid "duplicate filename or path"
-msgstr "duplikat av filnavn eller sti"
+msgstr "duplikat av filnavn eller filbane"
-#: ../../include/attach.php:1238
+#: ../../include/attach.php:1253
msgid "Path not found."
-msgstr "Stien ble ikke funnet."
+msgstr "Filbanen ble ikke funnet."
-#: ../../include/attach.php:1305
+#: ../../include/attach.php:1320
msgid "mkdir failed."
-msgstr "mkdir mislyktes."
+msgstr "mkdir fikk en feil."
-#: ../../include/attach.php:1309
+#: ../../include/attach.php:1324
msgid "database storage failed."
msgstr "databaselagring mislyktes."
-#: ../../include/attach.php:1355
+#: ../../include/attach.php:1370
msgid "Empty path"
-msgstr "Tom sti"
+msgstr "Tom filbane"
-#: ../../include/attach.php:1984
-#, fuzzy, php-format
-#| msgid "Files: shared with me"
+#: ../../include/attach.php:2007
+#, php-format
msgid "%s shared an %s with you"
-msgstr "Filer: delt med meg"
+msgstr "%s delte en %s med deg"
-#: ../../include/attach.php:1984
+#: ../../include/attach.php:2007
#, php-format
msgid "%s shared a %s with you"
-msgstr ""
+msgstr "%s delte et %s med deg"
-#: ../../include/attach.php:1984 ../../Zotlabs/Module/Like.php:447
-#, fuzzy
-#| msgid "Image"
+#: ../../include/attach.php:2007 ../../Zotlabs/Module/Like.php:438
msgid "image"
-msgstr "Bilde"
+msgstr "bilde"
-#: ../../include/attach.php:1984
-#: ../../extend/addon/hzaddons/redfiles/redfilehelper.php:64
+#: ../../include/attach.php:2007 ../../addon/redfiles/redfilehelper.php:64
msgid "file"
-msgstr ""
-
-#: ../../include/network.php:413
-msgid "url: "
-msgstr ""
-
-#: ../../include/network.php:414
-msgid "error_code: "
-msgstr ""
+msgstr "fil"
-#: ../../include/network.php:415
-msgid "error_string: "
-msgstr ""
+#: ../../include/selectors.php:17
+msgid "Select a profile to assign to this contact"
+msgstr "Velg en profil for denne kontakten"
-#: ../../include/network.php:416
-msgid "content-type: "
-msgstr ""
+#: ../../include/selectors.php:45
+msgid "Frequently"
+msgstr "Ofte"
-#: ../../include/network.php:1774 ../../include/network.php:1775
-msgid "Friendica"
-msgstr "Friendica"
+#: ../../include/selectors.php:46
+msgid "Hourly"
+msgstr "Hver time"
-#: ../../include/network.php:1776
-msgid "OStatus"
-msgstr "OStatus"
+#: ../../include/selectors.php:47
+msgid "Twice daily"
+msgstr "To ganger daglig"
-#: ../../include/network.php:1777
-msgid "GNU-Social"
-msgstr ""
+#: ../../include/selectors.php:48
+msgid "Daily"
+msgstr "Daglig"
-#: ../../include/network.php:1778
-msgid "RSS/Atom"
-msgstr "RSS/Atom"
+#: ../../include/selectors.php:49
+msgid "Weekly"
+msgstr "Ukentlig"
-#: ../../include/network.php:1779
-msgid "ActivityPub"
-msgstr ""
+#: ../../include/selectors.php:50
+msgid "Monthly"
+msgstr "Månedlig"
-#: ../../include/network.php:1780 ../../Zotlabs/Module/Connedit.php:736
-#: ../../Zotlabs/Module/Admin/Accounts.php:316
-#: ../../Zotlabs/Module/Admin/Accounts.php:330
-#: ../../Zotlabs/Module/Cdav.php:1372
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:55
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:69
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:56
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:57
-msgid "Email"
-msgstr "E-post"
+#: ../../include/selectors.php:64 ../../include/selectors.php:81
+#: ../../include/channel.php:1731 ../../addon/openid/Mod_Id.php:85
+msgid "Male"
+msgstr "Mannlig"
-#: ../../include/network.php:1781
-msgid "Diaspora"
-msgstr "Diaspora"
+#: ../../include/selectors.php:64 ../../include/selectors.php:81
+#: ../../include/channel.php:1729 ../../addon/openid/Mod_Id.php:87
+msgid "Female"
+msgstr "Kvinnelig"
-#: ../../include/network.php:1782
-msgid "Facebook"
-msgstr "Facebook"
+#: ../../include/selectors.php:64
+msgid "Currently Male"
+msgstr "For tiden mann"
-#: ../../include/network.php:1783
-msgid "Zot"
-msgstr "Zot"
+#: ../../include/selectors.php:64
+msgid "Currently Female"
+msgstr "For tiden kvinne"
-#: ../../include/network.php:1784
-msgid "LinkedIn"
-msgstr "LinkedIn"
+#: ../../include/selectors.php:64
+msgid "Mostly Male"
+msgstr "For det meste mann"
-#: ../../include/network.php:1785
-msgid "XMPP/IM"
-msgstr "XMPP/IM"
+#: ../../include/selectors.php:64
+msgid "Mostly Female"
+msgstr "For det meste kvinne"
-#: ../../include/network.php:1786
-msgid "MySpace"
-msgstr "MySpace"
+#: ../../include/selectors.php:64
+msgid "Transgender"
+msgstr "Transkjønnet"
-#: ../../include/auth.php:232
-msgid "Delegation session ended."
-msgstr ""
+#: ../../include/selectors.php:64
+msgid "Intersex"
+msgstr "interkjønnet"
-#: ../../include/auth.php:236
-msgid "Logged out."
-msgstr "Logget ut."
+#: ../../include/selectors.php:64
+msgid "Transsexual"
+msgstr "Transseksuell"
-#: ../../include/auth.php:342
-msgid "Email validation is incomplete. Please check your email."
-msgstr ""
+#: ../../include/selectors.php:64
+msgid "Hermaphrodite"
+msgstr "Hermafroditt"
-#: ../../include/auth.php:358
-msgid "Failed authentication"
-msgstr "Mislykket autentisering"
+#: ../../include/selectors.php:64 ../../include/channel.php:1735
+msgid "Neuter"
+msgstr "Intetkjønn"
-#: ../../include/auth.php:368
-#: ../../extend/addon/hzaddons/openid/Mod_Openid.php:189
-msgid "Login failed."
-msgstr "Innlogging mislyktes."
+#: ../../include/selectors.php:64 ../../include/channel.php:1737
+msgid "Non-specific"
+msgstr "Ubestemt"
-#: ../../include/opengraph.php:56
-#, php-format
-msgid "This is the home page of %s."
-msgstr ""
+#: ../../include/selectors.php:64
+msgid "Undecided"
+msgstr "Ubestemt"
-#: ../../include/nav.php:109
-msgid "Remote authentication"
-msgstr "Fjernautentisering"
+#: ../../include/selectors.php:100 ../../include/selectors.php:119
+msgid "Males"
+msgstr "Menn"
-#: ../../include/nav.php:109
-msgid "Click to authenticate to your home hub"
-msgstr "Klikk for å godkjennes mot din hjemme-hub"
+#: ../../include/selectors.php:100 ../../include/selectors.php:119
+msgid "Females"
+msgstr "Kvinner"
-#: ../../include/nav.php:115 ../../Zotlabs/Module/Admin/Channels.php:168
-#: ../../Zotlabs/Module/Admin.php:118 ../../Zotlabs/Module/Manage.php:162
-#: ../../Zotlabs/Widget/Channel_activities.php:239
-#: ../../Zotlabs/Widget/Admin.php:29
-msgid "Channels"
-msgstr "Kanaler"
+#: ../../include/selectors.php:100
+msgid "Gay"
+msgstr "Homo"
-#: ../../include/nav.php:115
-msgid "Manage your channels"
-msgstr "Behandle kanalene dine"
+#: ../../include/selectors.php:100
+msgid "Lesbian"
+msgstr "Lesbisk"
-#: ../../include/nav.php:118 ../../Zotlabs/Lib/Apps.php:345
-#: ../../Zotlabs/Module/Admin/Addons.php:349
-#: ../../Zotlabs/Module/Admin/Themes.php:141
-#: ../../Zotlabs/Widget/Settings_menu.php:71
-#: ../../Zotlabs/Widget/Newmember.php:60
-msgid "Settings"
-msgstr "Innstillinger"
+#: ../../include/selectors.php:100
+msgid "No Preference"
+msgstr "Ingen preferanse"
-#: ../../include/nav.php:118
-msgid "Account/Channel Settings"
-msgstr "Konto-/kanalinnstillinger"
+#: ../../include/selectors.php:100
+msgid "Bisexual"
+msgstr "Biseksuell"
-#: ../../include/nav.php:124 ../../include/nav.php:154
-#: ../../include/nav.php:175 ../../boot.php:1759
-msgid "Logout"
-msgstr "Logg ut"
+#: ../../include/selectors.php:100
+msgid "Autosexual"
+msgstr "Autoseksuell"
-#: ../../include/nav.php:124 ../../include/nav.php:154
-msgid "End this session"
-msgstr "Avslutt denne økten"
+#: ../../include/selectors.php:100
+msgid "Abstinent"
+msgstr "Avholdende"
-#: ../../include/nav.php:127 ../../include/conversation.php:902
-#: ../../Zotlabs/Lib/Apps.php:350 ../../Zotlabs/Module/Connedit.php:480
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:58
-msgid "View Profile"
-msgstr "Vis profil"
+#: ../../include/selectors.php:100
+msgid "Virgin"
+msgstr "Jomfru"
-#: ../../include/nav.php:127
-msgid "Your profile page"
-msgstr "Din profilside"
+#: ../../include/selectors.php:100
+msgid "Deviant"
+msgstr "Avviker"
-#: ../../include/nav.php:130 ../../include/channel.php:1539
-#: ../../Zotlabs/Module/Profiles.php:851
-msgid "Edit Profiles"
-msgstr "Rediger profiler"
+#: ../../include/selectors.php:100
+msgid "Fetish"
+msgstr "Fetisj"
-#: ../../include/nav.php:130
-msgid "Manage/Edit profiles"
-msgstr "Håndter/endre profiler"
+#: ../../include/selectors.php:100
+msgid "Oodles"
+msgstr "Masse"
-#: ../../include/nav.php:132 ../../include/channel.php:1543
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:59
-msgid "Edit Profile"
-msgstr "Rediger profil"
+#: ../../include/selectors.php:100
+msgid "Nonsexual"
+msgstr "Ikke-seksuell"
-#: ../../include/nav.php:132 ../../Zotlabs/Widget/Newmember.php:42
-msgid "Edit your profile"
-msgstr "Rediger profil"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Single"
+msgstr "Enslig"
-#: ../../include/nav.php:139 ../../include/nav.php:143 ../../boot.php:1760
-#: ../../Zotlabs/Lib/Apps.php:342
-msgid "Login"
-msgstr "Logg inn"
+#: ../../include/selectors.php:138
+msgid "Lonely"
+msgstr "Ensom"
-#: ../../include/nav.php:139 ../../include/nav.php:143
-msgid "Sign in"
-msgstr "Logg på"
+#: ../../include/selectors.php:138
+msgid "Available"
+msgstr "Tilgjengelig"
-#: ../../include/nav.php:173
-msgid "Take me home"
-msgstr ""
+#: ../../include/selectors.php:138
+msgid "Unavailable"
+msgstr "Ikke tilgjengelig"
-#: ../../include/nav.php:175
-msgid "Log me out of this site"
-msgstr ""
+#: ../../include/selectors.php:138
+msgid "Has crush"
+msgstr "Er forelsket"
-#: ../../include/nav.php:180 ../../boot.php:1737
-#: ../../Zotlabs/Module/Register.php:543
-msgid "Register"
-msgstr "Registrer"
+#: ../../include/selectors.php:138
+msgid "Infatuated"
+msgstr "Betatt"
-#: ../../include/nav.php:180
-msgid "Create an account"
-msgstr "Lag en konto"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Dating"
+msgstr "Sammen med"
-#: ../../include/nav.php:194 ../../include/nav.php:341
-#: ../../Zotlabs/Lib/Apps.php:354 ../../Zotlabs/Module/Layouts.php:184
-msgid "Help"
-msgstr "Hjelp"
+#: ../../include/selectors.php:138
+msgid "Unfaithful"
+msgstr "Utro"
-#: ../../include/nav.php:194
-msgid "Help and documentation"
-msgstr "Hjelp og dokumentasjon"
+#: ../../include/selectors.php:138
+msgid "Sex Addict"
+msgstr "Sexavhengig"
-#: ../../include/nav.php:208 ../../include/text.php:1190
-#: ../../include/text.php:1202 ../../include/acl_selectors.php:149
-#: ../../Zotlabs/Lib/Apps.php:357 ../../Zotlabs/Module/Connections.php:403
-#: ../../Zotlabs/Module/Search.php:47 ../../Zotlabs/Widget/Sitesearch.php:37
-#: ../../Zotlabs/Widget/Activity_filter.php:210
-msgid "Search"
-msgstr "Søk"
+#: ../../include/selectors.php:138 ../../include/channel.php:467
+#: ../../include/channel.php:470 ../../Zotlabs/Widget/Affinity.php:38
+#: ../../Zotlabs/Module/Contactedit.php:283
+#: ../../Zotlabs/Module/Connedit.php:581
+msgid "Friends"
+msgstr "Venner"
-#: ../../include/nav.php:208
-msgid "Search site @name, !forum, #tag, ?docs, content"
-msgstr "Søk etter @navn, !forum, #emne, ?dokumentasjon eller innhold"
+#: ../../include/selectors.php:138
+msgid "Friends/Benefits"
+msgstr "Venner/Frynsegoder"
-#: ../../include/nav.php:214 ../../Zotlabs/Widget/Admin.php:61
-msgid "Admin"
-msgstr "Administrator"
+#: ../../include/selectors.php:138
+msgid "Casual"
+msgstr "Tilfeldig"
-#: ../../include/nav.php:214
-msgid "Site Setup and Configuration"
-msgstr "Nettstedsoppsett og -konfigurasjon"
+#: ../../include/selectors.php:138
+msgid "Engaged"
+msgstr "Forlovet"
-#: ../../include/nav.php:345 ../../Zotlabs/Module/New_channel.php:158
-#: ../../Zotlabs/Module/New_channel.php:165
-#: ../../Zotlabs/Module/Defperms.php:254 ../../Zotlabs/Widget/Messages.php:36
-#: ../../Zotlabs/Widget/Notifications.php:175
-#, fuzzy
-msgid "Loading"
-msgstr "Laster..."
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Married"
+msgstr "Gift"
-#: ../../include/nav.php:350
-msgid "@name, #tag, ?doc, content"
-msgstr "@navn, #merkelapp, ?dokumentasjon, innhold"
+#: ../../include/selectors.php:138
+msgid "Imaginarily married"
+msgstr "Gift i fantasien"
-#: ../../include/nav.php:351
-msgid "Please wait..."
-msgstr "Vennligst vent..."
+#: ../../include/selectors.php:138
+msgid "Partners"
+msgstr "Partnere"
-#: ../../include/nav.php:357 ../../Zotlabs/Lib/Apps.php:329
-msgid "Apps"
-msgstr "Apper"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Cohabiting"
+msgstr "Samboer"
-#: ../../include/nav.php:358
-msgid "Channel Apps"
-msgstr ""
+#: ../../include/selectors.php:138
+msgid "Common law"
+msgstr "Samboer"
-#: ../../include/nav.php:359
-msgid "System Apps"
-msgstr ""
+#: ../../include/selectors.php:138
+msgid "Happy"
+msgstr "Lykkelig"
-#: ../../include/nav.php:360
-msgid "Pinned Apps"
-msgstr ""
+#: ../../include/selectors.php:138
+msgid "Not looking"
+msgstr "Ikke på utkikk"
-#: ../../include/nav.php:361
-msgid "Featured Apps"
-msgstr "Fremhevede apper"
+#: ../../include/selectors.php:138
+msgid "Swinger"
+msgstr "Partnerbytte"
-#: ../../include/nav.php:447 ../../Zotlabs/Lib/Apps.php:349
-#: ../../Zotlabs/Module/Admin/Channels.php:176
-msgid "Channel"
-msgstr "Kanal"
+#: ../../include/selectors.php:138
+msgid "Betrayed"
+msgstr "Bedratt"
-#: ../../include/nav.php:450
-msgid "Status Messages and Posts"
-msgstr "Statusmeldinger og -innlegg"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Separated"
+msgstr "Separert"
-#: ../../include/nav.php:460 ../../Zotlabs/Module/Help.php:239
-msgid "About"
-msgstr "Om"
+#: ../../include/selectors.php:138
+msgid "Unstable"
+msgstr "Ustabilt"
-#: ../../include/nav.php:463
-msgid "Profile Details"
-msgstr "Profildetaljer"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Divorced"
+msgstr "Skilt"
-#: ../../include/nav.php:473 ../../include/photos.php:722
-msgid "Photo Albums"
-msgstr "Fotoalbum"
+#: ../../include/selectors.php:138
+msgid "Imaginarily divorced"
+msgstr "Skilt i fantasien"
-#: ../../include/nav.php:478 ../../Zotlabs/Lib/Apps.php:346
-#: ../../Zotlabs/Storage/Browser.php:351 ../../Zotlabs/Module/Fbrowser.php:85
-#: ../../Zotlabs/Widget/Channel_activities.php:125
-#: ../../Zotlabs/Widget/Notifications.php:108
-msgid "Files"
-msgstr "Filer"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "Widowed"
+msgstr "Enke"
-#: ../../include/nav.php:481
-msgid "Files and Storage"
-msgstr "Filer og lagring"
+#: ../../include/selectors.php:138
+msgid "Uncertain"
+msgstr "Usikkert"
-#: ../../include/nav.php:503 ../../include/nav.php:506
-#: ../../Zotlabs/Lib/Apps.php:336 ../../Zotlabs/Widget/Chatroom_list.php:22
-msgid "Chatrooms"
-msgstr "Chatrom"
+#: ../../include/selectors.php:138 ../../include/selectors.php:155
+msgid "It's complicated"
+msgstr "Det er komplisert"
-#: ../../include/nav.php:516 ../../Zotlabs/Lib/Apps.php:335
-#: ../../Zotlabs/Module/Bookmarks.php:90
-msgid "Bookmarks"
-msgstr "Bokmerker"
+#: ../../include/selectors.php:138
+msgid "Don't care"
+msgstr "Bryr meg ikke"
-#: ../../include/nav.php:519
-msgid "Saved Bookmarks"
-msgstr "Lagrede bokmerker"
+#: ../../include/selectors.php:138
+msgid "Ask me"
+msgstr "Spør meg"
-#: ../../include/nav.php:527 ../../Zotlabs/Lib/Apps.php:347
-#: ../../Zotlabs/Module/Webpages.php:247
-#: ../../Zotlabs/Widget/Channel_activities.php:168
-msgid "Webpages"
-msgstr "Websider"
+#: ../../include/activities.php:30
+msgid " and "
+msgstr " og "
-#: ../../include/nav.php:530
-msgid "View Webpages"
-msgstr ""
+#: ../../include/activities.php:32
+msgid ", "
+msgstr "."
-#: ../../include/event.php:35 ../../include/event.php:133
-msgid "l F d, Y \\@ g:i A"
-msgstr "l F d, Y \\@ g:i A"
+#: ../../include/activities.php:36 ../../include/activities.php:38
+#: ../../addon/openid/MysqlProvider.php:58
+#: ../../addon/openid/MysqlProvider.php:59
+#: ../../addon/openid/MysqlProvider.php:60 ../../Zotlabs/Lib/Apps.php:365
+#: ../../Zotlabs/Module/Profile_photo.php:227
+msgid "Profile Photo"
+msgstr "Profilbilde"
-#: ../../include/event.php:43
-msgid "Starts:"
-msgstr "Starter:"
+#: ../../include/activities.php:36 ../../Zotlabs/Module/Cover_photo.php:230
+msgid "Cover Photo"
+msgstr "Forsidebilde"
-#: ../../include/event.php:53
-msgid "Finishes:"
-msgstr "Slutter:"
+#: ../../include/activities.php:45
+msgid "public profile"
+msgstr "offentlig profil"
-#: ../../include/event.php:67 ../../include/event.php:158
-#: ../../include/channel.php:1640 ../../Zotlabs/Module/Directory.php:354
-msgid "Location:"
-msgstr "Plassering:"
+#: ../../include/activities.php:62
+#, php-format
+msgid "%1$s %2$s has been updated to %3$s."
+msgstr "%1$s %2$s har blitt oppdatert til %3$s."
-#: ../../include/event.php:133
-msgid "l F d, Y"
-msgstr ""
+#: ../../include/activities.php:65
+#, php-format
+msgid "%1$s updated the %2$s. Changed %3$s."
+msgstr "%1$s har oppdatert %2$s, endret %3$s."
-#: ../../include/event.php:137
-msgid "Start:"
-msgstr ""
+#: ../../include/contact_widgets.php:13
+#, php-format
+msgid "%d invitation available"
+msgid_plural "%d invitations available"
+msgstr[0] "%d invitasjon tilgjengelig"
+msgstr[1] "%d invitasjoner tilgjengelig"
-#: ../../include/event.php:141
-msgid "End:"
-msgstr ""
+#: ../../include/contact_widgets.php:18 ../../include/acl_selectors.php:145
+#: ../../Zotlabs/Module/Admin/Site.php:407
+msgid "Advanced"
+msgstr "Avansert"
-#: ../../include/event.php:146
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:67
-msgid "Timezone"
-msgstr "Tidssone"
+#: ../../include/contact_widgets.php:21
+msgid "Find Channels"
+msgstr "Finn kanaler"
-#: ../../include/event.php:1246
-msgid "This event has been added to your calendar."
-msgstr "Denne hendelsen er lagt til i din kalender."
+#: ../../include/contact_widgets.php:22
+msgid "Enter name or interest"
+msgstr "Skriv navn eller interesse"
-#: ../../include/event.php:1400 ../../include/conversation.php:153
-#: ../../include/text.php:2361 ../../Zotlabs/Module/Channel_calendar.php:221
-#: ../../Zotlabs/Module/Tagger.php:75 ../../Zotlabs/Module/Like.php:450
-msgid "event"
-msgstr "hendelse"
+#: ../../include/contact_widgets.php:23
+msgid "Connect/Follow"
+msgstr "Forbindelse/Følg"
-#: ../../include/event.php:1479
-msgid "Not specified"
-msgstr "Ikke spesifisert"
+#: ../../include/contact_widgets.php:24
+msgid "Examples: Robert Morgenstein, Fishing"
+msgstr "Eksempler: Ola Nordmann, fisking"
-#: ../../include/event.php:1480
-msgid "Needs Action"
-msgstr "Trenger handling"
+#: ../../include/contact_widgets.php:25 ../../Zotlabs/Module/Directory.php:435
+#: ../../Zotlabs/Module/Directory.php:440
+#: ../../Zotlabs/Module/Connections.php:407
+msgid "Find"
+msgstr "Finn"
-#: ../../include/event.php:1481
-msgid "Completed"
-msgstr "Ferdig"
+#: ../../include/contact_widgets.php:26 ../../Zotlabs/Module/Suggest.php:77
+#: ../../Zotlabs/Module/Directory.php:439
+msgid "Channel Suggestions"
+msgstr "Kanalforslag"
-#: ../../include/event.php:1482
-msgid "In Process"
-msgstr "Igang"
+#: ../../include/contact_widgets.php:28
+msgid "Random Profile"
+msgstr "Tilfeldig profil"
-#: ../../include/event.php:1483
-msgid "Cancelled"
-msgstr "Avbrutt"
+#: ../../include/contact_widgets.php:29
+msgid "Invite Friends"
+msgstr "Inviter venner"
-#: ../../include/event.php:1564 ../../include/connections.php:790
-#: ../../Zotlabs/Module/Connedit.php:741 ../../Zotlabs/Module/Cdav.php:1377
-#, fuzzy
-msgid "Mobile"
-msgstr "mobil"
+#: ../../include/contact_widgets.php:31
+msgid "Advanced example: name=fred and country=iceland"
+msgstr "Avansert eksempel: navn=fred og land=island"
-#: ../../include/event.php:1565 ../../include/connections.php:791
-#: ../../Zotlabs/Module/Connedit.php:742 ../../Zotlabs/Module/Cdav.php:1378
-#: ../../Zotlabs/Widget/Notifications.php:43
-msgid "Home"
-msgstr "Hjem"
+#: ../../include/contact_widgets.php:58 ../../include/contact_widgets.php:121
+#: ../../include/contact_widgets.php:155
+#: ../../addon/cards/Widget/Cards_categories.php:83
+#: ../../addon/articles/Widget/Articles_categories.php:83
+#: ../../Zotlabs/Widget/Appcategories.php:52 ../../Zotlabs/Widget/Filer.php:36
+msgid "Everything"
+msgstr "Alt"
-#: ../../include/event.php:1566 ../../include/connections.php:792
-msgid "Home, Voice"
-msgstr ""
+#: ../../include/contact_widgets.php:118 ../../include/contact_widgets.php:152
+#: ../../include/taxonomy.php:425 ../../include/taxonomy.php:507
+#: ../../include/taxonomy.php:527 ../../include/taxonomy.php:548
+#: ../../addon/cards/Widget/Cards_categories.php:80
+#: ../../addon/articles/Widget/Articles_categories.php:80
+#: ../../Zotlabs/Storage/Browser.php:297 ../../Zotlabs/Storage/Browser.php:396
+#: ../../Zotlabs/Storage/Browser.php:410 ../../Zotlabs/Module/Cdav.php:1062
+msgid "Categories"
+msgstr "Kategorier"
-#: ../../include/event.php:1567 ../../include/connections.php:793
-msgid "Home, Fax"
-msgstr ""
+#: ../../include/contact_widgets.php:185
+msgid "Common Connections"
+msgstr "Felles forbindelser"
-#: ../../include/event.php:1568 ../../include/connections.php:794
-#: ../../Zotlabs/Module/Connedit.php:743 ../../Zotlabs/Module/Cdav.php:1379
-msgid "Work"
-msgstr ""
+#: ../../include/contact_widgets.php:189
+#, php-format
+msgid "View all %d common connections"
+msgstr "Vis alle %d felles forbindelser"
-#: ../../include/event.php:1569 ../../include/connections.php:795
-msgid "Work, Voice"
-msgstr ""
+#: ../../include/cdav.php:157
+msgid "INVALID EVENT DISMISSED!"
+msgstr "UGYLDIG HELDELSE AVVIST!"
-#: ../../include/event.php:1570 ../../include/connections.php:796
-msgid "Work, Fax"
-msgstr ""
+#: ../../include/cdav.php:158
+msgid "Summary: "
+msgstr "Sammendrag:"
+
+#: ../../include/cdav.php:158 ../../include/cdav.php:159
+#: ../../include/cdav.php:167 ../../Zotlabs/Lib/Apps.php:1168
+#: ../../Zotlabs/Lib/Apps.php:1252 ../../Zotlabs/Lib/Activity.php:1696
+#: ../../Zotlabs/Widget/Album.php:93 ../../Zotlabs/Widget/Portfolio.php:103
+#: ../../Zotlabs/Module/Embedphotos.php:179 ../../Zotlabs/Module/Photos.php:790
+#: ../../Zotlabs/Module/Photos.php:1240
+msgid "Unknown"
+msgstr "Ukjent"
-#: ../../include/event.php:1571 ../../include/event.php:1578
-#: ../../include/connections.php:797 ../../include/connections.php:804
-#: ../../include/selectors.php:64 ../../include/selectors.php:81
-#: ../../include/selectors.php:119 ../../include/selectors.php:155
-#: ../../Zotlabs/Access/PermissionRoles.php:362
-#: ../../Zotlabs/Module/Connedit.php:744 ../../Zotlabs/Module/Cdav.php:1380
-msgid "Other"
-msgstr "Annen"
+#: ../../include/cdav.php:159
+msgid "Date: "
+msgstr "Dato:"
-#: ../../include/photos.php:153
-#, php-format
-msgid "Image exceeds website size limit of %lu bytes"
-msgstr "Bilde overstiger nettstedets størrelsesbegrensning på %lu bytes"
+#: ../../include/cdav.php:160 ../../include/cdav.php:168
+msgid "Reason: "
+msgstr "Grunn:"
-#: ../../include/photos.php:164
-msgid "Image file is empty."
-msgstr "Bildefilen er tom."
+#: ../../include/cdav.php:166
+msgid "INVALID CARD DISMISSED!"
+msgstr "UGYLDIG KORT AVVIST!"
-#: ../../include/photos.php:198 ../../Zotlabs/Module/Cover_photo.php:240
-#: ../../Zotlabs/Module/Profile_photo.php:275
-msgid "Unable to process image"
-msgstr "Kan ikke behandle bildet"
+#: ../../include/cdav.php:167
+msgid "Name: "
+msgstr "Navn:"
-#: ../../include/photos.php:324
-msgid "Photo storage failed."
-msgstr "Bildelagring mislyktes."
+#: ../../include/group.php:23
+msgid ""
+"A deleted group with this name was revived. Existing item permissions "
+"<strong>may</strong> apply to this group and any future members. If this is "
+"not what you intended, please create another group with a different name."
+msgstr ""
+"En slettet gruppe med dette navnet ble gjenopprettet. Eksisterende "
+"tillatelser for elementet <strong>kan</strong> gjelde for denne gruppen og "
+"fremtidige medlemmer. Hvis du ønsket noe annet, vennligst lag en ny gruppe "
+"med et annet navn."
-#: ../../include/photos.php:373
-msgid "a new photo"
-msgstr "et nytt bilde"
+#: ../../include/group.php:271
+msgid "Add new connections to this privacy group"
+msgstr "Legg nye forbindelser i denne personverngruppen"
-#: ../../include/photos.php:377
-#, php-format
-msgctxt "photo_upload"
-msgid "%1$s posted %2$s to %3$s"
-msgstr "%1$s la inn %2$s til %3$s"
+#: ../../include/group.php:305
+msgid "edit"
+msgstr "endre"
-#: ../../include/photos.php:723 ../../Zotlabs/Module/Photos.php:1339
-#: ../../Zotlabs/Module/Photos.php:1352 ../../Zotlabs/Module/Photos.php:1353
-msgid "Recent Photos"
-msgstr "Nye bilder"
+#: ../../include/group.php:327 ../../include/acl_selectors.php:87
+#: ../../Zotlabs/Lib/Apps.php:368 ../../Zotlabs/Widget/Activity_filter.php:95
+#: ../../Zotlabs/Module/Group.php:144
+msgid "Privacy Groups"
+msgstr "Personverngrupper"
-#: ../../include/photos.php:727
-msgid "Upload New Photos"
-msgstr "Last opp nye bilder"
+#: ../../include/group.php:328
+msgid "Edit group"
+msgstr "Endre gruppe"
-#: ../../include/items.php:456 ../../Zotlabs/Web/WebServer.php:118
-#: ../../Zotlabs/Module/Import_items.php:116 ../../Zotlabs/Module/Group.php:109
-#: ../../Zotlabs/Module/Dreport.php:10 ../../Zotlabs/Module/Dreport.php:54
-#: ../../Zotlabs/Module/Profperm.php:29 ../../Zotlabs/Module/Subthread.php:89
-#: ../../Zotlabs/Module/Like.php:344
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:75
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:119
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:109
-msgid "Permission denied"
-msgstr "Tillatelse avvist"
+#: ../../include/group.php:329
+msgid "Manage privacy groups"
+msgstr "Administrer personverngrupper"
-#: ../../include/items.php:1241
-msgid "Visible to anybody on the internet."
-msgstr "Synlig for enhver på Internett."
+#: ../../include/group.php:330
+msgid "Channels not in any privacy group"
+msgstr "Kanaler uten personverngruppe"
-#: ../../include/items.php:1243
-msgid "Visible to you only."
-msgstr "Synlig bare for deg."
+#: ../../include/group.php:332 ../../Zotlabs/Widget/Savedsearch.php:90
+msgid "add"
+msgstr "legg til"
-#: ../../include/items.php:1245
-msgid "Visible to anybody in this network."
-msgstr "Synlig for enhver i dette nettverket."
+#: ../../include/taxonomy.php:326
+msgid "Trending"
+msgstr "Populært"
-#: ../../include/items.php:1247
-msgid "Visible to anybody authenticated."
-msgstr "Synlig for enhver som er autentisert."
+#: ../../include/taxonomy.php:326 ../../include/taxonomy.php:465
+#: ../../include/taxonomy.php:486 ../../Zotlabs/Widget/Tagcloud.php:26
+msgid "Tags"
+msgstr "Merkelapper"
-#: ../../include/items.php:1249
-#, php-format
-msgid "Visible to anybody on %s."
-msgstr "Synlig for alle på %s."
+#: ../../include/taxonomy.php:566
+msgid "Keywords"
+msgstr "Nøkkelord"
-#: ../../include/items.php:1251
-msgid "Visible to all connections."
-msgstr "Synlig for alle forbindelser."
+#: ../../include/taxonomy.php:587
+msgid "have"
+msgstr "har"
-#: ../../include/items.php:1253
-msgid "Visible to approved connections."
-msgstr "Synlig for godkjente forbindelser."
+#: ../../include/taxonomy.php:587
+msgid "has"
+msgstr "har"
-#: ../../include/items.php:1255
-msgid "Visible to specific connections."
-msgstr "Synlig for spesifikke forbindelser."
+#: ../../include/taxonomy.php:588
+msgid "want"
+msgstr "ønsker"
-#: ../../include/items.php:3345 ../../Zotlabs/Lib/Activity.php:2196
-#: ../../Zotlabs/Module/Share.php:124
-#, php-format
-msgid "&#x1f501; Repeated %1$s's %2$s"
-msgstr ""
+#: ../../include/taxonomy.php:588
+msgid "wants"
+msgstr "ønsker"
-#: ../../include/items.php:3848 ../../Zotlabs/Module/Viewsrc.php:25
-#: ../../Zotlabs/Module/Thing.php:113 ../../Zotlabs/Module/Filestorage.php:29
-#: ../../Zotlabs/Module/Admin/Addons.php:264
-#: ../../Zotlabs/Module/Admin/Themes.php:73 ../../Zotlabs/Module/Display.php:59
-#: ../../Zotlabs/Module/Display.php:120 ../../Zotlabs/Module/Display.php:391
-#: ../../Zotlabs/Module/Admin.php:63
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:291
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:292
-msgid "Item not found."
-msgstr "Elementet ble ikke funnet."
+#: ../../include/taxonomy.php:589
+msgid "like"
+msgstr "liker"
-#: ../../include/items.php:4430 ../../Zotlabs/Module/Group.php:63
-#: ../../Zotlabs/Module/Group.php:207
-msgid "Privacy group not found."
-msgstr "Personverngruppen ble ikke funnet."
+#: ../../include/taxonomy.php:589
+msgid "likes"
+msgstr "liker"
-#: ../../include/items.php:4446
-msgid "Privacy group is empty."
-msgstr "Personverngruppen er tom."
+#: ../../include/taxonomy.php:590
+msgid "dislike"
+msgstr "misliker"
-#: ../../include/items.php:4453
-#, php-format
-msgid "Privacy group: %s"
-msgstr "Personverngruppe: %s"
+#: ../../include/taxonomy.php:590
+msgid "dislikes"
+msgstr "misliker"
-#: ../../include/items.php:4463
-#, php-format
-msgid "Connection: %s"
-msgstr "Forbindelse: %s"
+#: ../../include/taxonomy.php:677 ../../include/conversation.php:1440
+#: ../../Zotlabs/Module/Photos.php:1118
+msgctxt "noun"
+msgid "Like"
+msgid_plural "Likes"
+msgstr[0] "Liker"
+msgstr[1] "Liker"
-#: ../../include/items.php:4465
-msgid "Connection not found."
-msgstr "Forbindelsen ble ikke funnet."
+#: ../../include/photo/photo_driver.php:439
+#: ../../Zotlabs/Module/Profile_photo.php:168
+#: ../../Zotlabs/Module/Profile_photo.php:337
+msgid "Profile Photos"
+msgstr "Profilbilder"
#: ../../include/account.php:39
msgid "The provided email address is not valid"
-msgstr ""
+msgstr "Epostadressen som er oppgitt er ikke gyldig"
#: ../../include/account.php:42
msgid "The provided email domain is not among those allowed on this site"
msgstr ""
+"Epostadressen som er oppgitt har ikke et domene som er tillatt på dette "
+"nettstedet"
#: ../../include/account.php:49
msgid "The provided email address is already registered at this site"
msgstr ""
+"Epostadressen som ble oppgitt er allerede registrert på dette nettstedet"
#: ../../include/account.php:56
msgid ""
"There is a pending registration for this address - click \"Register\" to "
"continue verification"
msgstr ""
+"Det er en ventende registrering for denne addressen - klikk på \"Registrér\" "
+"for å fortsette verifiseringen"
#: ../../include/account.php:95
msgid "An invitation is required."
@@ -1191,1072 +1709,1519 @@ msgstr "En invitasjon er påkrevd."
msgid "Invitation could not be verified."
msgstr "Invitasjon kunne ikke bekreftes."
-#: ../../include/account.php:192
-msgid "Please enter the required information."
-msgstr "Vennligst skriv inn nødvendig informasjon."
-
-#: ../../include/account.php:259 ../../include/account.php:367
+#: ../../include/account.php:206
msgid "Failed to store account information."
msgstr "Mislyktes med å lagre kontoinformasjon."
-#: ../../include/account.php:436 ../../include/account.php:504
-#: ../../Zotlabs/Module/Register.php:329
+#: ../../include/account.php:275 ../../Zotlabs/Module/Register.php:329
#, php-format
msgid "Registration confirmation for %s"
msgstr "Registreringsbekreftelse for %s"
-#: ../../include/account.php:579
+#: ../../include/account.php:348
#, php-format
msgid "Registration request at %s"
msgstr "Registreringsforespørsel hos %s"
-#: ../../include/account.php:601
+#: ../../include/account.php:370
msgid "your registration password"
msgstr "ditt registreringspassord"
-#: ../../include/account.php:607 ../../include/account.php:696
+#: ../../include/account.php:376 ../../include/account.php:449
#, php-format
msgid "Registration details for %s"
msgstr "Registreringsdetaljer for %s"
-#: ../../include/account.php:707
+#: ../../include/account.php:464
msgid "Account approved."
msgstr "Konto godkjent."
-#: ../../include/account.php:763
+#: ../../include/account.php:516
#, php-format
msgid "Registration revoked for %s"
msgstr "Registrering trukket tilbake for %s"
-#: ../../include/account.php:770
+#: ../../include/account.php:523
#, php-format
msgid "Could not revoke registration for %s"
-msgstr ""
+msgstr "Registreringen for %s kunne ikke tilbakekalles"
-#: ../../include/account.php:1187 ../../include/account.php:1189
+#: ../../include/account.php:940 ../../include/account.php:942
msgid "Click here to upgrade."
msgstr "Klikk her for å oppgradere."
-#: ../../include/account.php:1195
+#: ../../include/account.php:948
msgid "This action exceeds the limits set by your subscription plan."
msgstr "Denne handlingen går utenfor grensene satt i din abonnementsplan."
-#: ../../include/account.php:1200
+#: ../../include/account.php:953
msgid "This action is not available under your subscription plan."
msgstr "Denne handlingen er ikke tilgjengelig i din abonnementsplan."
-#: ../../include/account.php:1260
+#: ../../include/account.php:1013
msgid "open"
-msgstr ""
+msgstr "åpen"
-#: ../../include/account.php:1260
+#: ../../include/account.php:1013
msgid "closed"
-msgstr ""
+msgstr "lukket"
-#: ../../include/account.php:1267
+#: ../../include/account.php:1020
msgid "Registration is currently"
-msgstr ""
+msgstr "Registrering er for øyeblikket"
-#: ../../include/account.php:1276
+#: ../../include/account.php:1029
msgid "please come back"
-msgstr ""
+msgstr "kom tilbake"
-#: ../../include/photo/photo_driver.php:458
-#: ../../Zotlabs/Module/Profile_photo.php:168
-#: ../../Zotlabs/Module/Profile_photo.php:337
-msgid "Profile Photos"
-msgstr "Profilbilder"
+#: ../../include/js_strings.php:5
+msgid "Delete this item?"
+msgstr "Slett dette elementet?"
-#: ../../include/activities.php:30
-msgid " and "
-msgstr " og "
+#: ../../include/js_strings.php:6 ../../Zotlabs/Module/Moderate.php:78
+msgid "Item deleted"
+msgstr "Element slettet"
-#: ../../include/activities.php:32
-#, fuzzy
-#| msgid " "
-msgid ", "
-msgstr " "
+#: ../../include/js_strings.php:7 ../../Zotlabs/Lib/ThreadItem.php:808
+#: ../../Zotlabs/Module/Photos.php:1095 ../../Zotlabs/Module/Photos.php:1196
+msgid "Comment"
+msgstr "Kommentar"
-#: ../../include/activities.php:36 ../../include/activities.php:38
-#: ../../Zotlabs/Lib/Apps.php:365 ../../Zotlabs/Module/Profile_photo.php:227
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:58
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:59
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:60
-msgid "Profile Photo"
-msgstr "Profilbilde"
+#: ../../include/js_strings.php:8
+msgid "expand"
+msgstr "utvid"
-#: ../../include/activities.php:36 ../../Zotlabs/Module/Cover_photo.php:230
-#, fuzzy
-#| msgid "Cover Photos"
-msgid "Cover Photo"
-msgstr "Forsidebilder"
+#: ../../include/js_strings.php:9
+msgid "collapse"
+msgstr "kollaps"
-#: ../../include/activities.php:45
-msgid "public profile"
-msgstr "offentlig profil"
+#: ../../include/js_strings.php:10
+msgid "Password too short"
+msgstr "Passordet er for kort"
-#: ../../include/activities.php:62
-#, fuzzy, php-format
-#| msgid "%1$s has an updated %2$s, changing %3$s."
-msgid "%1$s %2$s has been updated to %3$s."
-msgstr "%1$s har oppdatert %2$s, endret %3$s."
+#: ../../include/js_strings.php:11 ../../Zotlabs/Module/Register.php:162
+msgid "Passwords do not match"
+msgstr "Passordene er ikke like"
-#: ../../include/activities.php:65
-#, fuzzy, php-format
-#| msgid "%1$s has an updated %2$s, changing %3$s."
-msgid "%1$s updated the %2$s. Changed %3$s."
-msgstr "%1$s har oppdatert %2$s, endret %3$s."
+#: ../../include/js_strings.php:12
+msgid "everybody"
+msgstr "alle"
-#: ../../include/group.php:23
-msgid ""
-"A deleted group with this name was revived. Existing item permissions "
-"<strong>may</strong> apply to this group and any future members. If this is "
-"not what you intended, please create another group with a different name."
+#: ../../include/js_strings.php:13
+msgid "Secret Passphrase"
+msgstr "Hemmelig passordsetning"
+
+#: ../../include/js_strings.php:14
+msgid "Passphrase hint"
+msgstr "Hint om passordsetning"
+
+#: ../../include/js_strings.php:15
+msgid "Notice: Permissions have changed but have not yet been submitted."
msgstr ""
-"En slettet gruppe med dette navnet ble gjenopprettet. Eksisterende "
-"tillatelser for elementet <strong>kan</strong> gjelde for denne gruppen og "
-"fremtidige medlemmer. Hvis du ønsket noe annet, vennligst lag en ny gruppe "
-"med et annet navn."
+"Varsel: Tillatelser har blitt endret, men de har ennå ikke blitt sendt inn."
-#: ../../include/group.php:271
-msgid "Add new connections to this privacy group"
-msgstr "Legg nye forbindelser i denne personverngruppen"
+#: ../../include/js_strings.php:16
+msgid "close all"
+msgstr "lukk alle"
-#: ../../include/group.php:305
-msgid "edit"
-msgstr "endre"
+#: ../../include/js_strings.php:17
+msgid "Nothing new here"
+msgstr "Ikke noe nytt her"
-#: ../../include/group.php:327 ../../include/acl_selectors.php:87
-#: ../../Zotlabs/Lib/Apps.php:368 ../../Zotlabs/Module/Group.php:144
-#: ../../Zotlabs/Widget/Activity_filter.php:95
-msgid "Privacy Groups"
-msgstr "Personverngrupper"
+#: ../../include/js_strings.php:18
+msgid "Rate This Channel (this is public)"
+msgstr "Vurder denne kanalen (dette er offentlig)"
-#: ../../include/group.php:328
-msgid "Edit group"
-msgstr "Endre gruppe"
+#: ../../include/js_strings.php:19
+msgid "Rating"
+msgstr "Vurdering"
-#: ../../include/group.php:329
-msgid "Manage privacy groups"
+#: ../../include/js_strings.php:20
+msgid "Describe (optional)"
+msgstr "Beskriv (valgfritt)"
+
+#: ../../include/js_strings.php:22
+msgid "Please enter a link URL"
+msgstr "Vennligst skriv inn en lenke URL"
+
+#: ../../include/js_strings.php:23
+msgid "Unsaved changes. Are you sure you wish to leave this page?"
msgstr ""
+"Endringene er ikke lagret. Er du sikker på at du ønsker å forlate denne "
+"siden?"
-#: ../../include/group.php:330
-msgid "Channels not in any privacy group"
-msgstr "Kanaler uten personverngruppe"
+#: ../../include/js_strings.php:24 ../../Zotlabs/Module/Locs.php:121
+#: ../../Zotlabs/Module/Pubsites.php:55 ../../Zotlabs/Module/Profiles.php:476
+#: ../../Zotlabs/Module/Profiles.php:749 ../../Zotlabs/Module/Cdav.php:1006
+msgid "Location"
+msgstr "Plassering"
-#: ../../include/group.php:332 ../../Zotlabs/Widget/Savedsearch.php:90
-msgid "add"
-msgstr "legg til"
+#: ../../include/js_strings.php:25
+msgid "lovely"
+msgstr "fint"
-#: ../../include/language.php:423 ../../include/text.php:2199
-msgid "default"
-msgstr "standard"
+#: ../../include/js_strings.php:26
+msgid "wonderful"
+msgstr "nydelig"
-#: ../../include/language.php:436
-msgid "Select an alternate language"
-msgstr "Velg et annet språk"
+#: ../../include/js_strings.php:27
+msgid "fantastic"
+msgstr "fantastisk"
-#: ../../include/import.php:31
-msgid "Unable to import a removed channel."
-msgstr ""
+#: ../../include/js_strings.php:28
+msgid "great"
+msgstr "kjempebra"
-#: ../../include/import.php:57
+#: ../../include/js_strings.php:29
msgid ""
-"Cannot create a duplicate channel identifier on this system. Import failed."
+"Your chosen nickname was either already taken or not valid. Please use our "
+"suggestion ("
msgstr ""
-"Kan ikke lage en kopi av kanal-identifikatoren på dette systemet. Import "
-"mislyktes."
+"Kallenavnet du har valgt er enten allerede opptatt, eller ikke gyldig. Bruk "
+"vårt forslag ("
-#: ../../include/import.php:78
-#: ../../extend/addon/hzaddons/diaspora/import_diaspora.php:43
-msgid "Unable to create a unique channel address. Import failed."
-msgstr "Klarte ikke å lage en unik kanaladresse. Import mislyktes."
+#: ../../include/js_strings.php:30
+msgid ") or enter a new one."
+msgstr ") eller velg et annet."
-#: ../../include/import.php:129
-msgid "Cloned channel not found. Import failed."
-msgstr "Klonet kanal ble ikke funnet. Import mislyktes."
+#: ../../include/js_strings.php:31
+msgid "Thank you, this nickname is valid."
+msgstr "Takk, dette kallenavnet er gyldig."
-#: ../../include/datetime.php:58 ../../Zotlabs/Module/Profiles.php:751
-#: ../../Zotlabs/Widget/Newmember.php:58
-msgid "Miscellaneous"
-msgstr "Forskjellig"
+#: ../../include/js_strings.php:32
+msgid "A channel name is required."
+msgstr "Et kanalnavn er påkrevet."
-#: ../../include/datetime.php:140
-#, fuzzy
-msgid "Birthday"
-msgstr "Fødselsdag:"
+#: ../../include/js_strings.php:33
+msgid "This is a "
+msgstr "Dette er et"
-#: ../../include/datetime.php:140
-msgid "Age: "
-msgstr "Alder: "
+#: ../../include/js_strings.php:34
+msgid " channel name"
+msgstr "kanalnavn"
-#: ../../include/datetime.php:140
-msgid "YYYY-MM-DD or MM-DD"
-msgstr "YYYY-MM-DD eller MM-DD"
+#: ../../include/js_strings.php:35
+msgid "Back to reply"
+msgstr "Tilbake til svar"
-#: ../../include/datetime.php:211 ../../include/js_strings.php:124
-#: ../../Zotlabs/Module/Profiles.php:760 ../../Zotlabs/Module/Profiles.php:764
-#: ../../Zotlabs/Module/Appman.php:218 ../../Zotlabs/Module/Appman.php:219
-#: ../../Zotlabs/Module/Register.php:501
-#: ../../Zotlabs/Module/Settings/Multifactor.php:84
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:334
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:358
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:434
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:458
-msgid "Required"
-msgstr "Påkrevd"
+#: ../../include/js_strings.php:36
+msgid "Pinned"
+msgstr "Festet"
-#: ../../include/datetime.php:238 ../../boot.php:2774
-msgid "never"
-msgstr "aldri"
+#: ../../include/js_strings.php:37 ../../Zotlabs/Lib/ThreadItem.php:454
+msgid "Pin to the top"
+msgstr "Fest til toppen"
-#: ../../include/datetime.php:244
-msgid "less than a second ago"
-msgstr "for mindre enn ett sekund siden"
+#: ../../include/js_strings.php:38 ../../Zotlabs/Lib/ThreadItem.php:454
+#: ../../Zotlabs/Widget/Pinned.php:153
+msgid "Unpin from the top"
+msgstr "Ikke fest til toppen lengre"
-#: ../../include/datetime.php:262
+#: ../../include/js_strings.php:39
+msgid "Double click to exit zoom"
+msgstr "Dobbelklikk for å gå ut av forstørring"
+
+#: ../../include/js_strings.php:45
#, php-format
-msgctxt "e.g. 22 hours ago, 1 minute ago"
-msgid "%1$d %2$s ago"
-msgstr "%1$d %2$s siden"
+msgid "%d minutes"
+msgid_plural "%d minutes"
+msgstr[0] "%d minutter"
+msgstr[1] "%d minutter"
-#: ../../include/datetime.php:273
-msgctxt "relative_date"
-msgid "year"
-msgid_plural "years"
-msgstr[0] "år"
-msgstr[1] "år"
+#: ../../include/js_strings.php:46
+#, php-format
+msgid "about %d hours"
+msgid_plural "about %d hours"
+msgstr[0] "omtrent %d timer"
+msgstr[1] "omtrent %d timer"
-#: ../../include/datetime.php:276
-msgctxt "relative_date"
+#: ../../include/js_strings.php:47
+#, php-format
+msgid "%d days"
+msgid_plural "%d days"
+msgstr[0] "%d dager"
+msgstr[1] "%d dager"
+
+#: ../../include/js_strings.php:48
+#, php-format
+msgid "%d months"
+msgid_plural "%d months"
+msgstr[0] "%d måneder"
+msgstr[1] "%d måneder"
+
+#: ../../include/js_strings.php:49
+#, php-format
+msgid "%d years"
+msgid_plural "%d years"
+msgstr[0] "%d år"
+msgstr[1] "%d år"
+
+#: ../../include/js_strings.php:51 ../../include/text.php:1525
+msgid "January"
+msgstr "januar"
+
+#: ../../include/js_strings.php:52 ../../include/text.php:1525
+msgid "February"
+msgstr "februar"
+
+#: ../../include/js_strings.php:53 ../../include/text.php:1525
+msgid "March"
+msgstr "mars"
+
+#: ../../include/js_strings.php:54 ../../include/text.php:1525
+msgid "April"
+msgstr "april"
+
+#: ../../include/js_strings.php:55
+msgctxt "long"
+msgid "May"
+msgstr "mai"
+
+#: ../../include/js_strings.php:56 ../../include/text.php:1525
+msgid "June"
+msgstr "juni"
+
+#: ../../include/js_strings.php:57 ../../include/text.php:1525
+msgid "July"
+msgstr "juli"
+
+#: ../../include/js_strings.php:58 ../../include/text.php:1525
+msgid "August"
+msgstr "august"
+
+#: ../../include/js_strings.php:59 ../../include/text.php:1525
+msgid "September"
+msgstr "september"
+
+#: ../../include/js_strings.php:60 ../../include/text.php:1525
+msgid "October"
+msgstr "oktober"
+
+#: ../../include/js_strings.php:61 ../../include/text.php:1525
+msgid "November"
+msgstr "november"
+
+#: ../../include/js_strings.php:62 ../../include/text.php:1525
+msgid "December"
+msgstr "desember"
+
+#: ../../include/js_strings.php:63
+msgid "Jan"
+msgstr "Jan"
+
+#: ../../include/js_strings.php:64
+msgid "Feb"
+msgstr "Feb"
+
+#: ../../include/js_strings.php:65
+msgid "Mar"
+msgstr "Mar"
+
+#: ../../include/js_strings.php:66
+msgid "Apr"
+msgstr "Apr"
+
+#: ../../include/js_strings.php:67
+msgctxt "short"
+msgid "May"
+msgstr "mai"
+
+#: ../../include/js_strings.php:68
+msgid "Jun"
+msgstr "Jun"
+
+#: ../../include/js_strings.php:69
+msgid "Jul"
+msgstr "Jul"
+
+#: ../../include/js_strings.php:70
+msgid "Aug"
+msgstr "Aug"
+
+#: ../../include/js_strings.php:71
+msgid "Sep"
+msgstr "Sep"
+
+#: ../../include/js_strings.php:72
+msgid "Oct"
+msgstr "Okt"
+
+#: ../../include/js_strings.php:73
+msgid "Nov"
+msgstr "Nov"
+
+#: ../../include/js_strings.php:74
+msgid "Dec"
+msgstr "Des"
+
+#: ../../include/js_strings.php:75 ../../include/text.php:1521
+msgid "Sunday"
+msgstr "søndag"
+
+#: ../../include/js_strings.php:76 ../../include/text.php:1521
+msgid "Monday"
+msgstr "mandag"
+
+#: ../../include/js_strings.php:77 ../../include/text.php:1521
+msgid "Tuesday"
+msgstr "tirsdag"
+
+#: ../../include/js_strings.php:78 ../../include/text.php:1521
+msgid "Wednesday"
+msgstr "onsdag"
+
+#: ../../include/js_strings.php:79 ../../include/text.php:1521
+msgid "Thursday"
+msgstr "torsdag"
+
+#: ../../include/js_strings.php:80 ../../include/text.php:1521
+msgid "Friday"
+msgstr "fredag"
+
+#: ../../include/js_strings.php:81 ../../include/text.php:1521
+msgid "Saturday"
+msgstr "lørdag"
+
+#: ../../include/js_strings.php:82
+msgid "Sun"
+msgstr "Søn"
+
+#: ../../include/js_strings.php:83
+msgid "Mon"
+msgstr "Man"
+
+#: ../../include/js_strings.php:84
+msgid "Tue"
+msgstr "Tirs"
+
+#: ../../include/js_strings.php:85
+msgid "Wed"
+msgstr "Ons"
+
+#: ../../include/js_strings.php:86
+msgid "Thu"
+msgstr "Tors"
+
+#: ../../include/js_strings.php:87
+msgid "Fri"
+msgstr "Fre"
+
+#: ../../include/js_strings.php:88
+msgid "Sat"
+msgstr "Lør"
+
+#: ../../include/js_strings.php:89
+msgctxt "calendar"
+msgid "today"
+msgstr "idag"
+
+#: ../../include/js_strings.php:90
+msgctxt "calendar"
msgid "month"
-msgid_plural "months"
-msgstr[0] "måned"
-msgstr[1] "måneder"
+msgstr "måned"
-#: ../../include/datetime.php:279
-msgctxt "relative_date"
+#: ../../include/js_strings.php:91
+msgctxt "calendar"
msgid "week"
-msgid_plural "weeks"
-msgstr[0] "uke"
-msgstr[1] "uker"
+msgstr "uke"
-#: ../../include/datetime.php:282
-msgctxt "relative_date"
+#: ../../include/js_strings.php:92
+msgctxt "calendar"
msgid "day"
-msgid_plural "days"
-msgstr[0] "dag"
-msgstr[1] "dager"
-
-#: ../../include/datetime.php:285
-msgctxt "relative_date"
-msgid "hour"
-msgid_plural "hours"
-msgstr[0] "time"
-msgstr[1] "timer"
+msgstr "dag"
-#: ../../include/datetime.php:288
-msgctxt "relative_date"
-msgid "minute"
-msgid_plural "minutes"
-msgstr[0] "minutt"
-msgstr[1] "minutter"
+#: ../../include/js_strings.php:93
+msgctxt "calendar"
+msgid "All day"
+msgstr "Hele dagen"
-#: ../../include/datetime.php:291
-msgctxt "relative_date"
-msgid "second"
-msgid_plural "seconds"
-msgstr[0] "sekund"
-msgstr[1] "sekunder"
+#: ../../include/js_strings.php:96
+msgid "Please stand by while your download is being prepared."
+msgstr "Nedlastingen din blir klargjort."
-#: ../../include/datetime.php:520
-#, php-format
-msgid "%1$s's birthday"
-msgstr "%1$s sin fødselsdag"
+#: ../../include/js_strings.php:99
+msgid "Email address not valid"
+msgstr "Epostadressen er ikke gyldig"
+
+#: ../../include/js_strings.php:100 ../../include/datetime.php:211
+#: ../../addon/cart/submodules/orderoptions.php:334
+#: ../../addon/cart/submodules/orderoptions.php:358
+#: ../../addon/cart/submodules/orderoptions.php:434
+#: ../../addon/cart/submodules/orderoptions.php:458
+#: ../../Zotlabs/Module/Register.php:501 ../../Zotlabs/Module/Appman.php:218
+#: ../../Zotlabs/Module/Appman.php:219 ../../Zotlabs/Module/Profiles.php:760
+#: ../../Zotlabs/Module/Profiles.php:764
+#: ../../Zotlabs/Module/Settings/Multifactor.php:84
+msgid "Required"
+msgstr "Påkrevd"
-#: ../../include/datetime.php:521
+#: ../../include/zid.php:420
#, php-format
-msgid "Happy Birthday %1$s"
-msgstr "Gratulerer med dagen, %1$s"
+msgid "OpenWebAuth: %1$s welcomes %2$s"
+msgstr "OpenWebAuth: %1$s ønsker %2$s velkommen"
-#: ../../include/conversation.php:149 ../../include/text.php:2358
-#: ../../Zotlabs/Module/Tagger.php:71 ../../Zotlabs/Module/Subthread.php:112
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:1693
-#: ../../extend/addon/hzaddons/redphotos/redphotohelper.php:71
+#: ../../include/conversation.php:149 ../../include/text.php:2377
+#: ../../addon/redphotos/redphotohelper.php:71
+#: ../../addon/diaspora/Receiver.php:1705
+#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Tagger.php:73
msgid "photo"
msgstr "foto"
-#: ../../include/conversation.php:157 ../../Zotlabs/Module/Like.php:183
+#: ../../include/conversation.php:157 ../../Zotlabs/Module/Like.php:171
msgid "channel"
msgstr "kanal"
-#: ../../include/conversation.php:182 ../../include/text.php:2366
-#: ../../Zotlabs/Module/Tagger.php:81
-msgid "comment"
-msgstr "kommentar"
+#: ../../include/conversation.php:180 ../../include/text.php:2385
+msgid "message"
+msgstr "melding"
-#: ../../include/conversation.php:196 ../../Zotlabs/Module/Like.php:483
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:1628
+#: ../../include/conversation.php:194 ../../addon/diaspora/Receiver.php:1640
+#: ../../Zotlabs/Module/Like.php:474
#, php-format
msgid "%1$s likes %2$s's %3$s"
msgstr "%1$s liker %2$s sin %3$s"
-#: ../../include/conversation.php:198
+#: ../../include/conversation.php:196
#, php-format
msgid "likes %1$s's %2$s"
-msgstr ""
+msgstr "liker %2$s til %1$s"
-#: ../../include/conversation.php:201 ../../Zotlabs/Module/Like.php:485
+#: ../../include/conversation.php:199 ../../Zotlabs/Module/Like.php:476
#, php-format
msgid "%1$s doesn't like %2$s's %3$s"
msgstr "%1$s liker ikke %2$s sin %3$s"
-#: ../../include/conversation.php:202
+#: ../../include/conversation.php:200
#, php-format
msgid "doesn't like %1$s's %2$s"
-msgstr ""
+msgstr "liker ikke %2$s til %1$s"
-#: ../../include/conversation.php:205
-#, fuzzy, php-format
-#| msgid "%1$s may attend %2$s's %3$s"
+#: ../../include/conversation.php:203
+#, php-format
msgid "%1$s repeated %2$s's %3$s"
-msgstr "%1$s deltar kanskje på %2$ss %3$s"
+msgstr "%1$s videreformidlet %3$s til %2$s"
-#: ../../include/conversation.php:206
-#, fuzzy, php-format
-#| msgid "Visit %1$s's %2$s"
+#: ../../include/conversation.php:204
+#, php-format
msgid "repeated %1$s's %2$s"
-msgstr "Besøk %1$s sitt %2$s"
+msgstr "videreformidlet %2$s til %1$s"
-#: ../../include/conversation.php:334 ../../Zotlabs/Lib/ThreadItem.php:536
+#: ../../include/conversation.php:332 ../../Zotlabs/Lib/ThreadItem.php:472
msgid "This is an unsaved preview"
-msgstr ""
-
-#: ../../include/conversation.php:471 ../../Zotlabs/Module/Photos.php:1110
-msgctxt "title"
-msgid "Likes"
-msgstr "Liker"
-
-#: ../../include/conversation.php:472 ../../Zotlabs/Module/Photos.php:1110
-msgctxt "title"
-msgid "Dislikes"
-msgstr "Liker ikke"
+msgstr "Dette er en forhåndsvisning som ikke er lagret"
-#: ../../include/conversation.php:473 ../../Zotlabs/Module/Photos.php:1111
-#: ../../Zotlabs/Widget/Pinned.php:73
-msgctxt "title"
-msgid "Attending"
-msgstr "Deltar"
-
-#: ../../include/conversation.php:474 ../../Zotlabs/Module/Photos.php:1111
-#: ../../Zotlabs/Widget/Pinned.php:74
-msgctxt "title"
-msgid "Not attending"
-msgstr "Deltar ikke"
-
-#: ../../include/conversation.php:475 ../../Zotlabs/Module/Photos.php:1111
-#: ../../Zotlabs/Widget/Pinned.php:75
-msgctxt "title"
-msgid "Might attend"
-msgstr "Deltar kanskje"
-
-#: ../../include/conversation.php:477
-msgctxt "title"
-msgid "Repeats"
-msgstr ""
-
-#: ../../include/conversation.php:546
+#: ../../include/conversation.php:533
msgid "Select"
msgstr "Velg"
-#: ../../include/conversation.php:547 ../../include/conversation.php:608
-#: ../../Zotlabs/Lib/Apps.php:618 ../../Zotlabs/Lib/ThreadItem.php:183
-#: ../../Zotlabs/Lib/ThreadItem.php:544 ../../Zotlabs/Storage/Browser.php:388
-#: ../../Zotlabs/Module/Editblock.php:139
-#: ../../Zotlabs/Module/Editlayout.php:138 ../../Zotlabs/Module/Photos.php:1173
-#: ../../Zotlabs/Module/Oauth.php:172 ../../Zotlabs/Module/Group.php:252
-#: ../../Zotlabs/Module/Oauth2.php:193 ../../Zotlabs/Module/Connedit.php:540
-#: ../../Zotlabs/Module/Connedit.php:749
-#: ../../Zotlabs/Module/Editwebpage.php:167 ../../Zotlabs/Module/Thing.php:295
-#: ../../Zotlabs/Module/Blocks.php:160 ../../Zotlabs/Module/Webpages.php:252
-#: ../../Zotlabs/Module/Admin/Accounts.php:320
+#: ../../include/conversation.php:534 ../../include/conversation.php:595
+#: ../../addon/cards/Mod_Card_edit.php:124
+#: ../../addon/articles/Mod_Article_edit.php:124 ../../Zotlabs/Lib/Apps.php:618
+#: ../../Zotlabs/Lib/ThreadItem.php:180 ../../Zotlabs/Lib/ThreadItem.php:480
+#: ../../Zotlabs/Storage/Browser.php:392
+#: ../../Zotlabs/Module/Editwebpage.php:167
+#: ../../Zotlabs/Module/Webpages.php:251 ../../Zotlabs/Module/Oauth.php:172
+#: ../../Zotlabs/Module/Thing.php:302 ../../Zotlabs/Module/Tokens.php:295
+#: ../../Zotlabs/Module/Contactedit.php:651
+#: ../../Zotlabs/Module/Connedit.php:540 ../../Zotlabs/Module/Connedit.php:749
+#: ../../Zotlabs/Module/Admin/Accounts.php:220
+#: ../../Zotlabs/Module/Admin/Profs.php:177
#: ../../Zotlabs/Module/Admin/Channels.php:171
-#: ../../Zotlabs/Module/Admin/Profs.php:177 ../../Zotlabs/Module/Tokens.php:295
-#: ../../Zotlabs/Module/Permcats.php:261 ../../Zotlabs/Module/Cdav.php:1047
-#: ../../Zotlabs/Module/Cdav.php:1385 ../../Zotlabs/Module/Contactedit.php:651
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:128
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:130
+#: ../../Zotlabs/Module/Permcats.php:261 ../../Zotlabs/Module/Group.php:252
+#: ../../Zotlabs/Module/Blocks.php:160 ../../Zotlabs/Module/Photos.php:1162
+#: ../../Zotlabs/Module/Editlayout.php:138 ../../Zotlabs/Module/Cdav.php:1047
+#: ../../Zotlabs/Module/Cdav.php:1385 ../../Zotlabs/Module/Oauth2.php:193
+#: ../../Zotlabs/Module/Editblock.php:139
msgid "Delete"
msgstr "Slett"
-#: ../../include/conversation.php:553 ../../Zotlabs/Lib/ThreadItem.php:287
+#: ../../include/conversation.php:540 ../../Zotlabs/Lib/ThreadItem.php:242
msgid "Toggle Star Status"
msgstr "Skru av og på stjernestatus"
-#: ../../include/conversation.php:559
+#: ../../include/conversation.php:546
msgid "Private Message"
msgstr "Privat melding"
-#: ../../include/conversation.php:568 ../../Zotlabs/Lib/ThreadItem.php:297
+#: ../../include/conversation.php:555 ../../Zotlabs/Lib/ThreadItem.php:251
#: ../../Zotlabs/Widget/Pinned.php:84
msgid "Message signature validated"
msgstr "Innleggets signatur er bekreftet"
-#: ../../include/conversation.php:569 ../../Zotlabs/Lib/ThreadItem.php:298
+#: ../../include/conversation.php:556 ../../Zotlabs/Lib/ThreadItem.php:252
#: ../../Zotlabs/Widget/Pinned.php:85
msgid "Message signature incorrect"
msgstr "Innleggets signatur er feil"
-#: ../../include/conversation.php:607 ../../Zotlabs/Lib/ThreadItem.php:543
+#: ../../include/conversation.php:594 ../../Zotlabs/Lib/ThreadItem.php:479
+#: ../../Zotlabs/Module/Admin/Accounts.php:218
#: ../../Zotlabs/Module/Connections.php:358
#: ../../Zotlabs/Module/Connections.php:409
-#: ../../Zotlabs/Module/Admin/Accounts.php:318
msgid "Approve"
msgstr "Godkjenn"
-#: ../../include/conversation.php:613
+#: ../../include/conversation.php:600
#, php-format
msgid "View %s's profile @ %s"
msgstr "Vis %s sin profile @ %s"
-#: ../../include/conversation.php:635
+#: ../../include/conversation.php:622
msgid "Categories:"
msgstr "Kategorier:"
-#: ../../include/conversation.php:636
+#: ../../include/conversation.php:623
msgid "Filed under:"
msgstr "Sortert under:"
-#: ../../include/conversation.php:642 ../../Zotlabs/Lib/ThreadItem.php:455
-#: ../../Zotlabs/Widget/Pinned.php:128
+#: ../../include/conversation.php:629 ../../Zotlabs/Lib/ThreadItem.php:411
+#: ../../Zotlabs/Widget/Pinned.php:130
#, php-format
msgid "from %s"
msgstr "fra %s"
-#: ../../include/conversation.php:645 ../../Zotlabs/Lib/ThreadItem.php:458
-#: ../../Zotlabs/Widget/Pinned.php:131
+#: ../../include/conversation.php:632 ../../Zotlabs/Widget/Pinned.php:133
#, php-format
msgid "last edited: %s"
msgstr "sist endret: %s"
-#: ../../include/conversation.php:646 ../../Zotlabs/Lib/ThreadItem.php:459
-#: ../../Zotlabs/Widget/Pinned.php:132
+#: ../../include/conversation.php:633 ../../Zotlabs/Widget/Pinned.php:134
#, php-format
msgid "Expires: %s"
msgstr "Utløper: %s"
-#: ../../include/conversation.php:661
-#: ../../extend/addon/hzaddons/articles/articles.php:83
-#: ../../extend/addon/hzaddons/cards/cards.php:82
+#: ../../include/conversation.php:648 ../../addon/cards/cards.php:82
+#: ../../addon/articles/articles.php:83
msgid "View in context"
msgstr "Vis i sammenheng"
-#: ../../include/conversation.php:663 ../../Zotlabs/Lib/ThreadItem.php:537
-#: ../../Zotlabs/Module/Photos.php:1077
+#: ../../include/conversation.php:650 ../../Zotlabs/Lib/ThreadItem.php:473
+#: ../../Zotlabs/Module/Photos.php:1078
msgid "Please wait"
msgstr "Vennligst vent"
-#: ../../include/conversation.php:764
+#: ../../include/conversation.php:746
msgid "remove"
msgstr "fjern"
-#: ../../include/conversation.php:768
+#: ../../include/conversation.php:750
msgid "Loading..."
msgstr "Laster..."
-#: ../../include/conversation.php:769 ../../Zotlabs/Lib/ThreadItem.php:314
+#: ../../include/conversation.php:751 ../../Zotlabs/Lib/ThreadItem.php:268
msgid "Conversation Features"
msgstr "Samtaleinnstillinger"
-#: ../../include/conversation.php:770
+#: ../../include/conversation.php:752
msgid "Delete Selected Items"
msgstr "Slett valgte elementer"
-#: ../../include/conversation.php:806
+#: ../../include/conversation.php:788
msgid "View Source"
msgstr "Vis kilde"
-#: ../../include/conversation.php:816
+#: ../../include/conversation.php:798
msgid "Follow Thread"
msgstr "Følg tråd"
-#: ../../include/conversation.php:825
+#: ../../include/conversation.php:807
msgid "Unfollow Thread"
msgstr "Ikke følg tråd"
-#: ../../include/conversation.php:914 ../../Zotlabs/Module/Connedit.php:501
+#: ../../include/conversation.php:884 ../../include/nav.php:127
+#: ../../addon/openclipatar/openclipatar.php:58 ../../Zotlabs/Lib/Apps.php:350
+#: ../../Zotlabs/Module/Connedit.php:480
+msgid "View Profile"
+msgstr "Vis profil"
+
+#: ../../include/conversation.php:896 ../../Zotlabs/Module/Connedit.php:501
msgid "Recent Activity"
msgstr "Nylig aktivitet"
-#: ../../include/conversation.php:926 ../../include/channel.php:1625
-#: ../../include/connections.php:150 ../../Zotlabs/Module/Suggest.php:69
+#: ../../include/conversation.php:908 ../../include/connections.php:150
+#: ../../include/channel.php:1627 ../../Zotlabs/Widget/Suggestions.php:51
+#: ../../Zotlabs/Widget/Follow.php:37 ../../Zotlabs/Module/Suggest.php:69
#: ../../Zotlabs/Module/Directory.php:371
#: ../../Zotlabs/Module/Connections.php:365
-#: ../../Zotlabs/Widget/Suggestions.php:51 ../../Zotlabs/Widget/Follow.php:37
msgid "Connect"
msgstr "Koble"
-#: ../../include/conversation.php:938
+#: ../../include/conversation.php:920
msgid "Edit Connection"
msgstr "Endre forbindelse"
-#: ../../include/conversation.php:1006 ../../include/cdav.php:158
-#: ../../include/cdav.php:159 ../../include/cdav.php:167
-#: ../../Zotlabs/Lib/Apps.php:1168 ../../Zotlabs/Lib/Apps.php:1252
-#: ../../Zotlabs/Lib/Activity.php:1684 ../../Zotlabs/Module/Photos.php:788
-#: ../../Zotlabs/Module/Photos.php:1246
-#: ../../Zotlabs/Module/Embedphotos.php:177 ../../Zotlabs/Widget/Pinned.php:257
-#: ../../Zotlabs/Widget/Album.php:90 ../../Zotlabs/Widget/Portfolio.php:99
-msgid "Unknown"
-msgstr "Ukjent"
-
-#: ../../include/conversation.php:1008
-#, fuzzy
-#| msgid "Remove this file"
-msgid "Approve this item"
-msgstr "Fjern denne filen"
-
-#: ../../include/conversation.php:1008
-#, fuzzy
-#| msgid "Delete this item?"
-msgid "Delete this item"
-msgstr "Slett dette elementet?"
-
-#: ../../include/conversation.php:1062
+#: ../../include/conversation.php:949
#, php-format
msgid "%s likes this."
msgstr "%s liker dette."
-#: ../../include/conversation.php:1062
+#: ../../include/conversation.php:949
#, php-format
msgid "%s doesn't like this."
msgstr "%s liker ikke dette."
-#: ../../include/conversation.php:1066
+#: ../../include/conversation.php:953
#, php-format
msgid "<span %1$s>%2$d people</span> like this."
msgid_plural "<span %1$s>%2$d people</span> like this."
msgstr[0] "<span %1$s>%2$d person</span> liker dette."
msgstr[1] "<span %1$s>%2$d personer</span> liker dette."
-#: ../../include/conversation.php:1068
+#: ../../include/conversation.php:955
#, php-format
msgid "<span %1$s>%2$d people</span> don't like this."
msgid_plural "<span %1$s>%2$d people</span> don't like this."
msgstr[0] "<span %1$s>%2$d person</span> liker ikke dette."
msgstr[1] "<span %1$s>%2$d personer</span> liker ikke dette."
-#: ../../include/conversation.php:1074
+#: ../../include/conversation.php:961
msgid "and"
msgstr "og"
-#: ../../include/conversation.php:1077
+#: ../../include/conversation.php:964
#, php-format
msgid ", and %d other people"
msgid_plural ", and %d other people"
msgstr[0] ", og %d annen person"
msgstr[1] ", og %d andre personer"
-#: ../../include/conversation.php:1078
+#: ../../include/conversation.php:965
#, php-format
msgid "%s like this."
msgstr "%s liker dette."
-#: ../../include/conversation.php:1078
+#: ../../include/conversation.php:965
#, php-format
msgid "%s don't like this."
msgstr "%s liker ikke dette."
-#: ../../include/conversation.php:1129
-#: ../../extend/addon/hzaddons/hsse/hsse.php:82
+#: ../../include/conversation.php:1014 ../../addon/hsse/hsse.php:82
msgid "Set your location"
msgstr "Angi din plassering"
-#: ../../include/conversation.php:1130
-#: ../../extend/addon/hzaddons/hsse/hsse.php:83
+#: ../../include/conversation.php:1015 ../../addon/hsse/hsse.php:83
msgid "Clear browser location"
msgstr "Fjern nettleserplassering"
-#: ../../include/conversation.php:1142 ../../Zotlabs/Module/Editblock.php:116
-#: ../../Zotlabs/Module/Chat.php:219 ../../Zotlabs/Module/Editwebpage.php:143
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:99
-#: ../../extend/addon/hzaddons/hsse/hsse.php:95
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:101
+#: ../../include/conversation.php:1027 ../../addon/cards/Mod_Card_edit.php:94
+#: ../../addon/articles/Mod_Article_edit.php:94 ../../addon/hsse/hsse.php:95
+#: ../../Zotlabs/Module/Editwebpage.php:143 ../../Zotlabs/Module/Chat.php:219
+#: ../../Zotlabs/Module/Editblock.php:116
msgid "Insert web link"
msgstr "Sett inn web-lenke"
-#: ../../include/conversation.php:1146
-#: ../../extend/addon/hzaddons/hsse/hsse.php:99
+#: ../../include/conversation.php:1031 ../../addon/hsse/hsse.php:99
+#: ../../Zotlabs/Lib/ThreadItem.php:816
msgid "Embed (existing) photo from your photo albums"
msgstr "Sett inn (eksisterende) bilde fra fotoalbumene dine"
-#: ../../include/conversation.php:1179 ../../Zotlabs/Module/Chat.php:217
-#: ../../extend/addon/hzaddons/hsse/hsse.php:134
+#: ../../include/conversation.php:1064 ../../addon/hsse/hsse.php:134
+#: ../../Zotlabs/Module/Chat.php:217
msgid "Please enter a link URL:"
msgstr "Vennligst skriv inn en lenke URL:"
-#: ../../include/conversation.php:1180
-#: ../../extend/addon/hzaddons/hsse/hsse.php:135
+#: ../../include/conversation.php:1065 ../../addon/hsse/hsse.php:135
msgid "Tag term:"
msgstr "Merkelapp:"
-#: ../../include/conversation.php:1181
-#: ../../extend/addon/hzaddons/hsse/hsse.php:136
+#: ../../include/conversation.php:1066 ../../addon/hsse/hsse.php:136
msgid "Where are you right now?"
msgstr "Hvor er du akkurat nå?"
-#: ../../include/conversation.php:1184 ../../Zotlabs/Module/Cover_photo.php:388
+#: ../../include/conversation.php:1069 ../../addon/wiki/Mod_Wiki.php:400
+#: ../../addon/hsse/hsse.php:139 ../../Zotlabs/Module/Cover_photo.php:388
#: ../../Zotlabs/Module/Profile_photo.php:555
-#: ../../extend/addon/hzaddons/hsse/hsse.php:139
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:400
msgid "Choose images to embed"
msgstr "Velg bilder"
-#: ../../include/conversation.php:1185 ../../Zotlabs/Module/Cover_photo.php:389
+#: ../../include/conversation.php:1070 ../../addon/wiki/Mod_Wiki.php:401
+#: ../../addon/hsse/hsse.php:140 ../../Zotlabs/Module/Cover_photo.php:389
#: ../../Zotlabs/Module/Profile_photo.php:556
-#: ../../extend/addon/hzaddons/hsse/hsse.php:140
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:401
msgid "Choose an album"
msgstr "Velg et album"
-#: ../../include/conversation.php:1186
-#: ../../extend/addon/hzaddons/hsse/hsse.php:141
+#: ../../include/conversation.php:1071 ../../addon/hsse/hsse.php:141
msgid "Choose a different album..."
msgstr "Velg et annet album..."
-#: ../../include/conversation.php:1187 ../../Zotlabs/Module/Cover_photo.php:391
+#: ../../include/conversation.php:1072 ../../addon/wiki/Mod_Wiki.php:403
+#: ../../addon/hsse/hsse.php:142 ../../Zotlabs/Module/Cover_photo.php:391
#: ../../Zotlabs/Module/Profile_photo.php:558
-#: ../../extend/addon/hzaddons/hsse/hsse.php:142
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:403
msgid "Error getting album list"
-msgstr ""
+msgstr "Klarte ikke å hente albumlisten"
-#: ../../include/conversation.php:1188 ../../Zotlabs/Module/Cover_photo.php:392
+#: ../../include/conversation.php:1073 ../../addon/wiki/Mod_Wiki.php:404
+#: ../../addon/hsse/hsse.php:143 ../../Zotlabs/Module/Cover_photo.php:392
#: ../../Zotlabs/Module/Profile_photo.php:559
-#: ../../extend/addon/hzaddons/hsse/hsse.php:143
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:404
msgid "Error getting photo link"
-msgstr ""
+msgstr "Klarte ikke å hente fotolenken"
-#: ../../include/conversation.php:1189 ../../Zotlabs/Module/Cover_photo.php:393
+#: ../../include/conversation.php:1074 ../../addon/wiki/Mod_Wiki.php:405
+#: ../../addon/hsse/hsse.php:144 ../../Zotlabs/Module/Cover_photo.php:393
#: ../../Zotlabs/Module/Profile_photo.php:560
-#: ../../extend/addon/hzaddons/hsse/hsse.php:144
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:405
msgid "Error getting album"
-msgstr ""
+msgstr "Klarte ikke å hente album"
-#: ../../include/conversation.php:1190
-#: ../../extend/addon/hzaddons/hsse/hsse.php:145
+#: ../../include/conversation.php:1075 ../../addon/hsse/hsse.php:145
msgid "Comments enabled"
-msgstr ""
+msgstr "Kommentarer er tillatt"
-#: ../../include/conversation.php:1191
-#: ../../extend/addon/hzaddons/hsse/hsse.php:146
+#: ../../include/conversation.php:1076 ../../addon/hsse/hsse.php:146
+#: ../../Zotlabs/Lib/ThreadItem.php:470
msgid "Comments disabled"
-msgstr ""
+msgstr "Komentarer er ikke tillatt"
-#: ../../include/conversation.php:1192
-#, fuzzy
-#| msgid "Profile deleted."
+#: ../../include/conversation.php:1077
msgid "Confirm delete"
-msgstr "Profilen er slettet."
+msgstr "Bekreft sletting"
-#: ../../include/conversation.php:1209 ../../Zotlabs/Lib/ThreadItem.php:878
-#: ../../Zotlabs/Module/Photos.php:1096 ../../Zotlabs/Module/Webpages.php:257
-#: ../../extend/addon/hzaddons/hsse/hsse.php:153
+#: ../../include/conversation.php:1094 ../../addon/hsse/hsse.php:153
+#: ../../Zotlabs/Lib/ThreadItem.php:820 ../../Zotlabs/Module/Webpages.php:256
+#: ../../Zotlabs/Module/Photos.php:1097
msgid "Preview"
msgstr "Forhåndsvisning"
-#: ../../include/conversation.php:1242 ../../Zotlabs/Lib/ThreadItem.php:344
-#: ../../Zotlabs/Module/Photos.php:1076 ../../Zotlabs/Module/Layouts.php:192
-#: ../../Zotlabs/Module/Blocks.php:159 ../../Zotlabs/Module/Webpages.php:251
-#: ../../Zotlabs/Widget/Cdav.php:142
-#: ../../extend/addon/hzaddons/hsse/hsse.php:186
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:304
-msgid "Share"
-msgstr "Del"
+#: ../../include/conversation.php:1128
+msgid "Start a conversation"
+msgstr "Start en samtale"
-#: ../../include/conversation.php:1251
-#: ../../extend/addon/hzaddons/hsse/hsse.php:195
+#: ../../include/conversation.php:1136 ../../addon/hsse/hsse.php:195
msgid "Page link name"
msgstr "Sidens lenkenavn"
-#: ../../include/conversation.php:1254
-#: ../../extend/addon/hzaddons/hsse/hsse.php:198
+#: ../../include/conversation.php:1139 ../../addon/hsse/hsse.php:198
msgid "Post as"
msgstr "Lag innlegg som"
-#: ../../include/conversation.php:1256 ../../Zotlabs/Lib/ThreadItem.php:869
-#: ../../extend/addon/hzaddons/hsse/hsse.php:200
+#: ../../include/conversation.php:1141 ../../addon/hsse/hsse.php:200
+#: ../../Zotlabs/Lib/ThreadItem.php:810
msgid "Bold"
msgstr "Uthevet"
-#: ../../include/conversation.php:1257 ../../Zotlabs/Lib/ThreadItem.php:870
-#: ../../extend/addon/hzaddons/hsse/hsse.php:201
+#: ../../include/conversation.php:1142 ../../addon/hsse/hsse.php:201
+#: ../../Zotlabs/Lib/ThreadItem.php:811
msgid "Italic"
msgstr "Kursiv"
-#: ../../include/conversation.php:1258 ../../Zotlabs/Lib/ThreadItem.php:871
-#: ../../extend/addon/hzaddons/hsse/hsse.php:202
+#: ../../include/conversation.php:1143 ../../Zotlabs/Lib/ThreadItem.php:812
+msgid "Highlight selected text"
+msgstr "Fremhev valgt tekst"
+
+#: ../../include/conversation.php:1144 ../../addon/hsse/hsse.php:202
+#: ../../Zotlabs/Lib/ThreadItem.php:813
msgid "Underline"
msgstr "Understreket"
-#: ../../include/conversation.php:1259 ../../Zotlabs/Lib/ThreadItem.php:872
-#: ../../extend/addon/hzaddons/hsse/hsse.php:203
+#: ../../include/conversation.php:1145 ../../addon/hsse/hsse.php:203
+#: ../../Zotlabs/Lib/ThreadItem.php:814
msgid "Quote"
msgstr "Sitat"
-#: ../../include/conversation.php:1260 ../../Zotlabs/Lib/ThreadItem.php:873
-#: ../../extend/addon/hzaddons/hsse/hsse.php:204
+#: ../../include/conversation.php:1146 ../../addon/hsse/hsse.php:204
+#: ../../Zotlabs/Lib/ThreadItem.php:815
msgid "Code"
msgstr "Kode"
-#: ../../include/conversation.php:1261 ../../Zotlabs/Lib/ThreadItem.php:875
-#: ../../extend/addon/hzaddons/hsse/hsse.php:205
+#: ../../include/conversation.php:1147 ../../addon/hsse/hsse.php:205
+#: ../../Zotlabs/Lib/ThreadItem.php:817
msgid "Attach/Upload file"
msgstr "Last opp fil/vedlegg"
-#: ../../include/conversation.php:1264
-#: ../../extend/addon/hzaddons/hsse/hsse.php:208
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:397
+#: ../../include/conversation.php:1150 ../../addon/wiki/Mod_Wiki.php:397
+#: ../../addon/hsse/hsse.php:208
msgid "Embed an image from your albums"
-msgstr ""
+msgstr "Sett inn et bilde fra albumene dine"
-#: ../../include/conversation.php:1265 ../../include/conversation.php:1320
-#: ../../Zotlabs/Storage/Browser.php:387 ../../Zotlabs/Module/Editblock.php:141
-#: ../../Zotlabs/Module/Editlayout.php:140 ../../Zotlabs/Module/Oauth.php:110
-#: ../../Zotlabs/Module/Oauth.php:136 ../../Zotlabs/Module/Editpost.php:114
-#: ../../Zotlabs/Module/Oauth2.php:115 ../../Zotlabs/Module/Oauth2.php:143
-#: ../../Zotlabs/Module/Cover_photo.php:386 ../../Zotlabs/Module/Tagrm.php:15
-#: ../../Zotlabs/Module/Tagrm.php:138 ../../Zotlabs/Module/Connedit.php:750
-#: ../../Zotlabs/Module/Editwebpage.php:169 ../../Zotlabs/Module/Filer.php:56
-#: ../../Zotlabs/Module/Fbrowser.php:66 ../../Zotlabs/Module/Fbrowser.php:88
-#: ../../Zotlabs/Module/Profile_photo.php:553
-#: ../../Zotlabs/Module/Admin/Addons.php:431 ../../Zotlabs/Module/Cdav.php:1049
-#: ../../Zotlabs/Module/Cdav.php:1386
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:130
-#: ../../extend/addon/hzaddons/hsse/hsse.php:209
-#: ../../extend/addon/hzaddons/hsse/hsse.php:258
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:365
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:398
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:132
+#: ../../include/conversation.php:1151 ../../include/conversation.php:1206
+#: ../../addon/cards/Mod_Card_edit.php:126
+#: ../../addon/articles/Mod_Article_edit.php:126
+#: ../../addon/wiki/Mod_Wiki.php:365 ../../addon/wiki/Mod_Wiki.php:398
+#: ../../addon/hsse/hsse.php:209 ../../addon/hsse/hsse.php:258
+#: ../../Zotlabs/Storage/Browser.php:391
+#: ../../Zotlabs/Module/Cover_photo.php:386
+#: ../../Zotlabs/Module/Editwebpage.php:169 ../../Zotlabs/Module/Oauth.php:111
+#: ../../Zotlabs/Module/Oauth.php:136 ../../Zotlabs/Module/Connedit.php:750
+#: ../../Zotlabs/Module/Profile_photo.php:553 ../../Zotlabs/Module/Tagrm.php:15
+#: ../../Zotlabs/Module/Tagrm.php:138 ../../Zotlabs/Module/Fbrowser.php:68
+#: ../../Zotlabs/Module/Fbrowser.php:90 ../../Zotlabs/Module/Editlayout.php:140
+#: ../../Zotlabs/Module/Editpost.php:115 ../../Zotlabs/Module/Cdav.php:1049
+#: ../../Zotlabs/Module/Cdav.php:1386 ../../Zotlabs/Module/Oauth2.php:116
+#: ../../Zotlabs/Module/Oauth2.php:143 ../../Zotlabs/Module/Editblock.php:141
+#: ../../Zotlabs/Module/Filer.php:67
msgid "Cancel"
msgstr "Avbryt"
-#: ../../include/conversation.php:1266 ../../include/conversation.php:1319
-#: ../../Zotlabs/Module/Cover_photo.php:387
+#: ../../include/conversation.php:1152 ../../include/conversation.php:1205
+#: ../../addon/wiki/Mod_Wiki.php:399 ../../addon/hsse/hsse.php:210
+#: ../../addon/hsse/hsse.php:257 ../../Zotlabs/Module/Cover_photo.php:387
#: ../../Zotlabs/Module/Profile_photo.php:554
-#: ../../extend/addon/hzaddons/hsse/hsse.php:210
-#: ../../extend/addon/hzaddons/hsse/hsse.php:257
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:399
msgid "OK"
msgstr "OK"
-#: ../../include/conversation.php:1268
-#: ../../extend/addon/hzaddons/hsse/hsse.php:212
+#: ../../include/conversation.php:1154 ../../addon/hsse/hsse.php:212
msgid "Toggle voting"
msgstr "Skru av eller på stemming"
-#: ../../include/conversation.php:1269
+#: ../../include/conversation.php:1155
msgid "Toggle poll"
msgstr "Spørreskjema (på/av)"
-#: ../../include/conversation.php:1270
+#: ../../include/conversation.php:1156
msgid "Option"
msgstr "Valg"
-#: ../../include/conversation.php:1271
+#: ../../include/conversation.php:1157
msgid "Add option"
msgstr "Legg til valg"
-#: ../../include/conversation.php:1272
+#: ../../include/conversation.php:1158
msgid "Minutes"
msgstr "Minutter"
-#: ../../include/conversation.php:1272
-#, fuzzy
+#: ../../include/conversation.php:1158
msgid "Hours"
-msgstr "timer"
+msgstr "Timer"
-#: ../../include/conversation.php:1272
-#, fuzzy
+#: ../../include/conversation.php:1158
msgid "Days"
-msgstr "dager"
+msgstr "Dager"
-#: ../../include/conversation.php:1273
+#: ../../include/conversation.php:1159
msgid "Allow multiple answers"
msgstr "Tillat flere svar"
-#: ../../include/conversation.php:1273
-#: ../../view/theme/redbasic/php/config.php:201
-#: ../../view/theme/redbasic/php/config.php:202
-#: ../../view/theme/redbasic/php/config.php:203
-#: ../../view/theme/redbasic/php/config.php:215 ../../boot.php:1765
-#: ../../Zotlabs/Lib/Libzotdir.php:166 ../../Zotlabs/Lib/Libzotdir.php:167
-#: ../../Zotlabs/Lib/Libzotdir.php:169 ../../Zotlabs/Storage/Browser.php:310
-#: ../../Zotlabs/Storage/Browser.php:311 ../../Zotlabs/Storage/Browser.php:312
-#: ../../Zotlabs/Storage/Browser.php:393 ../../Zotlabs/Storage/Browser.php:395
-#: ../../Zotlabs/Storage/Browser.php:559 ../../Zotlabs/Module/Menu.php:163
-#: ../../Zotlabs/Module/Menu.php:222 ../../Zotlabs/Module/Api.php:101
-#: ../../Zotlabs/Module/Photos.php:666 ../../Zotlabs/Module/Profiles.php:674
-#: ../../Zotlabs/Module/Profiles.php:684 ../../Zotlabs/Module/Profiles.php:692
-#: ../../Zotlabs/Module/Profiles.php:696 ../../Zotlabs/Module/Sources.php:122
-#: ../../Zotlabs/Module/Sources.php:157 ../../Zotlabs/Module/Group.php:138
-#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:148
-#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
-#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Connedit.php:622
-#: ../../Zotlabs/Module/Import.php:611 ../../Zotlabs/Module/Import.php:615
-#: ../../Zotlabs/Module/Import.php:616 ../../Zotlabs/Module/Register.php:515
-#: ../../Zotlabs/Module/Defperms.php:195
-#: ../../Zotlabs/Module/Filestorage.php:203
-#: ../../Zotlabs/Module/Filestorage.php:211
-#: ../../Zotlabs/Module/Admin/Site.php:306 ../../Zotlabs/Module/Mitem.php:176
-#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:256
-#: ../../Zotlabs/Module/Mitem.php:257 ../../Zotlabs/Module/Permcats.php:247
-#: ../../Zotlabs/Module/Settings/Channel.php:225
-#: ../../Zotlabs/Module/Settings/Multifactor.php:82
-#: ../../Zotlabs/Module/Settings/Privacy.php:133
-#: ../../Zotlabs/Module/Settings/Privacy.php:134
-#: ../../Zotlabs/Module/Settings/Privacy.php:135
-#: ../../Zotlabs/Module/Settings/Privacy.php:136
-#: ../../Zotlabs/Module/Settings/Privacy.php:137
-#: ../../Zotlabs/Module/Settings/Privacy.php:138
-#: ../../Zotlabs/Module/Settings/Display.php:87
-#: ../../Zotlabs/Module/Contactedit.php:270
-#: ../../Zotlabs/Module/Contactedit.php:315
-#: ../../extend/addon/hzaddons/cart/cart.php:1418
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:72
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:338
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:362
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:438
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:462
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:153
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:425
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:63
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:254
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:258
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:88
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:98
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:87
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:95
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:67
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:651
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:655
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:61
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:73
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:218
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:92
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:96
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:100
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:258
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:280
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:289
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:61
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:65
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:69
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:40
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:160
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:169
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:70
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:59
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:63
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:135
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:136
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:47
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:61
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:230
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:231
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:57
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:45
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:61
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:84
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:88
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:92
-msgid "No"
-msgstr "Nei"
-
-#: ../../include/conversation.php:1273
-#: ../../view/theme/redbasic/php/config.php:201
-#: ../../view/theme/redbasic/php/config.php:202
-#: ../../view/theme/redbasic/php/config.php:203
-#: ../../view/theme/redbasic/php/config.php:215 ../../boot.php:1765
-#: ../../Zotlabs/Lib/Libzotdir.php:166 ../../Zotlabs/Lib/Libzotdir.php:167
-#: ../../Zotlabs/Lib/Libzotdir.php:169 ../../Zotlabs/Storage/Browser.php:310
-#: ../../Zotlabs/Storage/Browser.php:311 ../../Zotlabs/Storage/Browser.php:312
-#: ../../Zotlabs/Storage/Browser.php:393 ../../Zotlabs/Storage/Browser.php:395
-#: ../../Zotlabs/Storage/Browser.php:559 ../../Zotlabs/Module/Menu.php:163
-#: ../../Zotlabs/Module/Menu.php:222 ../../Zotlabs/Module/Api.php:100
-#: ../../Zotlabs/Module/Photos.php:666 ../../Zotlabs/Module/Profiles.php:674
-#: ../../Zotlabs/Module/Profiles.php:684 ../../Zotlabs/Module/Profiles.php:692
-#: ../../Zotlabs/Module/Profiles.php:696 ../../Zotlabs/Module/Sources.php:122
-#: ../../Zotlabs/Module/Sources.php:157 ../../Zotlabs/Module/Group.php:138
-#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:148
-#: ../../Zotlabs/Module/Group.php:250 ../../Zotlabs/Module/Group.php:302
-#: ../../Zotlabs/Module/Group.php:303 ../../Zotlabs/Module/Import.php:611
-#: ../../Zotlabs/Module/Import.php:615 ../../Zotlabs/Module/Import.php:616
-#: ../../Zotlabs/Module/Register.php:515 ../../Zotlabs/Module/Defperms.php:195
-#: ../../Zotlabs/Module/Filestorage.php:203
-#: ../../Zotlabs/Module/Filestorage.php:211
-#: ../../Zotlabs/Module/Admin/Site.php:308 ../../Zotlabs/Module/Mitem.php:176
-#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:256
-#: ../../Zotlabs/Module/Mitem.php:257 ../../Zotlabs/Module/Permcats.php:247
-#: ../../Zotlabs/Module/Settings/Channel.php:225
-#: ../../Zotlabs/Module/Settings/Multifactor.php:82
-#: ../../Zotlabs/Module/Settings/Privacy.php:133
-#: ../../Zotlabs/Module/Settings/Privacy.php:134
-#: ../../Zotlabs/Module/Settings/Privacy.php:135
-#: ../../Zotlabs/Module/Settings/Privacy.php:136
-#: ../../Zotlabs/Module/Settings/Privacy.php:137
-#: ../../Zotlabs/Module/Settings/Privacy.php:138
-#: ../../Zotlabs/Module/Settings/Display.php:87
-#: ../../Zotlabs/Module/Contactedit.php:270
-#: ../../extend/addon/hzaddons/cart/cart.php:1418
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:72
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:337
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:361
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:437
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:461
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:153
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:425
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:63
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:254
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:258
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:88
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:98
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:87
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:95
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:67
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:651
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:655
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:61
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:73
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:218
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:92
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:96
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:100
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:258
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:280
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:289
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:61
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:65
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:69
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:40
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:160
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:169
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:70
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:59
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:63
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:135
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:136
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:47
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:61
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:230
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:231
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:57
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:45
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:61
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:84
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:88
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:92
-msgid "Yes"
-msgstr "Ja"
-
-#: ../../include/conversation.php:1275
-#: ../../extend/addon/hzaddons/hsse/hsse.php:215
+#: ../../include/conversation.php:1161 ../../addon/hsse/hsse.php:215
msgid "Disable comments"
msgstr "Slå av kommentarer"
-#: ../../include/conversation.php:1276
-#: ../../extend/addon/hzaddons/hsse/hsse.php:216
+#: ../../include/conversation.php:1162 ../../addon/hsse/hsse.php:216
msgid "Toggle comments"
-msgstr ""
+msgstr "Slå av/på kommentarer"
-#: ../../include/conversation.php:1282 ../../Zotlabs/Module/Editblock.php:129
-#: ../../Zotlabs/Module/Photos.php:667 ../../Zotlabs/Module/Photos.php:1042
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:116
-#: ../../extend/addon/hzaddons/hsse/hsse.php:221
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:118
+#: ../../include/conversation.php:1168 ../../addon/cards/Mod_Card_edit.php:111
+#: ../../addon/articles/Mod_Article_edit.php:111 ../../addon/hsse/hsse.php:221
+#: ../../Zotlabs/Module/Photos.php:669 ../../Zotlabs/Module/Photos.php:1043
+#: ../../Zotlabs/Module/Editblock.php:129
msgid "Title (optional)"
msgstr "Tittel (valgfri)"
-#: ../../include/conversation.php:1283
+#: ../../include/conversation.php:1169
msgid "Summary (optional)"
msgstr "Sammendrag (valgfritt)"
-#: ../../include/conversation.php:1286
-#: ../../extend/addon/hzaddons/hsse/hsse.php:224
+#: ../../include/conversation.php:1172 ../../addon/hsse/hsse.php:224
msgid "Categories (optional, comma-separated list)"
msgstr "Kategorier (valgfri, kommaseparert liste)"
-#: ../../include/conversation.php:1287
-#: ../../extend/addon/hzaddons/hsse/hsse.php:225
+#: ../../include/conversation.php:1173 ../../addon/hsse/hsse.php:225
msgid "Permission settings"
msgstr "Tillatelser - innstillinger"
-#: ../../include/conversation.php:1309
-#: ../../extend/addon/hzaddons/hsse/hsse.php:247
+#: ../../include/conversation.php:1195 ../../addon/hsse/hsse.php:247
msgid "Other networks and post services"
msgstr "Andre nettverk og innleggstjenester"
-#: ../../include/conversation.php:1312
-#: ../../extend/addon/hzaddons/hsse/hsse.php:250
+#: ../../include/conversation.php:1198 ../../addon/hsse/hsse.php:250
msgid "Set expiration date"
msgstr "Angi utløpsdato"
-#: ../../include/conversation.php:1315
-#: ../../extend/addon/hzaddons/hsse/hsse.php:253
+#: ../../include/conversation.php:1201 ../../addon/hsse/hsse.php:253
msgid "Set publish date"
msgstr "Angi publiseringsdato"
-#: ../../include/conversation.php:1317 ../../Zotlabs/Lib/ThreadItem.php:881
-#: ../../Zotlabs/Module/Chat.php:218
-#: ../../extend/addon/hzaddons/hsse/hsse.php:255
+#: ../../include/conversation.php:1203 ../../addon/hsse/hsse.php:255
+#: ../../Zotlabs/Lib/ThreadItem.php:823 ../../Zotlabs/Module/Chat.php:218
msgid "Encrypt text"
msgstr "Krypter tekst"
-#: ../../include/conversation.php:1560 ../../include/taxonomy.php:677
-#: ../../Zotlabs/Module/Photos.php:1129
-msgctxt "noun"
-msgid "Like"
-msgid_plural "Likes"
-msgstr[0] "Liker"
-msgstr[1] "Liker"
-
-#: ../../include/conversation.php:1563
+#: ../../include/conversation.php:1443
msgctxt "noun"
msgid "Repeat"
msgid_plural "Repeats"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Videresending"
+msgstr[1] "Videresendinger"
-#: ../../include/conversation.php:1566 ../../Zotlabs/Module/Photos.php:1134
+#: ../../include/conversation.php:1446 ../../Zotlabs/Module/Photos.php:1123
msgctxt "noun"
msgid "Dislike"
msgid_plural "Dislikes"
msgstr[0] "Liker ikke"
msgstr[1] "Liker ikke"
-#: ../../include/conversation.php:1569
+#: ../../include/conversation.php:1449
+msgctxt "noun"
+msgid "Comment"
+msgid_plural "Comments"
+msgstr[0] "Kommentar"
+msgstr[1] "Kommentarer"
+
+#: ../../include/conversation.php:1449
+msgctxt "noun"
+msgid "Reply"
+msgid_plural "Replies"
+msgstr[0] "Svar"
+msgstr[1] "Svar"
+
+#: ../../include/conversation.php:1452
msgctxt "noun"
msgid "Attending"
msgid_plural "Attending"
msgstr[0] "Deltar"
msgstr[1] "Deltar"
-#: ../../include/conversation.php:1572
+#: ../../include/conversation.php:1455
+#, fuzzy
+#| msgctxt "title"
+#| msgid "Not attending"
msgctxt "noun"
-msgid "Not Attending"
-msgid_plural "Not Attending"
+msgid "Not attending"
+msgid_plural "Not attending"
msgstr[0] "Deltar ikke"
msgstr[1] "Deltar ikke"
-#: ../../include/conversation.php:1575
+#: ../../include/conversation.php:1458
msgctxt "noun"
msgid "Undecided"
msgid_plural "Undecided"
msgstr[0] "Ikke bestemt"
msgstr[1] "Ikke bestemt"
+#: ../../include/nav.php:109
+msgid "Remote authentication"
+msgstr "Fjernautentisering"
+
+#: ../../include/nav.php:109
+msgid "Click to authenticate to your home hub"
+msgstr "Klikk for å godkjennes mot din hjemme-hub"
+
+#: ../../include/nav.php:115 ../../Zotlabs/Widget/Channel_activities.php:239
+#: ../../Zotlabs/Widget/Admin.php:29
+#: ../../Zotlabs/Module/Admin/Channels.php:168
+#: ../../Zotlabs/Module/Manage.php:162 ../../Zotlabs/Module/Admin.php:118
+msgid "Channels"
+msgstr "Kanaler"
+
+#: ../../include/nav.php:115
+msgid "Manage your channels"
+msgstr "Behandle kanalene dine"
+
+#: ../../include/nav.php:118 ../../Zotlabs/Lib/Apps.php:345
+#: ../../Zotlabs/Widget/Newmember.php:60
+#: ../../Zotlabs/Widget/Settings_menu.php:71
+#: ../../Zotlabs/Module/Admin/Themes.php:141
+#: ../../Zotlabs/Module/Admin/Addons.php:127
+msgid "Settings"
+msgstr "Innstillinger"
+
+#: ../../include/nav.php:118
+msgid "Account/Channel Settings"
+msgstr "Konto-/kanalinnstillinger"
+
+#: ../../include/nav.php:124 ../../include/nav.php:154
+#: ../../include/nav.php:175 ../../boot.php:1760
+msgid "Logout"
+msgstr "Logg ut"
+
+#: ../../include/nav.php:124 ../../include/nav.php:154
+msgid "End this session"
+msgstr "Avslutt denne økten"
+
+#: ../../include/nav.php:127
+msgid "Your profile page"
+msgstr "Din profilside"
+
+#: ../../include/nav.php:130 ../../include/channel.php:1541
+#: ../../Zotlabs/Module/Profiles.php:851
+msgid "Edit Profiles"
+msgstr "Rediger profiler"
+
+#: ../../include/nav.php:130
+msgid "Manage/Edit profiles"
+msgstr "Håndter/endre profiler"
+
+#: ../../include/nav.php:132 ../../include/channel.php:1545
+#: ../../addon/openclipatar/openclipatar.php:59
+msgid "Edit Profile"
+msgstr "Rediger profil"
+
+#: ../../include/nav.php:132 ../../Zotlabs/Widget/Newmember.php:42
+msgid "Edit your profile"
+msgstr "Rediger profil"
+
+#: ../../include/nav.php:139 ../../include/nav.php:143 ../../boot.php:1761
+#: ../../Zotlabs/Lib/Apps.php:342 ../../Zotlabs/Module/Login.php:15
+msgid "Login"
+msgstr "Logg inn"
+
+#: ../../include/nav.php:139 ../../include/nav.php:143
+msgid "Sign in"
+msgstr "Logg på"
+
+#: ../../include/nav.php:173
+msgid "Take me home"
+msgstr "Ta meg hjem"
+
+#: ../../include/nav.php:175
+msgid "Log me out of this site"
+msgstr "Logg meg ut fra dette nettstedet"
+
+#: ../../include/nav.php:180 ../../boot.php:1738
+#: ../../Zotlabs/Module/Register.php:543
+msgid "Register"
+msgstr "Registrer"
+
+#: ../../include/nav.php:180
+msgid "Create an account"
+msgstr "Lag en konto"
+
+#: ../../include/nav.php:194 ../../include/nav.php:341
+#: ../../Zotlabs/Lib/Apps.php:354 ../../Zotlabs/Module/Layouts.php:184
+msgid "Help"
+msgstr "Hjelp"
+
+#: ../../include/nav.php:194
+msgid "Help and documentation"
+msgstr "Hjelp og dokumentasjon"
+
+#: ../../include/nav.php:208 ../../include/acl_selectors.php:149
+#: ../../include/text.php:1190 ../../include/text.php:1202
+#: ../../Zotlabs/Lib/Apps.php:357 ../../Zotlabs/Widget/Sitesearch.php:37
+#: ../../Zotlabs/Widget/Activity_filter.php:210
+#: ../../Zotlabs/Module/Search.php:47 ../../Zotlabs/Module/Connections.php:403
+msgid "Search"
+msgstr "Søk"
+
+#: ../../include/nav.php:208
+msgid "Search site @name, !forum, #tag, ?docs, content"
+msgstr "Søk etter @navn, !forum, #emne, ?dokumentasjon eller innhold"
+
+#: ../../include/nav.php:214 ../../Zotlabs/Widget/Admin.php:61
+msgid "Admin"
+msgstr "Administrator"
+
+#: ../../include/nav.php:214
+msgid "Site Setup and Configuration"
+msgstr "Nettstedsoppsett og -konfigurasjon"
+
+#: ../../include/nav.php:345 ../../Zotlabs/Widget/Notifications.php:175
+#: ../../Zotlabs/Widget/Messages.php:52 ../../Zotlabs/Module/Defperms.php:254
+#: ../../Zotlabs/Module/New_channel.php:158
+#: ../../Zotlabs/Module/New_channel.php:165
+msgid "Loading"
+msgstr "Laster"
+
+#: ../../include/nav.php:350
+msgid "@name, #tag, ?doc, content"
+msgstr "@navn, #merkelapp, ?dokumentasjon, innhold"
+
+#: ../../include/nav.php:351
+msgid "Please wait..."
+msgstr "Vennligst vent..."
+
+#: ../../include/nav.php:357 ../../Zotlabs/Lib/Apps.php:329
+msgid "Apps"
+msgstr "Apper"
+
+#: ../../include/nav.php:358
+msgid "Channel Apps"
+msgstr "Kanalapper"
+
+#: ../../include/nav.php:359
+msgid "System Apps"
+msgstr "Systemapper"
+
+#: ../../include/nav.php:360
+msgid "Pinned Apps"
+msgstr "Festede apper"
+
+#: ../../include/nav.php:361
+msgid "Featured Apps"
+msgstr "Fremhevede apper"
+
+#: ../../include/nav.php:447 ../../Zotlabs/Lib/Apps.php:349
+#: ../../Zotlabs/Widget/Notifications.php:43
+#: ../../Zotlabs/Module/Admin/Channels.php:176
+msgid "Channel"
+msgstr "Kanal"
+
+#: ../../include/nav.php:450
+msgid "Status Messages and Posts"
+msgstr "Statusmeldinger og -innlegg"
+
+#: ../../include/nav.php:460 ../../Zotlabs/Module/Help.php:240
+msgid "About"
+msgstr "Om"
+
+#: ../../include/nav.php:463
+msgid "Profile Details"
+msgstr "Profildetaljer"
+
+#: ../../include/nav.php:473 ../../include/photos.php:747
+msgid "Photo Albums"
+msgstr "Fotoalbum"
+
+#: ../../include/nav.php:478 ../../Zotlabs/Lib/Apps.php:346
+#: ../../Zotlabs/Widget/Notifications.php:108
+#: ../../Zotlabs/Widget/Channel_activities.php:125
+#: ../../Zotlabs/Storage/Browser.php:355 ../../Zotlabs/Module/Fbrowser.php:87
+msgid "Files"
+msgstr "Filer"
+
+#: ../../include/nav.php:481
+msgid "Files and Storage"
+msgstr "Filer og lagring"
+
+#: ../../include/nav.php:503 ../../include/nav.php:506
+#: ../../Zotlabs/Lib/Apps.php:336 ../../Zotlabs/Widget/Chatroom_list.php:22
+msgid "Chatrooms"
+msgstr "Chatrom"
+
+#: ../../include/nav.php:516 ../../Zotlabs/Lib/Apps.php:335
+#: ../../Zotlabs/Module/Bookmarks.php:90
+msgid "Bookmarks"
+msgstr "Bokmerker"
+
+#: ../../include/nav.php:519
+msgid "Saved Bookmarks"
+msgstr "Lagrede bokmerker"
+
+#: ../../include/nav.php:527 ../../Zotlabs/Lib/Apps.php:347
+#: ../../Zotlabs/Widget/Channel_activities.php:168
+#: ../../Zotlabs/Module/Webpages.php:246
+msgid "Webpages"
+msgstr "Websider"
+
+#: ../../include/nav.php:530
+msgid "View Webpages"
+msgstr "Vis websider"
+
+#: ../../include/security.php:633
+msgid ""
+"The form security token was not correct. This probably happened because the "
+"form has been opened for too long (>3 hours) before submitting it."
+msgstr ""
+"Skjemaets sikkerhetspollett var ikke gyldig. Dette skjedde antakelig fordi "
+"skjemaet har vært åpnet for lenge (>3 timer) før det ble sendt inn."
+
+#: ../../include/datetime.php:58 ../../Zotlabs/Widget/Newmember.php:58
+#: ../../Zotlabs/Module/Profiles.php:751
+msgid "Miscellaneous"
+msgstr "Forskjellig"
+
+#: ../../include/datetime.php:140
+msgid "Birthday"
+msgstr "Fødselsdag"
+
+#: ../../include/datetime.php:140
+msgid "Age: "
+msgstr "Alder: "
+
+#: ../../include/datetime.php:140
+msgid "YYYY-MM-DD or MM-DD"
+msgstr "YYYY-MM-DD eller MM-DD"
+
+#: ../../include/datetime.php:238 ../../boot.php:2680
+msgid "never"
+msgstr "aldri"
+
+#: ../../include/datetime.php:244
+msgid "less than a second ago"
+msgstr "for mindre enn ett sekund siden"
+
+#: ../../include/datetime.php:262
+#, php-format
+msgctxt "e.g. 22 hours ago, 1 minute ago"
+msgid "%1$d %2$s ago"
+msgstr "%1$d %2$s siden"
+
+#: ../../include/datetime.php:284
+msgid "ago"
+msgstr "siden"
+
+#: ../../include/datetime.php:287
+msgid "in"
+msgstr "i"
+
+#: ../../include/datetime.php:304
+msgid "now"
+msgstr "nå"
+
+#: ../../include/datetime.php:312
+msgctxt "relative_date"
+msgid "year"
+msgid_plural "years"
+msgstr[0] "år"
+msgstr[1] "år"
+
+#: ../../include/datetime.php:315
+msgctxt "relative_date"
+msgid "month"
+msgid_plural "months"
+msgstr[0] "måned"
+msgstr[1] "måneder"
+
+#: ../../include/datetime.php:318
+msgctxt "relative_date"
+msgid "week"
+msgid_plural "weeks"
+msgstr[0] "uke"
+msgstr[1] "uker"
+
+#: ../../include/datetime.php:321
+msgctxt "relative_date"
+msgid "day"
+msgid_plural "days"
+msgstr[0] "dag"
+msgstr[1] "dager"
+
+#: ../../include/datetime.php:324
+msgctxt "relative_date"
+msgid "hour"
+msgid_plural "hours"
+msgstr[0] "time"
+msgstr[1] "timer"
+
+#: ../../include/datetime.php:327
+msgctxt "relative_date"
+msgid "minute"
+msgid_plural "minutes"
+msgstr[0] "minutt"
+msgstr[1] "minutter"
+
+#: ../../include/datetime.php:330
+msgctxt "relative_date"
+msgid "second"
+msgid_plural "seconds"
+msgstr[0] "sekund"
+msgstr[1] "sekunder"
+
+#: ../../include/datetime.php:559
+#, php-format
+msgid "%1$s's birthday"
+msgstr "%1$s sin fødselsdag"
+
+#: ../../include/datetime.php:560
+#, php-format
+msgid "Happy Birthday %1$s"
+msgstr "Gratulerer med dagen, %1$s"
+
+#: ../../include/photos.php:154
+#, php-format
+msgid "Image exceeds website size limit of %lu bytes"
+msgstr "Bilde overstiger nettstedets størrelsesbegrensning på %lu bytes"
+
+#: ../../include/photos.php:165
+msgid "Image file is empty."
+msgstr "Bildefilen er tom."
+
+#: ../../include/photos.php:199 ../../Zotlabs/Module/Cover_photo.php:240
+#: ../../Zotlabs/Module/Profile_photo.php:275
+msgid "Unable to process image"
+msgstr "Kan ikke behandle bildet"
+
+#: ../../include/photos.php:325
+msgid "Photo storage failed."
+msgstr "Bildelagring mislyktes."
+
+#: ../../include/photos.php:374
+msgid "a new photo"
+msgstr "et nytt bilde"
+
+#: ../../include/photos.php:378
+#, php-format
+msgctxt "photo_upload"
+msgid "%1$s posted %2$s to %3$s"
+msgstr "%1$s la inn %2$s til %3$s"
+
+#: ../../include/photos.php:748 ../../Zotlabs/Module/Photos.php:1333
+#: ../../Zotlabs/Module/Photos.php:1346 ../../Zotlabs/Module/Photos.php:1347
+msgid "Recent Photos"
+msgstr "Nye bilder"
+
+#: ../../include/photos.php:752
+msgid "Upload New Photos"
+msgstr "Last opp nye bilder"
+
+#: ../../include/connections.php:174
+msgid "New window"
+msgstr "Nytt vindu"
+
+#: ../../include/connections.php:175
+msgid "Open the selected location in a different window or browser tab"
+msgstr "Åpne det valgte stedet i et annet vindu eller nettleser-fane"
+
+#: ../../include/network.php:414
+msgid "url: "
+msgstr "url:"
+
+#: ../../include/network.php:415
+msgid "error_code: "
+msgstr "feilkode:"
+
+#: ../../include/network.php:416
+msgid "error_string: "
+msgstr "feilmelding"
+
+#: ../../include/network.php:417
+msgid "content-type: "
+msgstr ""
+
+#: ../../include/network.php:1774 ../../include/network.php:1775
+msgid "Friendica"
+msgstr "Friendica"
+
+#: ../../include/network.php:1776
+msgid "OStatus"
+msgstr "OStatus"
+
+#: ../../include/network.php:1777
+msgid "GNU-Social"
+msgstr ""
+
+#: ../../include/network.php:1778
+msgid "RSS/Atom"
+msgstr "RSS/Atom"
+
+#: ../../include/network.php:1779
+msgid "ActivityPub"
+msgstr ""
+
+#: ../../include/network.php:1780 ../../addon/openid/MysqlProvider.php:56
+#: ../../addon/openid/MysqlProvider.php:57 ../../addon/redred/Mod_Redred.php:69
+#: ../../addon/rtof/Mod_Rtof.php:55 ../../Zotlabs/Module/Connedit.php:736
+#: ../../Zotlabs/Module/Admin/Accounts.php:216
+#: ../../Zotlabs/Module/Admin/Accounts.php:230
+#: ../../Zotlabs/Module/Cdav.php:1372
+msgid "Email"
+msgstr "E-post"
+
+#: ../../include/network.php:1781
+msgid "Diaspora"
+msgstr "Diaspora"
+
+#: ../../include/network.php:1782
+msgid "Facebook"
+msgstr "Facebook"
+
+#: ../../include/network.php:1783
+msgid "Zot"
+msgstr "Zot"
+
+#: ../../include/network.php:1784
+msgid "LinkedIn"
+msgstr "LinkedIn"
+
+#: ../../include/network.php:1785
+msgid "XMPP/IM"
+msgstr "XMPP/IM"
+
+#: ../../include/network.php:1786
+msgid "MySpace"
+msgstr "MySpace"
+
+#: ../../include/markdown.php:190 ../../include/bbcode.php:576
+#, php-format
+msgid "%1$s wrote the following %2$s %3$s"
+msgstr "%1$s skrev følgende %2$s %3$s"
+
+#: ../../include/markdown.php:192 ../../include/bbcode.php:572
+#: ../../Zotlabs/Module/Tagger.php:81
+msgid "post"
+msgstr "innlegg"
+
+#: ../../include/markdown.php:262 ../../include/bbcode.php:661
+msgid "spoiler"
+msgstr "røpealarm"
+
+#: ../../include/menu.php:120 ../../include/channel.php:1541
+#: ../../include/channel.php:1545 ../../addon/cards/cards.php:74
+#: ../../addon/articles/articles.php:75 ../../addon/wiki/Mod_Wiki.php:214
+#: ../../addon/wiki/Mod_Wiki.php:381 ../../Zotlabs/Lib/Apps.php:617
+#: ../../Zotlabs/Lib/ThreadItem.php:159 ../../Zotlabs/Widget/Cdav.php:144
+#: ../../Zotlabs/Widget/Cdav.php:181 ../../Zotlabs/Module/Editwebpage.php:142
+#: ../../Zotlabs/Module/Webpages.php:249 ../../Zotlabs/Module/Oauth.php:171
+#: ../../Zotlabs/Module/Thing.php:301 ../../Zotlabs/Module/Layouts.php:191
+#: ../../Zotlabs/Module/Menu.php:176 ../../Zotlabs/Module/Admin/Profs.php:176
+#: ../../Zotlabs/Module/Group.php:246 ../../Zotlabs/Module/Blocks.php:158
+#: ../../Zotlabs/Module/Editlayout.php:114
+#: ../../Zotlabs/Module/Connections.php:338
+#: ../../Zotlabs/Module/Connections.php:387
+#: ../../Zotlabs/Module/Connections.php:408 ../../Zotlabs/Module/Oauth2.php:192
+#: ../../Zotlabs/Module/Editblock.php:114
+msgid "Edit"
+msgstr "Rediger"
+
+#: ../../include/acl_selectors.php:33
+#: ../../Zotlabs/Lib/PermissionDescription.php:34
+msgid "Visible to your default audience"
+msgstr "Synlig for ditt standard publikum"
+
+#: ../../include/acl_selectors.php:100
+msgid "Profile-Based Privacy Groups"
+msgstr "Profilbaserte personverngrupper"
+
+#: ../../include/acl_selectors.php:119
+msgid "Private Forum"
+msgstr "Privat forum"
+
+#: ../../include/acl_selectors.php:125
+#: ../../Zotlabs/Widget/Notifications.php:131
+#: ../../Zotlabs/Widget/Activity_filter.php:130
+#: ../../Zotlabs/Widget/Forums.php:77
+msgid "Forums"
+msgstr "Forum"
+
+#: ../../include/acl_selectors.php:136
+#: ../../Zotlabs/Lib/PermissionDescription.php:107
+#: ../../Zotlabs/Module/Settings/Privacy.php:66
+msgid "Only me"
+msgstr "Kun meg"
+
+#: ../../include/acl_selectors.php:143
+msgid "Share with"
+msgstr "Del med"
+
+#: ../../include/acl_selectors.php:144
+msgid "Custom selection"
+msgstr "Tilpasset utvalg"
+
+#: ../../include/acl_selectors.php:146
+msgid ""
+"Select \"Allow\" to allow viewing. \"Don't allow\" lets you override and "
+"limit the scope of \"Allow\"."
+msgstr ""
+"Velg \"Tillat\" for å tillate visning. \"Ikke tillat\" lar deg overstyre og "
+"begrense gyldigheten av \"Tillat\"."
+
+#: ../../include/acl_selectors.php:147 ../../Zotlabs/Module/Authorize.php:32
+msgid "Allow"
+msgstr "Tillat"
+
+#: ../../include/acl_selectors.php:148
+msgid "Don't allow"
+msgstr "Ikke tillat"
+
+#: ../../include/acl_selectors.php:154 ../../Zotlabs/Module/Thing.php:357
+#: ../../Zotlabs/Module/Thing.php:407 ../../Zotlabs/Module/Filestorage.php:195
+#: ../../Zotlabs/Module/Photos.php:673 ../../Zotlabs/Module/Photos.php:1046
+#: ../../Zotlabs/Module/Chat.php:240
+msgid "Permissions"
+msgstr "Tillatelser"
+
+#: ../../include/acl_selectors.php:156 ../../Zotlabs/Lib/ThreadItem.php:467
+#: ../../Zotlabs/Widget/Pinned.php:156 ../../Zotlabs/Storage/Browser.php:418
+#: ../../Zotlabs/Module/Photos.php:1260
+msgid "Close"
+msgstr "Lukk"
+
+#: ../../include/acl_selectors.php:181
+#, php-format
+msgid ""
+"Post permissions %s cannot be changed %s after a post is shared.</br />These "
+"permissions set who is allowed to view the post."
+msgstr ""
+"Tillatelsene til innlegget %s kan ikke endres %s etter at innlegget er delt."
+"</br />Disse tillatelsene avgjør hvem som kan se innlegget."
+
#: ../../include/text.php:562
msgid "prev"
msgstr "forrige"
@@ -2284,27 +3249,27 @@ msgstr "nyere"
#: ../../include/text.php:1086 ../../Zotlabs/Module/Viewconnections.php:80
#: ../../Zotlabs/Module/Connections.php:306
msgid "Accepts"
-msgstr ""
+msgstr "Godtar"
#: ../../include/text.php:1089 ../../Zotlabs/Module/Viewconnections.php:83
#: ../../Zotlabs/Module/Connections.php:309
msgid "Comments"
-msgstr ""
+msgstr "Kommentarer"
#: ../../include/text.php:1094 ../../Zotlabs/Module/Viewconnections.php:88
#: ../../Zotlabs/Module/Connections.php:314
msgid "Stream items"
-msgstr ""
+msgstr "Elementer fra tidslinjen"
#: ../../include/text.php:1099 ../../Zotlabs/Module/Viewconnections.php:93
#: ../../Zotlabs/Module/Connections.php:319
msgid "Wall posts"
-msgstr ""
+msgstr "Veggposter"
#: ../../include/text.php:1103 ../../Zotlabs/Module/Viewconnections.php:97
#: ../../Zotlabs/Module/Connections.php:323
msgid "Nothing"
-msgstr ""
+msgstr "Ingenting"
#: ../../include/text.php:1116
#, php-format
@@ -2314,784 +3279,222 @@ msgstr "Vis alle %s forbindelser"
#: ../../include/text.php:1179
#, php-format
msgid "Network: %s"
-msgstr ""
+msgstr "Nettverk: %s"
#: ../../include/text.php:1191 ../../include/text.php:1203
-#: ../../Zotlabs/Module/Rbmark.php:29 ../../Zotlabs/Module/Rbmark.php:85
-#: ../../Zotlabs/Module/Filer.php:54
+#: ../../addon/cards/Mod_Cards.php:116 ../../addon/cards/Mod_Card_edit.php:92
+#: ../../addon/articles/Mod_Article_edit.php:92
+#: ../../addon/articles/Mod_Articles.php:120
#: ../../Zotlabs/Module/Admin/Queueworker.php:115
#: ../../Zotlabs/Module/Admin/Profs.php:95
-#: ../../Zotlabs/Module/Admin/Profs.php:115
-#: ../../extend/addon/hzaddons/articles/Mod_Articles.php:120
-#: ../../extend/addon/hzaddons/cards/Mod_Cards.php:116
+#: ../../Zotlabs/Module/Admin/Profs.php:115 ../../Zotlabs/Module/Rbmark.php:29
+#: ../../Zotlabs/Module/Rbmark.php:85 ../../Zotlabs/Module/Filer.php:65
msgid "Save"
msgstr "Lagre"
-#: ../../include/text.php:1499 ../../include/js_strings.php:100
-msgid "Monday"
-msgstr "mandag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:101
-msgid "Tuesday"
-msgstr "tirsdag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:102
-msgid "Wednesday"
-msgstr "onsdag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:103
-msgid "Thursday"
-msgstr "torsdag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:104
-msgid "Friday"
-msgstr "fredag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:105
-msgid "Saturday"
-msgstr "lørdag"
-
-#: ../../include/text.php:1499 ../../include/js_strings.php:99
-msgid "Sunday"
-msgstr "søndag"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:75
-msgid "January"
-msgstr "januar"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:76
-msgid "February"
-msgstr "februar"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:77
-msgid "March"
-msgstr "mars"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:78
-msgid "April"
-msgstr "april"
-
-#: ../../include/text.php:1503
+#: ../../include/text.php:1525
msgid "May"
msgstr "mai"
-#: ../../include/text.php:1503 ../../include/js_strings.php:80
-msgid "June"
-msgstr "juni"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:81
-msgid "July"
-msgstr "juli"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:82
-msgid "August"
-msgstr "august"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:83
-msgid "September"
-msgstr "september"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:84
-msgid "October"
-msgstr "oktober"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:85
-msgid "November"
-msgstr "november"
-
-#: ../../include/text.php:1503 ../../include/js_strings.php:86
-msgid "December"
-msgstr "desember"
-
-#: ../../include/text.php:1577
-#, fuzzy
-#| msgid "Unknown Attachment"
+#: ../../include/text.php:1599
msgid "Unknown attachment"
msgstr "Ukjent vedlegg"
-#: ../../include/text.php:1580 ../../Zotlabs/Storage/Browser.php:383
+#: ../../include/text.php:1602 ../../Zotlabs/Storage/Browser.php:387
#: ../../Zotlabs/Module/Sharedwithme.php:109
msgid "Size"
msgstr "Størrelse"
-#: ../../include/text.php:1623
+#: ../../include/text.php:1643
msgid "remove category"
msgstr "fjern kategori"
-#: ../../include/text.php:1701
+#: ../../include/text.php:1721
msgid "remove from file"
msgstr "fjern fra fil"
-#: ../../include/text.php:1888
+#: ../../include/text.php:1908
msgid "Download binary/encrypted content"
-msgstr ""
+msgstr "Last ned binært/kryptert innhold"
-#: ../../include/text.php:1946 ../../include/text.php:1955
-#: ../../include/text.php:1982 ../../include/text.php:1991
+#: ../../include/text.php:1966 ../../include/text.php:1975
+#: ../../include/text.php:2002 ../../include/text.php:2011
#, php-format
msgctxt "noun"
msgid "%d Vote"
msgid_plural "%d Votes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d stemme"
+msgstr[1] "%d stemmer"
-#: ../../include/text.php:1998
+#: ../../include/text.php:2018
#, php-format
msgctxt "noun"
msgid "%d Vote in total"
msgid_plural "%d Votes in total"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d stemme totalt"
+msgstr[1] "%d stemmer totalt"
-#: ../../include/text.php:2004
+#: ../../include/text.php:2024
msgid "Poll has ended"
msgstr "Spørreskjema har utløpt"
-#: ../../include/text.php:2007
+#: ../../include/text.php:2027
#, php-format
-msgid "Poll ends in %s"
-msgstr "Spørreskjema utløper om %s"
+msgid "Poll ends %s"
+msgstr "Spørreskjema utløper %s"
-#: ../../include/text.php:2014 ../../Zotlabs/Lib/ThreadItem.php:471
+#: ../../include/text.php:2034 ../../Zotlabs/Lib/ThreadItem.php:427
msgid "Vote"
-msgstr ""
+msgstr "Stem"
-#: ../../include/text.php:2174
+#: ../../include/text.php:2193
msgid "Link to Source"
msgstr "Lenke til kilde"
-#: ../../include/text.php:2207
+#: ../../include/text.php:2218 ../../Zotlabs/Module/Lang.php:75
+msgid "default"
+msgstr "standard"
+
+#: ../../include/text.php:2226
msgid "Page layout"
msgstr "Sidens layout"
-#: ../../include/text.php:2207
+#: ../../include/text.php:2226
msgid "You can create your own with the layouts tool"
msgstr "Du kan lage din egen med layout-verktøyet"
-#: ../../include/text.php:2217
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:220
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:368
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:903
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:68
+#: ../../include/text.php:2236 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../addon/wiki/Mod_Wiki.php:368 ../../addon/wiki/Mod_Wiki.php:903
+#: ../../addon/wiki/Widget/Wiki_pages.php:68
msgid "BBcode"
msgstr "BBcode"
-#: ../../include/text.php:2218
+#: ../../include/text.php:2237
msgid "HTML"
msgstr ""
-#: ../../include/text.php:2219
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:220
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:368
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:903
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:68
-#: ../../extend/addon/hzaddons/mdpost/mdpost.php:41
+#: ../../include/text.php:2238 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../addon/wiki/Mod_Wiki.php:368 ../../addon/wiki/Mod_Wiki.php:903
+#: ../../addon/wiki/Widget/Wiki_pages.php:68 ../../addon/mdpost/mdpost.php:41
msgid "Markdown"
msgstr "Markdown"
-#: ../../include/text.php:2220
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:220
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:903
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:68
+#: ../../include/text.php:2239 ../../addon/wiki/Mod_Wiki.php:220
+#: ../../addon/wiki/Mod_Wiki.php:903 ../../addon/wiki/Widget/Wiki_pages.php:68
msgid "Text"
msgstr "Tekst"
-#: ../../include/text.php:2221
+#: ../../include/text.php:2240
msgid "Comanche Layout"
-msgstr ""
+msgstr "Comanche Utlegg"
-#: ../../include/text.php:2226
+#: ../../include/text.php:2245
msgid "PHP"
msgstr ""
-#: ../../include/text.php:2238
+#: ../../include/text.php:2257
msgid "Page content type"
msgstr "Sidens innholdstype"
-#: ../../include/text.php:2371
+#: ../../include/text.php:2383
+msgid "conversation"
+msgstr "samtale"
+
+#: ../../include/text.php:2390
msgid "activity"
msgstr "aktivitet"
-#: ../../include/text.php:2374
+#: ../../include/text.php:2394
msgid "poll"
msgstr "spørreskjema"
-#: ../../include/text.php:2487
+#: ../../include/text.php:2508
msgid "a-z, 0-9, -, and _ only"
-msgstr ""
+msgstr "kun a-z, 0-9, - og _"
-#: ../../include/text.php:2795
+#: ../../include/text.php:2818
msgid "Design Tools"
msgstr "Designverktøy"
-#: ../../include/text.php:2798 ../../Zotlabs/Module/Blocks.php:152
+#: ../../include/text.php:2821 ../../Zotlabs/Module/Blocks.php:152
msgid "Blocks"
msgstr "Byggeklosser"
-#: ../../include/text.php:2799 ../../Zotlabs/Module/Menu.php:171
+#: ../../include/text.php:2822 ../../Zotlabs/Module/Menu.php:171
msgid "Menus"
msgstr "Menyer"
-#: ../../include/text.php:2800 ../../Zotlabs/Module/Layouts.php:182
+#: ../../include/text.php:2823 ../../Zotlabs/Module/Layouts.php:182
msgid "Layouts"
msgstr "Layout"
-#: ../../include/text.php:2801
+#: ../../include/text.php:2824
msgid "Pages"
msgstr "Sider"
-#: ../../include/text.php:2813
+#: ../../include/text.php:2836
msgid "Import"
msgstr "Importer"
-#: ../../include/text.php:2814
+#: ../../include/text.php:2837
msgid "Import website..."
-msgstr ""
+msgstr "Importer vevside"
-#: ../../include/text.php:2815
+#: ../../include/text.php:2838
msgid "Select folder to import"
-msgstr ""
+msgstr "Velg mappe som skal importeres"
-#: ../../include/text.php:2816
+#: ../../include/text.php:2839
msgid "Import from a zipped folder:"
-msgstr ""
+msgstr "Importer fra en zippet mappe:"
-#: ../../include/text.php:2817
+#: ../../include/text.php:2840
msgid "Import from cloud files:"
-msgstr ""
+msgstr "Importer fra filer i skyen:"
-#: ../../include/text.php:2818
+#: ../../include/text.php:2841
msgid "/cloud/channel/path/to/folder"
-msgstr ""
+msgstr "/cloud/channel/bane/til/mappe"
-#: ../../include/text.php:2819
+#: ../../include/text.php:2842
msgid "Enter path to website files"
-msgstr ""
+msgstr "Oppgi bane til vesidens filer"
-#: ../../include/text.php:2820
+#: ../../include/text.php:2843
msgid "Select folder"
-msgstr ""
+msgstr "Velg mappe"
-#: ../../include/text.php:2821
+#: ../../include/text.php:2844
msgid "Export website..."
-msgstr ""
+msgstr "Eksporter vevside…"
-#: ../../include/text.php:2822
+#: ../../include/text.php:2845
msgid "Export to a zip file"
-msgstr ""
+msgstr "Eksporter til zip-fil"
-#: ../../include/text.php:2823
+#: ../../include/text.php:2846
msgid "website.zip"
msgstr ""
-#: ../../include/text.php:2824
+#: ../../include/text.php:2847
msgid "Enter a name for the zip file."
-msgstr ""
+msgstr "Oppgi et navn til zip-filen."
-#: ../../include/text.php:2825
+#: ../../include/text.php:2848
msgid "Export to cloud files"
-msgstr ""
+msgstr "Eksporter til filen i skyen"
-#: ../../include/text.php:2826
+#: ../../include/text.php:2849
msgid "/path/to/export/folder"
-msgstr ""
+msgstr "/bane/til/eksport/mappe"
-#: ../../include/text.php:2827
+#: ../../include/text.php:2850
msgid "Enter a path to a cloud files destination."
-msgstr ""
+msgstr "Oppgi bane i skyen hvor filene skal lagres"
-#: ../../include/text.php:2828
+#: ../../include/text.php:2851
msgid "Specify folder"
-msgstr ""
-
-#: ../../include/text.php:3520 ../../view/theme/redbasic/php/config.php:18
-#: ../../Zotlabs/Module/Admin/Site.php:232
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:335
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:359
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:435
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:459
-msgid "Default"
-msgstr "Standard"
-
-#: ../../include/oembed.php:155
-msgid "View PDF"
-msgstr ""
-
-#: ../../include/oembed.php:390
-msgid " by "
-msgstr " av "
-
-#: ../../include/oembed.php:391
-#, fuzzy
-msgid " on "
-msgstr "På"
-
-#: ../../include/oembed.php:424
-msgid "Embedded content"
-msgstr "Innebygget innhold"
-
-#: ../../include/oembed.php:433
-msgid "Embedding disabled"
-msgstr "Innbygging avskrudd"
-
-#: ../../include/js_strings.php:5
-msgid "Delete this item?"
-msgstr "Slett dette elementet?"
-
-#: ../../include/js_strings.php:6 ../../Zotlabs/Module/Moderate.php:78
-msgid "Item deleted"
-msgstr ""
-
-#: ../../include/js_strings.php:7 ../../Zotlabs/Lib/ThreadItem.php:867
-#: ../../Zotlabs/Module/Photos.php:1094 ../../Zotlabs/Module/Photos.php:1207
-msgid "Comment"
-msgstr "Kommentar"
-
-#: ../../include/js_strings.php:8 ../../Zotlabs/Lib/ThreadItem.php:576
-#, fuzzy
-#| msgid "View all"
-msgid "show all"
-msgstr "Vis alle"
-
-#: ../../include/js_strings.php:9
-#, fuzzy
-#| msgid "Show Oldest First"
-msgid "show less"
-msgstr "Vis eldste først"
-
-#: ../../include/js_strings.php:10
-msgid "expand"
-msgstr ""
-
-#: ../../include/js_strings.php:11
-msgid "collapse"
-msgstr ""
-
-#: ../../include/js_strings.php:12
-msgid "Password too short"
-msgstr "Passordet er for kort"
-
-#: ../../include/js_strings.php:13 ../../Zotlabs/Module/Register.php:162
-msgid "Passwords do not match"
-msgstr "Passordene er ikke like"
-
-#: ../../include/js_strings.php:14
-msgid "everybody"
-msgstr "alle"
-
-#: ../../include/js_strings.php:15
-msgid "Secret Passphrase"
-msgstr "Hemmelig passordsetning"
-
-#: ../../include/js_strings.php:16
-msgid "Passphrase hint"
-msgstr "Hint om passordsetning"
-
-#: ../../include/js_strings.php:17
-msgid "Notice: Permissions have changed but have not yet been submitted."
-msgstr ""
-"Varsel: Tillatelser har blitt endret, men de har ennå ikke blitt sendt inn."
-
-#: ../../include/js_strings.php:18
-msgid "close all"
-msgstr "lukk alle"
-
-#: ../../include/js_strings.php:19
-msgid "Nothing new here"
-msgstr "Ikke noe nytt her"
-
-#: ../../include/js_strings.php:20
-msgid "Rate This Channel (this is public)"
-msgstr "Vurder denne kanalen (dette er offentlig)"
-
-#: ../../include/js_strings.php:21
-msgid "Rating"
-msgstr "Vurdering"
-
-#: ../../include/js_strings.php:22
-msgid "Describe (optional)"
-msgstr "Beskriv (valgfritt)"
-
-#: ../../include/js_strings.php:23 ../../view/theme/redbasic/php/config.php:188
-#: ../../Zotlabs/Lib/ThreadItem.php:868 ../../Zotlabs/Storage/Browser.php:386
-#: ../../Zotlabs/Module/Import_items.php:125 ../../Zotlabs/Module/Setup.php:319
-#: ../../Zotlabs/Module/Setup.php:359 ../../Zotlabs/Module/Regate.php:408
-#: ../../Zotlabs/Module/Photos.php:1056 ../../Zotlabs/Module/Photos.php:1095
-#: ../../Zotlabs/Module/Photos.php:1208 ../../Zotlabs/Module/Oauth.php:109
-#: ../../Zotlabs/Module/Editpost.php:88 ../../Zotlabs/Module/Profiles.php:738
-#: ../../Zotlabs/Module/Invite.php:564 ../../Zotlabs/Module/Sources.php:123
-#: ../../Zotlabs/Module/Sources.php:160 ../../Zotlabs/Module/Group.php:151
-#: ../../Zotlabs/Module/Group.php:160 ../../Zotlabs/Module/Oauth2.php:114
-#: ../../Zotlabs/Module/Chat.php:208 ../../Zotlabs/Module/Chat.php:247
-#: ../../Zotlabs/Module/Appman.php:230 ../../Zotlabs/Module/Connedit.php:714
-#: ../../Zotlabs/Module/Import.php:622 ../../Zotlabs/Module/Pconfig.php:117
-#: ../../Zotlabs/Module/Xchan.php:15 ../../Zotlabs/Module/Thing.php:357
-#: ../../Zotlabs/Module/Thing.php:409 ../../Zotlabs/Module/Defperms.php:262
-#: ../../Zotlabs/Module/Locs.php:125 ../../Zotlabs/Module/Connect.php:107
-#: ../../Zotlabs/Module/Affinity.php:84
-#: ../../Zotlabs/Module/Filestorage.php:208
-#: ../../Zotlabs/Module/Pdledit.php:137 ../../Zotlabs/Module/Admin/Site.php:402
-#: ../../Zotlabs/Module/Admin/Accounts.php:309
-#: ../../Zotlabs/Module/Admin/Account_edit.php:73
-#: ../../Zotlabs/Module/Admin/Security.php:130
-#: ../../Zotlabs/Module/Admin/Channels.php:169
-#: ../../Zotlabs/Module/Admin/Features.php:66
-#: ../../Zotlabs/Module/Admin/Addons.php:446
-#: ../../Zotlabs/Module/Admin/Profs.php:179
-#: ../../Zotlabs/Module/Admin/Themes.php:174
-#: ../../Zotlabs/Module/Admin/Logs.php:85 ../../Zotlabs/Module/Mitem.php:259
-#: ../../Zotlabs/Module/Email_validation.php:41
-#: ../../Zotlabs/Module/Tokens.php:294 ../../Zotlabs/Module/Permcats.php:257
-#: ../../Zotlabs/Module/Settings/Channel.php:230
-#: ../../Zotlabs/Module/Settings/Editor.php:42
-#: ../../Zotlabs/Module/Settings/Photos.php:42
-#: ../../Zotlabs/Module/Settings/Profiles.php:52
-#: ../../Zotlabs/Module/Settings/Network.php:62
-#: ../../Zotlabs/Module/Settings/Directory.php:42
-#: ../../Zotlabs/Module/Settings/Channel_home.php:91
-#: ../../Zotlabs/Module/Settings/Calendar.php:42
-#: ../../Zotlabs/Module/Settings/Conversation.php:44
-#: ../../Zotlabs/Module/Settings/Features.php:48
-#: ../../Zotlabs/Module/Settings/Connections.php:42
-#: ../../Zotlabs/Module/Settings/Multifactor.php:85
-#: ../../Zotlabs/Module/Settings/Events.php:42
-#: ../../Zotlabs/Module/Settings/Account.php:109
-#: ../../Zotlabs/Module/Settings/Privacy.php:123
-#: ../../Zotlabs/Module/Settings/Display.php:188
-#: ../../Zotlabs/Module/Settings/Manage.php:43
-#: ../../Zotlabs/Module/Contactedit.php:415
-#: ../../Zotlabs/Module/Contactedit.php:448
-#: ../../extend/addon/hzaddons/cart/cart.php:1424
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:312
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:412
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:410
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:248
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:645
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:132
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:142
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:86
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:147
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:341
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:113
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:54
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:191
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:249
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:304
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:602
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:80
-#: ../../extend/addon/hzaddons/pageheader/Mod_Pageheader.php:52
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:51
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:57
-#: ../../extend/addon/hzaddons/piwik/piwik.php:95
-#: ../../extend/addon/hzaddons/startpage/Mod_Startpage.php:71
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:182
-#: ../../extend/addon/hzaddons/fuzzloc/Mod_Fuzzloc.php:54
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:101
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:90
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:136
-#: ../../extend/addon/hzaddons/skeleton/Mod_Skeleton.php:49
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:78
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:140
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:70
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:124
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:100
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:88
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1490
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1549
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1668
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2786
-#: ../../extend/addon/hzaddons/workflow/Settings/Mod_WorkflowSettings.php:94
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:59
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:63
-#: ../../extend/addon/hzaddons/logrot/logrot.php:35
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:70
-#: ../../extend/addon/hzaddons/faces/Mod_Faces.php:141
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:218
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:907
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:72
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:68
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:96
-#: ../../extend/addon/hzaddons/irc/irc.php:45
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:61
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:269
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:72
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:107
-msgid "Submit"
-msgstr "Lagre"
-
-#: ../../include/js_strings.php:24
-msgid "Please enter a link URL"
-msgstr "Vennligst skriv inn en lenke URL"
-
-#: ../../include/js_strings.php:25
-msgid "Unsaved changes. Are you sure you wish to leave this page?"
-msgstr ""
-"Endringene er ikke lagret. Er du sikker på at du ønsker å forlate denne "
-"siden?"
-
-#: ../../include/js_strings.php:26 ../../Zotlabs/Module/Profiles.php:476
-#: ../../Zotlabs/Module/Profiles.php:749 ../../Zotlabs/Module/Pubsites.php:55
-#: ../../Zotlabs/Module/Locs.php:121 ../../Zotlabs/Module/Cdav.php:1006
-msgid "Location"
-msgstr "Plassering"
-
-#: ../../include/js_strings.php:27
-msgid "lovely"
-msgstr ""
-
-#: ../../include/js_strings.php:28
-msgid "wonderful"
-msgstr ""
-
-#: ../../include/js_strings.php:29
-msgid "fantastic"
-msgstr ""
-
-#: ../../include/js_strings.php:30
-msgid "great"
-msgstr ""
-
-#: ../../include/js_strings.php:31
-msgid ""
-"Your chosen nickname was either already taken or not valid. Please use our "
-"suggestion ("
-msgstr ""
-
-#: ../../include/js_strings.php:32
-msgid ") or enter a new one."
-msgstr ""
-
-#: ../../include/js_strings.php:33
-msgid "Thank you, this nickname is valid."
-msgstr ""
-
-#: ../../include/js_strings.php:34
-msgid "A channel name is required."
-msgstr ""
-
-#: ../../include/js_strings.php:35
-msgid "This is a "
-msgstr ""
-
-#: ../../include/js_strings.php:36
-#, fuzzy
-msgid " channel name"
-msgstr "Kanalnavn"
-
-#: ../../include/js_strings.php:37
-msgid "Back to reply"
-msgstr ""
-
-#: ../../include/js_strings.php:38
-msgid "Pinned"
-msgstr ""
-
-#: ../../include/js_strings.php:39 ../../Zotlabs/Lib/ThreadItem.php:498
-msgid "Pin to the top"
-msgstr ""
-
-#: ../../include/js_strings.php:40 ../../Zotlabs/Lib/ThreadItem.php:498
-#: ../../Zotlabs/Widget/Pinned.php:151
-msgid "Unpin from the top"
-msgstr ""
-
-#: ../../include/js_strings.php:46
-#, php-format
-msgid "%d minutes"
-msgid_plural "%d minutes"
-msgstr[0] "%d minutter"
-msgstr[1] "%d minutter"
-
-#: ../../include/js_strings.php:47
-#, php-format
-msgid "about %d hours"
-msgid_plural "about %d hours"
-msgstr[0] "omtrent %d timer"
-msgstr[1] "omtrent %d timer"
-
-#: ../../include/js_strings.php:48
-#, php-format
-msgid "%d days"
-msgid_plural "%d days"
-msgstr[0] "%d dager"
-msgstr[1] "%d dager"
-
-#: ../../include/js_strings.php:49
-#, php-format
-msgid "%d months"
-msgid_plural "%d months"
-msgstr[0] "%d måneder"
-msgstr[1] "%d måneder"
-
-#: ../../include/js_strings.php:50
-#, php-format
-msgid "%d years"
-msgid_plural "%d years"
-msgstr[0] "%d år"
-msgstr[1] "%d år"
-
-#: ../../include/js_strings.php:55
-msgid "timeago.prefixAgo"
-msgstr "timeago.prefixAgo"
-
-#: ../../include/js_strings.php:56
-msgid "timeago.prefixFromNow"
-msgstr "timeago.prefixFromNow"
-
-#: ../../include/js_strings.php:57
-msgid "timeago.suffixAgo"
-msgstr "siden"
-
-#: ../../include/js_strings.php:58
-msgid "timeago.suffixFromNow"
-msgstr ""
-
-#: ../../include/js_strings.php:61
-msgid "less than a minute"
-msgstr "mindre enn ett minutt"
-
-#: ../../include/js_strings.php:62
-msgid "about a minute"
-msgstr "omtrent et minutt"
-
-#: ../../include/js_strings.php:64
-msgid "about an hour"
-msgstr "omtrent en time"
-
-#: ../../include/js_strings.php:66
-msgid "a day"
-msgstr "en dag"
-
-#: ../../include/js_strings.php:68
-msgid "about a month"
-msgstr "omtrent en måned"
-
-#: ../../include/js_strings.php:70
-msgid "about a year"
-msgstr "omtrent et år"
-
-#: ../../include/js_strings.php:72
-msgid " "
-msgstr " "
-
-#: ../../include/js_strings.php:73
-msgid "timeago.numbers"
-msgstr "timeago.numbers"
-
-#: ../../include/js_strings.php:79
-msgctxt "long"
-msgid "May"
-msgstr "mai"
-
-#: ../../include/js_strings.php:87
-msgid "Jan"
-msgstr "Jan"
-
-#: ../../include/js_strings.php:88
-msgid "Feb"
-msgstr "Feb"
-
-#: ../../include/js_strings.php:89
-msgid "Mar"
-msgstr "Mar"
-
-#: ../../include/js_strings.php:90
-msgid "Apr"
-msgstr "Apr"
-
-#: ../../include/js_strings.php:91
-msgctxt "short"
-msgid "May"
-msgstr "mai"
-
-#: ../../include/js_strings.php:92
-msgid "Jun"
-msgstr "Jun"
-
-#: ../../include/js_strings.php:93
-msgid "Jul"
-msgstr "Jul"
-
-#: ../../include/js_strings.php:94
-msgid "Aug"
-msgstr "Aug"
-
-#: ../../include/js_strings.php:95
-msgid "Sep"
-msgstr "Sep"
-
-#: ../../include/js_strings.php:96
-msgid "Oct"
-msgstr "Okt"
-
-#: ../../include/js_strings.php:97
-msgid "Nov"
-msgstr "Nov"
-
-#: ../../include/js_strings.php:98
-msgid "Dec"
-msgstr "Des"
-
-#: ../../include/js_strings.php:106
-msgid "Sun"
-msgstr "Søn"
-
-#: ../../include/js_strings.php:107
-msgid "Mon"
-msgstr "Man"
-
-#: ../../include/js_strings.php:108
-msgid "Tue"
-msgstr "Tirs"
-
-#: ../../include/js_strings.php:109
-msgid "Wed"
-msgstr "Ons"
-
-#: ../../include/js_strings.php:110
-msgid "Thu"
-msgstr "Tors"
-
-#: ../../include/js_strings.php:111
-msgid "Fri"
-msgstr "Fre"
-
-#: ../../include/js_strings.php:112
-msgid "Sat"
-msgstr "Lør"
-
-#: ../../include/js_strings.php:113
-msgctxt "calendar"
-msgid "today"
-msgstr "idag"
-
-#: ../../include/js_strings.php:114
-msgctxt "calendar"
-msgid "month"
-msgstr "måned"
-
-#: ../../include/js_strings.php:115
-msgctxt "calendar"
-msgid "week"
-msgstr "uke"
-
-#: ../../include/js_strings.php:116
-msgctxt "calendar"
-msgid "day"
-msgstr "dag"
-
-#: ../../include/js_strings.php:117
-msgctxt "calendar"
-msgid "All day"
-msgstr "Hele dagen"
-
-#: ../../include/js_strings.php:120
-msgid "Please stand by while your download is being prepared."
-msgstr ""
-
-#: ../../include/js_strings.php:123
-msgid "Email address not valid"
-msgstr ""
+msgstr "Velg mappe"
#: ../../include/channel.php:50
msgid "Unable to obtain identity information from database"
@@ -3105,20 +3508,20 @@ msgstr "Mangler navn"
msgid "Name too long"
msgstr "Navnet er for langt"
-#: ../../include/channel.php:203
+#: ../../include/channel.php:205
msgid "No account identifier"
msgstr "Ingen kontoidentifikator"
-#: ../../include/channel.php:215 ../../Zotlabs/Module/Register.php:96
+#: ../../include/channel.php:217 ../../Zotlabs/Module/Register.php:96
msgid "Nickname is required."
msgstr "Kallenavn er påkrevd."
-#: ../../include/channel.php:229 ../../include/channel.php:668
+#: ../../include/channel.php:231 ../../include/channel.php:670
#: ../../Zotlabs/Module/Register.php:101 ../../Zotlabs/Module/Changeaddr.php:46
msgid "Reserved nickname. Please choose another."
msgstr "Reservert kallenavn. Vennligst velg et annet."
-#: ../../include/channel.php:234 ../../include/channel.php:673
+#: ../../include/channel.php:236 ../../include/channel.php:675
#: ../../Zotlabs/Module/Register.php:106 ../../Zotlabs/Module/Changeaddr.php:51
msgid ""
"Nickname has unsupported characters or is already being used on this site."
@@ -3126,1447 +3529,3186 @@ msgstr ""
"Kallenavnet inneholder tegn som ikke er støttet eller det er allerede i bruk "
"på dette nettstedet."
-#: ../../include/channel.php:300
+#: ../../include/channel.php:302
msgid "Unable to retrieve created identity"
msgstr "Klarer ikke å hente den lagede identiteten"
-#: ../../include/channel.php:411
+#: ../../include/channel.php:413
msgid "Default Profile"
msgstr "Standardprofil"
-#: ../../include/channel.php:465 ../../include/channel.php:468
-#: ../../include/selectors.php:138 ../../Zotlabs/Module/Connedit.php:581
-#: ../../Zotlabs/Module/Contactedit.php:283
-#: ../../Zotlabs/Widget/Affinity.php:38
-msgid "Friends"
-msgstr "Venner"
-
-#: ../../include/channel.php:601 ../../include/channel.php:690
+#: ../../include/channel.php:603 ../../include/channel.php:692
msgid "Unable to retrieve modified identity"
-msgstr ""
+msgstr "Ikke i stand til å hente endret identitet"
-#: ../../include/channel.php:1382
+#: ../../include/channel.php:1384
msgid "Requested channel is not available"
-msgstr ""
+msgstr "Kanalen du prøver å nå er ikke tilgjengelig"
-#: ../../include/channel.php:1436 ../../Zotlabs/Module/Menu.php:92
-#: ../../Zotlabs/Module/Editblock.php:31 ../../Zotlabs/Module/Editlayout.php:31
-#: ../../Zotlabs/Module/Layouts.php:31 ../../Zotlabs/Module/Achievements.php:15
-#: ../../Zotlabs/Module/Editwebpage.php:32 ../../Zotlabs/Module/Hcard.php:12
-#: ../../Zotlabs/Module/Blocks.php:33 ../../Zotlabs/Module/Webpages.php:39
+#: ../../include/channel.php:1438 ../../addon/cards/Mod_Cards.php:42
+#: ../../addon/articles/Mod_Articles.php:46
+#: ../../addon/flashcards/Mod_Flashcards.php:74
+#: ../../addon/gallery/Mod_Gallery.php:49
+#: ../../Zotlabs/Module/Editwebpage.php:32 ../../Zotlabs/Module/Webpages.php:39
#: ../../Zotlabs/Module/Connect.php:17 ../../Zotlabs/Module/Filestorage.php:59
-#: ../../Zotlabs/Module/Profile.php:27
-#: ../../extend/addon/hzaddons/articles/Mod_Articles.php:46
-#: ../../extend/addon/hzaddons/gallery/Mod_Gallery.php:49
-#: ../../extend/addon/hzaddons/cards/Mod_Cards.php:42
+#: ../../Zotlabs/Module/Achievements.php:15 ../../Zotlabs/Module/Layouts.php:31
+#: ../../Zotlabs/Module/Menu.php:92 ../../Zotlabs/Module/Blocks.php:33
+#: ../../Zotlabs/Module/Hcard.php:12 ../../Zotlabs/Module/Profile.php:27
+#: ../../Zotlabs/Module/Editlayout.php:31 ../../Zotlabs/Module/Editblock.php:31
msgid "Requested profile is not available."
msgstr "Forespurt profil er ikke tilgjengelig."
-#: ../../include/channel.php:1532 ../../Zotlabs/Module/Profiles.php:743
+#: ../../include/channel.php:1534 ../../Zotlabs/Module/Profiles.php:743
msgid "Change profile photo"
msgstr "Endre profilbilde"
-#: ../../include/channel.php:1539 ../../include/channel.php:1543
-#: ../../include/menu.php:120 ../../Zotlabs/Lib/Apps.php:617
-#: ../../Zotlabs/Lib/ThreadItem.php:162 ../../Zotlabs/Module/Menu.php:176
-#: ../../Zotlabs/Module/Editblock.php:114
-#: ../../Zotlabs/Module/Editlayout.php:114 ../../Zotlabs/Module/Oauth.php:171
-#: ../../Zotlabs/Module/Group.php:246 ../../Zotlabs/Module/Layouts.php:191
-#: ../../Zotlabs/Module/Oauth2.php:192 ../../Zotlabs/Module/Editwebpage.php:142
-#: ../../Zotlabs/Module/Thing.php:294 ../../Zotlabs/Module/Blocks.php:158
-#: ../../Zotlabs/Module/Webpages.php:250
-#: ../../Zotlabs/Module/Connections.php:338
-#: ../../Zotlabs/Module/Connections.php:387
-#: ../../Zotlabs/Module/Connections.php:408
-#: ../../Zotlabs/Module/Admin/Profs.php:176 ../../Zotlabs/Widget/Cdav.php:144
-#: ../../Zotlabs/Widget/Cdav.php:181
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:97
-#: ../../extend/addon/hzaddons/articles/articles.php:75
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:214
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:381
-#: ../../extend/addon/hzaddons/cards/cards.php:74
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:99
-msgid "Edit"
-msgstr "Endre"
-
-#: ../../include/channel.php:1540
+#: ../../include/channel.php:1542
msgid "Create New Profile"
msgstr "Lag ny profil"
-#: ../../include/channel.php:1558 ../../Zotlabs/Module/Profiles.php:841
+#: ../../include/channel.php:1560 ../../Zotlabs/Module/Profiles.php:841
msgid "Profile Image"
msgstr "Profilbilde"
-#: ../../include/channel.php:1561
+#: ../../include/channel.php:1563
msgid "Visible to everybody"
-msgstr ""
+msgstr "Synlig for alle"
-#: ../../include/channel.php:1562 ../../Zotlabs/Module/Profiles.php:740
+#: ../../include/channel.php:1564 ../../Zotlabs/Module/Profiles.php:740
#: ../../Zotlabs/Module/Profiles.php:845
msgid "Edit visibility"
msgstr "Endre synlighet"
-#: ../../include/channel.php:1644 ../../include/channel.php:1772
+#: ../../include/channel.php:1646 ../../include/channel.php:1774
msgid "Gender:"
msgstr "Kjønn:"
-#: ../../include/channel.php:1645 ../../include/channel.php:1818
+#: ../../include/channel.php:1647 ../../include/channel.php:1820
msgid "Status:"
msgstr "Status:"
-#: ../../include/channel.php:1646 ../../include/channel.php:1842
+#: ../../include/channel.php:1648 ../../include/channel.php:1844
msgid "Homepage:"
msgstr "Hjemmeside:"
-#: ../../include/channel.php:1647 ../../include/channel.php:1844
+#: ../../include/channel.php:1649 ../../include/channel.php:1846
#: ../../Zotlabs/Module/Directory.php:368
msgid "Hometown:"
msgstr "Hjemby:"
-#: ../../include/channel.php:1648
+#: ../../include/channel.php:1650
msgid "Online Now"
msgstr "Online nå"
-#: ../../include/channel.php:1697
+#: ../../include/channel.php:1699
msgid "This channel has not added a profile description yet"
msgstr "Denne kanalen har ikke lagt til en profilbeskrivelse enda"
-#: ../../include/channel.php:1699
+#: ../../include/channel.php:1701
msgid "Change your profile photo"
-msgstr ""
-
-#: ../../include/channel.php:1727 ../../include/selectors.php:64
-#: ../../include/selectors.php:81
-#: ../../extend/addon/hzaddons/openid/Mod_Id.php:87
-msgid "Female"
-msgstr "Kvinnelig"
+msgstr "Endre profilbildet ditt"
-#: ../../include/channel.php:1729 ../../include/selectors.php:64
-#: ../../include/selectors.php:81
-#: ../../extend/addon/hzaddons/openid/Mod_Id.php:85
-msgid "Male"
-msgstr "Mannlig"
-
-#: ../../include/channel.php:1731
+#: ../../include/channel.php:1733
msgid "Trans"
msgstr ""
-#: ../../include/channel.php:1733 ../../include/selectors.php:64
-msgid "Neuter"
-msgstr "Intetkjønn"
-
-#: ../../include/channel.php:1735 ../../include/selectors.php:64
-msgid "Non-specific"
-msgstr "Ubestemt"
-
-#: ../../include/channel.php:1770
+#: ../../include/channel.php:1772
msgid "Full Name:"
msgstr "Fullt navn:"
-#: ../../include/channel.php:1803
+#: ../../include/channel.php:1805
msgid "j F, Y"
msgstr "j F, Y"
-#: ../../include/channel.php:1804
+#: ../../include/channel.php:1806
msgid "j F"
msgstr "j F"
-#: ../../include/channel.php:1811
+#: ../../include/channel.php:1813
msgid "Birthday:"
msgstr "Fødselsdag:"
-#: ../../include/channel.php:1815 ../../Zotlabs/Module/Directory.php:349
+#: ../../include/channel.php:1817 ../../Zotlabs/Module/Directory.php:349
msgid "Age:"
msgstr "Alder:"
-#: ../../include/channel.php:1824
+#: ../../include/channel.php:1826
#, php-format
msgid "for %1$d %2$s"
msgstr "for %1$d %2$s"
-#: ../../include/channel.php:1836
+#: ../../include/channel.php:1838
msgid "Tags:"
msgstr "Merkelapper:"
-#: ../../include/channel.php:1840
+#: ../../include/channel.php:1842
msgid "Sexual Preference:"
msgstr "Seksuell preferanse:"
-#: ../../include/channel.php:1846
+#: ../../include/channel.php:1848
msgid "Political Views:"
msgstr "Politiske synspunkter:"
-#: ../../include/channel.php:1848
+#: ../../include/channel.php:1850
msgid "Religion:"
msgstr "Religion:"
-#: ../../include/channel.php:1850 ../../Zotlabs/Module/Directory.php:370
+#: ../../include/channel.php:1852 ../../Zotlabs/Module/Directory.php:370
msgid "About:"
msgstr "Om:"
-#: ../../include/channel.php:1852
+#: ../../include/channel.php:1854
msgid "Hobbies/Interests:"
msgstr "Hobbyer/interesser:"
-#: ../../include/channel.php:1854
+#: ../../include/channel.php:1856
msgid "Likes:"
msgstr "Liker:"
-#: ../../include/channel.php:1856
+#: ../../include/channel.php:1858
msgid "Dislikes:"
msgstr "Misliker:"
-#: ../../include/channel.php:1858
+#: ../../include/channel.php:1860
msgid "Contact information and Social Networks:"
msgstr "Kontaktinformasjon og sosiale nettverk:"
-#: ../../include/channel.php:1860
+#: ../../include/channel.php:1862
msgid "My other channels:"
msgstr "Mine andre kanaler:"
-#: ../../include/channel.php:1862
+#: ../../include/channel.php:1864
msgid "Musical interests:"
msgstr "Musikkinteresse:"
-#: ../../include/channel.php:1864
+#: ../../include/channel.php:1866
msgid "Books, literature:"
msgstr "Bøker, litteratur:"
-#: ../../include/channel.php:1866
+#: ../../include/channel.php:1868
msgid "Television:"
msgstr "TV:"
-#: ../../include/channel.php:1868
+#: ../../include/channel.php:1870
msgid "Film/dance/culture/entertainment:"
msgstr "Film/dans/kultur/underholdning:"
-#: ../../include/channel.php:1870
+#: ../../include/channel.php:1872
msgid "Love/Romance:"
msgstr "Kjærlighet/romantikk:"
-#: ../../include/channel.php:1872
+#: ../../include/channel.php:1874
msgid "Work/employment:"
msgstr "Arbeid/sysselsetting:"
-#: ../../include/channel.php:1874
+#: ../../include/channel.php:1876
msgid "School/education:"
msgstr "Skole/utdannelse:"
-#: ../../include/channel.php:1895 ../../Zotlabs/Lib/Apps.php:366
-#: ../../Zotlabs/Module/Profperm.php:114
+#: ../../include/channel.php:1897 ../../Zotlabs/Lib/Apps.php:366
+#: ../../Zotlabs/Module/Profperm.php:116
msgid "Profile"
msgstr "Profil"
-#: ../../include/channel.php:1897
+#: ../../include/channel.php:1899
msgid "Like this thing"
msgstr "Lik denne tingen"
-#: ../../include/channel.php:1898
+#: ../../include/channel.php:1900
msgid "Export"
msgstr "Eksport"
-#: ../../include/channel.php:2351
+#: ../../include/channel.php:2353
msgid "cover photo"
msgstr "forsidebilde"
-#: ../../include/channel.php:2632 ../../boot.php:1761
+#: ../../include/channel.php:2628 ../../boot.php:1762
#: ../../Zotlabs/Module/Rmagic.php:96
msgid "Remote Authentication"
msgstr "Fjernautentisering"
-#: ../../include/channel.php:2633 ../../Zotlabs/Module/Rmagic.php:97
+#: ../../include/channel.php:2629 ../../Zotlabs/Module/Rmagic.php:97
msgid "Enter your channel address (e.g. channel@example.com)"
msgstr "Skriv din kanaladresse (for eksempel channel@exampel.com)"
-#: ../../include/channel.php:2634 ../../Zotlabs/Module/Rmagic.php:98
+#: ../../include/channel.php:2630 ../../Zotlabs/Module/Rmagic.php:98
msgid "Authenticate"
msgstr "Autentiser"
-#: ../../include/channel.php:2794 ../../Zotlabs/Module/Admin/Accounts.php:184
+#: ../../include/channel.php:2790 ../../Zotlabs/Module/Admin/Accounts.php:84
#, php-format
msgid "Account '%s' deleted"
msgstr "Kontoen '%s' slettet"
-#: ../../include/contact_widgets.php:13
+#: ../../include/bbcode.php:234 ../../include/bbcode.php:994
+#: ../../include/bbcode.php:1676 ../../include/bbcode.php:1684
+msgid "Image/photo"
+msgstr "Bilde/fotografi"
+
+#: ../../include/bbcode.php:286
+msgid "Encrypted content"
+msgstr "Kryptert innhold"
+
+#: ../../include/bbcode.php:342
#, php-format
-msgid "%d invitation available"
-msgid_plural "%d invitations available"
-msgstr[0] "%d invitasjon tilgjengelig"
-msgstr[1] "%d invitasjoner tilgjengelig"
+msgid "Install %1$s element %2$s"
+msgstr "Installer %1$s element %2$s"
-#: ../../include/contact_widgets.php:18 ../../include/acl_selectors.php:145
-#: ../../Zotlabs/Module/Admin/Site.php:406
-msgid "Advanced"
-msgstr "Avansert"
+#: ../../include/bbcode.php:346
+#, php-format
+msgid ""
+"This post contains an installable %s element, however you lack permissions "
+"to install it on this site."
+msgstr ""
+"Dette innlegget inneholder det installerbare elementet %s, men du mangler "
+"tillatelse til å installere det på dette nettstedet."
-#: ../../include/contact_widgets.php:21
-msgid "Find Channels"
-msgstr "Finn kanaler"
+#: ../../include/bbcode.php:356 ../../Zotlabs/Module/Impel.php:47
+msgid "webpage"
+msgstr "nettside"
-#: ../../include/contact_widgets.php:22
-msgid "Enter name or interest"
-msgstr "Skriv navn eller interesse"
+#: ../../include/bbcode.php:359 ../../Zotlabs/Module/Impel.php:57
+msgid "layout"
+msgstr "layout"
-#: ../../include/contact_widgets.php:23
-msgid "Connect/Follow"
-msgstr "Forbindelse/Følg"
+#: ../../include/bbcode.php:362 ../../Zotlabs/Module/Impel.php:52
+msgid "block"
+msgstr "byggekloss"
-#: ../../include/contact_widgets.php:24
-msgid "Examples: Robert Morgenstein, Fishing"
-msgstr "Eksempler: Ola Nordmann, fisking"
+#: ../../include/bbcode.php:365 ../../Zotlabs/Module/Impel.php:64
+msgid "menu"
+msgstr "meny"
-#: ../../include/contact_widgets.php:25 ../../Zotlabs/Module/Directory.php:435
-#: ../../Zotlabs/Module/Directory.php:440
-#: ../../Zotlabs/Module/Connections.php:407
-msgid "Find"
-msgstr "Finn"
+#: ../../include/bbcode.php:568
+msgid "card"
+msgstr "kort"
-#: ../../include/contact_widgets.php:26 ../../Zotlabs/Module/Suggest.php:77
-#: ../../Zotlabs/Module/Directory.php:439
-msgid "Channel Suggestions"
-msgstr "Kanalforslag"
+#: ../../include/bbcode.php:570
+msgid "article"
+msgstr "artikkel"
-#: ../../include/contact_widgets.php:28
-msgid "Random Profile"
-msgstr "Tilfeldig profil"
+#: ../../include/bbcode.php:653 ../../include/bbcode.php:661
+msgid "Click to open/close"
+msgstr "Klikk for å åpne/lukke"
-#: ../../include/contact_widgets.php:29
-msgid "Invite Friends"
-msgstr "Inviter venner"
+#: ../../include/bbcode.php:674
+msgid "View article"
+msgstr "Vis artikkel"
-#: ../../include/contact_widgets.php:31
-msgid "Advanced example: name=fred and country=iceland"
-msgstr "Avansert eksempel: navn=fred og land=island"
+#: ../../include/bbcode.php:674
+msgid "View summary"
+msgstr "Vis sammendrag"
-#: ../../include/contact_widgets.php:58 ../../include/contact_widgets.php:121
-#: ../../include/contact_widgets.php:155 ../../Zotlabs/Widget/Filer.php:36
-#: ../../Zotlabs/Widget/Appcategories.php:52
-#: ../../extend/addon/hzaddons/articles/Widget/Articles_categories.php:83
-#: ../../extend/addon/hzaddons/cards/Widget/Cards_categories.php:83
-msgid "Everything"
-msgstr "Alt"
+#: ../../include/bbcode.php:1127 ../../include/bbcode.php:1314
+#: ../../addon/wiki/Lib/NativeWikiPage.php:634
+msgid "Different viewers will see this text differently"
+msgstr "Denne teksten vil se forskjellig ut for ulike besøkende"
-#: ../../include/contact_widgets.php:118 ../../include/contact_widgets.php:152
-#: ../../include/taxonomy.php:425 ../../include/taxonomy.php:507
-#: ../../include/taxonomy.php:527 ../../include/taxonomy.php:548
-#: ../../Zotlabs/Storage/Browser.php:293 ../../Zotlabs/Storage/Browser.php:392
-#: ../../Zotlabs/Storage/Browser.php:407 ../../Zotlabs/Module/Cdav.php:1062
-#: ../../extend/addon/hzaddons/articles/Widget/Articles_categories.php:80
-#: ../../extend/addon/hzaddons/cards/Widget/Cards_categories.php:80
-msgid "Categories"
-msgstr "Kategorier"
+#: ../../include/bbcode.php:1652
+msgid "$1 wrote:"
+msgstr "$1 skrev:"
-#: ../../include/contact_widgets.php:185
-msgid "Common Connections"
+#: ../../include/import.php:31
+msgid "Unable to import a removed channel."
+msgstr "Ikke i stand til å importere en kanal som er slettet."
+
+#: ../../include/import.php:57
+msgid ""
+"Cannot create a duplicate channel identifier on this system. Import failed."
msgstr ""
+"Kan ikke lage en kopi av kanal-identifikatoren på dette systemet. Import "
+"mislyktes."
-#: ../../include/contact_widgets.php:189
+#: ../../include/import.php:78 ../../addon/diaspora/import_diaspora.php:56
+msgid "Unable to create a unique channel address. Import failed."
+msgstr "Klarte ikke å lage en unik kanaladresse. Import mislyktes."
+
+#: ../../include/import.php:129
+msgid "Cloned channel not found. Import failed."
+msgstr "Klonet kanal ble ikke funnet. Import mislyktes."
+
+#: ../../addon/irc/Mod_Irc.php:23 ../../addon/irc/irc.php:41
+msgid "Popular Channels"
+msgstr "Populære kanaler"
+
+#: ../../addon/irc/irc.php:37
+msgid "Channels to auto connect"
+msgstr "Kanaler som skal kobles automatisk"
+
+#: ../../addon/irc/irc.php:37 ../../addon/irc/irc.php:41
+msgid "Comma separated list"
+msgstr "Kommaseparert liste"
+
+#: ../../addon/irc/irc.php:45
+msgid "IRC Settings"
+msgstr "Innstillinger for IRC"
+
+#: ../../addon/irc/irc.php:54
+msgid "IRC settings saved."
+msgstr "Innstillinger for IRC lagret."
+
+#: ../../addon/irc/irc.php:58
+msgid "IRC Chatroom"
+msgstr "IRC-kanal"
+
+#: ../../addon/fediquest/fediquest.php:215
+msgid "ERROR: word length is not correct!"
+msgstr "FEIL: lengden på ordet er ikke riktig!"
+
+#: ../../addon/fediquest/Mod_Fediquest.php:22
+msgid "Fediquest App"
+msgstr "Fediquest"
+
+#: ../../addon/fediquest/Mod_Fediquest.php:23
+msgid "A distributed quest for a given word (game)."
+msgstr "En distribuert gjettelek om ord (spill)"
+
+#: ../../addon/fediquest/Mod_Fediquest.php:24
+msgid ""
+"To start a game, enter [fediquest]your_word[/fediquest] somewhere in a "
+"toplevel post."
+msgstr ""
+"For å starte et spill, skriv [fediquest]ditt_ord[/fediquest] et sted i et "
+"nytt innlegg."
+
+#: ../../addon/fediquest/Mod_Fediquest.php:25
+msgid "Your contacts can post their guess in the comments."
+msgstr ""
+"Forbindelsene dine kan poste sine gjetninger i kommentarene til innlegget."
+
+#: ../../addon/fediquest/Mod_Fediquest.php:26
+msgid ""
+"Your channel will evaluate the guess and automatically post the response."
+msgstr ""
+"Kanalen din vil automatisk vurdere svarene og poste resultatet i tråden."
+
+#: ../../addon/fediquest/Mod_Fediquest.php:28
+msgid "Correct letters"
+msgstr "Riktige bokstaver"
+
+#: ../../addon/fediquest/Mod_Fediquest.php:29
+msgid "Letters contained in the word but at the wrong spot"
+msgstr "Bokstaver som er riktige, men på feil plass"
+
+#: ../../addon/fediquest/Mod_Fediquest.php:30
+msgid "Letters not contained in the word"
+msgstr "Bokstaver som ikke finnes i ordet"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:106
+#: ../../addon/socialauth/Mod_SocialAuth.php:179
+msgid "Network error"
+msgstr "Nettverksfeil"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:110
+#: ../../addon/socialauth/Mod_SocialAuth.php:183
+msgid "API error"
+msgstr "API-feil"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:114
+#: ../../addon/socialauth/Mod_SocialAuth.php:187
+msgid "Unknown issue"
+msgstr "Ukjent problem"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:152
+msgid "Unable to retrieve email address from remote identity provider"
+msgstr "Fikk ikke til å hente epostadressen din fra identitetstilbyderen"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:171
+msgid "Unable to login using email address "
+msgstr "Fikk ikke til å logge inn ved bruk av epostadresse"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:211
+msgid "Social Authentication using your social media account"
+msgstr "Sosial Autentisering ved bruk av din sosiale nettverkskonto"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:215
+msgid ""
+"This app enables one or more social provider sign-in buttons on the login "
+"page."
+msgstr ""
+"Denne appen legger til en eller flere login-knapper fra sosiale nettverk på "
+"innloggingssiden."
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:233
+msgid "Add an identity provider"
+msgstr "Legg til en identitetstilbyder"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:260
+msgid "Enable "
+msgstr "Skru på "
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:267
+msgid "Key"
+msgstr "Nøkkel"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:267
+#: ../../addon/socialauth/Mod_SocialAuth.php:272
+#: ../../addon/socialauth/Mod_SocialAuth.php:288
+#: ../../addon/socialauth/Mod_SocialAuth.php:299
+#: ../../addon/socialauth/Mod_SocialAuth.php:308
+msgid "Word"
+msgstr "Ord"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:272
+msgid "Secret"
+msgstr "Hemmelighet"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:308
+msgid "Add a custom provider"
+msgstr "Legg til en egendefinert tilbyder"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:328
+msgid "Remove an identity provider"
+msgstr "Fjern en identitetstilbyder"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:338
+msgid "Social authentication"
+msgstr "Sosial autentisering"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:380
+msgid "Error while saving provider settings"
+msgstr "Det oppsto en feil ved lagring av innstillingene for tilbyderen"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:403
+msgid "Custom provider already exists"
+msgstr "Den egendefinerte tilbyderen finnes allerede"
+
+#: ../../addon/socialauth/Mod_SocialAuth.php:420
+msgid "Social authentication settings saved."
+msgstr "Innstillingene for sosial autentisering ble lagret."
+
+#: ../../addon/upload_limits/upload_limits.php:25
+msgid "Show Upload Limits"
+msgstr "Vis opplastingsgrenser"
+
+#: ../../addon/upload_limits/upload_limits.php:27
+msgid "Hubzilla configured maximum size: "
+msgstr "Maksimum størrelse definert av Hubzilla:"
+
+#: ../../addon/upload_limits/upload_limits.php:28
+msgid "PHP upload_max_filesize: "
+msgstr ""
+
+#: ../../addon/upload_limits/upload_limits.php:29
+msgid "PHP post_max_size (must be larger than upload_max_filesize): "
+msgstr "PHP post_max_size (må være større enn upload_max_filesize):"
+
+#: ../../addon/sendzid/Mod_Sendzid.php:21
+msgid "Send your identity to all websites"
+msgstr "Send din identitet til alle nettsteder"
+
+#: ../../addon/sendzid/Mod_Sendzid.php:29
+msgid "Send ZID"
+msgstr ""
+
+#: ../../addon/hubwall/hubwall.php:19
+msgid "Send email to all members"
+msgstr "Send epost til alle medlemmer"
+
+#: ../../addon/hubwall/hubwall.php:33 ../../Zotlabs/Lib/Enotify.php:66
#, php-format
-msgid "View all %d common connections"
+msgid "%s Administrator"
+msgstr "%s administrator"
+
+#: ../../addon/hubwall/hubwall.php:50 ../../addon/mailtest/mailtest.php:50
+msgid "No recipients found."
+msgstr "Fant ingen mottakere"
+
+#: ../../addon/hubwall/hubwall.php:74
+#, php-format
+msgid "%1$d of %2$d messages sent."
+msgstr "%1$d av %2$d meldinger sendt."
+
+#: ../../addon/hubwall/hubwall.php:82
+msgid "Send email to all hub members."
+msgstr "Send epost til alle medlemmer av hub'en"
+
+#: ../../addon/hubwall/hubwall.php:93 ../../addon/mailtest/mailtest.php:96
+msgid "Message subject"
+msgstr "Emne for meldingen"
+
+#: ../../addon/hubwall/hubwall.php:94
+msgid "Sender Email address"
+msgstr "Avsenderens epostadresse"
+
+#: ../../addon/hubwall/hubwall.php:95
+msgid "Test mode (only send to hub administrator)"
+msgstr "Testmodus (send kun til hubens administrator)"
+
+#: ../../addon/rainbowtag/Mod_Rainbowtag.php:22
+msgid "Rainbow Tag App"
msgstr ""
-#: ../../include/security.php:633
+#: ../../addon/rainbowtag/Mod_Rainbowtag.php:23
+msgid "Add some colour to tag clouds"
+msgstr ""
+
+#: ../../addon/rainbowtag/Mod_Rainbowtag.php:30
+msgid "Rainbow Tag"
+msgstr ""
+
+#: ../../addon/openid/openid.php:49
msgid ""
-"The form security token was not correct. This probably happened because the "
-"form has been opened for too long (>3 hours) before submitting it."
+"We encountered a problem while logging in with the OpenID you provided. "
+"Please check the correct spelling of the ID."
msgstr ""
-"Skjemaets sikkerhetspollett var ikke gyldig. Dette skjedde antakelig fordi "
-"skjemaet har vært åpnet for lenge (>3 timer) før det ble sendt inn."
+"Vi støtte på et problem under innloggingen med din OpenID. Vennligst sjekk "
+"at ID-en er stavet riktig."
-#: ../../include/taxonomy.php:326
-msgid "Trending"
+#: ../../addon/openid/openid.php:49
+msgid "The error message was:"
+msgstr "Feilmeldingen var:"
+
+#: ../../addon/openid/Mod_Openid.php:32
+msgid "OpenID protocol error. No ID returned."
+msgstr "OpenID protokollfeil. Ingen ID ble returnert."
+
+#: ../../addon/openid/Mod_Openid.php:78 ../../addon/openid/Mod_Openid.php:179
+#, php-format
+msgid "Welcome %s. Remote authentication successful."
+msgstr "Velkommen %s. Ekstern autentisering er vellykket."
+
+#: ../../addon/openid/MysqlProvider.php:52
+msgid "First Name"
+msgstr "Fornavn"
+
+#: ../../addon/openid/MysqlProvider.php:53
+msgid "Last Name"
+msgstr "Etternavn"
+
+#: ../../addon/openid/MysqlProvider.php:54 ../../addon/redred/Mod_Redred.php:73
+#: ../../boot.php:1755
+msgid "Nickname"
+msgstr "Kallenavn"
+
+#: ../../addon/openid/MysqlProvider.php:55
+msgid "Full Name"
+msgstr "Fullt navn"
+
+#: ../../addon/openid/MysqlProvider.php:61
+msgid "Profile Photo 16px"
+msgstr "Profilbilde 16px"
+
+#: ../../addon/openid/MysqlProvider.php:62
+msgid "Profile Photo 32px"
+msgstr "Profilbilde 32px"
+
+#: ../../addon/openid/MysqlProvider.php:63
+msgid "Profile Photo 48px"
+msgstr "Profilbilde 48px"
+
+#: ../../addon/openid/MysqlProvider.php:64
+msgid "Profile Photo 64px"
+msgstr "Profilbilde 64px"
+
+#: ../../addon/openid/MysqlProvider.php:65
+msgid "Profile Photo 80px"
+msgstr "Profilbilde 80px"
+
+#: ../../addon/openid/MysqlProvider.php:66
+msgid "Profile Photo 128px"
+msgstr "Profilbilde 128px"
+
+#: ../../addon/openid/MysqlProvider.php:68
+#: ../../Zotlabs/Module/Profiles.php:782
+msgid "Homepage URL"
+msgstr "Hjemmeside URL"
+
+#: ../../addon/openid/MysqlProvider.php:69 ../../Zotlabs/Lib/Apps.php:363
+msgid "Language"
+msgstr "Språk"
+
+#: ../../addon/openid/MysqlProvider.php:70
+msgid "Birth Year"
+msgstr "Fødselsår"
+
+#: ../../addon/openid/MysqlProvider.php:71
+msgid "Birth Month"
+msgstr "Fødselsmåne"
+
+#: ../../addon/openid/MysqlProvider.php:72
+msgid "Birth Day"
+msgstr "Fødselsdag"
+
+#: ../../addon/openid/MysqlProvider.php:73
+msgid "Birthdate"
+msgstr "Fødselsdato"
+
+#: ../../addon/openid/MysqlProvider.php:74
+#: ../../Zotlabs/Module/Profiles.php:453
+msgid "Gender"
+msgstr "Kjønn"
+
+#: ../../addon/openclipatar/openclipatar.php:51
+#: ../../addon/openclipatar/openclipatar.php:129
+msgid "System defaults:"
msgstr ""
-#: ../../include/taxonomy.php:326 ../../include/taxonomy.php:465
-#: ../../include/taxonomy.php:486 ../../Zotlabs/Widget/Tagcloud.php:27
-msgid "Tags"
-msgstr "Merkelapper"
+#: ../../addon/openclipatar/openclipatar.php:55
+msgid "Preferred Clipart IDs"
+msgstr ""
-#: ../../include/taxonomy.php:566
-msgid "Keywords"
-msgstr "Nøkkelord"
+#: ../../addon/openclipatar/openclipatar.php:55
+msgid "List of preferred clipart ids. These will be shown first."
+msgstr ""
-#: ../../include/taxonomy.php:587
-msgid "have"
-msgstr "har"
+#: ../../addon/openclipatar/openclipatar.php:56
+msgid "Default Search Term"
+msgstr "Standard søkeord"
-#: ../../include/taxonomy.php:587
-msgid "has"
-msgstr "har"
+#: ../../addon/openclipatar/openclipatar.php:56
+msgid "The default search term. These will be shown second."
+msgstr "Standard søkeord. Disse vil vises som nummer to."
-#: ../../include/taxonomy.php:588
-msgid "want"
-msgstr "ønsker"
+#: ../../addon/openclipatar/openclipatar.php:57
+msgid "Return After"
+msgstr ""
-#: ../../include/taxonomy.php:588
-msgid "wants"
-msgstr "ønsker"
+#: ../../addon/openclipatar/openclipatar.php:57
+msgid "Page to load after image selection."
+msgstr ""
-#: ../../include/taxonomy.php:589 ../../Zotlabs/Lib/ThreadItem.php:335
-msgid "like"
-msgstr "liker"
+#: ../../addon/openclipatar/openclipatar.php:60
+msgid "Profile List"
+msgstr ""
-#: ../../include/taxonomy.php:589
-msgid "likes"
-msgstr "liker"
+#: ../../addon/openclipatar/openclipatar.php:62
+msgid "Order of Preferred"
+msgstr ""
-#: ../../include/taxonomy.php:590 ../../Zotlabs/Lib/ThreadItem.php:336
-msgid "dislike"
-msgstr "misliker"
+#: ../../addon/openclipatar/openclipatar.php:62
+msgid "Sort order of preferred clipart ids."
+msgstr ""
-#: ../../include/taxonomy.php:590
-msgid "dislikes"
-msgstr "misliker"
+#: ../../addon/openclipatar/openclipatar.php:63
+#: ../../addon/openclipatar/openclipatar.php:69
+msgid "Newest first"
+msgstr ""
-#: ../../include/connections.php:174
-msgid "New window"
-msgstr "Nytt vindu"
+#: ../../addon/openclipatar/openclipatar.php:66
+msgid "As entered"
+msgstr ""
-#: ../../include/connections.php:175
-msgid "Open the selected location in a different window or browser tab"
-msgstr "Åpne det valgte stedet i et annet vindu eller nettleser-fane"
+#: ../../addon/openclipatar/openclipatar.php:68
+msgid "Order of other"
+msgstr ""
-#: ../../include/acl_selectors.php:33
-#: ../../Zotlabs/Lib/PermissionDescription.php:34
-msgid "Visible to your default audience"
-msgstr "Synlig for ditt standard publikum"
+#: ../../addon/openclipatar/openclipatar.php:68
+msgid "Sort order of other clipart ids."
+msgstr ""
-#: ../../include/acl_selectors.php:100
-msgid "Profile-Based Privacy Groups"
+#: ../../addon/openclipatar/openclipatar.php:70
+msgid "Most downloaded first"
msgstr ""
-#: ../../include/acl_selectors.php:119
-msgid "Private Forum"
+#: ../../addon/openclipatar/openclipatar.php:71
+msgid "Most liked first"
msgstr ""
-#: ../../include/acl_selectors.php:125 ../../Zotlabs/Widget/Forums.php:77
-#: ../../Zotlabs/Widget/Activity_filter.php:130
-#: ../../Zotlabs/Widget/Notifications.php:131
-#: ../../Zotlabs/Widget/Notifications.php:132
-msgid "Forums"
-msgstr "Forum"
+#: ../../addon/openclipatar/openclipatar.php:73
+msgid "Preferred IDs Message"
+msgstr ""
-#: ../../include/acl_selectors.php:136
-#: ../../Zotlabs/Lib/PermissionDescription.php:107
-#: ../../Zotlabs/Module/Settings/Privacy.php:66
-msgid "Only me"
-msgstr "Kun meg"
+#: ../../addon/openclipatar/openclipatar.php:73
+msgid "Message to display above preferred results."
+msgstr ""
-#: ../../include/acl_selectors.php:143
-msgid "Share with"
-msgstr "Del med"
+#: ../../addon/openclipatar/openclipatar.php:79
+msgid "Uploaded by: "
+msgstr ""
-#: ../../include/acl_selectors.php:144
-msgid "Custom selection"
-msgstr "Tilpasset utvalg"
+#: ../../addon/openclipatar/openclipatar.php:79
+msgid "Drawn by: "
+msgstr ""
-#: ../../include/acl_selectors.php:146
+#: ../../addon/openclipatar/openclipatar.php:183
+#: ../../addon/openclipatar/openclipatar.php:195
+msgid "Use this image"
+msgstr ""
+
+#: ../../addon/openclipatar/openclipatar.php:193
+msgid "Or select from a free OpenClipart.org image:"
+msgstr ""
+
+#: ../../addon/openclipatar/openclipatar.php:196
+msgid "Search Term"
+msgstr "Søkeord"
+
+#: ../../addon/openclipatar/openclipatar.php:233
+msgid "Unknown error. Please try again later."
+msgstr ""
+
+#: ../../addon/openclipatar/openclipatar.php:299
+#: ../../Zotlabs/Module/Profile_photo.php:269
msgid ""
-"Select \"Allow\" to allow viewing. \"Don't allow\" lets you override and "
-"limit the scope of \"Allow\"."
+"Shift-reload the page or clear browser cache if the new photo does not "
+"display immediately."
+msgstr ""
+"Hold nede Shift-knappen og last siden på nytt eller tøm nettleserens "
+"mellomlager hvis det nye bildet ikke vises umiddelbart."
+
+#: ../../addon/openclipatar/openclipatar.php:309
+msgid "Profile photo updated successfully."
msgstr ""
-#: ../../include/acl_selectors.php:147 ../../Zotlabs/Module/Authorize.php:32
-msgid "Allow"
+#: ../../addon/nsabait/Mod_Nsabait.php:23
+msgid "NSA Bait App"
msgstr ""
-#: ../../include/acl_selectors.php:148
-msgid "Don't allow"
+#: ../../addon/nsabait/Mod_Nsabait.php:25
+msgid "Make yourself a political target."
msgstr ""
-#: ../../include/acl_selectors.php:154 ../../Zotlabs/Module/Photos.php:671
-#: ../../Zotlabs/Module/Photos.php:1045 ../../Zotlabs/Module/Chat.php:240
-#: ../../Zotlabs/Module/Thing.php:350 ../../Zotlabs/Module/Thing.php:402
-#: ../../Zotlabs/Module/Filestorage.php:195
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:261
-msgid "Permissions"
-msgstr "Tillatelser"
+#: ../../addon/visage/Mod_Visage.php:23
+msgid "Recent Channel/Profile Viewers"
+msgstr ""
-#: ../../include/acl_selectors.php:156 ../../Zotlabs/Lib/ThreadItem.php:531
-#: ../../Zotlabs/Storage/Browser.php:415 ../../Zotlabs/Module/Photos.php:1266
-#: ../../Zotlabs/Widget/Pinned.php:154
-msgid "Close"
-msgstr "Lukk"
+#: ../../addon/visage/Mod_Visage.php:34
+msgid "No entries."
+msgstr ""
-#: ../../include/acl_selectors.php:181
+#: ../../addon/hzfiles/hzfiles.php:81
+msgid "Hubzilla File Storage Import"
+msgstr ""
+
+#: ../../addon/hzfiles/hzfiles.php:82
+msgid "This will import all your cloud files from another server."
+msgstr ""
+
+#: ../../addon/hzfiles/hzfiles.php:83
+msgid "Hubzilla Server base URL"
+msgstr ""
+
+#: ../../addon/hzfiles/hzfiles.php:84
+#: ../../addon/content_import/Mod_content_import.php:138
+msgid "Since modified date yyyy-mm-dd"
+msgstr ""
+
+#: ../../addon/hzfiles/hzfiles.php:85
+#: ../../addon/content_import/Mod_content_import.php:139
+msgid "Until modified date yyyy-mm-dd"
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:48
+msgid "Your channel has been upgraded to $Projectname version"
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:50
+msgid "Please have a look at the"
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:52
+msgid "git history"
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:54
+msgid "change log"
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:55
+msgid "for further info."
+msgstr ""
+
+#: ../../addon/upgrade_info/upgrade_info.php:60
+#, fuzzy
+#| msgid "$Projectname"
+msgid "$Projectname Upgrade Info"
+msgstr "$Projectname"
+
+#: ../../addon/upgrade_info/upgrade_info.php:64
+msgid "Do not show this again"
+msgstr ""
+
+#: ../../addon/opensearch/opensearch.php:26
#, php-format
+msgctxt "opensearch"
+msgid "Search %1$s (%2$s)"
+msgstr "Søk %1$s (%2$s)"
+
+#: ../../addon/opensearch/opensearch.php:28
+msgctxt "opensearch"
+msgid "$Projectname"
+msgstr "$Projectname"
+
+#: ../../addon/opensearch/opensearch.php:42 ../../Zotlabs/Lib/Enotify.php:67
+#: ../../Zotlabs/Module/Home.php:92 ../../Zotlabs/Module/Home.php:100
+#: ../../Zotlabs/Module/Invite.php:239 ../../Zotlabs/Module/Invite.php:491
+#: ../../Zotlabs/Module/Invite.php:505
+msgid "$Projectname"
+msgstr "$Projectname"
+
+#: ../../addon/opensearch/opensearch.php:43
+msgid "Search $Projectname"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:94
+msgid "Hubzilla Directory Stats"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:95
+msgid "Total Hubs"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:97
+msgid "Hubzilla Hubs"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:99
+msgid "Friendica Hubs"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:101
+msgid "Diaspora Pods"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:103
+msgid "Hubzilla Channels"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:105
+msgid "Friendica Channels"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:107
+msgid "Diaspora Channels"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:109
+msgid "Aged 35 and above"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:111
+msgid "Aged 34 and under"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:113
+msgid "Average Age"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:115
+msgid "Known Chatrooms"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:117
+msgid "Known Tags"
+msgstr ""
+
+#: ../../addon/dirstats/dirstats.php:119
msgid ""
-"Post permissions %s cannot be changed %s after a post is shared.</br />These "
-"permissions set who is allowed to view the post."
+"Please note Diaspora and Friendica statistics are merely those **this "
+"directory** is aware of, and not all those known in the network. This also "
+"applies to chatrooms,"
msgstr ""
-"Tillatelsene til innlegget %s kan ikke endres %s etter at innlegget er delt."
-"</br />Disse tillatelsene avgjør hvem som kan se innlegget."
-#: ../../include/cdav.php:157
-msgid "INVALID EVENT DISMISSED!"
+#: ../../addon/piwik/piwik.php:85
+msgid ""
+"This website is tracked using the <a href='http://www.piwik.org'>Piwik</a> "
+"analytics tool."
msgstr ""
-#: ../../include/cdav.php:158
-#, fuzzy
-msgid "Summary: "
-msgstr "Sammendrag"
+#: ../../addon/piwik/piwik.php:88
+#, php-format
+msgid ""
+"If you do not want that your visits are logged this way you <a href='%s'>can "
+"set a cookie to prevent Piwik from tracking further visits of the site</a> "
+"(opt-out)."
+msgstr ""
-#: ../../include/cdav.php:159
-#, fuzzy
-msgid "Date: "
-msgstr "Dato"
+#: ../../addon/piwik/piwik.php:96
+msgid "Piwik Base URL"
+msgstr ""
-#: ../../include/cdav.php:160 ../../include/cdav.php:168
-msgid "Reason: "
+#: ../../addon/piwik/piwik.php:96
+msgid ""
+"Absolute path to your Piwik installation. (without protocol (http/s), with "
+"trailing slash)"
msgstr ""
-#: ../../include/cdav.php:166
-msgid "INVALID CARD DISMISSED!"
+#: ../../addon/piwik/piwik.php:97
+msgid "Site ID"
msgstr ""
-#: ../../include/cdav.php:167
-#, fuzzy
-msgid "Name: "
-msgstr "Navn"
+#: ../../addon/piwik/piwik.php:98
+msgid "Show opt-out cookie link?"
+msgstr ""
-#: ../../include/selectors.php:17
-msgid "Select a profile to assign to this contact"
-msgstr "Velg en profil for denne kontakten"
+#: ../../addon/piwik/piwik.php:99
+msgid "Asynchronous tracking"
+msgstr ""
-#: ../../include/selectors.php:45
-msgid "Frequently"
-msgstr "Ofte"
+#: ../../addon/piwik/piwik.php:100
+msgid "Enable frontend JavaScript error tracking"
+msgstr ""
-#: ../../include/selectors.php:46
-msgid "Hourly"
-msgstr "Hver time"
+#: ../../addon/piwik/piwik.php:100
+msgid "This feature requires Piwik >= 2.2.0"
+msgstr ""
-#: ../../include/selectors.php:47
-msgid "Twice daily"
-msgstr "To ganger daglig"
+#: ../../addon/piwik/piwik.php:116 ../../addon/logrot/logrot.php:54
+#: ../../addon/diaspora/diaspora.php:108 ../../addon/msgfooter/msgfooter.php:54
+#: ../../addon/rendezvous/rendezvous.php:82 ../../addon/xmpp/xmpp.php:54
+#: ../../addon/faces/faces.php:291 ../../addon/twitter/twitter.php:493
+#: ../../addon/openstreetmap/openstreetmap.php:163
+#: ../../Zotlabs/Module/Defperms.php:111
+#: ../../Zotlabs/Module/Settings/Channel.php:151
+msgid "Settings updated."
+msgstr "Innstillinger oppdatert."
-#: ../../include/selectors.php:48
-msgid "Daily"
-msgstr "Daglig"
+#: ../../addon/redphotos/redphotos.php:106
+msgid "Photos imported"
+msgstr ""
-#: ../../include/selectors.php:49
-msgid "Weekly"
-msgstr "Ukentlig"
+#: ../../addon/redphotos/redphotos.php:129
+msgid "Redmatrix Photo Album Import"
+msgstr ""
-#: ../../include/selectors.php:50
-msgid "Monthly"
-msgstr "Månedlig"
+#: ../../addon/redphotos/redphotos.php:130
+msgid "This will import all your Redmatrix photo albums to this channel."
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Currently Male"
-msgstr "For tiden mann"
+#: ../../addon/redphotos/redphotos.php:131
+#: ../../addon/redfiles/redfiles.php:121
+msgid "Redmatrix Server base URL"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Currently Female"
-msgstr "For tiden kvinne"
+#: ../../addon/redphotos/redphotos.php:132
+#: ../../addon/redfiles/redfiles.php:122
+msgid "Redmatrix Login Username"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Mostly Male"
-msgstr "For det meste mann"
+#: ../../addon/redphotos/redphotos.php:133
+#: ../../addon/redfiles/redfiles.php:123
+msgid "Redmatrix Login Password"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Mostly Female"
-msgstr "For det meste kvinne"
+#: ../../addon/redphotos/redphotos.php:134
+msgid "Import just this album"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Transgender"
-msgstr "Transkjønnet"
+#: ../../addon/redphotos/redphotos.php:134
+msgid "Leave blank to import all albums"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Intersex"
-msgstr "interkjønnet"
+#: ../../addon/redphotos/redphotos.php:135
+msgid "Maximum count to import"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Transsexual"
-msgstr "Transseksuell"
+#: ../../addon/redphotos/redphotos.php:135
+msgid "0 or blank to import all available"
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Hermaphrodite"
-msgstr "Hermafroditt"
+#: ../../addon/pageheader/Mod_Pageheader.php:22
+msgid "pageheader Settings saved."
+msgstr ""
-#: ../../include/selectors.php:64
-msgid "Undecided"
-msgstr "Ubestemt"
+#: ../../addon/pageheader/Mod_Pageheader.php:41
+msgid "Message to display on every page on this server"
+msgstr ""
-#: ../../include/selectors.php:100 ../../include/selectors.php:119
-msgid "Males"
-msgstr "Menn"
+#: ../../addon/pageheader/Mod_Pageheader.php:49
+msgid "Page Header"
+msgstr ""
-#: ../../include/selectors.php:100 ../../include/selectors.php:119
-msgid "Females"
-msgstr "Kvinner"
+#: ../../addon/buglink/buglink.php:16 ../../Zotlabs/Lib/Apps.php:334
+msgid "Report Bug"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Gay"
-msgstr "Homo"
+#: ../../addon/hideaside/Mod_Hideaside.php:28
+msgid "Hide Aside App"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Lesbian"
-msgstr "Lesbisk"
+#: ../../addon/hideaside/Mod_Hideaside.php:29
+msgid "Fade out aside areas after a while when using endless scroll"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "No Preference"
-msgstr "Ingen preferanse"
+#: ../../addon/ijpost/ijpost.php:44
+msgid "Post to Insane Journal"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Bisexual"
-msgstr "Biseksuell"
+#: ../../addon/ijpost/Mod_Ijpost.php:23
+msgid "Insane Journal Crosspost Connector Settings saved."
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Autosexual"
-msgstr "Autoseksuell"
+#: ../../addon/ijpost/Mod_Ijpost.php:35
+msgid "Insane Journal Crosspost Connector App"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Abstinent"
-msgstr "Avholdende"
+#: ../../addon/ijpost/Mod_Ijpost.php:35 ../../addon/xmpp/Mod_Xmpp.php:35
+#: ../../Zotlabs/Module/Invite.php:70 ../../Zotlabs/Module/Lang.php:20
+msgid "Not Installed"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Virgin"
-msgstr "Jomfru"
+#: ../../addon/ijpost/Mod_Ijpost.php:36
+msgid "Relay public postings to Insane Journal"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Deviant"
-msgstr "Avviker"
+#: ../../addon/ijpost/Mod_Ijpost.php:53
+msgid "InsaneJournal username"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Fetish"
-msgstr "Fetisj"
+#: ../../addon/ijpost/Mod_Ijpost.php:57
+msgid "InsaneJournal password"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Oodles"
-msgstr "Masse"
+#: ../../addon/ijpost/Mod_Ijpost.php:61
+msgid "Post to InsaneJournal by default"
+msgstr ""
-#: ../../include/selectors.php:100
-msgid "Nonsexual"
-msgstr "Ikke-seksuell"
+#: ../../addon/ijpost/Mod_Ijpost.php:69
+msgid "Insane Journal Crosspost Connector"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Single"
-msgstr "Enslig"
+#: ../../addon/wholikesme/wholikesme.php:30
+msgid "Who likes me?"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Lonely"
-msgstr "Ensom"
+#: ../../addon/redred/redred.php:50
+msgid "Post to Hubzilla"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Available"
-msgstr "Tilgjengelig"
+#: ../../addon/redred/Mod_Redred.php:24
+msgid "Channel is required."
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Unavailable"
-msgstr "Ikke tilgjengelig"
+#: ../../addon/redred/Mod_Redred.php:29 ../../Zotlabs/Module/Network.php:317
+#, fuzzy
+msgid "Invalid channel."
+msgstr "Ugyldig kanal"
-#: ../../include/selectors.php:138
-msgid "Has crush"
-msgstr "Er forelsket"
+#: ../../addon/redred/Mod_Redred.php:38
+msgid "Hubzilla Crosspost Connector Settings saved."
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Infatuated"
-msgstr "Betatt"
+#: ../../addon/redred/Mod_Redred.php:61
+msgid "Send public postings to Hubzilla channel by default"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Dating"
-msgstr "Sammen med"
+#: ../../addon/redred/Mod_Redred.php:65
+msgid "Hubzilla API Path"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Unfaithful"
-msgstr "Utro"
+#: ../../addon/redred/Mod_Redred.php:65 ../../addon/rtof/Mod_Rtof.php:51
+msgid "https://{sitename}/api"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Sex Addict"
-msgstr "Sexavhengig"
+#: ../../addon/redred/Mod_Redred.php:69
+msgid "Hubzilla login name"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Friends/Benefits"
-msgstr "Venner/Frynsegoder"
+#: ../../addon/redred/Mod_Redred.php:73
+msgid "Hubzilla channel name"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Casual"
-msgstr "Tilfeldig"
+#: ../../addon/redred/Mod_Redred.php:77
+msgid "Hubzilla password"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Engaged"
-msgstr "Forlovet"
+#: ../../addon/redred/Mod_Redred.php:85
+msgid "Hubzilla Crosspost Connector"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Married"
-msgstr "Gift"
+#: ../../addon/cards/cards.php:48 ../../addon/cards/cards.php:161
+#: ../../addon/cards/Mod_Cards.php:209 ../../Zotlabs/Lib/Apps.php:332
+msgid "Cards"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Imaginarily married"
-msgstr "Gift i fantasien"
+#: ../../addon/cards/cards.php:51 ../../addon/wiki/wiki.php:48
+msgid "View Cards"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Partners"
-msgstr "Partnere"
+#: ../../addon/cards/cards.php:104 ../../addon/articles/articles.php:105
+#: ../../addon/wiki/Lib/NativeWikiPage.php:549
+#: ../../Zotlabs/Module/Page.php:136 ../../Zotlabs/Module/Display.php:155
+#: ../../Zotlabs/Module/Block.php:77 ../../Zotlabs/Module/Help.php:173
+#: ../../Zotlabs/Web/Router.php:194
+msgid "Page not found."
+msgstr "Siden ikke funnet."
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Cohabiting"
-msgstr "Samboer"
+#: ../../addon/cards/Mod_Cards.php:115
+msgid "Add Card"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Common law"
-msgstr "Samboer"
+#: ../../addon/cards/Mod_Card_edit.php:17
+#: ../../addon/cards/Mod_Card_edit.php:33
+#: ../../addon/articles/Mod_Article_edit.php:17
+#: ../../addon/articles/Mod_Article_edit.php:33
+#: ../../Zotlabs/Module/Editwebpage.php:80
+#: ../../Zotlabs/Module/Editlayout.php:79 ../../Zotlabs/Module/Editpost.php:24
+#: ../../Zotlabs/Module/Editblock.php:79 ../../Zotlabs/Module/Editblock.php:95
+msgid "Item not found"
+msgstr "Elementet ble ikke funnet."
-#: ../../include/selectors.php:138
-msgid "Happy"
-msgstr "Lykkelig"
+#: ../../addon/cards/Mod_Card_edit.php:44
+#: ../../addon/articles/Mod_Article_edit.php:44
+#: ../../Zotlabs/Module/Page.php:75 ../../Zotlabs/Module/Block.php:41
+#: ../../Zotlabs/Module/Cal.php:31 ../../Zotlabs/Module/Wall_upload.php:30
+#: ../../Zotlabs/Module/Attach_edit.php:52 ../../Zotlabs/Module/Attach.php:22
+#: ../../Zotlabs/Module/Chanview.php:95
+msgid "Channel not found."
+msgstr "Kanalen ble ikke funnet."
-#: ../../include/selectors.php:138
-msgid "Not looking"
-msgstr "Ikke på utkikk"
+#: ../../addon/cards/Mod_Card_edit.php:123
+msgid "Edit Card"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Swinger"
-msgstr "Partnerbytte"
+#: ../../addon/startpage/Mod_Startpage.php:61
+msgid "Page to load after login"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Betrayed"
-msgstr "Bedratt"
+#: ../../addon/startpage/Mod_Startpage.php:63
+msgid ""
+"Examples: \"apps\", \"network?f=&gid=37\" (privacy collection), \"channel\" "
+"or \"notifications/system\" (leave blank for default.)"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Separated"
-msgstr "Separert"
+#: ../../addon/startpage/Mod_Startpage.php:72
+msgid "Startpage"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Unstable"
-msgstr "Ustabilt"
+#: ../../addon/libertree/libertree.php:43
+msgid "Post to Libertree"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Divorced"
-msgstr "Skilt"
+#: ../../addon/libertree/Mod_Libertree.php:25
+msgid "Libertree Crosspost Connector Settings saved."
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Imaginarily divorced"
-msgstr "Skilt i fantasien"
+#: ../../addon/libertree/Mod_Libertree.php:49
+msgid "Libertree API token"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "Widowed"
-msgstr "Enke"
+#: ../../addon/libertree/Mod_Libertree.php:53
+msgid "Libertree site URL"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Uncertain"
-msgstr "Usikkert"
+#: ../../addon/libertree/Mod_Libertree.php:57
+msgid "Post to Libertree by default"
+msgstr ""
-#: ../../include/selectors.php:138 ../../include/selectors.php:155
-msgid "It's complicated"
-msgstr "Det er komplisert"
+#: ../../addon/libertree/Mod_Libertree.php:65
+msgid "Libertree Crosspost Connector"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Don't care"
-msgstr "Bryr meg ikke"
+#: ../../addon/logrot/logrot.php:36
+msgid "Logfile archive directory"
+msgstr ""
-#: ../../include/selectors.php:138
-msgid "Ask me"
-msgstr "Spør meg"
+#: ../../addon/logrot/logrot.php:36
+msgid "Directory to store rotated logs"
+msgstr ""
-#: ../../include/zid.php:417
+#: ../../addon/logrot/logrot.php:37
+msgid "Logfile size in bytes before rotating"
+msgstr ""
+
+#: ../../addon/logrot/logrot.php:38
+msgid "Number of logfiles to retain"
+msgstr ""
+
+#: ../../addon/testdrive/testdrive.php:104
#, php-format
-msgid "OpenWebAuth: %1$s welcomes %2$s"
-msgstr "OpenWebAuth: %1$s ønsker %2$s velkommen"
+msgid "Your account on %s will expire in a few days."
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:19
-#: ../../view/theme/redbasic/php/config.php:22
-msgid "Focus (Hubzilla default)"
-msgstr "Focus (Hubzilla standard)"
+#: ../../addon/testdrive/testdrive.php:105
+msgid "Your test account is about to expire."
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:192
-msgid "Theme settings"
-msgstr "Instillinger for utseende"
+#: ../../addon/pubcrawl/Mod_Pubcrawl.php:24
+msgid ""
+"The activitypub protocol does not support location independence. Connections "
+"you make within that network may be unreachable from alternate channel "
+"locations."
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:193
-msgid "Dark style"
+#: ../../addon/pubcrawl/Mod_Pubcrawl.php:31
+msgid "Activitypub Protocol"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:194
-msgid "Light style"
+#: ../../addon/pubcrawl/pubcrawl.php:1099 ../../addon/diaspora/diaspora.php:417
+#: ../../Zotlabs/Module/Contactedit.php:494
+#, fuzzy
+#| msgid "Refresh"
+msgid "Refresh failed"
+msgstr "Forny"
+
+#: ../../addon/pubcrawl/pubcrawl.php:1106 ../../addon/diaspora/diaspora.php:422
+#: ../../Zotlabs/Module/Contactedit.php:491
+msgid "Refresh succeeded"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:195
-msgid "Common settings"
+#: ../../addon/bookmarker/bookmarker.php:38
+#: ../../Zotlabs/Lib/ThreadItem.php:455
+msgid "Save Bookmarks"
+msgstr "Lagre bokmerker"
+
+#: ../../addon/planets/Mod_Planets.php:23
+msgid "Random Planet App"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:196
-msgid "Primary theme color"
+#: ../../addon/planets/Mod_Planets.php:25
+msgid ""
+"Set a random planet from the Star Wars Empire as your location when posting"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:196
-#: ../../view/theme/redbasic/php/config.php:197
-#: ../../view/theme/redbasic/php/config.php:198
-#: ../../view/theme/redbasic/php/config.php:199
-#: ../../view/theme/redbasic/php/config.php:200
-#, fuzzy
-#| msgid "Leave empty for default width"
-msgid "Current color, leave empty for default"
-msgstr "La feltet stå tomt for å bruke standard bredde"
+#: ../../addon/dwpost/dwpost.php:49
+msgid "Post to Dreamwidth"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:197
-msgid "Success theme color"
+#: ../../addon/dwpost/dwpost.php:134 ../../addon/dwpost/Mod_Dwpost.php:67
+#: ../../addon/wppost/wppost.php:175 ../../addon/wppost/Mod_Wppost.php:96
+#: ../../addon/wiki/Mod_Wiki.php:381 ../../addon/ljpost/ljpost.php:134
+msgid "Source"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:198
-#, fuzzy
-#| msgid "Link hover color"
-msgid "Info theme color"
-msgstr "Lenkefarge når musepekeren er over"
+#: ../../addon/dwpost/Mod_Dwpost.php:26
+msgid "Dreamwidth Crosspost Connector Settings saved."
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:199
-#, fuzzy
-#| msgid "Link hover color"
-msgid "Warning theme color"
-msgstr "Lenkefarge når musepekeren er over"
+#: ../../addon/dwpost/Mod_Dwpost.php:51
+msgid "Dreamwidth username"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:200
-#, fuzzy
-#| msgid "Link hover color"
-msgid "Danger theme color"
-msgstr "Lenkefarge når musepekeren er over"
+#: ../../addon/dwpost/Mod_Dwpost.php:55
+msgid "Dreamwidth password"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:201
-#, fuzzy
-#| msgid "Default photo upload folder"
-msgid "Default to dark mode"
-msgstr "Standard mappe for opplasting av bilder"
+#: ../../addon/dwpost/Mod_Dwpost.php:59
+msgid "Post to Dreamwidth by default"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:202
-msgid "Always use light icons for navbar"
+#: ../../addon/dwpost/Mod_Dwpost.php:63 ../../addon/wppost/Mod_Wppost.php:92
+#: ../../addon/ljpost/Mod_Ljpost.php:69
+msgid "Add link to original post"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:202
-msgid "Enable this option if you use a dark navbar color in light mode"
+#: ../../addon/dwpost/Mod_Dwpost.php:67 ../../addon/wppost/Mod_Wppost.php:96
+msgid "Link description (default:"
msgstr ""
-#: ../../view/theme/redbasic/php/config.php:203
-msgid "Narrow navbar"
-msgstr "Smal navigasjonslinje"
+#: ../../addon/dwpost/Mod_Dwpost.php:75
+msgid "Dreamwidth Crosspost Connector"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:204
-msgid "Navigation bar background color"
-msgstr "Navigasjonslinjens bakgrunnsfarge"
+#: ../../addon/diaspora/diaspora.php:81
+msgid ""
+"Please install the statistics addon to be able to configure a diaspora relay"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:205
-#, fuzzy
-#| msgid "Navigation bar background color"
-msgid "Dark navigation bar background color"
-msgstr "Navigasjonslinjens bakgrunnsfarge"
+#: ../../addon/diaspora/diaspora.php:91
+msgid "Diaspora Relay Handle"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:206
-msgid "Set the background color"
-msgstr "Angi bakgrunnsfargen"
+#: ../../addon/diaspora/diaspora.php:91
+msgid "Address of a diaspora relay. Example: relay@diasporarelay.tld"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:207
-#, fuzzy
-#| msgid "Set the background color"
-msgid "Set the dark background color"
-msgstr "Angi bakgrunnsfargen"
+#: ../../addon/diaspora/diaspora.php:111
+msgid "Diaspora relay could not be imported"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:208
-msgid "Set the background image"
-msgstr "Angi bakgrunnsbilde"
+#: ../../addon/diaspora/diaspora.php:1112
+msgid "No subject"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:209
+#: ../../addon/diaspora/import_diaspora.php:16
#, fuzzy
-#| msgid "Set the background image"
-msgid "Set the dark background image"
-msgstr "Angi bakgrunnsbilde"
+#| msgid "Nothing to import."
+msgid "No account to import to."
+msgstr "Ingenting å importere."
-#: ../../view/theme/redbasic/php/config.php:210
-msgid "Set font-size for the entire application"
-msgstr "Angi skriftstørrelsen for hele programmet"
+#: ../../addon/diaspora/import_diaspora.php:21
+msgid "Incompatible data version - aborting"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:210
-msgid "Examples: 1rem, 100%, 16px"
-msgstr "For eksempel: 1rem, 100%, 16px"
+#: ../../addon/diaspora/import_diaspora.php:27
+msgid "No username found in import file."
+msgstr "Ingen brukernavn ble funnet i importfilen."
-#: ../../view/theme/redbasic/php/config.php:211
-#, fuzzy
-#| msgid "Set radius of corners"
-msgid "Set radius of corners in rem"
-msgstr "Angi hjørneradius"
+#: ../../addon/diaspora/import_diaspora.php:170
+msgid "Import completed."
+msgstr "Import ferdig."
-#: ../../view/theme/redbasic/php/config.php:211
-#, fuzzy
-#| msgid "Leave empty for default width"
-msgid "Leave empty for default radius"
-msgstr "La feltet stå tomt for å bruke standard bredde"
+#: ../../addon/diaspora/p.php:48 ../../addon/diaspora/util.php:346
+#: ../../addon/diaspora/util.php:359 ../../Zotlabs/Lib/Enotify.php:62
+msgid "$projectname"
+msgstr "$projectname"
-#: ../../view/theme/redbasic/php/config.php:212
-msgid "Set maximum width of content region in rem"
-msgstr "Set maksbredde for hovedregionen i rem"
+#: ../../addon/diaspora/Receiver.php:1644
+#, php-format
+msgid "%1$s dislikes %2$s's %3$s"
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:212
-msgid "Leave empty for default width"
-msgstr "La feltet stå tomt for å bruke standard bredde"
+#: ../../addon/diaspora/Receiver.php:1705
+#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Like.php:447
+msgid "status"
+msgstr "status"
-#: ../../view/theme/redbasic/php/config.php:213
-msgid "Set size of conversation author photo"
-msgstr "Angi størrelsen for samtalens forfatterbilde"
+#: ../../addon/diaspora/Receiver.php:2266 ../../Zotlabs/Module/Like.php:478
+#, php-format
+msgid "%1$s is attending %2$s's %3$s"
+msgstr "%1$s deltar på %2$ss %3$s"
-#: ../../view/theme/redbasic/php/config.php:213
-#: ../../view/theme/redbasic/php/config.php:214
+#: ../../addon/diaspora/Receiver.php:2268 ../../Zotlabs/Module/Like.php:480
+#, php-format
+msgid "%1$s is not attending %2$s's %3$s"
+msgstr "%1$s deltar ikke på %2$ss %3$s"
+
+#: ../../addon/diaspora/Receiver.php:2270 ../../Zotlabs/Module/Like.php:482
+#, php-format
+msgid "%1$s may attend %2$s's %3$s"
+msgstr "%1$s deltar kanskje på %2$ss %3$s"
+
+#: ../../addon/diaspora/Mod_Diaspora.php:43
+msgid "Diaspora Protocol Settings updated."
+msgstr ""
+
+#: ../../addon/diaspora/Mod_Diaspora.php:52
+msgid ""
+"The diaspora protocol does not support location independence. Connections "
+"you make within that network may be unreachable from alternate channel "
+"locations."
+msgstr ""
+
+#: ../../addon/diaspora/Mod_Diaspora.php:80
+msgid "Prevent your hashtags from being redirected to other sites"
+msgstr ""
+
+#: ../../addon/diaspora/Mod_Diaspora.php:84
+msgid "Sign and forward posts and comments with no existing Diaspora signature"
+msgstr ""
+
+#: ../../addon/diaspora/Mod_Diaspora.php:89
+msgid "Followed hashtags (comma separated, do not include the #)"
+msgstr ""
+
+#: ../../addon/diaspora/Mod_Diaspora.php:98
+msgid "Diaspora Protocol"
+msgstr ""
+
+#: ../../addon/fuzzloc/Mod_Fuzzloc.php:22
+msgid "Fuzzloc Settings updated."
+msgstr ""
+
+#: ../../addon/fuzzloc/Mod_Fuzzloc.php:38
+msgid "Minimum offset in meters"
+msgstr ""
+
+#: ../../addon/fuzzloc/Mod_Fuzzloc.php:42
+msgid "Maximum offset in meters"
+msgstr ""
+
+#: ../../addon/fuzzloc/Mod_Fuzzloc.php:51
+msgid "Fuzzy Location"
+msgstr ""
+
+#: ../../addon/qrator/qrator.php:48
+msgid "QR code"
+msgstr ""
+
+#: ../../addon/qrator/qrator.php:63
+msgid "QR Generator"
+msgstr ""
+
+#: ../../addon/qrator/qrator.php:64
+msgid "Enter some text"
+msgstr ""
+
+#: ../../addon/superblock/Mod_Superblock.php:117
+msgid "superblock settings updated"
+msgstr "innstillingene til superblokk ble oppdatert"
+
+#: ../../addon/superblock/Mod_Superblock.php:141
+msgid "Currently blocked"
+msgstr "Blokkert for øyeblikket"
+
+#: ../../addon/superblock/Mod_Superblock.php:143
+msgid "No channels currently blocked"
+msgstr "Ingen kanaler er blokkert i øyeblikket"
+
+#: ../../addon/superblock/Mod_Superblock.php:145
+#: ../../Zotlabs/Module/Cover_photo.php:382 ../../Zotlabs/Module/Tagrm.php:137
+#: ../../Zotlabs/Module/Photos.php:995
+msgid "Remove"
+msgstr "Fjern"
+
+#: ../../addon/superblock/superblock.php:378
+msgid "Block Completely"
+msgstr "Blokker helt"
+
+#: ../../addon/superblock/superblock.php:387
#, fuzzy
-#| msgid "Leave empty for default width"
-msgid "Leave empty for default size"
-msgstr "La feltet stå tomt for å bruke standard bredde"
+#| msgid "Block Title"
+msgid "Block from site"
+msgstr "Byggeklossens tittel"
-#: ../../view/theme/redbasic/php/config.php:214
-msgid "Set size of followup author photos"
-msgstr "Angi størrelsen på forfatterbilder ved oppfølging"
+#: ../../addon/randpost/randpost.php:101
+msgid "You're welcome."
+msgstr ""
-#: ../../view/theme/redbasic/php/config.php:215
-msgid "Show advanced settings"
-msgstr "Vis avanserte innstillinger"
+#: ../../addon/randpost/randpost.php:102
+msgid "Ah shucks..."
+msgstr ""
-#: ../../boot.php:1736
-msgid "Create an account to access services and applications"
+#: ../../addon/randpost/randpost.php:103
+msgid "Don't mention it."
msgstr ""
-#: ../../boot.php:1754
-msgid "Email or nickname"
-msgstr "Epost eller brukernavn"
+#: ../../addon/randpost/randpost.php:104
+msgid "&lt;blush&gt;"
+msgstr ""
-#: ../../boot.php:1754 ../../extend/addon/hzaddons/redred/Mod_Redred.php:73
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:54
-msgid "Nickname"
-msgstr "Kallenavn"
+#: ../../addon/msgfooter/msgfooter.php:46 ../../addon/xmpp/xmpp.php:43
+#: ../../addon/gravatar/gravatar.php:150
+msgid "Save Settings"
+msgstr ""
-#: ../../boot.php:1764
-msgid "Password"
-msgstr "Passord"
+#: ../../addon/msgfooter/msgfooter.php:47
+msgid "text to include in all outgoing posts from this site"
+msgstr ""
-#: ../../boot.php:1765
-msgid "Remember me"
-msgstr "Husk meg"
+#: ../../addon/mailtest/mailtest.php:19
+msgid "Send test email"
+msgstr ""
-#: ../../boot.php:1768
-msgid "Forgot your password?"
-msgstr "Glemt passordet ditt?"
+#: ../../addon/mailtest/mailtest.php:66
+msgid "Mail sent."
+msgstr ""
-#: ../../boot.php:1769 ../../Zotlabs/Module/Lostpass.php:91
-msgid "Password Reset"
-msgstr "Tilbakestill passord"
+#: ../../addon/mailtest/mailtest.php:68
+msgid "Sending of mail failed."
+msgstr ""
-#: ../../boot.php:2647
-#, php-format
-msgid "[$Projectname] Website SSL error for %s"
+#: ../../addon/mailtest/mailtest.php:77
+msgid "Mail Test"
msgstr ""
-#: ../../boot.php:2652
-msgid "Website SSL certificate is not valid. Please correct."
-msgstr "Nettstedets SSL-sertifikat er ikke gyldig. Vennligst fiks dette."
+#: ../../addon/nsfw/Mod_Nsfw.php:22
+msgid "NSFW Settings saved."
+msgstr ""
-#: ../../boot.php:2768
+#: ../../addon/nsfw/Mod_Nsfw.php:42
+msgid ""
+"This app looks in posts for the words/text you specify below, and collapses "
+"any content containing those keywords so it is not displayed at "
+"inappropriate times, such as sexual innuendo that may be improper in a work "
+"setting. It is polite and recommended to tag any content containing nudity "
+"with #NSFW. This filter can also match any other word/text you specify, and "
+"can thereby be used as a general purpose content filter."
+msgstr ""
+
+#: ../../addon/nsfw/Mod_Nsfw.php:47
+msgid "Comma separated list of keywords to hide"
+msgstr ""
+
+#: ../../addon/nsfw/Mod_Nsfw.php:47
+msgid "Word, /regular-expression/, lang=xx, lang!=xx"
+msgstr ""
+
+#: ../../addon/nsfw/Mod_Nsfw.php:56
+msgid "NSFW"
+msgstr ""
+
+#: ../../addon/nsfw/nsfw.php:153
+msgid "Possible adult content"
+msgstr ""
+
+#: ../../addon/nsfw/nsfw.php:168
#, php-format
-msgid "[$Projectname] Cron tasks not running on %s"
+msgid "%s - view"
msgstr ""
-#: ../../boot.php:2773
-msgid "Cron/Scheduled tasks not running."
-msgstr "Cron/planlagte oppgaver kjører ikke."
+#: ../../addon/rendezvous/rendezvous.php:57
+msgid "Errors encountered deleting database table "
+msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:10
-msgid "0. Beginner/Basic"
+#: ../../addon/rendezvous/rendezvous.php:95 ../../addon/twitter/twitter.php:502
+msgid "Submit Settings"
msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:11
-msgid "1. Novice - not skilled but willing to learn"
+#: ../../addon/rendezvous/rendezvous.php:96
+msgid "Drop tables when uninstalling?"
msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:12
-msgid "2. Intermediate - somewhat comfortable"
+#: ../../addon/rendezvous/rendezvous.php:96
+msgid ""
+"If checked, the Rendezvous database tables will be deleted when the plugin "
+"is uninstalled."
msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:13
-msgid "3. Advanced - very comfortable"
+#: ../../addon/rendezvous/rendezvous.php:97
+msgid "Mapbox Access Token"
msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:14
-msgid "4. Expert - I can write computer code"
+#: ../../addon/rendezvous/rendezvous.php:97
+msgid ""
+"If you enter a Mapbox access token, it will be used to retrieve map tiles "
+"from Mapbox instead of the default OpenStreetMap tile server."
msgstr ""
-#: ../../Zotlabs/Lib/Techlevels.php:15
-msgid "5. Wizard - I probably know more than you do"
+#: ../../addon/rendezvous/rendezvous.php:162
+msgid "Rendezvous"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:330
-msgid "Affinity Tool"
-msgstr "Nærhetsverktøy"
+#: ../../addon/rendezvous/rendezvous.php:167
+msgid ""
+"This identity has been deleted by another member due to inactivity. Please "
+"press the \"New identity\" button or refresh the page to register a new "
+"identity. You may use the same name."
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:168
+msgid "Welcome to Rendezvous!"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:169
+msgid ""
+"Enter your name to join this rendezvous. To begin sharing your location with "
+"the other members, tap the GPS control. When your location is discovered, a "
+"red dot will appear and others will be able to see you on the map."
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:171
+msgid "Let's meet here"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:172 ../../addon/wiki/Mod_Wiki.php:221
+#: ../../addon/wiki/Lib/NativeWikiPage.php:592
+#: ../../addon/wiki/Widget/Wiki_page_history.php:28
+#: ../../Zotlabs/Storage/Browser.php:385 ../../Zotlabs/Module/Oauth.php:112
+#: ../../Zotlabs/Module/Oauth.php:137 ../../Zotlabs/Module/Connedit.php:732
+#: ../../Zotlabs/Module/Admin/Channels.php:181
+#: ../../Zotlabs/Module/Sharedwithme.php:107 ../../Zotlabs/Module/Chat.php:256
+#: ../../Zotlabs/Module/Cdav.php:1368 ../../Zotlabs/Module/Oauth2.php:117
+#: ../../Zotlabs/Module/Oauth2.php:144
+msgid "Name"
+msgstr "Navn"
+
+#: ../../addon/rendezvous/rendezvous.php:173
+#: ../../addon/cart/submodules/hzservices.php:657
+#: ../../addon/cart/submodules/manualcat.php:260
+#: ../../Zotlabs/Module/Appman.php:220 ../../Zotlabs/Module/Rbmark.php:82
+#: ../../Zotlabs/Module/Cdav.php:1005
+msgid "Description"
+msgstr "Beskrivelse"
+
+#: ../../addon/rendezvous/rendezvous.php:174
+msgid "New marker"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:175
+msgid "Edit marker"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:176
+msgid "New identity"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:177
+msgid "Delete marker"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:178
+msgid "Delete member"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:179
+msgid "Edit proximity alert"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:180
+msgid ""
+"A proximity alert will be issued when this member is within a certain radius "
+"of you.<br><br>Enter a radius in meters (0 to disable):"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:180
+#: ../../addon/rendezvous/rendezvous.php:185
+msgid "distance"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:181
+msgid "Proximity alert distance (meters)"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:182
+#: ../../addon/rendezvous/rendezvous.php:184
+msgid ""
+"A proximity alert will be issued when you are within a certain radius of the "
+"marker location.<br><br>Enter a radius in meters (0 to disable):"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:183
+msgid "Marker proximity alert"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:331
-#: ../../extend/addon/hzaddons/articles/articles.php:48
-#: ../../extend/addon/hzaddons/articles/articles.php:160
-#: ../../extend/addon/hzaddons/articles/Mod_Articles.php:228
+#: ../../addon/rendezvous/rendezvous.php:186
+msgid "Reminder note"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:187
+msgid ""
+"Enter a note to be displayed when you are within the specified proximity..."
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:199
+msgid "Add new rendezvous"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:200
+msgid ""
+"Create a new rendezvous and share the access link with those you wish to "
+"invite to the group. Those who open the link become members of the "
+"rendezvous. They can view other member locations, add markers to the map, or "
+"share their own locations with the group."
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:232
+msgid "You have no rendezvous. Press the button above to create a rendezvous!"
+msgstr ""
+
+#: ../../addon/rendezvous/rendezvous.php:401 ../../Zotlabs/Module/Setup.php:754
+msgid "Errors encountered creating database tables."
+msgstr "Feil oppstod under opprettelsen av databasetabeller."
+
+#: ../../addon/pumpio/pumpio.php:152
+msgid "You are now authenticated to pumpio."
+msgstr ""
+
+#: ../../addon/pumpio/pumpio.php:153
+msgid "return to the featured settings page"
+msgstr "tilbake til funksjonsinnstillinger"
+
+#: ../../addon/pumpio/pumpio.php:168
+msgid "Post to Pump.io"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:40
+msgid "Pump.io Settings saved."
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:71
+msgid "Pump.io servername"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:71
+msgid "Without \"http://\" or \"https://\""
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:75
+msgid "Pump.io username"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:75
+msgid "Without the servername"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:86
+msgid "You are not authenticated to pumpio"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:88
+msgid "(Re-)Authenticate your pump.io connection"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:92
+msgid "Post to pump.io by default"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:96
+msgid "Should posts be public"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:100
+msgid "Mirror all public posts"
+msgstr ""
+
+#: ../../addon/pumpio/Mod_Pumpio.php:110
+msgid "Pump.io Crosspost Connector"
+msgstr ""
+
+#: ../../addon/wppost/wppost.php:47
+msgid "Post to WordPress"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:30
+msgid "Wordpress Settings saved."
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:67
+msgid "WordPress username"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:71
+msgid "WordPress password"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:75
+msgid "WordPress API URL"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:76
+msgid "Typically https://your-blog.tld/xmlrpc.php"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:79
+msgid "WordPress blogid"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:80
+msgid "For multi-user sites such as wordpress.com, otherwise leave blank"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:84
+msgid "Post to WordPress by default"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:88
+msgid "Forward comments (requires hubzilla_wp plugin)"
+msgstr ""
+
+#: ../../addon/wppost/Mod_Wppost.php:104
+msgid "Wordpress Post"
+msgstr ""
+
+#: ../../addon/articles/Mod_Article_edit.php:123
+msgid "Edit Article"
+msgstr ""
+
+#: ../../addon/articles/articles.php:48 ../../addon/articles/articles.php:161
+#: ../../addon/articles/Mod_Articles.php:228 ../../Zotlabs/Lib/Apps.php:331
msgid "Articles"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:332
-#: ../../extend/addon/hzaddons/cards/cards.php:48
-#: ../../extend/addon/hzaddons/cards/cards.php:160
-#: ../../extend/addon/hzaddons/cards/Mod_Cards.php:209
-msgid "Cards"
+#: ../../addon/articles/articles.php:51
+msgid "View Articles"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:333
-msgid "Site Admin"
-msgstr "Nettstedsadministrator"
+#: ../../addon/articles/Mod_Articles.php:119
+msgid "Add Article"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:334
-#: ../../extend/addon/hzaddons/buglink/buglink.php:16
-msgid "Report Bug"
+#: ../../addon/wiki/Mod_Wiki.php:36
+#: ../../addon/flashcards/Mod_Flashcards.php:58
+#: ../../addon/faces/Mod_Faces.php:64 ../../addon/cart/cart.php:1458
+msgid "Profile Unavailable."
+msgstr "Profilen er ikke tilgjengelig."
+
+#: ../../addon/wiki/Mod_Wiki.php:81 ../../addon/cart/manual_payments.php:93
+#: ../../addon/cart/submodules/paypalbuttonV2.php:486
+#: ../../addon/cart/submodules/paypalbutton.php:456
+#: ../../addon/cart/myshop.php:37 ../../addon/cart/cart.php:1608
+msgid "Invalid channel"
+msgstr "Ugyldig kanal"
+
+#: ../../addon/wiki/Mod_Wiki.php:136
+msgid "Error retrieving wiki"
+msgstr "Det oppstod en feil ved henting av wikien"
+
+#: ../../addon/wiki/Mod_Wiki.php:143
+msgid "Error creating zip file export folder"
+msgstr "Det oppstod en feil ved eksport av mappe til zip fil"
+
+#: ../../addon/wiki/Mod_Wiki.php:194
+msgid "Error downloading wiki: "
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:337
-msgid "Content Filter"
+#: ../../addon/wiki/Mod_Wiki.php:209 ../../addon/wiki/Widget/Wiki_list.php:23
+#: ../../addon/wiki/wiki.php:45 ../../addon/wiki/wiki.php:99
+msgid "Wikis"
+msgstr "Wikier"
+
+#: ../../addon/wiki/Mod_Wiki.php:215 ../../Zotlabs/Storage/Browser.php:411
+msgid "Download"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:338
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:133
-msgid "Content Import"
+#: ../../addon/wiki/Mod_Wiki.php:216 ../../addon/wiki/Mod_Wiki.php:406
+#: ../../Zotlabs/Module/Webpages.php:255 ../../Zotlabs/Module/Layouts.php:196
+#: ../../Zotlabs/Module/Pubsites.php:63 ../../Zotlabs/Module/Blocks.php:164
+msgid "View"
+msgstr "Vis"
+
+#: ../../addon/wiki/Mod_Wiki.php:217 ../../Zotlabs/Module/Manage.php:137
+#: ../../Zotlabs/Module/Profiles.php:852
+msgid "Create New"
+msgstr "Lag ny profil"
+
+#: ../../addon/wiki/Mod_Wiki.php:219
+msgid "Wiki name"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:340
-msgid "Remote Diagnostics"
+#: ../../addon/wiki/Mod_Wiki.php:220
+msgid "Content type"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:341
-msgid "Suggest Channels"
-msgstr "Foreslå kanaler"
+#: ../../addon/wiki/Mod_Wiki.php:222 ../../Zotlabs/Storage/Browser.php:386
+msgid "Type"
+msgstr "Type"
-#: ../../Zotlabs/Lib/Apps.php:343
-msgid "Channel Manager"
-msgstr "Kanalstyring"
+#: ../../addon/wiki/Mod_Wiki.php:223
+msgid "Any&nbsp;type"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:344
-msgid "Stream"
-msgstr "Tidslinje"
+#: ../../addon/wiki/Mod_Wiki.php:230
+msgid "Lock content type"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:348
-msgid "Wiki"
+#: ../../addon/wiki/Mod_Wiki.php:231
+msgid "Create a status post for this wiki"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:355
-msgid "Mail"
+#: ../../addon/wiki/Mod_Wiki.php:232
+msgid "Edit Wiki Name"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:277
+#, fuzzy
+msgid "Wiki not found"
+msgstr "Fant ikke wikien."
+
+#: ../../addon/wiki/Mod_Wiki.php:303
+msgid "Rename page"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:304 ../../addon/hsse/hsse.php:186
+#: ../../Zotlabs/Lib/ThreadItem.php:294 ../../Zotlabs/Widget/Cdav.php:142
+#: ../../Zotlabs/Module/Webpages.php:250 ../../Zotlabs/Module/Layouts.php:192
+#: ../../Zotlabs/Module/Blocks.php:159 ../../Zotlabs/Module/Photos.php:1077
+msgid "Share"
+msgstr "Del"
+
+#: ../../addon/wiki/Mod_Wiki.php:318
+msgid "Error retrieving page content"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:326 ../../addon/wiki/Mod_Wiki.php:328
+msgid "New page"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:363
+msgid "Revision Comparison"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:364
+#: ../../addon/wiki/Lib/NativeWikiPage.php:595
+#: ../../addon/wiki/Widget/Wiki_page_history.php:31
+msgid "Revert"
+msgstr "Tilbakestill"
+
+#: ../../addon/wiki/Mod_Wiki.php:371
+msgid "Short description of your changes (optional)"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:391
+msgid "New page name"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:396
+msgid "Embed image from photo albums"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:402 ../../Zotlabs/Module/Cover_photo.php:390
+#: ../../Zotlabs/Module/Profile_photo.php:557
+msgid "Choose a different album"
+msgstr "Velg et annet album"
+
+#: ../../addon/wiki/Mod_Wiki.php:407
+msgid "History"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:485
+msgid "Error creating wiki. Invalid name."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:492
+msgid "A wiki with this name already exists."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:505
+msgid "Wiki created, but error creating Home page."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:512
+msgid "Error creating wiki"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:536
+msgid "Error updating wiki. Invalid name."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:555
+msgid "Error updating wiki"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:570
+msgid "Wiki delete permission denied."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:580
+msgid "Error deleting wiki"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:613
+msgid "New page created"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:739
+msgid "Cannot delete Home"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:815
+msgid "Current Revision"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:815
+msgid "Selected Revision"
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:870
+msgid "You must be authenticated."
+msgstr ""
+
+#: ../../addon/wiki/Mod_Wiki.php:899 ../../addon/wiki/Widget/Wiki_pages.php:64
+msgid "Add new page"
+msgstr "Legg til ny side"
+
+#: ../../addon/wiki/Mod_Wiki.php:906 ../../addon/wiki/Widget/Wiki_pages.php:71
+#: ../../Zotlabs/Module/Dreport.php:138
+msgid "Options"
+msgstr "Valg"
+
+#: ../../addon/wiki/Lib/NativeWiki.php:146
+msgid "Wiki updated successfully"
+msgstr "Wikien ble oppdatert"
+
+#: ../../addon/wiki/Lib/NativeWiki.php:206
+msgid "Wiki files deleted successfully"
+msgstr "Wikifiler ble slettet"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:42
+#: ../../addon/wiki/Lib/NativeWikiPage.php:110
+msgid "(No Title)"
+msgstr "(Ingen tittel)"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:124
+msgid "Wiki page create failed."
+msgstr "Kunne ikke opprette wikiside."
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:138
+msgid "Wiki not found."
+msgstr "Fant ikke wikien."
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:149
+msgid "Destination name already exists"
+msgstr "Navnet finnes allerede"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:182
+#: ../../addon/wiki/Lib/NativeWikiPage.php:377
+msgid "Page not found"
+msgstr "Fant ikke siden"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:212
+msgid "Error reading page content"
+msgstr "Det oppstod en feil under lesing av innholder på siden"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:368
+#: ../../addon/wiki/Lib/NativeWikiPage.php:426
+#: ../../addon/wiki/Lib/NativeWikiPage.php:494
+#: ../../addon/wiki/Lib/NativeWikiPage.php:538
+msgid "Error reading wiki"
+msgstr "Det oppstod en feil ved lesing av wikien"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:412
+msgid "Page update failed."
+msgstr "Oppdatering av siden mislyktes."
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:448
+msgid "Nothing deleted"
+msgstr "Ingenting ble slettet"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:518
+msgid "Compare: object not found."
+msgstr "Sammanlign: fant ikke objektet."
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:525
+msgid "Page updated"
+msgstr "Siden ble oppdatert"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:533
+msgid "Wiki resource_id required for git commit"
+msgstr "Wiki ressurs_id er nødvendig for å lagre i git"
+
+#: ../../addon/wiki/Lib/NativeWikiPage.php:593
+#: ../../addon/wiki/Widget/Wiki_page_history.php:29
+msgctxt "wiki_history"
+msgid "Message"
msgstr "Melding"
-#: ../../Zotlabs/Lib/Apps.php:356
-msgid "Chat"
-msgstr "Chat"
+#: ../../addon/wiki/Lib/NativeWikiPage.php:594
+#: ../../addon/wiki/Widget/Wiki_page_history.php:30
+msgid "Date"
+msgstr "Dato"
-#: ../../Zotlabs/Lib/Apps.php:358
-msgid "Probe"
-msgstr "Undersøk"
+#: ../../addon/wiki/Lib/NativeWikiPage.php:596
+#: ../../addon/wiki/Widget/Wiki_page_history.php:32
+msgid "Compare"
+msgstr "Sammenlign"
-#: ../../Zotlabs/Lib/Apps.php:359
-msgid "Suggest"
-msgstr "Forreslå"
+#: ../../addon/wiki/Widget/Wiki_pages.php:58
+msgid "Wiki Pages"
+msgstr "Wikisider"
-#: ../../Zotlabs/Lib/Apps.php:360
-msgid "Random Channel"
-msgstr "Tilfeldig kanal"
+#: ../../addon/wiki/Widget/Wiki_pages.php:69
+msgid "Page name"
+msgstr "Sidenavn"
-#: ../../Zotlabs/Lib/Apps.php:361
-msgid "Invite"
-msgstr "Inviter"
+#: ../../addon/donate/donate.php:21
+msgid "Project Servers and Resources"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:362 ../../Zotlabs/Widget/Admin.php:31
-msgid "Features"
-msgstr "Funksjoner"
+#: ../../addon/donate/donate.php:22
+msgid "Project Creator and Tech Lead"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:363
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:69
-msgid "Language"
-msgstr "Språk"
+#: ../../addon/donate/donate.php:49
+msgid ""
+"And the hundreds of other people and organisations who helped make the "
+"Hubzilla possible."
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:364 ../../Zotlabs/Storage/Browser.php:411
-msgid "Post"
-msgstr "Innlegg"
+#: ../../addon/donate/donate.php:52
+msgid ""
+"The Redmatrix/Hubzilla projects are provided primarily by volunteers giving "
+"their time and expertise - and often paying out of pocket for services they "
+"share with others."
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:369
-msgid "Notifications"
-msgstr "Varsler"
+#: ../../addon/donate/donate.php:53
+msgid ""
+"There is no corporate funding and no ads, and we do not collect and sell "
+"your personal information. (We don't control your personal information - "
+"<strong>you do</strong>.)"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:370
-msgid "Order Apps"
+#: ../../addon/donate/donate.php:54
+msgid ""
+"Help support our ground-breaking work in decentralisation, web identity, and "
+"privacy."
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:371
-msgid "CardDAV"
+#: ../../addon/donate/donate.php:56
+msgid ""
+"Your donations keep servers and services running and also helps us to "
+"provide innovative new features and continued development."
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:372 ../../Zotlabs/Module/Sources.php:105
-msgid "Channel Sources"
-msgstr "Kanalkilder"
+#: ../../addon/donate/donate.php:59
+msgid "Donate"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:373 ../../Zotlabs/Module/Tokens.php:288
-msgid "Guest Access"
+#: ../../addon/donate/donate.php:61
+msgid ""
+"Choose a project, developer, or public hub to support with a one-time "
+"donation"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:374 ../../Zotlabs/Widget/Notes.php:38
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2661
-msgid "Notes"
-msgstr "Merknader"
+#: ../../addon/donate/donate.php:62
+msgid "Donate Now"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:375
-msgid "OAuth Apps Manager"
+#: ../../addon/donate/donate.php:63
+msgid ""
+"<strong><em>Or</em></strong> become a project sponsor (Hubzilla Project only)"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:376
-msgid "OAuth2 Apps Manager"
+#: ../../addon/donate/donate.php:64
+msgid ""
+"Please indicate if you would like your first name or full name (or nothing) "
+"to appear in our sponsor listing"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:377
-msgid "PDL Editor"
+#: ../../addon/donate/donate.php:65
+msgid "Sponsor"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:378 ../../Zotlabs/Module/Permcats.php:248
-msgid "Contact Roles"
+#: ../../addon/donate/donate.php:68
+msgid "Special thanks to: "
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:379 ../../Zotlabs/Module/Pubstream.php:109
-#: ../../Zotlabs/Widget/Notifications.php:154
-msgid "Public Stream"
+#: ../../addon/xmpp/xmpp.php:44
+msgid "Jabber BOSH host"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:380
-msgid "My Chatrooms"
+#: ../../addon/xmpp/xmpp.php:45
+msgid "Use central userbase"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:381
-msgid "Channel Export"
+#: ../../addon/xmpp/xmpp.php:45
+msgid ""
+"If enabled, members will automatically login to an ejabberd server that has "
+"to be installed on this machine with synchronized credentials via the "
+"\"auth_ejabberd.php\" script."
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:562 ../../Zotlabs/Module/Oauth.php:53
-#: ../../Zotlabs/Module/Oauth.php:135 ../../Zotlabs/Module/Oauth2.php:58
-#: ../../Zotlabs/Module/Oauth2.php:142 ../../Zotlabs/Module/Connedit.php:748
-#: ../../Zotlabs/Module/Admin/Addons.php:461 ../../Zotlabs/Module/Cdav.php:1044
-#: ../../Zotlabs/Module/Cdav.php:1384
-msgid "Update"
-msgstr "Oppdater"
+#: ../../addon/xmpp/Mod_Xmpp.php:23
+msgid "XMPP settings updated."
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:562 ../../Zotlabs/Module/Admin/Addons.php:430
-msgid "Install"
-msgstr "Installer"
+#: ../../addon/xmpp/Mod_Xmpp.php:35
+msgid "XMPP App"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:592 ../../Zotlabs/Lib/Apps.php:614
-msgid "Purchase"
-msgstr "Kjøp"
+#: ../../addon/xmpp/Mod_Xmpp.php:36
+msgid "Embedded XMPP (Jabber) client"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:619
-msgid "Undelete"
+#: ../../addon/xmpp/Mod_Xmpp.php:52
+msgid "Individual credentials"
msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:627
-msgid "Add to app-tray"
-msgstr "Legg til i meny"
+#: ../../addon/xmpp/Mod_Xmpp.php:58
+msgid "Jabber BOSH server"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:628
-msgid "Remove from app-tray"
-msgstr "Fjern fra meny"
+#: ../../addon/xmpp/Mod_Xmpp.php:67
+msgid "XMPP Settings"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:629
-msgid "Pin to navbar"
-msgstr "Fest til navigasjonslinjen"
+#: ../../addon/notifyadmin/notifyadmin.php:34
+msgid "New registration"
+msgstr ""
-#: ../../Zotlabs/Lib/Apps.php:630
-msgid "Unpin from navbar"
-msgstr "Fjern fra navigasjonslinjen"
+#: ../../addon/notifyadmin/notifyadmin.php:40
+#: ../../Zotlabs/Module/Invite.php:266
+#, php-format
+msgid "%s : Message delivery failed."
+msgstr "%s : meldingslevering feilet."
-#: ../../Zotlabs/Lib/Libzotdir.php:164
-msgid "Directory Options"
-msgstr "Kataloginnstillinger"
+#: ../../addon/notifyadmin/notifyadmin.php:42
+#, php-format
+msgid "Message sent to %s. New account registration: %s"
+msgstr ""
-#: ../../Zotlabs/Lib/Libzotdir.php:166
-msgid "Safe Mode"
-msgstr "Trygg modus"
+#: ../../addon/statusnet/statusnet.php:145
+msgid "Post to GNU social"
+msgstr ""
-#: ../../Zotlabs/Lib/Libzotdir.php:167
-msgid "Public Forums Only"
-msgstr "Bare offentlige forum"
+#: ../../addon/statusnet/statusnet.php:593
+#: ../../Zotlabs/Module/Admin/Site.php:411
+msgid "Site name"
+msgstr "Nettstedets navn"
-#: ../../Zotlabs/Lib/Libzotdir.php:169
-msgid "This Website Only"
-msgstr "Kun dette nettstedet"
+#: ../../addon/statusnet/statusnet.php:594
+msgid "API URL"
+msgstr ""
-#: ../../Zotlabs/Lib/AccessList.php:26
+#: ../../addon/statusnet/statusnet.php:595 ../../addon/twitter/twitter.php:505
+#: ../../Zotlabs/Module/Oauth.php:114 ../../Zotlabs/Module/Oauth.php:139
+#: ../../Zotlabs/Module/Oauth2.php:118 ../../Zotlabs/Module/Oauth2.php:145
+msgid "Consumer Secret"
+msgstr "Consumer Secret"
+
+#: ../../addon/statusnet/statusnet.php:596 ../../addon/twitter/twitter.php:504
+#: ../../Zotlabs/Module/Oauth.php:113 ../../Zotlabs/Module/Oauth.php:138
+msgid "Consumer Key"
+msgstr "Consumer Key"
+
+#: ../../addon/statusnet/statusnet.php:597
+msgid "Application name"
+msgstr ""
+
+#: ../../addon/statusnet/Mod_Statusnet.php:61
msgid ""
-"A deleted privacy group with this name was revived. Existing item "
-"permissions <strong>may</strong> apply to this privacy group and any future "
-"members. If this is not what you intended, please create another privacy "
-"group with a different name."
+"Please contact your site administrator.<br />The provided API URL is not "
+"valid."
msgstr ""
+"Den oppgitte URLen for APIet er ikke gyldig. Kontakt serverens administrator."
-#: ../../Zotlabs/Lib/AccessList.php:268
-msgid "Select a privacy group"
+#: ../../addon/statusnet/Mod_Statusnet.php:98
+msgid "We could not contact the GNU social API with the Path you entered."
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:110
-msgid "Restricted message"
-msgstr "Begrenset melding"
+#: ../../addon/statusnet/Mod_Statusnet.php:130
+msgid "GNU social settings updated."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:117
-msgid "Direct message"
-msgstr "Direktemelding"
+#: ../../addon/statusnet/Mod_Statusnet.php:179
+msgid "Globally Available GNU social OAuthKeys"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:122
-msgid "Public Policy"
+#: ../../addon/statusnet/Mod_Statusnet.php:181
+msgid ""
+"There are preconfigured OAuth key pairs for some GNU social servers "
+"available. If you are using one of them, please use these credentials.<br /"
+">If not feel free to connect to any other GNU social instance (see below)."
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:156
-msgid "Privacy conflict. Discretion advised."
+#: ../../addon/statusnet/Mod_Statusnet.php:196
+msgid "Provide your own OAuth Credentials"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:187 ../../Zotlabs/Storage/Browser.php:373
-msgid "Admin Delete"
+#: ../../addon/statusnet/Mod_Statusnet.php:198
+msgid ""
+"No consumer key pair for GNU social found. Register your Hubzilla Account as "
+"an desktop client on your GNU social account, copy the consumer key pair "
+"here and enter the API base root.<br />Before you register your own OAuth "
+"key pair ask the administrator if there is already a key pair for this "
+"Hubzilla installation at your favourite GNU social installation."
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:190 ../../Zotlabs/Module/Filer.php:55
-msgid "Save to Folder"
-msgstr "Lagre i mappe"
+#: ../../addon/statusnet/Mod_Statusnet.php:202
+msgid "OAuth Consumer Key"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
-msgid "I will attend"
-msgstr "Jeg vil delta"
+#: ../../addon/statusnet/Mod_Statusnet.php:206
+msgid "OAuth Consumer Secret"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
-msgid "I will not attend"
-msgstr "Jeg deltar ikke"
+#: ../../addon/statusnet/Mod_Statusnet.php:210
+msgid "Base API Path"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:217 ../../Zotlabs/Widget/Pinned.php:77
-msgid "I might attend"
-msgstr "Jeg vil kanskje delta"
+#: ../../addon/statusnet/Mod_Statusnet.php:210
+msgid "Remember the trailing /"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:335 ../../Zotlabs/Module/Photos.php:1074
-msgid "I like this (toggle)"
-msgstr "Jeg liker dette (skru av og på)"
+#: ../../addon/statusnet/Mod_Statusnet.php:214
+msgid "GNU social application name"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:336 ../../Zotlabs/Module/Photos.php:1075
-msgid "I don't like this (toggle)"
-msgstr "Jeg liker ikke dette (skru av og på)"
+#: ../../addon/statusnet/Mod_Statusnet.php:237
+msgid ""
+"To connect to your GNU social account click the button below to get a "
+"security code from GNU social which you have to copy into the input box "
+"below and submit the form. Only your <strong>public</strong> posts will be "
+"posted to GNU social."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:337
-#, fuzzy
-#| msgid "Delete this menu"
-msgid "Reply to this comment"
-msgstr "Slett denne menyen"
+#: ../../addon/statusnet/Mod_Statusnet.php:239
+msgid "Log in with GNU social"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:337
-msgid "reply"
+#: ../../addon/statusnet/Mod_Statusnet.php:242
+msgid "Copy the security code from GNU social here"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:337
-msgid "Reply to"
+#: ../../addon/statusnet/Mod_Statusnet.php:252
+msgid "Cancel Connection Process"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:344 ../../Zotlabs/Widget/Pinned.php:95
-msgid "share"
-msgstr "del"
+#: ../../addon/statusnet/Mod_Statusnet.php:254
+msgid "Current GNU social API is"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:348
-msgid "Repeat"
+#: ../../addon/statusnet/Mod_Statusnet.php:258
+msgid "Cancel GNU social Connection"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:348
-msgid "repeat"
+#: ../../addon/statusnet/Mod_Statusnet.php:270
+#: ../../addon/twitter/Mod_Twitter.php:145
+msgid "Currently connected to: "
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:359
-msgid "Delivery Report"
-msgstr "Leveringsrapport"
+#: ../../addon/statusnet/Mod_Statusnet.php:275
+msgid ""
+"<strong>Note</strong>: Due your privacy settings (<em>Hide your profile "
+"details from unknown viewers?</em>) the link potentially included in public "
+"postings relayed to GNU social will lead the visitor to a blank page "
+"informing the visitor that the access to your profile has been restricted."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:380
-#, fuzzy, php-format
-#| msgid "%d comment"
-#| msgid_plural "%d comments"
-msgid "%d comment"
-msgid_plural "%d comments"
-msgstr[0] "%d kommentar"
-msgstr[1] "%d kommentarer"
+#: ../../addon/statusnet/Mod_Statusnet.php:280
+msgid "Post to GNU social by default"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:381
-#, php-format
-msgid "%d unseen"
-msgstr "%d uleste"
+#: ../../addon/statusnet/Mod_Statusnet.php:280
+msgid ""
+"If enabled your public postings will be posted to the associated GNU-social "
+"account by default"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:431
-msgid "Forum"
+#: ../../addon/statusnet/Mod_Statusnet.php:289
+#: ../../addon/twitter/Mod_Twitter.php:169
+msgid "Clear OAuth configuration"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:438
-msgid "to"
-msgstr "til"
+#: ../../addon/statusnet/Mod_Statusnet.php:301
+msgid "GNU-Social Crosspost Connector"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:439 ../../Zotlabs/Widget/Pinned.php:123
-#: ../../Zotlabs/Widget/Messages.php:146 ../../Zotlabs/Widget/Messages.php:149
-msgid "via"
-msgstr "via"
+#: ../../addon/flashcards/Mod_Flashcards.php:161
+#: ../../Zotlabs/Module/Contactedit.php:431
+#: ../../Zotlabs/Module/Connedit.php:572
+msgid "Affinity"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:440
-msgid "Wall-to-Wall"
-msgstr "Vegg-til-vegg"
+#: ../../addon/ljpost/ljpost.php:49
+msgid "Post to Livejournal"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:441
-msgid "via Wall-To-Wall:"
-msgstr "via vegg-til-vegg:"
+#: ../../addon/ljpost/ljpost.php:127
+msgid "Posted by"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:469
-msgid "Attend"
+#: ../../addon/ljpost/Mod_Ljpost.php:53
+msgid "Livejournal username"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:470 ../../Zotlabs/Widget/Pinned.php:137
-msgid "Attendance Options"
+#: ../../addon/ljpost/Mod_Ljpost.php:57
+msgid "Livejournal password"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:472 ../../Zotlabs/Widget/Pinned.php:138
-msgid "Voting Options"
+#: ../../addon/ljpost/Mod_Ljpost.php:61
+msgid "Post to Livejournal by default"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:487
-msgid "Go to previous comment"
+#: ../../addon/ljpost/Mod_Ljpost.php:65
+msgid "Send wall-to-wall posts to Livejournal"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:496 ../../Zotlabs/Widget/Pinned.php:150
-msgid "Pinned post"
+#: ../../addon/ljpost/Mod_Ljpost.php:77
+msgid "Livejournal Crosspost Connector"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:499
-#: ../../extend/addon/hzaddons/bookmarker/bookmarker.php:38
-msgid "Save Bookmarks"
-msgstr "Lagre bokmerker"
+#: ../../addon/cart/manual_payments.php:7
+msgid "Error: order mismatch. Please try again."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:500
-msgid "Add to Calendar"
-msgstr "Legg til i kalender"
+#: ../../addon/cart/manual_payments.php:61
+msgid "Manual payments are not enabled."
+msgstr ""
+
+#: ../../addon/cart/manual_payments.php:68
+#: ../../addon/cart/submodules/paypalbuttonV2.php:417
+#: ../../addon/cart/submodules/paypalbutton.php:392
+#: ../../addon/cart/cart.php:1630
+msgid "Order not found."
+msgstr ""
+
+#: ../../addon/cart/manual_payments.php:77
+msgid "Finished"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:65
+msgid "Enable Hubzilla Services Module"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:164
+#: ../../addon/cart/submodules/manualcat.php:172
+msgid "New Sku"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:199
+#: ../../addon/cart/submodules/manualcat.php:208
+msgid "Cannot save edits to locked item."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:248
+#: ../../addon/cart/submodules/hzservices.php:335
+msgid "SKU not found."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:301
+#: ../../addon/cart/submodules/hzservices.php:305
+msgid "Invalid Activation Directive."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:376
+#: ../../addon/cart/submodules/hzservices.php:380
+msgid "Invalid Deactivation Directive."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:566
+msgid "Add to this privacy group"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:582
+msgid "Set user service class"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:609
+msgid "You must be using a local account to purchase this service."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:649
+#: ../../addon/cart/submodules/manualcat.php:252
+msgid "Changes Locked"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:653
+#: ../../addon/cart/submodules/manualcat.php:256
+msgid "Item available for purchase."
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:660
+#: ../../addon/cart/submodules/manualcat.php:263
+#: ../../addon/cart/widgets/catalogitem.php:57
+msgid "Price"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:663
+#: ../../addon/cart/submodules/manualcat.php:266
+msgid "Photo URL"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:667
+msgid "Add buyer to privacy group"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:672
+msgid "Add buyer as connection"
+msgstr ""
+
+#: ../../addon/cart/submodules/hzservices.php:680
+#: ../../addon/cart/submodules/hzservices.php:722
+msgid "Set Service Class"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:151
+msgid "Enable Subscription Management Module"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:223
+msgid ""
+"Cannot include subscription items with different terms in the same order."
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:372
+msgid "Select Subscription to Edit"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:380
+msgid "Edit Subscriptions"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:414
+msgid "Subscription SKU"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:419
+msgid "Catalog Description"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:423
+msgid "Subscription available for purchase."
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:428
+msgid "Maximum active subscriptions to this item per account."
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:431
+msgid "Subscription price."
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:435
+msgid "Quantity"
+msgstr ""
+
+#: ../../addon/cart/submodules/subscriptions.php:439
+msgid "Term"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:86
+msgid "Enable Paypal Button Module (API-v2)"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:508
+#: ../../addon/cart/submodules/paypalbuttonV2.php:96
+#: ../../addon/cart/submodules/paypalbutton.php:93
+msgid "Use Production Key"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:103
+#: ../../addon/cart/submodules/paypalbutton.php:100
+msgid "Paypal Sandbox Client Key"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:110
+#: ../../addon/cart/submodules/paypalbutton.php:107
+msgid "Paypal Sandbox Secret Key"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:116
+#: ../../addon/cart/submodules/paypalbutton.php:113
+msgid "Paypal Production Client Key"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:123
+#: ../../addon/cart/submodules/paypalbutton.php:120
+msgid "Paypal Production Secret Key"
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:271
+#: ../../addon/cart/submodules/paypalbutton.php:252
+msgid "Paypal button payments are not enabled."
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbuttonV2.php:289
+#: ../../addon/cart/submodules/paypalbutton.php:270
+msgid ""
+"Paypal button payments are not properly configured. Please choose another "
+"payment option."
+msgstr ""
+
+#: ../../addon/cart/submodules/paypalbutton.php:85
+msgid "Enable Paypal Button Module"
+msgstr ""
+
+#: ../../addon/cart/submodules/manualcat.php:61
+msgid "Enable Manual Cart Module"
+msgstr ""
+
+#: ../../addon/cart/submodules/orderoptions.php:70
+msgid "Enable Order/Item Options"
+msgstr ""
+
+#: ../../addon/cart/submodules/orderoptions.php:333
+#: ../../addon/cart/submodules/orderoptions.php:357
+#: ../../addon/cart/submodules/orderoptions.php:433
+#: ../../addon/cart/submodules/orderoptions.php:457
+msgid "Label"
+msgstr ""
+
+#: ../../addon/cart/submodules/orderoptions.php:336
+#: ../../addon/cart/submodules/orderoptions.php:360
+#: ../../addon/cart/submodules/orderoptions.php:436
+#: ../../addon/cart/submodules/orderoptions.php:460
+msgid "Instructions"
+msgstr ""
+
+#: ../../addon/cart/myshop.php:30
#, fuzzy
-#| msgid "Mark all events seen"
-msgid "Mark all comments seen"
-msgstr "Merk alle hendelser som sett"
+msgid "Access Denied."
+msgstr "Ingen tilgang"
+
+#: ../../addon/cart/myshop.php:113 ../../addon/cart/cart.php:1494
+msgid "Order Not Found"
+msgstr ""
+
+#: ../../addon/cart/myshop.php:145 ../../addon/cart/myshop.php:181
+#: ../../addon/cart/myshop.php:215 ../../addon/cart/myshop.php:265
+#: ../../addon/cart/myshop.php:300 ../../addon/cart/myshop.php:323
+msgid "Access Denied"
+msgstr "Ingen tilgang"
-#: ../../Zotlabs/Lib/ThreadItem.php:547
+#: ../../addon/cart/myshop.php:190 ../../addon/cart/myshop.php:224
+#: ../../addon/cart/myshop.php:275 ../../addon/cart/myshop.php:333
#, fuzzy
-#| msgid "Add Photos"
-msgid "Add yours"
-msgstr "Legg til bilder"
+msgid "Invalid Item"
+msgstr "Ugyldig element."
+
+#: ../../addon/cart/cart.php:259
+msgid "DB Cleanup Failure"
+msgstr ""
+
+#: ../../addon/cart/cart.php:692
+msgid "[cart] Item Added"
+msgstr ""
+
+#: ../../addon/cart/cart.php:1107
+msgid "Order already checked out."
+msgstr ""
+
+#: ../../addon/cart/cart.php:1416
+msgid "Drop database tables when uninstalling."
+msgstr ""
+
+#: ../../addon/cart/cart.php:1423 ../../addon/cart/Settings/Cart.php:129
+msgid "Cart Settings"
+msgstr ""
+
+#: ../../addon/cart/cart.php:1435 ../../addon/cart/cart.php:1438
+msgid "Shop"
+msgstr ""
+
+#: ../../addon/cart/cart.php:1597
+msgid "You must be logged into the Grid to shop."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:547
+#: ../../addon/cart/cart.php:1646
#, fuzzy
-#| msgid "Remove term"
-msgid "Remove yours"
-msgstr "Fjern begrep"
+msgid "Access denied."
+msgstr "Ingen tilgang"
-#: ../../Zotlabs/Lib/ThreadItem.php:865 ../../Zotlabs/Module/Photos.php:1092
-#: ../../Zotlabs/Module/Photos.php:1205
-msgid "This is you"
-msgstr "Dette er deg"
+#: ../../addon/cart/cart.php:1698 ../../addon/cart/cart.php:1841
+msgid "No Order Found"
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:874
-msgid "Image"
-msgstr "Bilde"
+#: ../../addon/cart/cart.php:1707
+msgid "An unknown error has occurred Please start again."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:876
-msgid "Insert Link"
-msgstr "Sett inn lenke"
+#: ../../addon/cart/cart.php:1850
+msgid "Requirements not met."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:877
-msgid "Video"
-msgstr "Video"
+#: ../../addon/cart/cart.php:1850
+msgid "Review your order and complete any needed requirements."
+msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:886
-msgid "Your full name (required)"
+#: ../../addon/cart/cart.php:1876
+msgid "Invalid Payment Type. Please start again."
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:887
-msgid "Your email address (required)"
+#: ../../addon/cart/cart.php:1883
+msgid "Order not found"
msgstr ""
-#: ../../Zotlabs/Lib/ThreadItem.php:888
-msgid "Your website URL (optional)"
+#: ../../addon/cart/Settings/Cart.php:58
+msgid "Enable Test Catalog"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2173
-#, php-format
-msgid "Likes %1$s's %2$s"
+#: ../../addon/cart/Settings/Cart.php:70
+msgid "Enable Manual Payments"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2176
-#, php-format
-msgid "Doesn't like %1$s's %2$s"
+#: ../../addon/cart/Settings/Cart.php:90
+msgid "Base Merchant Currency"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2182
-#, php-format
-msgid "Will attend %s's event"
+#: ../../addon/twitter/twitter.php:109
+msgid "Post to Twitter"
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2185
-#, php-format
-msgid "Will not attend %s's event"
+#: ../../addon/twitter/Mod_Twitter.php:65
+msgid "Twitter settings updated."
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2188
-#, php-format
-msgid "May attend %s's event"
+#: ../../addon/twitter/Mod_Twitter.php:101
+msgid ""
+"No consumer key pair for Twitter found. Please contact your site "
+"administrator."
msgstr ""
-#: ../../Zotlabs/Lib/Activity.php:2191
-#, php-format
-msgid "May not attend %s's event"
+#: ../../addon/twitter/Mod_Twitter.php:123
+msgid ""
+"At this Hubzilla instance the Twitter plugin was enabled but you have not "
+"yet connected your account to your Twitter account. To do so click the "
+"button below to get a PIN from Twitter which you have to copy into the input "
+"box below and submit the form. Only your <strong>public</strong> posts will "
+"be posted to Twitter."
msgstr ""
-#: ../../Zotlabs/Lib/Chatroom.php:25
-msgid "Missing room name"
-msgstr "Mangler romnavn"
+#: ../../addon/twitter/Mod_Twitter.php:125
+msgid "Log in with Twitter"
+msgstr ""
-#: ../../Zotlabs/Lib/Chatroom.php:34
-msgid "Duplicate room name"
-msgstr "Duplikat romnavn"
+#: ../../addon/twitter/Mod_Twitter.php:128
+msgid "Copy the PIN from Twitter here"
+msgstr ""
-#: ../../Zotlabs/Lib/Chatroom.php:84 ../../Zotlabs/Lib/Chatroom.php:92
-msgid "Invalid room specifier."
-msgstr "Ugyldig rom-spesifisering."
+#: ../../addon/twitter/Mod_Twitter.php:150
+msgid ""
+"<strong>Note:</strong> Due your privacy settings (<em>Hide your profile "
+"details from unknown viewers?</em>) the link potentially included in public "
+"postings relayed to Twitter will lead the visitor to a blank page informing "
+"the visitor that the access to your profile has been restricted."
+msgstr ""
-#: ../../Zotlabs/Lib/Chatroom.php:124
-msgid "Room not found."
-msgstr "Rommet ble ikke funnet."
+#: ../../addon/twitter/Mod_Twitter.php:155
+msgid "Twitter post length"
+msgstr ""
-#: ../../Zotlabs/Lib/Chatroom.php:145
-msgid "Room is full"
-msgstr "Rommet er fullt"
+#: ../../addon/twitter/Mod_Twitter.php:155
+msgid "Maximum tweet length"
+msgstr ""
+
+#: ../../addon/twitter/Mod_Twitter.php:160
+msgid "Send public postings to Twitter by default"
+msgstr ""
+
+#: ../../addon/twitter/Mod_Twitter.php:160
+msgid ""
+"If enabled your public postings will be posted to the associated Twitter "
+"account by default"
+msgstr ""
+
+#: ../../addon/twitter/Mod_Twitter.php:179
+msgid "Twitter Crosspost Connector"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:123
+msgid "generic profile image"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:124
+msgid "random geometric pattern"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:125
+msgid "monster face"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:126
+msgid "computer generated face"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:127
+msgid "retro arcade style face"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:128
+msgid "Hub default profile photo"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:143
+msgid "Information"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:143
+msgid ""
+"Libravatar addon is installed, too. Please disable Libravatar addon or this "
+"Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if "
+"nothing was found at Libravatar."
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:151
+msgid "Default avatar image"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:151
+msgid "Select default avatar image if none was found at Gravatar. See README"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:152
+msgid "Rating of images"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:152
+msgid "Select the appropriate avatar rating for your site. See README"
+msgstr ""
+
+#: ../../addon/gravatar/gravatar.php:165
+msgid "Gravatar settings updated."
+msgstr ""
+
+#: ../../addon/hsse/Mod_Hsse.php:15
+msgid "WYSIWYG status editor"
+msgstr ""
+
+#: ../../addon/hsse/Mod_Hsse.php:24
+msgid "WYSIWYG Status App"
+msgstr ""
+
+#: ../../addon/hsse/Mod_Hsse.php:32
+msgid "WYSIWYG Status"
+msgstr ""
+
+#: ../../addon/redfiles/redfiles.php:119
+msgid "Redmatrix File Storage Import"
+msgstr ""
+
+#: ../../addon/redfiles/redfiles.php:120
+msgid "This will import all your Redmatrix cloud files to this channel."
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:125
+msgid "View Larger"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:148
+msgid "Tile Server URL"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:148
+msgid ""
+"A list of <a href=\"http://wiki.openstreetmap.org/wiki/TMS\" "
+"target=\"_blank\">public tile servers</a>"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:149
+msgid "Nominatim (reverse geocoding) Server URL"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:149
+msgid ""
+"A list of <a href=\"http://wiki.openstreetmap.org/wiki/Nominatim\" "
+"target=\"_blank\">Nominatim servers</a>"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:150
+msgid "Default zoom"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:150
+msgid ""
+"The default zoom level. (1:world, 18:highest, also depends on tile server)"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:151
+msgid "Include marker on map"
+msgstr ""
+
+#: ../../addon/openstreetmap/openstreetmap.php:151
+msgid "Include a marker on the map."
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:224
+msgid "Workflow user."
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:275
+msgid "This channel"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:288 ../../Zotlabs/Module/Locs.php:123
+msgid "Primary"
+msgstr "Primær"
+
+#: ../../addon/workflow/workflow.php:330
+msgid "Create New Workflow Item"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:564 ../../addon/workflow/workflow.php:1466
+#: ../../addon/workflow/workflow.php:1485
+msgid "Workflow"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:1454
+msgid "No Workflows Available"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:1484
+msgid "Add item to which workflow"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:1544
+#: ../../addon/workflow/workflow.php:1663
+msgid "Create Workflow Item"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2661
+msgid "Link"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2663
+msgid "Web link."
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2682
+#: ../../addon/workflow/workflow.php:2751 ../../Zotlabs/Module/Connedit.php:734
+#: ../../Zotlabs/Module/Cdav.php:1370
+msgid "Title"
+msgstr "Tittel"
+
+#: ../../addon/workflow/workflow.php:2684
+#: ../../addon/workflow/workflow.php:2753
+msgid "Brief description or title"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2690 ../../Zotlabs/Lib/Apps.php:374
+#: ../../Zotlabs/Widget/Notes.php:41
+msgid "Notes"
+msgstr "Merknader"
+
+#: ../../addon/workflow/workflow.php:2692
+#: ../../addon/workflow/workflow.php:2761
+msgid "Notes and Info"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2697
+#: ../../Zotlabs/Module/Admin/Queue.php:47
+msgid "Priority"
+msgstr "Prioritet"
+
+#: ../../addon/workflow/workflow.php:2699
+msgid "Used to order links"
+msgstr ""
+
+#: ../../addon/workflow/workflow.php:2759
+msgid "Body"
+msgstr ""
+
+#: ../../addon/workflow/Settings/Mod_WorkflowSettings.php:101
+msgid "Workflow Settings"
+msgstr ""
+
+#: ../../addon/workflow/Settings/WorkflowSettingsUtil.php:145
+#, fuzzy
+#| msgid "Editor Settings"
+msgid "Workflow settings"
+msgstr "Instillinger for redigering"
+
+#: ../../addon/skeleton/Mod_Skeleton.php:38
+msgid "Some setting"
+msgstr ""
+
+#: ../../addon/skeleton/Mod_Skeleton.php:38
+msgid "A setting"
+msgstr ""
+
+#: ../../addon/skeleton/Mod_Skeleton.php:46
+msgid "Skeleton Settings"
+msgstr ""
+
+#: ../../addon/authchoose/Mod_Authchoose.php:30
+msgid ""
+"Allow magic authentication only to websites of your immediate connections"
+msgstr ""
+
+#: ../../addon/authchoose/Mod_Authchoose.php:36
+msgid "Authchoose"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:27
+msgid "No server specified"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:72
+msgid "Posts imported"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:112
+msgid "Files imported"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:133
+#: ../../Zotlabs/Lib/Apps.php:338
+msgid "Content Import"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:134
+msgid ""
+"This will import all your conversations and cloud files from a cloned "
+"channel on another server. This may take a while if you have lots of posts "
+"and or files."
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:135
+msgid "Include posts"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:135
+msgid "Conversations, Articles, Cards, and other posted content"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:136
+msgid "Include files"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:136
+msgid "Files, Photos and other cloud storage"
+msgstr ""
+
+#: ../../addon/content_import/Mod_content_import.php:137
+msgid "Original Server base URL"
+msgstr ""
+
+#: ../../addon/mdpost/mdpost.php:42
+msgid "Use markdown for editing posts"
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:24
+msgid "Friendica Crosspost Connector Settings saved."
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:47
+msgid "Send public postings to Friendica by default"
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:51
+msgid "Friendica API Path"
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:55
+msgid "Friendica login name"
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:59
+msgid "Friendica password"
+msgstr ""
+
+#: ../../addon/rtof/Mod_Rtof.php:67
+msgid "Friendica Crosspost Connector"
+msgstr ""
+
+#: ../../addon/rtof/rtof.php:51
+msgid "Post to Friendica"
+msgstr ""
+
+#: ../../addon/ldapauth/ldapauth.php:101
+msgid "An account has been created for you."
+msgstr ""
+
+#: ../../addon/ldapauth/ldapauth.php:108
+msgid "Authentication successful but rejected: account creation is disabled."
+msgstr ""
+
+#: ../../addon/nofed/nofed.php:47
+msgid "Federate"
+msgstr ""
+
+#: ../../addon/nofed/Mod_Nofed.php:21
+msgid "nofed Settings saved."
+msgstr ""
+
+#: ../../addon/nofed/Mod_Nofed.php:40
+msgid "Federate posts by default"
+msgstr ""
+
+#: ../../addon/nofed/Mod_Nofed.php:48
+msgid "No Federation"
+msgstr ""
+
+#: ../../addon/photocache/Mod_Photocache.php:27
+msgid "Photo Cache settings saved."
+msgstr ""
+
+#: ../../addon/photocache/Mod_Photocache.php:43
+msgid ""
+"Saves a copy of images from external sites locally to increase your "
+"anonymity in the web."
+msgstr ""
+
+#: ../../addon/photocache/Mod_Photocache.php:49
+msgid "Minimal photo size for caching"
+msgstr ""
+
+#: ../../addon/photocache/Mod_Photocache.php:51
+msgid "In pixels. From 1 up to 1024, 0 will be replaced with system default."
+msgstr ""
+
+#: ../../addon/photocache/Mod_Photocache.php:60
+msgid "Photo Cache"
+msgstr ""
+
+#: ../../addon/gallery/gallery.php:43 ../../addon/gallery/Mod_Gallery.php:135
+msgid "Gallery"
+msgstr ""
+
+#: ../../addon/gallery/gallery.php:46
+msgid "Photo Gallery"
+msgstr ""
+
+#: ../../addon/likebanner/likebanner.php:51
+msgid "Your Webbie:"
+msgstr ""
+
+#: ../../addon/likebanner/likebanner.php:54
+msgid "Fontsize (px):"
+msgstr ""
+
+#: ../../addon/likebanner/likebanner.php:68
+msgid "Link:"
+msgstr ""
+
+#: ../../addon/likebanner/likebanner.php:70
+msgid "Like us on Hubzilla"
+msgstr ""
+
+#: ../../addon/likebanner/likebanner.php:72
+msgid "Embed:"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:21
+msgid "Three Dimensional Tic-Tac-Toe"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:54
+msgid "3D Tic-Tac-Toe"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:59
+msgid "New game"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:60
+msgid "New game with handicap"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:61
+msgid ""
+"Three dimensional tic-tac-toe is just like the traditional game except that "
+"it is played on multiple levels simultaneously. "
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:62
+msgid ""
+"In this case there are three levels. You win by getting three in a row on "
+"any level, as well as up, down, and diagonally across the different levels."
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:64
+msgid ""
+"The handicap game disables the center position on the middle level because "
+"the player claiming this square often has an unfair advantage."
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:183
+msgid "You go first..."
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:188
+msgid "I'm going first this time..."
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:194
+msgid "You won!"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:200 ../../addon/tictac/tictac.php:225
+msgid "\"Cat\" game!"
+msgstr ""
+
+#: ../../addon/tictac/tictac.php:223
+msgid "I won!"
+msgstr ""
+
+#: ../../boot.php:1737
+msgid "Create an account to access services and applications"
+msgstr ""
+
+#: ../../boot.php:1755
+msgid "Email or nickname"
+msgstr "Epost eller brukernavn"
+
+#: ../../boot.php:1765
+msgid "Password"
+msgstr "Passord"
+
+#: ../../boot.php:1766
+msgid "Remember me"
+msgstr "Husk meg"
+
+#: ../../boot.php:1769
+msgid "Forgot your password?"
+msgstr "Glemt passordet ditt?"
+
+#: ../../boot.php:1770 ../../Zotlabs/Module/Lostpass.php:91
+msgid "Password Reset"
+msgstr "Tilbakestill passord"
+
+#: ../../boot.php:2551
+#, php-format
+msgid "[$Projectname] Website SSL error for %s"
+msgstr ""
+
+#: ../../boot.php:2556
+msgid "Website SSL certificate is not valid. Please correct."
+msgstr "Nettstedets SSL-sertifikat er ikke gyldig. Vennligst fiks dette."
+
+#: ../../boot.php:2674
+#, php-format
+msgid "[$Projectname] Cron tasks not running on %s"
+msgstr ""
+
+#: ../../boot.php:2679
+msgid "Cron/Scheduled tasks not running."
+msgstr "Cron/planlagte oppgaver kjører ikke."
#: ../../Zotlabs/Lib/Enotify.php:61
msgid "$Projectname Notification"
msgstr "$Projectname varsling"
-#: ../../Zotlabs/Lib/Enotify.php:62
-#: ../../extend/addon/hzaddons/diaspora/p.php:48
-#: ../../extend/addon/hzaddons/diaspora/util.php:346
-#: ../../extend/addon/hzaddons/diaspora/util.php:359
-msgid "$projectname"
-msgstr "$projectname"
-
#: ../../Zotlabs/Lib/Enotify.php:64
msgid "Thank You,"
msgstr "Tusen takk,"
-#: ../../Zotlabs/Lib/Enotify.php:66
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:33
-#, php-format
-msgid "%s Administrator"
-msgstr "%s administrator"
-
#: ../../Zotlabs/Lib/Enotify.php:67
#, php-format
msgid "This email was sent by %1$s at %2$s."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:67 ../../Zotlabs/Module/Invite.php:239
-#: ../../Zotlabs/Module/Invite.php:508 ../../Zotlabs/Module/Invite.php:522
-#: ../../Zotlabs/Module/Home.php:88 ../../Zotlabs/Module/Home.php:96
-#: ../../extend/addon/hzaddons/opensearch/opensearch.php:42
-msgid "$Projectname"
-msgstr "$Projectname"
-
#: ../../Zotlabs/Lib/Enotify.php:68
#, php-format
msgid ""
@@ -4595,8 +6737,9 @@ msgid "[$Projectname:Notify] New direct message received at %s"
msgstr "[$Projectname:Notify] Ny direktemelding mottatt kl. %s"
#: ../../Zotlabs/Lib/Enotify.php:134
-#, php-format
-msgid "%1$s sent you a new direct message at %2$s"
+#, fuzzy, php-format
+#| msgid "%1$s sent you a new direct message at %2$s"
+msgid "%1$s sent you a new private message at %2$s"
msgstr "%1$s sendte deg en ny direktemelding kl. %2$s"
#: ../../Zotlabs/Lib/Enotify.php:135
@@ -4609,315 +6752,416 @@ msgid "a direct message"
msgstr "en direktemelding"
#: ../../Zotlabs/Lib/Enotify.php:136
-#, php-format
-msgid "Please visit %s to view and/or reply to your direct messages."
+#, fuzzy, php-format
+#| msgid "Please visit %s to view and/or reply to your direct messages."
+msgid "Please visit %s to view and/or reply to your private messages."
msgstr "Gå til %s for å vise og/eller svare på dine direktemeldinger."
#: ../../Zotlabs/Lib/Enotify.php:149
-msgid "requested to comment on"
-msgstr ""
+#, fuzzy
+#| msgid "requested to like"
+msgid "requested to post in"
+msgstr "vil like"
#: ../../Zotlabs/Lib/Enotify.php:149
-msgid "commented on"
-msgstr ""
-
-#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:318
#, fuzzy
-#| msgid "Request date"
+#| msgid "posted"
+msgid "posted in"
+msgstr "lagt inn"
+
+#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:325
msgid "requested to like"
-msgstr "Dato for forespørsel"
+msgstr "vil like"
-#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:318
+#: ../../Zotlabs/Lib/Enotify.php:162 ../../Zotlabs/Lib/Enotify.php:325
msgid "liked"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:321
+#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:328
msgid "requested to dislike"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:321
+#: ../../Zotlabs/Lib/Enotify.php:165 ../../Zotlabs/Lib/Enotify.php:328
msgid "disliked"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:168
+#: ../../Zotlabs/Lib/Enotify.php:168 ../../Zotlabs/Lib/Enotify.php:331
#, fuzzy
-#| msgid "Created"
+#| msgid "requested to like"
+msgid "requested to repeat"
+msgstr "vil like"
+
+#: ../../Zotlabs/Lib/Enotify.php:168 ../../Zotlabs/Lib/Enotify.php:331
msgid "repeated"
-msgstr "Laget"
+msgstr "videresendte"
#: ../../Zotlabs/Lib/Enotify.php:173
msgid "voted on"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:216
+#: ../../Zotlabs/Lib/Enotify.php:217
#, php-format
msgid "%1$s %2$s [zrl=%3$s]a %4$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:224
+#: ../../Zotlabs/Lib/Enotify.php:227
#, php-format
msgid "%1$s %2$s [zrl=%3$s]%4$s's %5$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:233 ../../Zotlabs/Lib/Enotify.php:325
+#: ../../Zotlabs/Lib/Enotify.php:239 ../../Zotlabs/Lib/Enotify.php:335
#, php-format
msgid "%1$s %2$s [zrl=%3$s]your %4$s[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:245
+#: ../../Zotlabs/Lib/Enotify.php:253
#, php-format
msgid "[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:247
+#: ../../Zotlabs/Lib/Enotify.php:255
#, php-format
msgid "[$Projectname:Notify] Comment to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:248
+#: ../../Zotlabs/Lib/Enotify.php:256
#, php-format
msgid "%1$s commented on an item/conversation you have been following"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:251 ../../Zotlabs/Lib/Enotify.php:345
-#: ../../Zotlabs/Lib/Enotify.php:361 ../../Zotlabs/Lib/Enotify.php:385
-#: ../../Zotlabs/Lib/Enotify.php:402 ../../Zotlabs/Lib/Enotify.php:415
+#: ../../Zotlabs/Lib/Enotify.php:259 ../../Zotlabs/Lib/Enotify.php:356
+#: ../../Zotlabs/Lib/Enotify.php:372 ../../Zotlabs/Lib/Enotify.php:396
+#: ../../Zotlabs/Lib/Enotify.php:413 ../../Zotlabs/Lib/Enotify.php:427
#, php-format
msgid "Please visit %s to view and/or reply to the conversation."
msgstr "Vennligst besøk %s for å se og/eller svare i samtalen."
-#: ../../Zotlabs/Lib/Enotify.php:255 ../../Zotlabs/Lib/Enotify.php:256
+#: ../../Zotlabs/Lib/Enotify.php:263 ../../Zotlabs/Lib/Enotify.php:264
#, php-format
msgid "Please visit %s to approve or reject this comment."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:341
+#: ../../Zotlabs/Lib/Enotify.php:352
#, php-format
msgid "[$Projectname:Notify] Like received to conversation #%1$d by %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:342
+#: ../../Zotlabs/Lib/Enotify.php:353
#, php-format
msgid "%1$s liked an item/conversation you created"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:353
+#: ../../Zotlabs/Lib/Enotify.php:364
#, php-format
msgid "[$Projectname:Notify] %s posted to your profile wall"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:355
+#: ../../Zotlabs/Lib/Enotify.php:366
#, php-format
msgid "%1$s posted to your profile wall at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:357
+#: ../../Zotlabs/Lib/Enotify.php:368
#, php-format
msgid "%1$s posted to [zrl=%2$s]your wall[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:379
+#: ../../Zotlabs/Lib/Enotify.php:390
#, php-format
msgid "[$Projectname:Notify] %s tagged you"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:380
+#: ../../Zotlabs/Lib/Enotify.php:391
#, php-format
msgid "%1$s tagged you at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:381
+#: ../../Zotlabs/Lib/Enotify.php:392
#, php-format
msgid "%1$s [zrl=%2$s]tagged you[/zrl]."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:392
+#: ../../Zotlabs/Lib/Enotify.php:403
#, php-format
msgid "[$Projectname:Notify] %1$s poked you"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:393
+#: ../../Zotlabs/Lib/Enotify.php:404
#, php-format
msgid "%1$s poked you at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:394
+#: ../../Zotlabs/Lib/Enotify.php:405
#, php-format
msgid "%1$s [zrl=%2$s]poked you[/zrl]."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:409
+#: ../../Zotlabs/Lib/Enotify.php:421
#, php-format
msgid "[$Projectname:Notify] %s tagged your post"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:410
+#: ../../Zotlabs/Lib/Enotify.php:422
#, php-format
msgid "%1$s tagged your post at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:411
+#: ../../Zotlabs/Lib/Enotify.php:423
#, php-format
msgid "%1$s tagged [zrl=%2$s]your post[/zrl]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:422
+#: ../../Zotlabs/Lib/Enotify.php:433
msgid "[$Projectname:Notify] Introduction received"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:423
+#: ../../Zotlabs/Lib/Enotify.php:434
#, php-format
msgid "You've received an new connection request from '%1$s' at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:424
+#: ../../Zotlabs/Lib/Enotify.php:435
#, php-format
msgid "You've received [zrl=%1$s]a new connection request[/zrl] from %2$s."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:427 ../../Zotlabs/Lib/Enotify.php:445
+#: ../../Zotlabs/Lib/Enotify.php:438 ../../Zotlabs/Lib/Enotify.php:457
#, php-format
msgid "You may visit their profile at %s"
msgstr "Du kan besøke profilen deres på %s"
-#: ../../Zotlabs/Lib/Enotify.php:429
+#: ../../Zotlabs/Lib/Enotify.php:440
#, php-format
msgid "Please visit %s to approve or reject the connection request."
msgstr ""
"Vennligst besøk %s for å godkjenne eller avslå forespørselen om forbindelse."
-#: ../../Zotlabs/Lib/Enotify.php:436
+#: ../../Zotlabs/Lib/Enotify.php:448
msgid "[$Projectname:Notify] Friend suggestion received"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:437
+#: ../../Zotlabs/Lib/Enotify.php:449
#, php-format
msgid "You've received a friend suggestion from '%1$s' at %2$s"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:438
+#: ../../Zotlabs/Lib/Enotify.php:450
#, php-format
msgid "You've received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s."
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:443
+#: ../../Zotlabs/Lib/Enotify.php:455
msgid "Name:"
msgstr "Navn:"
-#: ../../Zotlabs/Lib/Enotify.php:444
+#: ../../Zotlabs/Lib/Enotify.php:456
msgid "Photo:"
msgstr "Bilde:"
-#: ../../Zotlabs/Lib/Enotify.php:447
+#: ../../Zotlabs/Lib/Enotify.php:459
#, php-format
msgid "Please visit %s to approve or reject the suggestion."
msgstr "Vennligst besøk %s for å godkjenne eller avslå dette forslaget."
-#: ../../Zotlabs/Lib/Enotify.php:677
+#: ../../Zotlabs/Lib/Enotify.php:694
msgid "[$Projectname:Notify]"
msgstr ""
-#: ../../Zotlabs/Lib/Enotify.php:843
-msgid "created a new poll"
+#: ../../Zotlabs/Lib/Enotify.php:860
+msgid "started a poll"
msgstr "opprettet spørreskjema"
-#: ../../Zotlabs/Lib/Enotify.php:843
-msgid "created a new post"
-msgstr "laget et nytt innlegg"
+#: ../../Zotlabs/Lib/Enotify.php:860
+msgid "started a conversation"
+msgstr "started en samtale"
-#: ../../Zotlabs/Lib/Enotify.php:844
+#: ../../Zotlabs/Lib/Enotify.php:861
#, php-format
msgid "voted on %s's poll"
msgstr "stemte på %s sitt spørreskjema"
-#: ../../Zotlabs/Lib/Enotify.php:844
+#: ../../Zotlabs/Lib/Enotify.php:861
#, php-format
-msgid "commented on %s's post"
-msgstr "kommenterte på %s sitt innlegg"
+msgid "posted in %s's conversation"
+msgstr "postet i %s sin samtale"
-#: ../../Zotlabs/Lib/Enotify.php:848 ../../Zotlabs/Lib/Enotify.php:948
+#: ../../Zotlabs/Lib/Enotify.php:865 ../../Zotlabs/Lib/Enotify.php:957
msgid "shared a file with you"
-msgstr ""
+msgstr "delte en fil med deg"
-#: ../../Zotlabs/Lib/Enotify.php:857
+#: ../../Zotlabs/Lib/Enotify.php:873
#, php-format
-msgid "edited a post dated %s"
-msgstr ""
+msgid "edited a message dated %s"
+msgstr "redigerte en melding datert %s"
-#: ../../Zotlabs/Lib/Enotify.php:860
-#, php-format
-msgid "edited a comment dated %s"
-msgstr ""
-
-#: ../../Zotlabs/Lib/Enotify.php:933
+#: ../../Zotlabs/Lib/Enotify.php:942
msgid "added your channel"
msgstr "la til din kanal"
-#: ../../Zotlabs/Lib/Enotify.php:963
+#: ../../Zotlabs/Lib/Enotify.php:972
msgid "sent you a direct message"
msgstr "sendte deg en direktemelding"
-#: ../../Zotlabs/Lib/Enotify.php:970
+#: ../../Zotlabs/Lib/Enotify.php:979
msgid "g A l F d"
msgstr "g A l F d"
-#: ../../Zotlabs/Lib/Enotify.php:973
+#: ../../Zotlabs/Lib/Enotify.php:982
msgid "[today]"
msgstr "[idag]"
-#: ../../Zotlabs/Lib/Enotify.php:983
+#: ../../Zotlabs/Lib/Enotify.php:992
msgid "created an event"
-msgstr ""
+msgstr "opprettet et arrangement"
-#: ../../Zotlabs/Lib/Enotify.php:998
+#: ../../Zotlabs/Lib/Enotify.php:1007
msgid "status verified"
-msgstr ""
+msgstr "status verifisert"
-#: ../../Zotlabs/Lib/DB_Upgrade.php:68
-msgid "Source code of failed update: "
+#: ../../Zotlabs/Lib/Apps.php:330
+msgid "Affinity Tool"
+msgstr "Nærhetsverktøy"
+
+#: ../../Zotlabs/Lib/Apps.php:333
+msgid "Site Admin"
+msgstr "Nettstedsadministrator"
+
+#: ../../Zotlabs/Lib/Apps.php:337
+msgid "Content Filter"
+msgstr "Innholdsfilter"
+
+#: ../../Zotlabs/Lib/Apps.php:340
+msgid "Remote Diagnostics"
+msgstr "Fjerndiagnostikk"
+
+#: ../../Zotlabs/Lib/Apps.php:341
+msgid "Suggest Channels"
+msgstr "Foreslå kanaler"
+
+#: ../../Zotlabs/Lib/Apps.php:343
+msgid "Channel Manager"
+msgstr "Kanalstyring"
+
+#: ../../Zotlabs/Lib/Apps.php:348
+msgid "Wiki"
msgstr ""
-#: ../../Zotlabs/Lib/DB_Upgrade.php:89
-#, php-format
-msgid "Update Error at %s"
-msgstr "Oppdateringsfeil ved %s"
+#: ../../Zotlabs/Lib/Apps.php:355
+msgid "Mail"
+msgstr "Melding"
-#: ../../Zotlabs/Lib/DB_Upgrade.php:95
-#, php-format
-msgid "Update %s failed. See error logs."
-msgstr "Oppdatering %s mislyktes. Se feilloggen."
+#: ../../Zotlabs/Lib/Apps.php:356
+msgid "Chat"
+msgstr "Chat"
-#: ../../Zotlabs/Lib/Connect.php:45 ../../Zotlabs/Lib/Connect.php:146
-msgid "Channel is blocked on this site."
-msgstr "Kanalen er blokkert på dette nettstedet."
+#: ../../Zotlabs/Lib/Apps.php:358
+msgid "Probe"
+msgstr "Undersøk"
-#: ../../Zotlabs/Lib/Connect.php:50
-msgid "Channel location missing."
-msgstr "Kanalplassering mangler."
+#: ../../Zotlabs/Lib/Apps.php:359
+msgid "Suggest"
+msgstr "Forreslå"
-#: ../../Zotlabs/Lib/Connect.php:104
-msgid "Remote channel or protocol unavailable."
+#: ../../Zotlabs/Lib/Apps.php:360
+msgid "Random Channel"
+msgstr "Tilfeldig kanal"
+
+#: ../../Zotlabs/Lib/Apps.php:361
+msgid "Invite"
+msgstr "Inviter"
+
+#: ../../Zotlabs/Lib/Apps.php:362 ../../Zotlabs/Widget/Admin.php:31
+msgid "Features"
+msgstr "Funksjoner"
+
+#: ../../Zotlabs/Lib/Apps.php:364 ../../Zotlabs/Storage/Browser.php:414
+msgid "Post"
+msgstr "Innlegg"
+
+#: ../../Zotlabs/Lib/Apps.php:369 ../../Zotlabs/Widget/Notifications.php:116
+#: ../../Zotlabs/Widget/Messages.php:51
+msgid "Notifications"
+msgstr "Varsler"
+
+#: ../../Zotlabs/Lib/Apps.php:370
+msgid "Order Apps"
+msgstr "Ordne apper"
+
+#: ../../Zotlabs/Lib/Apps.php:371
+msgid "CardDAV"
msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:140
-msgid "Channel discovery failed."
-msgstr "Kanaloppdagelse mislyktes."
+#: ../../Zotlabs/Lib/Apps.php:372 ../../Zotlabs/Module/Sources.php:105
+msgid "Channel Sources"
+msgstr "Kanalkilder"
-#: ../../Zotlabs/Lib/Connect.php:158
-msgid "Protocol disabled."
-msgstr "Protokollen er avskrudd."
+#: ../../Zotlabs/Lib/Apps.php:373 ../../Zotlabs/Module/Tokens.php:288
+msgid "Guest Access"
+msgstr "Gjestetilgang"
-#: ../../Zotlabs/Lib/Connect.php:170
-msgid "Cannot connect to yourself."
-msgstr "Kan ikke lage forbindelse med deg selv."
+#: ../../Zotlabs/Lib/Apps.php:375
+msgid "OAuth Apps Manager"
+msgstr ""
-#: ../../Zotlabs/Lib/Connect.php:275
-msgid "error saving data"
+#: ../../Zotlabs/Lib/Apps.php:376
+msgid "OAuth2 Apps Manager"
msgstr ""
-#: ../../Zotlabs/Lib/Libzot.php:688
-msgid "Unable to verify channel signature"
-msgstr "Ikke i stand til å sjekke kanalsignaturen"
+#: ../../Zotlabs/Lib/Apps.php:377
+msgid "PDL Editor"
+msgstr "PDL Redigering"
+
+#: ../../Zotlabs/Lib/Apps.php:378 ../../Zotlabs/Module/Permcats.php:248
+msgid "Contact Roles"
+msgstr "Kontaktroller"
+
+#: ../../Zotlabs/Lib/Apps.php:379 ../../Zotlabs/Widget/Notifications.php:154
+#: ../../Zotlabs/Module/Pubstream.php:109
+msgid "Public Stream"
+msgstr "Offentlig strøm"
+
+#: ../../Zotlabs/Lib/Apps.php:380
+msgid "My Chatrooms"
+msgstr "Mine chattekanaler"
+
+#: ../../Zotlabs/Lib/Apps.php:381
+msgid "Channel Export"
+msgstr "Kanaleksport"
+
+#: ../../Zotlabs/Lib/Apps.php:562 ../../Zotlabs/Module/Oauth.php:53
+#: ../../Zotlabs/Module/Oauth.php:135 ../../Zotlabs/Module/Connedit.php:748
+#: ../../Zotlabs/Module/Cdav.php:1044 ../../Zotlabs/Module/Cdav.php:1384
+#: ../../Zotlabs/Module/Oauth2.php:58 ../../Zotlabs/Module/Oauth2.php:142
+msgid "Update"
+msgstr "Oppdater"
+
+#: ../../Zotlabs/Lib/Apps.php:562
+msgid "Install"
+msgstr "Installer"
+
+#: ../../Zotlabs/Lib/Apps.php:592 ../../Zotlabs/Lib/Apps.php:614
+msgid "Purchase"
+msgstr "Kjøp"
+
+#: ../../Zotlabs/Lib/Apps.php:619
+msgid "Undelete"
+msgstr "Hent tilbake"
+
+#: ../../Zotlabs/Lib/Apps.php:627
+msgid "Add to app-tray"
+msgstr "Legg til i meny"
+
+#: ../../Zotlabs/Lib/Apps.php:628
+msgid "Remove from app-tray"
+msgstr "Fjern fra meny"
+
+#: ../../Zotlabs/Lib/Apps.php:629
+msgid "Pin to navbar"
+msgstr "Fest til navigasjonslinjen"
+
+#: ../../Zotlabs/Lib/Apps.php:630
+msgid "Unpin from navbar"
+msgstr "Fjern fra navigasjonslinjen"
#: ../../Zotlabs/Lib/PermissionDescription.php:108
#: ../../Zotlabs/Access/PermissionRoles.php:386
@@ -4926,51 +7170,332 @@ msgstr "Offentlig"
#: ../../Zotlabs/Lib/PermissionDescription.php:109
msgid "Anybody in the $Projectname network"
-msgstr ""
+msgstr "Alle i $Projectname-nettverket"
#: ../../Zotlabs/Lib/PermissionDescription.php:110
#, php-format
msgid "Any account on %s"
-msgstr ""
+msgstr "Brukerkontoer på %s"
#: ../../Zotlabs/Lib/PermissionDescription.php:111
msgid "Any of my connections"
-msgstr ""
+msgstr "Alle mine forbindelser"
#: ../../Zotlabs/Lib/PermissionDescription.php:112
msgid "Only connections I specifically allow"
-msgstr ""
+msgstr "Kun forbindelser jeg gir tilgang"
#: ../../Zotlabs/Lib/PermissionDescription.php:113
msgid "Anybody authenticated (could include visitors from other networks)"
msgstr ""
+"Alle som er autentisert (det kan inkludere besøkende fra andre nettverk)"
#: ../../Zotlabs/Lib/PermissionDescription.php:114
msgid "Any connections including those who haven't yet been approved"
msgstr ""
+"Alle forbindelser inkludert forbindelser som ikke enda har blitt godkjent"
#: ../../Zotlabs/Lib/PermissionDescription.php:150
msgid ""
"This is your default setting for the audience of your normal stream, and "
"posts."
msgstr ""
+"Dette er standardinnstillingene for hvem som vil motta din vanlige strøm og "
+"innlegg."
#: ../../Zotlabs/Lib/PermissionDescription.php:151
msgid ""
"This is your default setting for who can view your default channel profile"
msgstr ""
+"Dette er standardinnstillingene for hvem som kan se kanalens standardprofil"
#: ../../Zotlabs/Lib/PermissionDescription.php:152
msgid "This is your default setting for who can view your connections"
-msgstr ""
+msgstr "Dette er standardinnstillingene for hvem som kan se dine forbindelser"
#: ../../Zotlabs/Lib/PermissionDescription.php:153
msgid ""
"This is your default setting for who can view your file storage and photos"
msgstr ""
+"Dette er standardinnstillingene for hvem som kan se dine filer og bilder"
#: ../../Zotlabs/Lib/PermissionDescription.php:154
msgid "This is your default setting for the audience of your webpages"
+msgstr "Dette er standardinnstillingene for hvem som kan se dine websider"
+
+#: ../../Zotlabs/Lib/Permcat.php:83
+msgctxt "permcat"
+msgid "Default"
+msgstr "Standard"
+
+#: ../../Zotlabs/Lib/DB_Upgrade.php:85
+msgid "Source code of failed update: "
+msgstr "Kildekoden til oppdateringen som mislyktes:"
+
+#: ../../Zotlabs/Lib/DB_Upgrade.php:106
+#, php-format
+msgid "Update Error at %s"
+msgstr "Oppdateringsfeil ved %s"
+
+#: ../../Zotlabs/Lib/DB_Upgrade.php:112
+#, php-format
+msgid "Update %s failed. See error logs."
+msgstr "Oppdatering %s mislyktes. Se feilloggen."
+
+#: ../../Zotlabs/Lib/Connect.php:51 ../../Zotlabs/Lib/Connect.php:152
+msgid "Channel is blocked on this site."
+msgstr "Kanalen er blokkert på dette nettstedet."
+
+#: ../../Zotlabs/Lib/Connect.php:56
+msgid "Channel location missing."
+msgstr "Kanalplassering mangler."
+
+#: ../../Zotlabs/Lib/Connect.php:110
+msgid "Remote channel or protocol unavailable."
+msgstr "Kanalen eller protokollen er ikke tilgjengelig på den andre serveren."
+
+#: ../../Zotlabs/Lib/Connect.php:146
+msgid "Channel discovery failed."
+msgstr "Kanaloppdagelse mislyktes."
+
+#: ../../Zotlabs/Lib/Connect.php:164
+msgid "Protocol disabled."
+msgstr "Protokollen er avskrudd."
+
+#: ../../Zotlabs/Lib/Connect.php:176
+msgid "Cannot connect to yourself."
+msgstr "Kan ikke lage forbindelse med deg selv."
+
+#: ../../Zotlabs/Lib/Connect.php:281
+msgid "error saving data"
+msgstr "feil ved lagring av data"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:107
+msgid "Restricted message"
+msgstr "Begrenset melding"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:114
+msgid "Private message"
+msgstr "Privat melding"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:119
+msgid "Public Policy"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:153
+msgid "Privacy conflict. Discretion advised."
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:184 ../../Zotlabs/Storage/Browser.php:377
+msgid "Admin Delete"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:187 ../../Zotlabs/Module/Filer.php:66
+msgid "Save to Folder"
+msgstr "Lagre i mappe"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:214
+msgid "I will attend"
+msgstr "Jeg vil delta"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:214
+msgid "I will not attend"
+msgstr "Jeg deltar ikke"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:214
+msgid "I might attend"
+msgstr "Jeg vil kanskje delta"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:287
+msgid "Reply to this message"
+msgstr "Svar på denne meldingen"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:287
+msgid "reply"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:287
+msgid "Reply to"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:294 ../../Zotlabs/Widget/Pinned.php:95
+msgid "share"
+msgstr "del"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:298
+msgid "Repeat"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:298
+msgid "repeat"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:309
+msgid "Delivery Report"
+msgstr "Leveringsrapport"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:330
+#, php-format
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d kommentar"
+msgstr[1] "%d kommentarer"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:332
+#, php-format
+msgid "%d unseen"
+msgstr "%d uleste"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:363
+#, php-format
+msgid "Load the next few of total %d comments"
+msgstr "Last inn noen flere av ialt %d kommentarer"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:369
+msgid "Expand Replies"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:388
+msgid "Forum"
+msgstr ""
+
+#: ../../Zotlabs/Lib/ThreadItem.php:395
+msgid "to"
+msgstr "til"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:396 ../../Zotlabs/Widget/Messages.php:179
+#: ../../Zotlabs/Widget/Messages.php:182 ../../Zotlabs/Widget/Pinned.php:125
+msgid "via"
+msgstr "via"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:397
+msgid "Wall-to-Wall"
+msgstr "Vegg-til-vegg"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:398
+msgid "via Wall-To-Wall:"
+msgstr "via vegg-til-vegg:"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:414
+#, php-format
+msgid "Last edited %s"
+msgstr "Sist endret: %s"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:415
+#, php-format
+msgid "Expires %s"
+msgstr "Utløper %s"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:418
+#, php-format
+msgid "Published %s"
+msgstr "Publisert %s"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:425
+msgid "Attend"
+msgstr "Delta"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:426 ../../Zotlabs/Widget/Pinned.php:139
+msgid "Attendance Options"
+msgstr "Valg for deltagelse"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:428 ../../Zotlabs/Widget/Pinned.php:140
+msgid "Voting Options"
+msgstr "Valg for avstemning"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:442
+msgid "Go to previous comment"
+msgstr "Gå til forrige kommentar"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:452 ../../Zotlabs/Widget/Pinned.php:152
+msgid "Pinned post"
+msgstr "Festet innlegg"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:456
+msgid "Add to Calendar"
+msgstr "Legg til i kalender"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:464
+msgid "Mark all comments seen"
+msgstr "Merk alle kommentarer som sett"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:483
+msgid "Add yours"
+msgstr "Legg til din"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:483
+msgid "Remove yours"
+msgstr "Fjern din"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:496
+msgid "show less"
+msgstr "vis færre"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:497
+msgid "show more"
+msgstr "vis flere"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:497
+msgid "show all"
+msgstr "vis alle"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:806 ../../Zotlabs/Module/Photos.php:1093
+#: ../../Zotlabs/Module/Photos.php:1194
+msgid "This is you"
+msgstr "Dette er deg"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:818
+msgid "Insert Link"
+msgstr "Sett inn lenke"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:819
+msgid "Video"
+msgstr "Video"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:828
+msgid "Your full name (required)"
+msgstr "Fullt navn (påkrevd)"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:829
+msgid "Your email address (required)"
+msgstr "Epostaddresse (påkrevd)"
+
+#: ../../Zotlabs/Lib/ThreadItem.php:830
+msgid "Your website URL (optional)"
+msgstr "Webside (valgfritt)"
+
+#: ../../Zotlabs/Lib/Libzot.php:693
+msgid "Unable to verify channel signature"
+msgstr "Ikke i stand til å sjekke kanalsignaturen"
+
+#: ../../Zotlabs/Lib/Activity.php:2202
+#, php-format
+msgid "Likes %1$s's %2$s"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Activity.php:2205
+#, php-format
+msgid "Doesn't like %1$s's %2$s"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Activity.php:2211
+#, php-format
+msgid "Will attend %s's event"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Activity.php:2214
+#, php-format
+msgid "Will not attend %s's event"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Activity.php:2217
+#, php-format
+msgid "May attend %s's event"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Activity.php:2220
+#, php-format
+msgid "May not attend %s's event"
msgstr ""
#: ../../Zotlabs/Lib/Libsync.php:824
@@ -4978,10 +7503,77 @@ msgstr ""
msgid "Unable to verify site signature for %s"
msgstr "Ikke i stand til å bekrefte signaturen til %s"
-#: ../../Zotlabs/Lib/Permcat.php:83
-msgctxt "permcat"
-msgid "Default"
-msgstr "Standard"
+#: ../../Zotlabs/Lib/Libzotdir.php:164
+msgid "Directory Options"
+msgstr "Kataloginnstillinger"
+
+#: ../../Zotlabs/Lib/Libzotdir.php:166
+msgid "Safe Mode"
+msgstr "Trygg modus"
+
+#: ../../Zotlabs/Lib/Libzotdir.php:167
+msgid "Public Forums Only"
+msgstr "Bare offentlige forum"
+
+#: ../../Zotlabs/Lib/Libzotdir.php:169
+msgid "This Website Only"
+msgstr "Kun dette nettstedet"
+
+#: ../../Zotlabs/Lib/Chatroom.php:25
+msgid "Missing room name"
+msgstr "Mangler romnavn"
+
+#: ../../Zotlabs/Lib/Chatroom.php:34
+msgid "Duplicate room name"
+msgstr "Duplikat romnavn"
+
+#: ../../Zotlabs/Lib/Chatroom.php:84 ../../Zotlabs/Lib/Chatroom.php:92
+msgid "Invalid room specifier."
+msgstr "Ugyldig rom-spesifisering."
+
+#: ../../Zotlabs/Lib/Chatroom.php:124
+msgid "Room not found."
+msgstr "Rommet ble ikke funnet."
+
+#: ../../Zotlabs/Lib/Chatroom.php:145
+msgid "Room is full"
+msgstr "Rommet er fullt"
+
+#: ../../Zotlabs/Lib/Techlevels.php:10
+msgid "0. Beginner/Basic"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Techlevels.php:11
+msgid "1. Novice - not skilled but willing to learn"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Techlevels.php:12
+msgid "2. Intermediate - somewhat comfortable"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Techlevels.php:13
+msgid "3. Advanced - very comfortable"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Techlevels.php:14
+msgid "4. Expert - I can write computer code"
+msgstr ""
+
+#: ../../Zotlabs/Lib/Techlevels.php:15
+msgid "5. Wizard - I probably know more than you do"
+msgstr ""
+
+#: ../../Zotlabs/Lib/AccessList.php:26
+msgid ""
+"A deleted privacy group with this name was revived. Existing item "
+"permissions <strong>may</strong> apply to this privacy group and any future "
+"members. If this is not what you intended, please create another privacy "
+"group with a different name."
+msgstr ""
+
+#: ../../Zotlabs/Lib/AccessList.php:268
+msgid "Select a privacy group"
+msgstr ""
#: ../../Zotlabs/Access/PermissionRoles.php:339
msgid "Social Networking"
@@ -4989,7 +7581,7 @@ msgstr "Sosialt nettverk"
#: ../../Zotlabs/Access/PermissionRoles.php:340
msgid "Social - Federation"
-msgstr ""
+msgstr "Sosial - føderert"
#: ../../Zotlabs/Access/PermissionRoles.php:341
msgid "Social - Mostly Public"
@@ -5052,9 +7644,8 @@ msgid "Personal"
msgstr "Personlig"
#: ../../Zotlabs/Access/PermissionRoles.php:388
-#, fuzzy
msgid "Community forum"
-msgstr "Forum for fellesskap"
+msgstr "Forum"
#: ../../Zotlabs/Access/PermissionRoles.php:389
msgid "Custom"
@@ -5062,7 +7653,7 @@ msgstr ""
#: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts"
-msgstr ""
+msgstr "Kan se min kanalstrøm og innlegg"
#: ../../Zotlabs/Access/Permissions.php:57
msgid "Can send me their channel stream and posts"
@@ -5082,27 +7673,27 @@ msgstr "Kan se mine filer og bilder"
#: ../../Zotlabs/Access/Permissions.php:61
msgid "Can upload/modify my file storage and photos"
-msgstr ""
+msgstr "Kan laste opp/redigere mine filer og bilder"
#: ../../Zotlabs/Access/Permissions.php:62
msgid "Can view my channel webpages"
-msgstr ""
+msgstr "Kan se min kanal sine websider"
#: ../../Zotlabs/Access/Permissions.php:63
msgid "Can view my wiki pages"
-msgstr ""
+msgstr "Kan se mine wiki-sider"
#: ../../Zotlabs/Access/Permissions.php:64
msgid "Can create/edit my channel webpages"
-msgstr ""
+msgstr "Kan opprette/redigere websider på min kanal"
#: ../../Zotlabs/Access/Permissions.php:65
msgid "Can write to my wiki pages"
-msgstr ""
+msgstr "Kan skrive på mine wiki-sider"
#: ../../Zotlabs/Access/Permissions.php:66
msgid "Can post on my channel (wall) page"
-msgstr ""
+msgstr "Kan poste på min kanal (vegg)"
#: ../../Zotlabs/Access/Permissions.php:67
msgid "Can comment on or like my posts"
@@ -5114,1540 +7705,1170 @@ msgstr "Kan sende meg direktemeldinger"
#: ../../Zotlabs/Access/Permissions.php:69
msgid "Can like/dislike profiles and profile things"
-msgstr ""
+msgstr "Kan like/ikke like profiler og profilting"
#: ../../Zotlabs/Access/Permissions.php:70
msgid "Can chat with me"
-msgstr ""
+msgstr "Kan chatte med meg"
#: ../../Zotlabs/Access/Permissions.php:71
msgid "Can source/mirror my public posts in derived channels"
-msgstr ""
+msgstr "Kan speile eller bruke mine offentlige innlegg i avledede kanaler"
#: ../../Zotlabs/Access/Permissions.php:73
msgid "Can administer my channel"
-msgstr ""
+msgstr "Kan administrere min kanal"
-#: ../../Zotlabs/Web/Router.php:188 ../../Zotlabs/Module/Help.php:172
-#: ../../Zotlabs/Module/Block.php:77 ../../Zotlabs/Module/Page.php:136
-#: ../../Zotlabs/Module/Display.php:155
-#: ../../extend/addon/hzaddons/articles/articles.php:105
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:545
-#: ../../extend/addon/hzaddons/cards/cards.php:104
-msgid "Page not found."
-msgstr "Siden ikke funnet."
-
-#: ../../Zotlabs/Storage/Browser.php:292
-msgid "Change filename to"
-msgstr ""
+#: ../../Zotlabs/Widget/Tasklist.php:31
+msgid "Tasks"
+msgstr "Oppgaver"
-#: ../../Zotlabs/Storage/Browser.php:309 ../../Zotlabs/Storage/Browser.php:394
-msgid "Select a target location"
+#: ../../Zotlabs/Widget/Cover_photo.php:74
+msgid "Click to show more"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:310 ../../Zotlabs/Storage/Browser.php:395
-msgid "Copy to target location"
-msgstr ""
+#: ../../Zotlabs/Widget/Appstore.php:16
+msgid "App Collections"
+msgstr "Appsamlinger"
-#: ../../Zotlabs/Storage/Browser.php:311 ../../Zotlabs/Storage/Browser.php:393
-msgid "Set permissions for all files and sub folders"
-msgstr ""
+#: ../../Zotlabs/Widget/Appstore.php:18
+msgid "Installed apps"
+msgstr "Installerte apper"
-#: ../../Zotlabs/Storage/Browser.php:312
-msgid "Notify your contacts about this file"
-msgstr "Varsle dine kontakter om denne filen"
+#: ../../Zotlabs/Widget/Appstore.php:19 ../../Zotlabs/Module/Apps.php:51
+msgid "Available Apps"
+msgstr "Tilgjengelige apper"
-#: ../../Zotlabs/Storage/Browser.php:351
-msgid "File category"
-msgstr ""
+#: ../../Zotlabs/Widget/Rating.php:59
+msgid "Rating Tools"
+msgstr "Vurderingsverktøy"
-#: ../../Zotlabs/Storage/Browser.php:365
-msgid "Total"
-msgstr "Totalt"
+#: ../../Zotlabs/Widget/Rating.php:63 ../../Zotlabs/Widget/Rating.php:65
+msgid "Rate Me"
+msgstr "Vurder meg"
-#: ../../Zotlabs/Storage/Browser.php:367
-msgid "Shared"
-msgstr "Delt"
+#: ../../Zotlabs/Widget/Rating.php:68
+msgid "View Ratings"
+msgstr "Vis vurderinger"
-#: ../../Zotlabs/Storage/Browser.php:368 ../../Zotlabs/Storage/Browser.php:545
-#: ../../Zotlabs/Module/Menu.php:182 ../../Zotlabs/Module/New_channel.php:190
-#: ../../Zotlabs/Module/Layouts.php:183 ../../Zotlabs/Module/Connedit.php:747
-#: ../../Zotlabs/Module/Blocks.php:157 ../../Zotlabs/Module/Webpages.php:249
-#: ../../Zotlabs/Module/Cdav.php:1050 ../../Zotlabs/Module/Cdav.php:1383
-#: ../../Zotlabs/Widget/Cdav.php:146 ../../Zotlabs/Widget/Cdav.php:184
-msgid "Create"
-msgstr "Lag"
+#: ../../Zotlabs/Widget/Notifications.php:24
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Unseen network activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:369
-msgid "Add Files"
+#: ../../Zotlabs/Widget/Notifications.php:27
+msgid "Network stream"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:381 ../../Zotlabs/Module/Oauth.php:111
-#: ../../Zotlabs/Module/Oauth.php:137 ../../Zotlabs/Module/Oauth2.php:116
-#: ../../Zotlabs/Module/Oauth2.php:144 ../../Zotlabs/Module/Chat.php:256
-#: ../../Zotlabs/Module/Connedit.php:732
-#: ../../Zotlabs/Module/Admin/Channels.php:181
-#: ../../Zotlabs/Module/Sharedwithme.php:107 ../../Zotlabs/Module/Cdav.php:1368
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:172
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:588
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:221
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_page_history.php:28
-msgid "Name"
-msgstr "Navn"
-
-#: ../../Zotlabs/Storage/Browser.php:382
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:222
-msgid "Type"
-msgstr "Type"
-
-#: ../../Zotlabs/Storage/Browser.php:384
-#: ../../Zotlabs/Module/Sharedwithme.php:110
-msgid "Last Modified"
-msgstr "Sist endret"
+#: ../../Zotlabs/Widget/Notifications.php:30
+#: ../../Zotlabs/Widget/Notifications.php:69
+#, fuzzy
+#| msgid "Mark all seen"
+msgid "Mark all read"
+msgstr "Merk alle som sett"
-#: ../../Zotlabs/Storage/Browser.php:385
-msgid "parent"
-msgstr "opp et nivå"
+#: ../../Zotlabs/Widget/Notifications.php:33
+#: ../../Zotlabs/Widget/Notifications.php:53
+#: ../../Zotlabs/Widget/Notifications.php:72
+#: ../../Zotlabs/Widget/Notifications.php:166
+#, fuzzy
+#| msgid "Conversation Features"
+msgid "Conversation starters"
+msgstr "Samtaleinnstillinger"
-#: ../../Zotlabs/Storage/Browser.php:390
-#: ../../Zotlabs/Module/Filestorage.php:206
-msgid "Copy/paste this code to attach file to a post"
-msgstr "Kopier og lim inn denne koden for å legge til filen i et innlegg"
+#: ../../Zotlabs/Widget/Notifications.php:34
+#: ../../Zotlabs/Widget/Notifications.php:54
+#: ../../Zotlabs/Widget/Notifications.php:73
+#: ../../Zotlabs/Widget/Notifications.php:134
+#: ../../Zotlabs/Widget/Notifications.php:167
+#: ../../Zotlabs/Widget/Messages.php:55
+msgid "Filter by name or address"
+msgstr "Filtrer etter navn eller adresse"
-#: ../../Zotlabs/Storage/Browser.php:391
-#: ../../Zotlabs/Module/Filestorage.php:207
-msgid "Copy/paste this URL to link file from a web page"
-msgstr "Kopier og lim inn denne URL-en for å lenke til filen fra en webside"
+#: ../../Zotlabs/Widget/Notifications.php:44
+#: ../../Zotlabs/Module/Settings/Channel.php:259
+msgid "Unseen channel activity"
+msgstr "Ny kanalaktivitet"
-#: ../../Zotlabs/Storage/Browser.php:403
+#: ../../Zotlabs/Widget/Notifications.php:47
#, fuzzy
-msgid "Select All"
-msgstr "velg alle"
+msgid "Channel stream"
+msgstr "Kanalnavn"
-#: ../../Zotlabs/Storage/Browser.php:404
-msgid "Bulk Actions"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:50
+#: ../../Zotlabs/Widget/Notifications.php:88
+#: ../../Zotlabs/Widget/Notifications.php:123
+#: ../../Zotlabs/Module/Notifications.php:111
+msgid "Mark all seen"
+msgstr "Merk alle som sett"
-#: ../../Zotlabs/Storage/Browser.php:405
-msgid "Adjust Permissions"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:62
+#, fuzzy
+#| msgid "Private Forum"
+msgid "Private"
+msgstr "Privat forum"
-#: ../../Zotlabs/Storage/Browser.php:406
-msgid "Move or Copy"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:63
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Unseen private activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:408
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:215
-msgid "Download"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:66
+#, fuzzy
+#| msgid "Private Forum"
+msgid "Private stream"
+msgstr "Privat forum"
-#: ../../Zotlabs/Storage/Browser.php:409
-msgid "Info"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:81
+#: ../../Zotlabs/Widget/Activity_filter.php:53
+msgid "Events"
+msgstr "Hendelser"
-#: ../../Zotlabs/Storage/Browser.php:410
-msgid "Rename"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:82
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Unseen events activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:412
-msgid "Attachment BBcode"
+#: ../../Zotlabs/Widget/Notifications.php:85
+msgid "View events"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:413
-msgid "Embed BBcode"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:96
+#: ../../Zotlabs/Module/Connections.php:168
+msgid "New Connections"
+msgstr "Nye forbindelser"
-#: ../../Zotlabs/Storage/Browser.php:414
-msgid "Link BBcode"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:97
+#: ../../Zotlabs/Module/Settings/Channel.php:267
+msgid "New connections"
+msgstr "Nye forbindelser"
-#: ../../Zotlabs/Storage/Browser.php:487
-#, php-format
-msgid "You are using %1$s of your available file storage."
-msgstr "Du bruker %1$s av din tilgjengelige lagringsplass."
+#: ../../Zotlabs/Widget/Notifications.php:100
+#: ../../Zotlabs/Widget/Notifications.php:120
+#: ../../Zotlabs/Module/Photos.php:1114 ../../Zotlabs/Module/Photos.php:1126
+msgid "View all"
+msgstr "Vis alle"
-#: ../../Zotlabs/Storage/Browser.php:492
-#, php-format
-msgid "You are using %1$s of %2$s available file storage. (%3$s&#37;)"
-msgstr "Du bruker %1$s av %2$s tilgjengelig lagringsplass (%3$s&#37;)"
+#: ../../Zotlabs/Widget/Notifications.php:109
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Useen files activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:503
-msgid "WARNING:"
-msgstr "ADVARSEL:"
+#: ../../Zotlabs/Widget/Notifications.php:117
+#, fuzzy
+#| msgid "System Notifications"
+msgid "Unseen notifications"
+msgstr "Systemvarsler"
-#: ../../Zotlabs/Storage/Browser.php:544
-msgid "Create new folder"
-msgstr "Lag ny mappe"
+#: ../../Zotlabs/Widget/Notifications.php:132
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Unseen forums activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:546
-msgid "Upload file"
-msgstr "Last opp fil"
+#: ../../Zotlabs/Widget/Notifications.php:144
+msgid "Registrations"
+msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:547 ../../Zotlabs/Module/Photos.php:681
-#: ../../Zotlabs/Module/Cover_photo.php:381
-#: ../../Zotlabs/Module/Profile_photo.php:547
-#: ../../Zotlabs/Module/Embedphotos.php:189 ../../Zotlabs/Widget/Album.php:103
-#: ../../Zotlabs/Widget/Portfolio.php:114 ../../Zotlabs/Widget/Cdav.php:152
-#: ../../Zotlabs/Widget/Cdav.php:188
-msgid "Upload"
-msgstr "Last opp"
+#: ../../Zotlabs/Widget/Notifications.php:145
+#, fuzzy
+#| msgid "Unseen stream activity"
+msgid "Unseen registration activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../Zotlabs/Storage/Browser.php:558
-msgid "Drop files here to immediately upload"
-msgstr ""
+#: ../../Zotlabs/Widget/Notifications.php:155
+#: ../../Zotlabs/Module/Settings/Channel.php:270
+msgid "Unseen public stream activity"
+msgstr "Ny aktivitet i den offentlige strømmen"
-#: ../../Zotlabs/Storage/Browser.php:559
-#: ../../Zotlabs/Module/Filestorage.php:211
-msgid "Show in your contacts shared folder"
+#: ../../Zotlabs/Widget/Notifications.php:158
+msgid "Public stream"
msgstr ""
-#: ../../Zotlabs/Storage/Browser.php:561
-msgid ""
-"You can select files via the upload button or drop them right here or into "
-"an existing folder."
+#: ../../Zotlabs/Widget/Notifications.php:174
+msgid "Sorry, you have got no notifications at the moment"
msgstr ""
-#: ../../Zotlabs/Module/Menu.php:68
-msgid "Unable to update menu."
-msgstr "Ikke i stand til å oppdatere meny."
-
-#: ../../Zotlabs/Module/Menu.php:79
-msgid "Unable to create menu."
-msgstr "Ikke i stand til å lage meny."
-
-#: ../../Zotlabs/Module/Menu.php:161 ../../Zotlabs/Module/Menu.php:174
-msgid "Menu Name"
-msgstr "Menynavn"
+#: ../../Zotlabs/Widget/Bookmarkedchats.php:25
+msgid "Bookmarked Chatrooms"
+msgstr "Bokmerkede chatrom"
-#: ../../Zotlabs/Module/Menu.php:161
-msgid "Unique name (not visible on webpage) - required"
-msgstr "Unikt navn (ikke synlig på websiden) - påkrevet"
+#: ../../Zotlabs/Widget/Chatroom_list.php:26
+msgid "Overview"
+msgstr ""
-#: ../../Zotlabs/Module/Menu.php:162 ../../Zotlabs/Module/Menu.php:175
-msgid "Menu Title"
-msgstr "Menytittel"
+#: ../../Zotlabs/Widget/Tokens.php:41
+msgid "Add new guest"
+msgstr ""
-#: ../../Zotlabs/Module/Menu.php:162
-msgid "Visible on webpage - leave empty for no title"
-msgstr "Synlig på websiden - la stå tomt for ingen tittel"
+#: ../../Zotlabs/Widget/Tokens.php:49 ../../Zotlabs/Module/Lockview.php:236
+msgid "Guest access"
+msgstr ""
-#: ../../Zotlabs/Module/Menu.php:163
-msgid "Allow Bookmarks"
-msgstr "Tillat bokmerker"
+#: ../../Zotlabs/Widget/Newmember.php:38
+msgid "Profile Creation"
+msgstr "Oppretting av profil"
-#: ../../Zotlabs/Module/Menu.php:163 ../../Zotlabs/Module/Menu.php:222
-msgid "Menu may be used to store saved bookmarks"
-msgstr "Menyen kan brukes til å lagre lagrede bokmerker"
+#: ../../Zotlabs/Widget/Newmember.php:40
+msgid "Upload profile photo"
+msgstr "Last opp profilbilde"
-#: ../../Zotlabs/Module/Menu.php:164 ../../Zotlabs/Module/Menu.php:225
-msgid "Submit and proceed"
-msgstr "Send inn og fortsett"
+#: ../../Zotlabs/Widget/Newmember.php:41
+msgid "Upload cover photo"
+msgstr "Last opp omslagsbilde"
-#: ../../Zotlabs/Module/Menu.php:177 ../../Zotlabs/Module/Locs.php:124
-msgid "Drop"
-msgstr "Slett"
+#: ../../Zotlabs/Widget/Newmember.php:45
+msgid "Find and Connect with others"
+msgstr "Finn andre"
-#: ../../Zotlabs/Module/Menu.php:178 ../../Zotlabs/Module/Layouts.php:189
-#: ../../Zotlabs/Module/Blocks.php:155 ../../Zotlabs/Module/Webpages.php:261
-msgid "Created"
-msgstr "Laget"
+#: ../../Zotlabs/Widget/Newmember.php:47
+msgid "View the directory"
+msgstr "Se i katalogen"
-#: ../../Zotlabs/Module/Menu.php:179 ../../Zotlabs/Module/Layouts.php:190
-#: ../../Zotlabs/Module/Blocks.php:156 ../../Zotlabs/Module/Webpages.php:262
-msgid "Edited"
-msgstr "Endret"
+#: ../../Zotlabs/Widget/Newmember.php:48 ../../Zotlabs/Module/Go.php:39
+msgid "View friend suggestions"
+msgstr "Vis venneforslag"
-#: ../../Zotlabs/Module/Menu.php:180 ../../Zotlabs/Module/Connections.php:83
-#: ../../Zotlabs/Module/Connections.php:92
-#: ../../Zotlabs/Module/Notifications.php:101
-msgid "New"
-msgstr "Nye"
+#: ../../Zotlabs/Widget/Newmember.php:49
+msgid "Manage your connections"
+msgstr "Behandle forbindelser"
-#: ../../Zotlabs/Module/Menu.php:181
-msgid "Bookmarks allowed"
-msgstr "Bokmerker tillatt"
+#: ../../Zotlabs/Widget/Newmember.php:52
+msgid "Communicate"
+msgstr "Kommuniser"
-#: ../../Zotlabs/Module/Menu.php:183
-msgid "Delete this menu"
-msgstr "Slett denne menyen"
+#: ../../Zotlabs/Widget/Newmember.php:54
+msgid "View your channel homepage"
+msgstr "Vis kanalens hjemmeside"
-#: ../../Zotlabs/Module/Menu.php:184 ../../Zotlabs/Module/Menu.php:219
-msgid "Edit menu contents"
-msgstr "Endre menyinnholdet"
+#: ../../Zotlabs/Widget/Newmember.php:55
+msgid "View your network stream"
+msgstr "Vis nettverksstrømmen"
-#: ../../Zotlabs/Module/Menu.php:185
-msgid "Edit this menu"
-msgstr "Endre denne menyen"
+#: ../../Zotlabs/Widget/Newmember.php:61
+msgid "Documentation"
+msgstr "Dokumentasjon"
-#: ../../Zotlabs/Module/Menu.php:201
-msgid "Menu could not be deleted."
-msgstr "Menyen kunne ikke bli slettet."
+#: ../../Zotlabs/Widget/Newmember.php:64
+msgid "Missing Features?"
+msgstr "Noe som mangler?"
-#: ../../Zotlabs/Module/Menu.php:209 ../../Zotlabs/Module/Mitem.php:31
-msgid "Menu not found."
-msgstr "Menyen ble ikke funnet."
+#: ../../Zotlabs/Widget/Newmember.php:66
+msgid "Pin apps to navigation bar"
+msgstr "Fest apper til navigasjonslinjen"
-#: ../../Zotlabs/Module/Menu.php:214
-msgid "Edit Menu"
-msgstr "Endre meny"
+#: ../../Zotlabs/Widget/Newmember.php:67
+msgid "Install more apps"
+msgstr "Legg til flere apper"
-#: ../../Zotlabs/Module/Menu.php:218
-msgid "Add or remove entries to this menu"
-msgstr "Legg til eller fjern punkter i denne menyen"
+#: ../../Zotlabs/Widget/Newmember.php:78
+msgid "View public stream"
+msgstr ""
-#: ../../Zotlabs/Module/Menu.php:220
-msgid "Menu name"
-msgstr "Menynavn"
+#: ../../Zotlabs/Widget/Newmember.php:82
+#: ../../Zotlabs/Module/Settings/Display.php:201
+msgid "New Member Links"
+msgstr "Lenker for nye medlemmer"
-#: ../../Zotlabs/Module/Menu.php:220
-msgid "Must be unique, only seen by you"
-msgstr "Må være unik, ses bare av deg"
+#: ../../Zotlabs/Widget/Suggestions.php:53 ../../Zotlabs/Module/Suggest.php:71
+msgid "Ignore/Hide"
+msgstr "Ignorer/Skjul"
-#: ../../Zotlabs/Module/Menu.php:221
-msgid "Menu title"
-msgstr "Menytittel"
+#: ../../Zotlabs/Widget/Suggestions.php:58
+msgid "Suggestions"
+msgstr "Forslag"
-#: ../../Zotlabs/Module/Menu.php:221
-msgid "Menu title as seen by others"
-msgstr "Menytittelen andre ser"
+#: ../../Zotlabs/Widget/Suggestions.php:59
+msgid "See more..."
+msgstr "Se mer..."
-#: ../../Zotlabs/Module/Menu.php:222
-msgid "Allow bookmarks"
-msgstr "Tillat bokmerker"
+#: ../../Zotlabs/Widget/Messages.php:47
+msgid "Public and restricted conversations"
+msgstr "Offentlige og begrensede samtaler"
-#: ../../Zotlabs/Module/Menu.php:232 ../../Zotlabs/Module/Xchan.php:41
-#: ../../Zotlabs/Module/Mitem.php:134
-msgid "Not found."
-msgstr "Ikke funnet."
+#: ../../Zotlabs/Widget/Messages.php:48
+msgid "Private conversations"
+msgstr "Private samtaler"
-#: ../../Zotlabs/Module/Help.php:61
-msgid "Documentation Search"
-msgstr "Søk i dokumentasjon"
+#: ../../Zotlabs/Widget/Messages.php:49
+msgid "Starred conversations"
+msgstr "Stjernemerkede samtaler"
-#: ../../Zotlabs/Module/Help.php:169
-msgid "Not Found"
-msgstr "Ikke funnet"
+#: ../../Zotlabs/Widget/Messages.php:50
+msgid "Filed messages"
+msgstr "Lagrede meldinger"
-#: ../../Zotlabs/Module/Help.php:222
-msgid "$Projectname Documentation"
-msgstr "$Projectname dokumentasjon"
+#: ../../Zotlabs/Widget/Messages.php:53
+msgid "No conversations"
+msgstr "Ingen samtaler"
-#: ../../Zotlabs/Module/Help.php:233
-msgid "Contents"
-msgstr ""
+#: ../../Zotlabs/Widget/Messages.php:54
+msgid "Unseen reactions"
+msgstr "Usette reaksjoner"
-#: ../../Zotlabs/Module/Help.php:240
-msgid "Members"
-msgstr "Medlemmer"
+#: ../../Zotlabs/Widget/Messages.php:56
+msgid "Filter by file name"
+msgstr "Filtrer etter navn"
-#: ../../Zotlabs/Module/Help.php:241
-msgid "Administrators"
-msgstr ""
+#: ../../Zotlabs/Widget/Hq_controls.php:23
+msgid "Toggle post editor"
+msgstr "Vis redigering av innlegg"
-#: ../../Zotlabs/Module/Help.php:242
-msgid "Developers"
+#: ../../Zotlabs/Widget/Hq_controls.php:33
+msgid "Toggle personal notes"
msgstr ""
-#: ../../Zotlabs/Module/Help.php:243
-msgid "Tutorials"
+#: ../../Zotlabs/Widget/Hq_controls.php:43
+msgid "Channel activities"
msgstr ""
-#: ../../Zotlabs/Module/Help.php:261
-msgid "Help:"
-msgstr "Hjelp:"
+#: ../../Zotlabs/Widget/Album.php:87 ../../Zotlabs/Widget/Portfolio.php:95
+#: ../../Zotlabs/Module/Embedphotos.php:173 ../../Zotlabs/Module/Photos.php:784
+#: ../../Zotlabs/Module/Photos.php:1318
+msgid "View Photo"
+msgstr "Vis foto"
-#: ../../Zotlabs/Module/Editblock.php:79 ../../Zotlabs/Module/Editblock.php:95
-#: ../../Zotlabs/Module/Editlayout.php:79 ../../Zotlabs/Module/Editpost.php:24
-#: ../../Zotlabs/Module/Editwebpage.php:80
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:17
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:33
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:17
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:33
-msgid "Item not found"
-msgstr "Elementet ble ikke funnet."
+#: ../../Zotlabs/Widget/Album.php:104 ../../Zotlabs/Widget/Portfolio.php:116
+#: ../../Zotlabs/Module/Embedphotos.php:189 ../../Zotlabs/Module/Photos.php:815
+msgid "Edit Album"
+msgstr "Endre album"
-#: ../../Zotlabs/Module/Editblock.php:113 ../../Zotlabs/Module/Blocks.php:97
-#: ../../Zotlabs/Module/Blocks.php:153
-msgid "Block Name"
-msgstr "Byggeklossens navn"
+#: ../../Zotlabs/Widget/Album.php:106 ../../Zotlabs/Widget/Portfolio.php:118
+#: ../../Zotlabs/Widget/Cdav.php:152 ../../Zotlabs/Widget/Cdav.php:188
+#: ../../Zotlabs/Storage/Browser.php:550
+#: ../../Zotlabs/Module/Cover_photo.php:381
+#: ../../Zotlabs/Module/Embedphotos.php:191
+#: ../../Zotlabs/Module/Profile_photo.php:547
+#: ../../Zotlabs/Module/Photos.php:683
+msgid "Upload"
+msgstr "Last opp"
-#: ../../Zotlabs/Module/Editblock.php:138
-msgid "Edit Block"
-msgstr "Endre byggekloss"
+#: ../../Zotlabs/Widget/Pinned.php:95
+msgid "Share This"
+msgstr "Del dette"
-#: ../../Zotlabs/Module/Channel.php:150 ../../Zotlabs/Module/Hcard.php:37
-#: ../../Zotlabs/Module/Profile.php:62
-msgid "Posts and comments"
-msgstr "Innlegg og kommentarer"
+#: ../../Zotlabs/Widget/Pinned.php:120 ../../Zotlabs/Widget/Pinned.php:121
+#, php-format
+msgid "View %s's profile - %s"
+msgstr "Vis %s sin profil - %s"
-#: ../../Zotlabs/Module/Channel.php:157 ../../Zotlabs/Module/Hcard.php:44
-#: ../../Zotlabs/Module/Profile.php:69
-msgid "Only posts"
-msgstr "Kun innlegg"
+#: ../../Zotlabs/Widget/Pinned.php:154
+msgid "Don't show"
+msgstr "Ikke vis"
-#: ../../Zotlabs/Module/Channel.php:195 ../../Zotlabs/Module/Oep.php:82
-#: ../../Zotlabs/Module/Pubstream.php:55 ../../Zotlabs/Module/Display.php:53
-msgid "Malformed message id."
-msgstr ""
+#: ../../Zotlabs/Widget/Follow.php:27 ../../Zotlabs/Module/Connections.php:377
+#, php-format
+msgid "You have %1$.0f of %2$.0f allowed connections."
+msgstr "Du har %1$.0f av %2$.0f tillate forbindelser."
-#: ../../Zotlabs/Module/Channel.php:231
-msgid "Insufficient permissions. Request redirected to profile page."
-msgstr "Utilstrekkelig tillatelse. Forespørsel omdirigert til profilsiden."
+#: ../../Zotlabs/Widget/Follow.php:34
+msgid "Add New Connection"
+msgstr "Legg til ny forbindelse"
-#: ../../Zotlabs/Module/Channel.php:246 ../../Zotlabs/Module/Network.php:172
-msgid "Search Results For:"
-msgstr "Søkeresultat for:"
+#: ../../Zotlabs/Widget/Follow.php:35
+msgid "Enter channel address"
+msgstr "Skriv kanaladressen"
-#: ../../Zotlabs/Module/Channel.php:279 ../../Zotlabs/Module/Rpost.php:111
-#: ../../Zotlabs/Module/Network.php:213 ../../Zotlabs/Module/Hq.php:99
-#: ../../Zotlabs/Module/Pubstream.php:98 ../../Zotlabs/Module/Display.php:95
-msgid "Reset form"
-msgstr "Nullstill skjema"
+#: ../../Zotlabs/Widget/Follow.php:36
+msgid "Examples: bob@example.com, https://example.com/barbara"
+msgstr "Eksempel: ola@eksempel.no, https://eksempel.no/kari"
-#: ../../Zotlabs/Module/Channel.php:501 ../../Zotlabs/Module/Display.php:320
-msgid ""
-"You must enable javascript for your browser to be able to view this content."
+#: ../../Zotlabs/Widget/Notes.php:42
+msgid "Read mode"
msgstr ""
-#: ../../Zotlabs/Module/New_channel.php:148 ../../Zotlabs/Module/Manage.php:130
-#, php-format
-msgid "You have created %1$.0f of %2$.0f allowed channels."
-msgstr "Du har laget %1$.0f av %2$.0f tillatte kanaler."
-
-#: ../../Zotlabs/Module/New_channel.php:160
-msgid "Your real name is recommended."
+#: ../../Zotlabs/Widget/Notes.php:43
+msgid "Edit mode"
msgstr ""
-#: ../../Zotlabs/Module/New_channel.php:161
-msgid ""
-"Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation "
-"Group\""
+#: ../../Zotlabs/Widget/Notes.php:44
+msgid "Editing"
msgstr ""
-"Eksempel: \"Ola Nordmann\", \"Lisa og hestene hennes\", \"Fotball\", "
-"\"Sykkelgruppa\""
-#: ../../Zotlabs/Module/New_channel.php:166
-msgid ""
-"This will be used to create a unique network address (like an email address)."
+#: ../../Zotlabs/Widget/Notes.php:45
+msgid "Saving"
msgstr ""
-#: ../../Zotlabs/Module/New_channel.php:168
-msgid "Allowed characters are a-z 0-9, - and _"
+#: ../../Zotlabs/Widget/Notes.php:46
+msgid "Saved"
msgstr ""
-#: ../../Zotlabs/Module/New_channel.php:176
-#, fuzzy
-msgid "Channel name"
-msgstr "Kanalnavn"
-
-#: ../../Zotlabs/Module/New_channel.php:178
-#: ../../Zotlabs/Module/Register.php:513
-msgid "Choose a short nickname"
-msgstr "Velg et kort kallenavn"
-
-#: ../../Zotlabs/Module/New_channel.php:179
-#: ../../Zotlabs/Module/Settings/Channel.php:234
-msgid "Channel role"
-msgstr "Kanalrolle"
+#: ../../Zotlabs/Widget/Activity_order.php:94
+msgid "Posted Date"
+msgstr "Innleggsdato"
-#: ../../Zotlabs/Module/New_channel.php:182
-msgid "Create a Channel"
-msgstr ""
+#: ../../Zotlabs/Widget/Activity_order.php:98
+msgid "Order by last posted date"
+msgstr "Sorter etter dato innlegg ble postet"
-#: ../../Zotlabs/Module/New_channel.php:183
-msgid ""
-"A channel is a unique network identity. It can represent a person (social "
-"network profile), a forum (group), a business or celebrity page, a newsfeed, "
-"and many other things."
-msgstr ""
+#: ../../Zotlabs/Widget/Activity_order.php:102
+msgid "Commented Date"
+msgstr "Sist kommentert"
-#: ../../Zotlabs/Module/New_channel.php:184
-msgid ""
-"or <a href=\"import\">import an existing channel</a> from another location."
-msgstr ""
-"eller <a href=\"import\">importer en eksisterende kanal</a> fra et annet "
-"sted."
+#: ../../Zotlabs/Widget/Activity_order.php:106
+msgid "Order by last commented date"
+msgstr "Sorter etter dato for siste kommentar"
-#: ../../Zotlabs/Module/New_channel.php:189
-msgid "Validate"
-msgstr ""
+#: ../../Zotlabs/Widget/Activity_order.php:110
+msgid "Date Unthreaded"
+msgstr "Utrådet"
-#: ../../Zotlabs/Module/Oexchange.php:27
-msgid "Unable to find your hub."
-msgstr "Ikke i stand til å finne hubben din."
+#: ../../Zotlabs/Widget/Activity_order.php:114
+msgid "Order unthreaded by date"
+msgstr "Sorter innlegg og kommentarer uavhengig av hverandre etter dato"
-#: ../../Zotlabs/Module/Oexchange.php:41
-msgid "Post successful."
-msgstr "Innlegg vellykket."
+#: ../../Zotlabs/Widget/Activity_order.php:129
+msgid "Stream Order"
+msgstr "Sortering av innlegg"
-#: ../../Zotlabs/Module/Import_items.php:50
-msgid "Not a zip file or zip file corrupted."
-msgstr ""
+#: ../../Zotlabs/Widget/Activity_filter.php:44
+msgid "Direct Messages"
+msgstr "Direktemeldinger"
-#: ../../Zotlabs/Module/Import_items.php:121
-msgid "Import Items"
-msgstr "Importer elementer"
+#: ../../Zotlabs/Widget/Activity_filter.php:48
+msgid "Show direct (private) messages"
+msgstr "Vis direktemeldinger (private meldinger)"
-#: ../../Zotlabs/Module/Import_items.php:122
-msgid "Use this form to import existing posts and content from an export file."
+#: ../../Zotlabs/Widget/Activity_filter.php:57
+msgid "Show posts that include events"
msgstr ""
-"Bruk dette skjemaet for å importere eksisterende innlegg og innhold fra en "
-"eksportfil."
-#: ../../Zotlabs/Module/Import_items.php:123
-#: ../../Zotlabs/Module/Import.php:605
-msgid "File to Upload"
-msgstr "Fil som skal lastes opp"
+#: ../../Zotlabs/Widget/Activity_filter.php:63
+msgid "Polls"
+msgstr "Spørreskjema"
-#: ../../Zotlabs/Module/Import_items.php:136
-#: ../../Zotlabs/Module/Import.php:108
-msgid "Imported file is empty."
-msgstr "Importert fil er tom."
+#: ../../Zotlabs/Widget/Activity_filter.php:67
+msgid "Show posts that include polls"
+msgstr "Vis innlegg som inneholder spørreskjema"
-#: ../../Zotlabs/Module/Import_items.php:159
-msgid "Content import completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:90
+#, php-format
+msgid "Show posts related to the %s privacy group"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:164
-msgid "Chatroom import completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:99
+msgid "Show my privacy groups"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:170
-msgid "Channel calendar import 1/2 completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:123
+msgid "Show posts to this forum"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:176
-msgid "Channel calendar import 2/2 completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:134
+msgid "Show forums"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:181
-msgid "Menu import completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:148
+msgid "Starred Posts"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:186
-msgid "Wiki import completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:152
+msgid "Show posts that I have starred"
msgstr ""
-#: ../../Zotlabs/Module/Import_items.php:191
-msgid "Webpages import completed"
+#: ../../Zotlabs/Widget/Activity_filter.php:163
+msgid "Personal Posts"
msgstr ""
-#: ../../Zotlabs/Module/Rpost.php:117 ../../Zotlabs/Module/Editpost.php:113
-msgid "Edit post"
-msgstr "Endre innlegg"
-
-#: ../../Zotlabs/Module/Block.php:29 ../../Zotlabs/Module/Page.php:39
-msgid "Invalid item."
-msgstr "Ugyldig element."
-
-#: ../../Zotlabs/Module/Block.php:41 ../../Zotlabs/Module/Chanview.php:95
-#: ../../Zotlabs/Module/Cal.php:31 ../../Zotlabs/Module/Page.php:75
-#: ../../Zotlabs/Module/Wall_upload.php:30
-#: ../../Zotlabs/Module/Attach_edit.php:52 ../../Zotlabs/Module/Attach.php:22
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:44
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:44
-msgid "Channel not found."
-msgstr "Kanalen ble ikke funnet."
-
-#: ../../Zotlabs/Module/Lang.php:20
-msgid "Language App"
+#: ../../Zotlabs/Widget/Activity_filter.php:167
+msgid "Show posts that mention or involve me"
msgstr ""
-#: ../../Zotlabs/Module/Lang.php:20 ../../Zotlabs/Module/Invite.php:70
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:35
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:35
-msgid "Not Installed"
+#: ../../Zotlabs/Widget/Activity_filter.php:190
+#, php-format
+msgid "Show posts that I have filed to %s"
msgstr ""
-#: ../../Zotlabs/Module/Email_resend.php:12
-#: ../../Zotlabs/Module/Email_validation.php:25
-msgid "Token verification failed."
+#: ../../Zotlabs/Widget/Activity_filter.php:200
+msgid "Show filed post categories"
msgstr ""
-#: ../../Zotlabs/Module/Email_resend.php:30
-msgid "Email verification resent"
+#: ../../Zotlabs/Widget/Activity_filter.php:214
+msgid "Panel search"
msgstr ""
-#: ../../Zotlabs/Module/Email_resend.php:33
-msgid "Unable to resend email verification message."
-msgstr ""
+#: ../../Zotlabs/Widget/Activity_filter.php:224
+msgid "Filter by name"
+msgstr "Filtrer etter navn"
-#: ../../Zotlabs/Module/Setup.php:180
-msgid "$Projectname Server - Setup"
-msgstr "$Projectname-tjener - oppsett"
+#: ../../Zotlabs/Widget/Activity_filter.php:239
+msgid "Remove active filter"
+msgstr ""
-#: ../../Zotlabs/Module/Setup.php:184
-msgid "Could not connect to database."
-msgstr "Fikk ikke kontakt med databasen."
+#: ../../Zotlabs/Widget/Activity_filter.php:255
+msgid "Stream Filters"
+msgstr "Filtere for tidslinjen"
-#: ../../Zotlabs/Module/Setup.php:188
-msgid ""
-"Could not connect to specified site URL. Possible SSL certificate or DNS "
-"issue."
-msgstr ""
-"Fikk ikke kontakt med det angitte nettstedets URL. Problemet kan muligens "
-"skyldes SSL-sertifikatet eller DNS."
+#: ../../Zotlabs/Widget/Channel_activities.php:29
+#: ../../Zotlabs/Module/Go.php:28
+msgid "Welcome"
+msgstr "Velkommen"
-#: ../../Zotlabs/Module/Setup.php:195
-msgid "Could not create table."
-msgstr "Kunne ikke lage tabellen."
+#: ../../Zotlabs/Widget/Channel_activities.php:46
+#, fuzzy
+#| msgid "Recent activity"
+msgid "No recent activities"
+msgstr "Nylig aktivitet"
-#: ../../Zotlabs/Module/Setup.php:201
-msgid "Your site database has been installed."
-msgstr "Databasen til ditt nettsted har blitt installert."
+#: ../../Zotlabs/Widget/Channel_activities.php:214
+msgctxt "noun"
+msgid "new connection"
+msgid_plural "new connections"
+msgstr[0] "ny forbindelse"
+msgstr[1] "nye forbindelser"
-#: ../../Zotlabs/Module/Setup.php:207
-msgid ""
-"You may need to import the file \"install/schema_xxx.sql\" manually using a "
-"database client."
-msgstr ""
-"Du må kanskje importere filen \"install/schmea_xxx.sql\" manuelt ved å bruke "
-"en databaseklient."
+#: ../../Zotlabs/Widget/Channel_activities.php:220
+msgctxt "noun"
+msgid "notice"
+msgid_plural "notices"
+msgstr[0] "varsel"
+msgstr[1] "varsel"
-#: ../../Zotlabs/Module/Setup.php:208 ../../Zotlabs/Module/Setup.php:274
-#: ../../Zotlabs/Module/Setup.php:799
-msgid "Please see the file \"install/INSTALL.txt\"."
-msgstr "Vennligst les filen \"install/INSTALL.txt\"."
+#: ../../Zotlabs/Widget/Affinity.php:36
+#: ../../Zotlabs/Module/Contactedit.php:281
+#: ../../Zotlabs/Module/Connedit.php:579
+msgid "Me"
+msgstr "Meg"
-#: ../../Zotlabs/Module/Setup.php:271
-msgid "System check"
-msgstr "Systemsjekk"
+#: ../../Zotlabs/Widget/Affinity.php:37
+#: ../../Zotlabs/Module/Contactedit.php:282
+#: ../../Zotlabs/Module/Connedit.php:580
+msgid "Family"
+msgstr "Familie"
-#: ../../Zotlabs/Module/Setup.php:275 ../../Zotlabs/Module/Photos.php:955
-#: ../../Zotlabs/Module/Cal.php:200 ../../Zotlabs/Module/Cdav.php:1027
-msgid "Next"
-msgstr "Neste"
+#: ../../Zotlabs/Widget/Affinity.php:39
+#: ../../Zotlabs/Module/Contactedit.php:284
+#: ../../Zotlabs/Module/Connedit.php:582
+msgid "Acquaintances"
+msgstr "Bekjente"
-#: ../../Zotlabs/Module/Setup.php:276
-msgid "Check again"
-msgstr "Sjekk igjen"
+#: ../../Zotlabs/Widget/Affinity.php:40
+#: ../../Zotlabs/Module/Contactedit.php:285
+#: ../../Zotlabs/Module/Connedit.php:583
+#: ../../Zotlabs/Module/Connections.php:97
+#: ../../Zotlabs/Module/Connections.php:111
+msgid "All"
+msgstr "Alle"
-#: ../../Zotlabs/Module/Setup.php:297
-msgid "Database connection"
-msgstr "Databaseforbindelse"
+#: ../../Zotlabs/Widget/Affinity.php:60
+#: ../../Zotlabs/Module/Contactedit.php:618
+msgid "Refresh"
+msgstr "Forny"
-#: ../../Zotlabs/Module/Setup.php:298
-msgid ""
-"In order to install $Projectname we need to know how to connect to your "
-"database."
-msgstr ""
-"For å installere $Projectname må du oppgi hvordan din database kan kontaktes."
+#: ../../Zotlabs/Widget/Admin.php:27 ../../Zotlabs/Module/Admin/Site.php:402
+msgid "Site"
+msgstr "Nettsted"
-#: ../../Zotlabs/Module/Setup.php:299
-msgid ""
-"Please contact your hosting provider or site administrator if you have "
-"questions about these settings."
-msgstr ""
-"Vennligst kontakt din nettstedstilbyder eller nettstedsadministrator hvis du "
-"har spørsmål om disse innstillingene."
+#: ../../Zotlabs/Widget/Admin.php:28
+#: ../../Zotlabs/Module/Admin/Accounts.php:208
+#: ../../Zotlabs/Module/Admin/Accounts.php:227
+#: ../../Zotlabs/Module/Admin.php:97
+msgid "Accounts"
+msgstr "Kontoer"
-#: ../../Zotlabs/Module/Setup.php:300
-msgid ""
-"The database you specify below should already exist. If it does not, please "
-"create it before continuing."
+#: ../../Zotlabs/Widget/Admin.php:28 ../../Zotlabs/Widget/Admin.php:66
+msgid "Member registrations waiting for confirmation"
msgstr ""
-"Databasen du oppgir nedenfor må finnes på forhånd. Hvis den ikke finnes, "
-"vennligst lag den før du fortsetter."
-
-#: ../../Zotlabs/Module/Setup.php:304
-msgid "Database Server Name"
-msgstr "Navn på databasetjener"
-#: ../../Zotlabs/Module/Setup.php:304
-msgid "Default is 127.0.0.1"
-msgstr "Standard er 127.0.0.1"
+#: ../../Zotlabs/Widget/Admin.php:30
+#: ../../Zotlabs/Module/Admin/Security.php:107
+msgid "Security"
+msgstr "Sikkerhet"
-#: ../../Zotlabs/Module/Setup.php:305
-msgid "Database Port"
-msgstr "Databaseport"
+#: ../../Zotlabs/Widget/Admin.php:32 ../../Zotlabs/Module/Admin/Addons.php:125
+#: ../../Zotlabs/Module/Admin/Addons.php:192
+msgid "Addons"
+msgstr "Tillegg"
-#: ../../Zotlabs/Module/Setup.php:305
-msgid "Communication port number - use 0 for default"
-msgstr "Kommunikasjonsportnummer - bruk 0 for standard"
+#: ../../Zotlabs/Widget/Admin.php:33 ../../Zotlabs/Module/Admin/Themes.php:139
+#: ../../Zotlabs/Module/Admin/Themes.php:173
+msgid "Themes"
+msgstr "Utseende"
-#: ../../Zotlabs/Module/Setup.php:306
-msgid "Database Login Name"
-msgstr "Database innloggingsnavn"
+#: ../../Zotlabs/Widget/Admin.php:34
+msgid "Inspect queue"
+msgstr "Inspiser kø"
-#: ../../Zotlabs/Module/Setup.php:307
-msgid "Database Login Password"
-msgstr "Database innloggingspassord"
+#: ../../Zotlabs/Widget/Admin.php:35
+msgid "Queueworker"
+msgstr ""
-#: ../../Zotlabs/Module/Setup.php:308
-msgid "Database Name"
-msgstr "Databasenavn"
+#: ../../Zotlabs/Widget/Admin.php:36 ../../Zotlabs/Module/Admin/Profs.php:169
+msgid "Profile Fields"
+msgstr "Profilfelter"
-#: ../../Zotlabs/Module/Setup.php:309
-msgid "Database Type"
-msgstr "Databasetype"
+#: ../../Zotlabs/Widget/Admin.php:37
+msgid "DB updates"
+msgstr "Databaseoppdateringer"
-#: ../../Zotlabs/Module/Setup.php:311 ../../Zotlabs/Module/Setup.php:351
-msgid "Site administrator email address"
-msgstr "E-postadressen til administrator ved nettstedet"
+#: ../../Zotlabs/Widget/Admin.php:54 ../../Zotlabs/Widget/Admin.php:64
+#: ../../Zotlabs/Module/Admin/Logs.php:84
+msgid "Logs"
+msgstr "Logger"
-#: ../../Zotlabs/Module/Setup.php:311 ../../Zotlabs/Module/Setup.php:351
-msgid ""
-"Your account email address must match this in order to use the web admin "
-"panel."
+#: ../../Zotlabs/Widget/Admin.php:62
+msgid "Addon Features"
msgstr ""
-"Din konto sin e-postadresse må være lik denne for å kunne bruke web-"
-"administrasjonspanelet."
-#: ../../Zotlabs/Module/Setup.php:312 ../../Zotlabs/Module/Setup.php:353
-msgid "Website URL"
-msgstr "Nettstedets URL"
+#: ../../Zotlabs/Widget/Permcats.php:43
+msgid "Add new role"
+msgstr "Ny rolle"
-#: ../../Zotlabs/Module/Setup.php:312 ../../Zotlabs/Module/Setup.php:353
-msgid "Please use SSL (https) URL if available."
-msgstr "Vennligst bruk SSL (https) URL hvis tilgjengelig."
+#: ../../Zotlabs/Widget/Permcats.php:92
+#: ../../Zotlabs/Module/Contactedit.php:395
+msgid "Contact roles"
+msgstr "Kontaktroller"
-#: ../../Zotlabs/Module/Setup.php:313 ../../Zotlabs/Module/Setup.php:355
-msgid "Please select a default timezone for your website"
-msgstr "Vennligst velg en standard tidssone for ditt nettsted"
+#: ../../Zotlabs/Widget/Permcats.php:93
+msgid "Role members"
+msgstr "Medlemmer"
-#: ../../Zotlabs/Module/Setup.php:340
-msgid "Site settings"
-msgstr "Nettstedets innstillinger"
+#: ../../Zotlabs/Widget/Activity.php:55
+msgctxt "widget"
+msgid "Activity"
+msgstr "Aktivitet"
-#: ../../Zotlabs/Module/Setup.php:394
-msgid "PHP version 8.0 or greater is required."
-msgstr ""
+#: ../../Zotlabs/Widget/Photo.php:54 ../../Zotlabs/Widget/Photo_rand.php:63
+msgid "photo/image"
+msgstr "foto/bilde"
-#: ../../Zotlabs/Module/Setup.php:395
-msgid "PHP version"
-msgstr ""
+#: ../../Zotlabs/Widget/Savedsearch.php:81
+msgid "Remove term"
+msgstr "Fjern begrep"
-#: ../../Zotlabs/Module/Setup.php:411
-msgid "Could not find a command line version of PHP in the web server PATH."
-msgstr "Fant ikke en kommandolinjeversjon av PHP i webtjenerens sti (PATH)."
+#: ../../Zotlabs/Widget/Chatroom_members.php:17
+msgid "Chat Members"
+msgstr "Medlemmer"
-#: ../../Zotlabs/Module/Setup.php:412
-msgid ""
-"If you don't have a command line version of PHP installed on server, you "
-"will not be able to run background polling via cron."
-msgstr ""
-"Hvis du ikke har en kommandolinjeversjon av PHP installert på tjeneren, så "
-"vil du ikke kunne kjøre bakgrunnshenting via cron."
+#: ../../Zotlabs/Widget/Suggestedchats.php:36
+msgid "Suggested Chatrooms"
+msgstr "Foreslåtte chatrom"
-#: ../../Zotlabs/Module/Setup.php:416
-msgid "PHP executable path"
-msgstr "PHP-kjørefilens sti"
+#: ../../Zotlabs/Widget/Appcategories.php:49
+msgid "App Categories"
+msgstr "Appkategorier"
-#: ../../Zotlabs/Module/Setup.php:416
-msgid ""
-"Enter full path to php executable. You can leave this blank to continue the "
-"installation."
-msgstr ""
-"Skriv full sti til kjørefilen for PHP. Du kan la denne stå blank for å "
-"fortsette installasjonen."
+#: ../../Zotlabs/Widget/Settings_menu.php:37
+msgid "Account settings"
+msgstr "Kontoinnstillinger"
-#: ../../Zotlabs/Module/Setup.php:421
-msgid "Command line PHP"
-msgstr "Kommandolinje PHP"
+#: ../../Zotlabs/Widget/Settings_menu.php:43
+msgid "Channel settings"
+msgstr "Kanalinnstillinger"
-#: ../../Zotlabs/Module/Setup.php:431
-msgid ""
-"Unable to check command line PHP, as shell_exec() is disabled. This is "
-"required."
-msgstr ""
+#: ../../Zotlabs/Widget/Settings_menu.php:49
+msgid "Privacy settings"
+msgstr "Personverninnstillinger"
-#: ../../Zotlabs/Module/Setup.php:435
-msgid ""
-"The command line version of PHP on your system does not have "
-"\"register_argc_argv\" enabled."
-msgstr ""
-"Kommandolinjeversjonen av PHP på ditt system har ikke \"register_argc_argv\" "
-"påskrudd."
+#: ../../Zotlabs/Widget/Settings_menu.php:56
+msgid "Display settings"
+msgstr "Visningsinnstillinger"
-#: ../../Zotlabs/Module/Setup.php:436
-msgid "This is required for message delivery to work."
-msgstr "Dette er påkrevd for at meldingslevering skal virke."
+#: ../../Zotlabs/Widget/Settings_menu.php:63
+msgid "Manage locations"
+msgstr ""
-#: ../../Zotlabs/Module/Setup.php:439
-msgid "PHP register_argc_argv"
-msgstr "PHP register_argc_argv"
+#: ../../Zotlabs/Widget/Archive.php:49
+msgid "Archives"
+msgstr "Arkiv"
-#: ../../Zotlabs/Module/Setup.php:459
-msgid ""
-"This is not sufficient to upload larger images or files. You should be able "
-"to upload at least 4 MB at once."
+#: ../../Zotlabs/Widget/Cdav.php:41
+msgid "Select Channel"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:461
-#, php-format
-msgid ""
-"Your max allowed total upload size is set to %s. Maximum size of one file to "
-"upload is set to %s. You are allowed to upload up to %d files at once."
+#: ../../Zotlabs/Widget/Cdav.php:46
+msgid "Read-write"
msgstr ""
-"Den største totale opplastingsstørrelsen du er tillatt er satt til %s. "
-"Filstørrelsen på en enkelt fil er satt til å maksimalt være %s. Du har lov "
-"til å laste opp inntil %d filer samtidig."
-#: ../../Zotlabs/Module/Setup.php:467
-msgid "You can adjust these settings in the server php.ini file."
+#: ../../Zotlabs/Widget/Cdav.php:47
+msgid "Read-only"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:469
-msgid "PHP upload limits"
-msgstr "PHP opplastingsgrenser"
-
-#: ../../Zotlabs/Module/Setup.php:492
-msgid ""
-"Error: the \"openssl_pkey_new\" function on this system is not able to "
-"generate encryption keys"
+#: ../../Zotlabs/Widget/Cdav.php:133
+msgid "Channel Calendar"
msgstr ""
-"Feil: \"openssl_pkey_new\"-funksjonen på dette systemet er ikke i stand til "
-"å lage krypteringsnøkler"
-#: ../../Zotlabs/Module/Setup.php:493
-msgid ""
-"If running under Windows, please see \"http://www.php.net/manual/en/openssl."
-"installation.php\"."
+#: ../../Zotlabs/Widget/Cdav.php:135 ../../Zotlabs/Widget/Cdav.php:149
+#: ../../Zotlabs/Module/Cdav.php:1046
+msgid "CalDAV Calendars"
msgstr ""
-"Ved kjøring på Windows, vennligst se \"http://www.php.net/manual/en/openssl."
-"installation.php\"."
-
-#: ../../Zotlabs/Module/Setup.php:496
-msgid "Generate encryption keys"
-msgstr "Lag krypteringsnøkler"
-#: ../../Zotlabs/Module/Setup.php:500
-msgid "Error: the sodium encryption library is not installed."
+#: ../../Zotlabs/Widget/Cdav.php:137
+msgid "Shared CalDAV Calendars"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:502
-#, fuzzy
-#| msgid "Generate encryption keys"
-msgid "Generate ed25519 encryption keys"
-msgstr "Lag krypteringsnøkler"
-
-#: ../../Zotlabs/Module/Setup.php:507
-msgid ""
-"Error: one of \"bcmath\" or \"gmp\" (bigmath library) extensions are "
-"required."
+#: ../../Zotlabs/Widget/Cdav.php:141
+msgid "Share this calendar"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:509
-msgid "Bigmath library (either bcmath or gmp)"
+#: ../../Zotlabs/Widget/Cdav.php:143
+msgid "Calendar name and color"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:526
-msgid "libCurl PHP module"
-msgstr "libCurl PHP-modul"
-
-#: ../../Zotlabs/Module/Setup.php:527
-msgid "GD graphics PHP module"
-msgstr "GD graphics PHP-modul"
-
-#: ../../Zotlabs/Module/Setup.php:528
-msgid "OpenSSL PHP module"
-msgstr "OpenSSL PHP-modul"
-
-#: ../../Zotlabs/Module/Setup.php:529
-msgid "PDO database PHP module"
+#: ../../Zotlabs/Widget/Cdav.php:145
+msgid "Create new CalDAV calendar"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:530
-msgid "mb_string PHP module"
-msgstr "mb_string PHP-modul"
-
-#: ../../Zotlabs/Module/Setup.php:531
-msgid "xml PHP module"
-msgstr "xml PHP modul"
+#: ../../Zotlabs/Widget/Cdav.php:146 ../../Zotlabs/Widget/Cdav.php:184
+#: ../../Zotlabs/Storage/Browser.php:372 ../../Zotlabs/Storage/Browser.php:548
+#: ../../Zotlabs/Module/Webpages.php:248 ../../Zotlabs/Module/Connedit.php:747
+#: ../../Zotlabs/Module/Layouts.php:183 ../../Zotlabs/Module/Menu.php:182
+#: ../../Zotlabs/Module/Blocks.php:157 ../../Zotlabs/Module/New_channel.php:190
+#: ../../Zotlabs/Module/Cdav.php:1050 ../../Zotlabs/Module/Cdav.php:1383
+msgid "Create"
+msgstr "Lag"
-#: ../../Zotlabs/Module/Setup.php:532
-msgid "zip PHP module"
+#: ../../Zotlabs/Widget/Cdav.php:147
+msgid "Calendar Name"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:533
-#, fuzzy
-#| msgid "xml PHP module"
-msgid "intl PHP module"
-msgstr "xml PHP modul"
-
-#: ../../Zotlabs/Module/Setup.php:537 ../../Zotlabs/Module/Setup.php:539
-msgid "Apache mod_rewrite module"
-msgstr "Apache mod_rewrite-modul"
-
-#: ../../Zotlabs/Module/Setup.php:537
-msgid ""
-"Error: Apache webserver mod-rewrite module is required but not installed."
+#: ../../Zotlabs/Widget/Cdav.php:148
+msgid "Calendar Tools"
msgstr ""
-"Feil: Apache web-tjenerens mod-rewrite-modul er påkrevd, men ikke installert."
-#: ../../Zotlabs/Module/Setup.php:543 ../../Zotlabs/Module/Setup.php:546
-msgid "exec"
+#: ../../Zotlabs/Widget/Cdav.php:149 ../../Zotlabs/Module/Cdav.php:1046
+msgid "Channel Calendars"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:543
-msgid ""
-"Error: exec is required but is either not installed or has been disabled in "
-"php.ini"
+#: ../../Zotlabs/Widget/Cdav.php:150
+msgid "Import calendar"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:549 ../../Zotlabs/Module/Setup.php:552
-msgid "shell_exec"
+#: ../../Zotlabs/Widget/Cdav.php:151
+msgid "Select a calendar to import to"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:549
-msgid ""
-"Error: shell_exec is required but is either not installed or has been "
-"disabled in php.ini"
+#: ../../Zotlabs/Widget/Cdav.php:178
+msgid "Addressbooks"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:557
-msgid "Error: libCURL PHP module required but not installed."
-msgstr "Feil: libCURL PHP-modul er påkrevd, men er ikke installert."
-
-#: ../../Zotlabs/Module/Setup.php:561
-msgid ""
-"Error: GD PHP module with JPEG support or ImageMagick graphics library "
-"required but not installed."
+#: ../../Zotlabs/Widget/Cdav.php:180
+msgid "Addressbook name"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:565
-msgid "Error: openssl PHP module required but not installed."
-msgstr "Feil: openssl PHP-modul er påkrevd, men er ikke installert."
-
-#: ../../Zotlabs/Module/Setup.php:571
-msgid ""
-"Error: PDO database PHP module missing a driver for either mysql or pgsql."
+#: ../../Zotlabs/Widget/Cdav.php:182
+msgid "Create new addressbook"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:576
-msgid "Error: PDO database PHP module required but not installed."
+#: ../../Zotlabs/Widget/Cdav.php:183
+msgid "Addressbook Name"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:580
-msgid "Error: mb_string PHP module required but not installed."
-msgstr "Feil: mb_string PHP-modul er påkrevd, men er ikke installert."
-
-#: ../../Zotlabs/Module/Setup.php:584
-msgid "Error: xml PHP module required for DAV but not installed."
-msgstr "Feil: XML PHP modul er påkrevet for DAV, men den er ikke installert."
-
-#: ../../Zotlabs/Module/Setup.php:588
-msgid "Error: zip PHP module required but not installed."
+#: ../../Zotlabs/Widget/Cdav.php:185
+msgid "Addressbook Tools"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:592
-#, fuzzy
-#| msgid "Error: openssl PHP module required but not installed."
-msgid "Error: intl PHP module required but not installed."
-msgstr "Feil: openssl PHP-modul er påkrevd, men er ikke installert."
-
-#: ../../Zotlabs/Module/Setup.php:611 ../../Zotlabs/Module/Setup.php:620
-msgid ".htconfig.php is writable"
-msgstr ".htconfig.php kan skrives til"
-
-#: ../../Zotlabs/Module/Setup.php:616
-msgid ""
-"The web installer needs to be able to create a file called \".htconfig.php\" "
-"in the top folder of your web server and it is unable to do so."
+#: ../../Zotlabs/Widget/Cdav.php:186
+msgid "Import addressbook"
msgstr ""
-"Web-installasjonen må kunne lage en fil kalt \".htconfig.php\" i "
-"toppkatalogen til web-tjeneren din, men dette får den ikke til."
-#: ../../Zotlabs/Module/Setup.php:617
-msgid ""
-"This is most often a permission setting, as the web server may not be able "
-"to write files in your folder - even if you can."
+#: ../../Zotlabs/Widget/Cdav.php:187
+msgid "Select an addressbook to import to"
msgstr ""
-"Dette er oftest tillatelsesinnstilling, ettersom webtjeneren kanskje kan "
-"skrive til filer i din mappe - selv om du kan."
-#: ../../Zotlabs/Module/Setup.php:618
-msgid "Please see install/INSTALL.txt for additional information."
+#: ../../Zotlabs/Widget/Privacygroups.php:45
+msgid "Add new group"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:634
-msgid ""
-"This software uses the Smarty3 template engine to render its web views. "
-"Smarty3 compiles templates to PHP to speed up rendering."
-msgstr ""
+#: ../../Zotlabs/Widget/Privacygroups.php:54
+#: ../../Zotlabs/Module/Contactedit.php:429
+msgid "Privacy groups"
+msgstr "Personverngrupper"
-#: ../../Zotlabs/Module/Setup.php:635
-#, php-format
-msgid ""
-"In order to store these compiled templates, the web server needs to have "
-"write access to the directory %s under the top level web folder."
+#: ../../Zotlabs/Storage/Browser.php:296
+msgid "Change filename to"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:636 ../../Zotlabs/Module/Setup.php:657
-msgid ""
-"Please ensure that the user that your web server runs as (e.g. www-data) has "
-"write access to this folder."
+#: ../../Zotlabs/Storage/Browser.php:313 ../../Zotlabs/Storage/Browser.php:398
+msgid "Select a target location"
msgstr ""
-"Vennligst sikre at brukeren som din web-tjeneste kjører som (for eksempel "
-"www-data) har skrivetilgang til denne katalogen."
-#: ../../Zotlabs/Module/Setup.php:637
-#, php-format
-msgid ""
-"Note: as a security measure, you should give the web server write access to "
-"%s only--not the template files (.tpl) that it contains."
+#: ../../Zotlabs/Storage/Browser.php:314 ../../Zotlabs/Storage/Browser.php:399
+msgid "Copy to target location"
msgstr ""
-"Merknad: som et sikkerhetstiltak bør du bare gi webtjerenn skrivetilgang til "
-"%s - ikke til malfilene (.tpl) som den inneholder."
-
-#: ../../Zotlabs/Module/Setup.php:640
-#, php-format
-msgid "%s is writable"
-msgstr "%s kan skrives til"
-#: ../../Zotlabs/Module/Setup.php:656
-msgid ""
-"This software uses the store directory to save uploaded files. The web "
-"server needs to have write access to the store directory under the top level "
-"web folder"
+#: ../../Zotlabs/Storage/Browser.php:315 ../../Zotlabs/Storage/Browser.php:397
+msgid "Set permissions for all files and sub folders"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:660
-msgid "store is writable"
-msgstr "lageret kan skrives til"
-
-#: ../../Zotlabs/Module/Setup.php:692
-msgid ""
-"SSL certificate cannot be validated. Fix certificate or disable https access "
-"to this site."
-msgstr ""
-"SSL-sertifikatet kan ikke kontrolleres. Fiks sertifikatet eller skru av "
-"https tilgang til dette nettstedet."
+#: ../../Zotlabs/Storage/Browser.php:316
+msgid "Notify your contacts about this file"
+msgstr "Varsle dine kontakter om denne filen"
-#: ../../Zotlabs/Module/Setup.php:693
-msgid ""
-"If you have https access to your website or allow connections to TCP port "
-"443 (the https: port), you MUST use a browser-valid certificate. You MUST "
-"NOT use self-signed certificates!"
+#: ../../Zotlabs/Storage/Browser.php:355
+msgid "File category"
msgstr ""
-"Hvis du har HTTPS-tilgang til ditt nettsted eller tillater forbindelser til "
-"TCP port 443 (HTTPS-porten), så MÅ du bruke nettlesergodkjent sertifkater. "
-"Du MÅ IKKE bruke egensignert sertifikater!"
-#: ../../Zotlabs/Module/Setup.php:694
-msgid ""
-"This restriction is incorporated because public posts from you may for "
-"example contain references to images on your own hub."
-msgstr ""
-"Denne begrensningen er tatt inn fordi offentlige innlegg fra deg kan for "
-"eksempel inneholde referanser til bilder på din egen hub."
+#: ../../Zotlabs/Storage/Browser.php:369
+msgid "Total"
+msgstr "Totalt"
-#: ../../Zotlabs/Module/Setup.php:695
-msgid ""
-"If your certificate is not recognized, members of other sites (who may "
-"themselves have valid certificates) will get a warning message on their own "
-"site complaining about security issues."
-msgstr ""
-"Hvis sertifikatet ditt ikke gjenkjennes, så vil medlemmer på andre "
-"nettsteder (som selv kan ha godkjente sertifikater) få en beskjed med en "
-"advarsel på deres eget nettsted som klager over sikkerhetsproblemer."
+#: ../../Zotlabs/Storage/Browser.php:371
+msgid "Shared"
+msgstr "Delt"
-#: ../../Zotlabs/Module/Setup.php:696
-msgid ""
-"This can cause usability issues elsewhere (not just on your own site) so we "
-"must insist on this requirement."
+#: ../../Zotlabs/Storage/Browser.php:373
+msgid "Add Files"
msgstr ""
-"Dette kan gi problemer med brukervennlighet (ikke bare på ditt eget "
-"nettsted), så vi må insistere på dette kravet."
-#: ../../Zotlabs/Module/Setup.php:697
-msgid ""
-"Providers are available that issue free certificates which are browser-valid."
-msgstr ""
-"Det finnes tilbydere som utsteder gratis sertifikater som er gyldige i "
-"nettlesere."
+#: ../../Zotlabs/Storage/Browser.php:388
+#: ../../Zotlabs/Module/Sharedwithme.php:110
+msgid "Last Modified"
+msgstr "Sist endret"
-#: ../../Zotlabs/Module/Setup.php:698
-msgid ""
-"If you are confident that the certificate is valid and signed by a trusted "
-"authority, check to see if you have failed to install an intermediate cert. "
-"These are not normally required by browsers, but are required for server-to-"
-"server communications."
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:389
+msgid "parent"
+msgstr "opp et nivå"
-#: ../../Zotlabs/Module/Setup.php:700
-msgid "SSL certificate validation"
-msgstr "SSL sertifikat-kontroll"
+#: ../../Zotlabs/Storage/Browser.php:394
+#: ../../Zotlabs/Module/Filestorage.php:206
+msgid "Copy/paste this code to attach file to a post"
+msgstr "Kopier og lim inn denne koden for å legge til filen i et innlegg"
-#: ../../Zotlabs/Module/Setup.php:706
-msgid ""
-"Url rewrite in .htaccess is not working. Check your server configuration."
-"Test: "
-msgstr ""
-"URL omskriving (rewrite) i .htaccess virker ikke. Sjekk konfigurasjonen til "
-"tjeneren din. Test: "
+#: ../../Zotlabs/Storage/Browser.php:395
+#: ../../Zotlabs/Module/Filestorage.php:207
+msgid "Copy/paste this URL to link file from a web page"
+msgstr "Kopier og lim inn denne URL-en for å lenke til filen fra en webside"
-#: ../../Zotlabs/Module/Setup.php:709
-msgid "Url rewrite is working"
-msgstr "URL rewrite virker"
+#: ../../Zotlabs/Storage/Browser.php:406
+msgid "Select All"
+msgstr "Velg alle"
-#: ../../Zotlabs/Module/Setup.php:722
-msgid ""
-"The database configuration file \".htconfig.php\" could not be written. "
-"Please use the enclosed text to create a configuration file in your web "
-"server root."
+#: ../../Zotlabs/Storage/Browser.php:407
+msgid "Bulk Actions"
msgstr ""
-"Databasekonfigurasjonsfilen \".htconfig.php\" kunne ikke skrives. Vennligst "
-"bruk den medfølgende teksten for å lage en konfigurasjonsfil i toppkatalogen "
-"av din web-tjener."
-#: ../../Zotlabs/Module/Setup.php:751
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:401
-msgid "Errors encountered creating database tables."
-msgstr "Feil oppstod under opprettelsen av databasetabeller."
-
-#: ../../Zotlabs/Module/Setup.php:797
-msgid "<h1>What next?</h1>"
+#: ../../Zotlabs/Storage/Browser.php:408
+msgid "Adjust Permissions"
msgstr ""
-#: ../../Zotlabs/Module/Setup.php:798
-msgid ""
-"IMPORTANT: You will need to [manually] setup a scheduled task for the poller."
+#: ../../Zotlabs/Storage/Browser.php:409
+msgid "Move or Copy"
msgstr ""
-"VIKTIG: Du må [manuelt] sette opp en automatisert tidfestet oppgave til "
-"bakgrunnshenteren."
-
-#: ../../Zotlabs/Module/Bookmarks.php:62
-msgid "Bookmark added"
-msgstr "Bokmerke lagt til"
-
-#: ../../Zotlabs/Module/Bookmarks.php:101
-msgid "My Connections Bookmarks"
-msgstr "Mine forbindelsers bokmerker"
-#: ../../Zotlabs/Module/Z6trans.php:19
-msgid "Update to Hubzilla 5.0 step 2"
+#: ../../Zotlabs/Storage/Browser.php:412
+msgid "Info"
msgstr ""
-#: ../../Zotlabs/Module/Z6trans.php:21
-msgid "To complete the update please run"
+#: ../../Zotlabs/Storage/Browser.php:413
+msgid "Rename"
msgstr ""
-#: ../../Zotlabs/Module/Z6trans.php:23
-msgid "php util/z6convert.php"
+#: ../../Zotlabs/Storage/Browser.php:415
+msgid "Attachment BBcode"
msgstr ""
-#: ../../Zotlabs/Module/Z6trans.php:25
-msgid "from the terminal."
+#: ../../Zotlabs/Storage/Browser.php:416
+msgid "Embed BBcode"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:85
-msgid "Email resent"
+#: ../../Zotlabs/Storage/Browser.php:417
+msgid "Link BBcode"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:85
-msgid "Email resend failed"
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:490
+#, php-format
+msgid "You are using %1$s of your available file storage."
+msgstr "Du bruker %1$s av din tilgjengelige lagringsplass."
-#: ../../Zotlabs/Module/Regate.php:110
-msgid "Verification successful"
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:495
+#, php-format
+msgid "You are using %1$s of %2$s available file storage. (%3$s&#37;)"
+msgstr "Du bruker %1$s av %2$s tilgjengelig lagringsplass (%3$s&#37;)"
-#: ../../Zotlabs/Module/Regate.php:154
-msgid "Account successfull created"
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:506
+msgid "WARNING:"
+msgstr "ADVARSEL:"
-#: ../../Zotlabs/Module/Regate.php:212
-msgid "Channel successfull created"
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:547
+msgid "Create new folder"
+msgstr "Lag ny mappe"
-#: ../../Zotlabs/Module/Regate.php:218
-msgid "Automatic channel creation failed. Please create a channel."
-msgstr ""
+#: ../../Zotlabs/Storage/Browser.php:549
+msgid "Upload file"
+msgstr "Last opp fil"
-#: ../../Zotlabs/Module/Regate.php:230
-msgid "Account creation error"
+#: ../../Zotlabs/Storage/Browser.php:561
+msgid "Drop files here to immediately upload"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:242
-msgid "Verify failed"
+#: ../../Zotlabs/Storage/Browser.php:562
+#: ../../Zotlabs/Module/Filestorage.php:211
+msgid "Show in your contacts shared folder"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:247
-msgid "Token verification failed"
+#: ../../Zotlabs/Storage/Browser.php:564
+msgid ""
+"You can select files via the upload button or drop them right here or into "
+"an existing folder."
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:252
-msgid "Request not inside time frame"
-msgstr ""
+#: ../../Zotlabs/Module/Page.php:39 ../../Zotlabs/Module/Block.php:29
+msgid "Invalid item."
+msgstr "Ugyldig element."
-#: ../../Zotlabs/Module/Regate.php:258 ../../Zotlabs/Module/Regate.php:288
-msgid "Identity unknown"
+#: ../../Zotlabs/Module/Page.php:174
+msgid ""
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
+"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
+"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
+"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
msgstr ""
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
+"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
+"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
+"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
+"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
-#: ../../Zotlabs/Module/Regate.php:264
-msgid "dId2 mistaken"
+#: ../../Zotlabs/Module/Import_progress.php:40
+msgid "Item sync completed!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:292
-msgid "Your Registration ID"
+#: ../../Zotlabs/Module/Import_progress.php:63
+#: ../../Zotlabs/Module/Import_progress.php:112
+msgid "Import host does not seem to be online or compatible"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:305 ../../Zotlabs/Module/Regate.php:397
-#: ../../Zotlabs/Module/Regate.php:429
-msgid "Registration verification"
+#: ../../Zotlabs/Module/Import_progress.php:74
+msgid "Item sync completed but no items were found!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:312 ../../Zotlabs/Module/Regate.php:434
-msgid "Hold on, you can start verification in"
+#: ../../Zotlabs/Module/Import_progress.php:91
+msgid "File sync completed!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:313
-msgid "Please remember your verification token for ID"
+#: ../../Zotlabs/Module/Import_progress.php:123
+msgid "File sync completed but no files were found!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:315
-msgid "Token validity"
+#: ../../Zotlabs/Module/Import_progress.php:141
+msgid "Channel clone status"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:351
-msgid "Resend email"
+#: ../../Zotlabs/Module/Import_progress.php:142
+msgid "Item sync status"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:356
-msgid "Registration status"
+#: ../../Zotlabs/Module/Import_progress.php:143
+msgid "File sync status"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:359
-msgid "Verification successful!"
+#: ../../Zotlabs/Module/Import_progress.php:150
+msgid "Channel cloning completed!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:360
-msgid "Your login ID is"
+#: ../../Zotlabs/Module/Import_progress.php:151
+msgid "Resume"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:361
-msgid ""
-"After your account has been approved by our administrator you will be able "
-"to login with your login ID and your provided password."
+#: ../../Zotlabs/Module/Import_progress.php:152
+msgid "Only resume if sync stalled!"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:373
-msgid "Registration request revoked"
-msgstr ""
+#: ../../Zotlabs/Module/Display.php:27 ../../Zotlabs/Module/Search.php:24
+#: ../../Zotlabs/Module/Directory.php:72 ../../Zotlabs/Module/Directory.php:77
+#: ../../Zotlabs/Module/Viewconnections.php:23
+#: ../../Zotlabs/Module/Photos.php:511
+msgid "Public access denied."
+msgstr "Offentlig tilgang avvist."
-#: ../../Zotlabs/Module/Regate.php:374
-msgid "Sorry for any inconvience. Thank you for your response."
+#: ../../Zotlabs/Module/Display.php:53 ../../Zotlabs/Module/Oep.php:83
+#: ../../Zotlabs/Module/Pubstream.php:55 ../../Zotlabs/Module/Channel.php:195
+msgid "Malformed message id."
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:398
-msgid "Please enter your verification token for ID"
-msgstr ""
+#: ../../Zotlabs/Module/Display.php:59 ../../Zotlabs/Module/Display.php:120
+#: ../../Zotlabs/Module/Display.php:391 ../../Zotlabs/Module/Thing.php:120
+#: ../../Zotlabs/Module/Filestorage.php:29 ../../Zotlabs/Module/Viewsrc.php:25
+#: ../../Zotlabs/Module/Admin/Themes.php:73
+#: ../../Zotlabs/Module/Admin/Addons.php:42 ../../Zotlabs/Module/Admin.php:63
+msgid "Item not found."
+msgstr "Elementet ble ikke funnet."
-#: ../../Zotlabs/Module/Regate.php:399 ../../Zotlabs/Module/Regate.php:426
-msgid "Please check your email!"
-msgstr ""
+#: ../../Zotlabs/Module/Display.php:95 ../../Zotlabs/Module/Network.php:215
+#: ../../Zotlabs/Module/Hq.php:101 ../../Zotlabs/Module/Pubstream.php:98
+#: ../../Zotlabs/Module/Channel.php:279 ../../Zotlabs/Module/Rpost.php:111
+msgid "Reset form"
+msgstr "Nullstill skjema"
-#: ../../Zotlabs/Module/Regate.php:409
-msgid "Verification token"
+#: ../../Zotlabs/Module/Display.php:315 ../../Zotlabs/Module/Channel.php:503
+msgid ""
+"You must enable javascript for your browser to be able to view this content."
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:420
-msgid "ID expired"
+#: ../../Zotlabs/Module/Display.php:335
+msgid "Article"
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:435
-msgid "You will require the verification token for ID"
+#: ../../Zotlabs/Module/Display.php:384
+msgid "Item has been removed."
msgstr ""
-#: ../../Zotlabs/Module/Regate.php:444
-msgid "Unknown or expired ID"
-msgstr ""
+#: ../../Zotlabs/Module/Cover_photo.php:86
+#: ../../Zotlabs/Module/Profile_photo.php:129
+msgid "Image uploaded but image cropping failed."
+msgstr "Bildet ble lastet opp, men beskjæring av bildet mislyktes."
-#: ../../Zotlabs/Module/Regate.php:455
-msgid "dId2 malformed"
-msgstr ""
+#: ../../Zotlabs/Module/Cover_photo.php:195
+#: ../../Zotlabs/Module/Cover_photo.php:252
+msgid "Cover Photos"
+msgstr "Forsidebilder"
-#: ../../Zotlabs/Module/Api.php:76 ../../Zotlabs/Module/Api.php:97
-msgid "Authorize application connection"
-msgstr "Tillat programforbindelse"
+#: ../../Zotlabs/Module/Cover_photo.php:211
+#: ../../Zotlabs/Module/Profile_photo.php:185
+msgid "Image resize failed."
+msgstr "Endring av bildestørrelse mislyktes."
-#: ../../Zotlabs/Module/Api.php:77
-msgid "Return to your app and insert this Security Code:"
-msgstr ""
+#: ../../Zotlabs/Module/Cover_photo.php:263
+#: ../../Zotlabs/Module/Profile_photo.php:350
+msgid "Image upload failed."
+msgstr "Opplasting av bildet mislyktes."
-#: ../../Zotlabs/Module/Api.php:87
-msgid "Please login to continue."
-msgstr "Vennligst logg inn for å fortsette."
+#: ../../Zotlabs/Module/Cover_photo.php:280
+#: ../../Zotlabs/Module/Profile_photo.php:370
+msgid "Unable to process image."
+msgstr "Kan ikke behandle bildet."
-#: ../../Zotlabs/Module/Api.php:99
-msgid ""
-"Do you want to authorize this application to access your posts and contacts, "
-"and/or create new posts for you?"
-msgstr ""
-"Vil du tillate dette programmet å få tilgang til dine innlegg og kontakter, "
-"og/eller lage nye innlegg for deg?"
+#: ../../Zotlabs/Module/Cover_photo.php:325
+#: ../../Zotlabs/Module/Cover_photo.php:340
+#: ../../Zotlabs/Module/Profile_photo.php:432
+#: ../../Zotlabs/Module/Profile_photo.php:497
+msgid "Photo not available."
+msgstr "Bildet er ikke tilgjengelig."
-#: ../../Zotlabs/Module/Channel_calendar.php:62
-msgid "Event can not end before it has started."
-msgstr "Hendelsen kan ikke slutte før den starter."
+#: ../../Zotlabs/Module/Cover_photo.php:376
+msgid "Your cover photo may be visible to anybody on the internet"
+msgstr "Omslagsbildet ditt vil være synlig for alle på internett"
-#: ../../Zotlabs/Module/Channel_calendar.php:64
-#: ../../Zotlabs/Module/Channel_calendar.php:72
-#: ../../Zotlabs/Module/Channel_calendar.php:87
-msgid "Unable to generate preview."
-msgstr "Klarer ikke å lage forhåndsvisning."
+#: ../../Zotlabs/Module/Cover_photo.php:378
+msgid "Upload File:"
+msgstr "Last opp fil:"
-#: ../../Zotlabs/Module/Channel_calendar.php:70
-msgid "Event title and start time are required."
-msgstr "Hendelsestittel og starttidspunkt er påkrevd."
+#: ../../Zotlabs/Module/Cover_photo.php:379
+msgid "Select a profile:"
+msgstr "Velg en profil:"
-#: ../../Zotlabs/Module/Channel_calendar.php:85
-#: ../../Zotlabs/Module/Channel_calendar.php:226
-msgid "Event not found."
-msgstr "Hendelsen ble ikke funnet."
+#: ../../Zotlabs/Module/Cover_photo.php:380
+#, fuzzy
+msgid "Change Cover Photo"
+msgstr "Endre omslagsbilde"
-#: ../../Zotlabs/Module/Channel_calendar.php:370
-msgid "Edit event"
-msgstr "Endre hendelse"
+#: ../../Zotlabs/Module/Cover_photo.php:384
+#: ../../Zotlabs/Module/Cover_photo.php:385
+#: ../../Zotlabs/Module/Profile_photo.php:551
+#: ../../Zotlabs/Module/Profile_photo.php:552
+msgid "Use a photo from your albums"
+msgstr ""
-#: ../../Zotlabs/Module/Channel_calendar.php:372
-msgid "Delete event"
-msgstr "Slett hendelse"
+#: ../../Zotlabs/Module/Cover_photo.php:396
+msgid "Select existing photo"
+msgstr "Velg eksisterende bilde"
-#: ../../Zotlabs/Module/Channel_calendar.php:392
-#: ../../Zotlabs/Module/Cal.php:161 ../../Zotlabs/Module/Cdav.php:935
-#, fuzzy
-msgid "Link to source"
-msgstr "Lenke til kilde"
+#: ../../Zotlabs/Module/Cover_photo.php:413
+#: ../../Zotlabs/Module/Profile_photo.php:581
+msgid "Crop Image"
+msgstr "Beskjær bildet"
-#: ../../Zotlabs/Module/Channel_calendar.php:406
-msgid "calendar"
-msgstr "kalender"
+#: ../../Zotlabs/Module/Cover_photo.php:414
+#: ../../Zotlabs/Module/Profile_photo.php:582
+msgid "Please adjust the image cropping for optimum viewing."
+msgstr "Vennligst juster bildebeskjæringen for optimal visning."
-#: ../../Zotlabs/Module/Channel_calendar.php:493
-msgid "Failed to remove event"
-msgstr "Mislyktes med å slette hendelse"
+#: ../../Zotlabs/Module/Cover_photo.php:416
+msgid "Done Editing"
+msgstr "Lagre endringer"
-#: ../../Zotlabs/Module/Lockview.php:101
-msgid "Remote privacy information not available"
-msgstr ""
+#: ../../Zotlabs/Module/Editwebpage.php:139
+msgid "Page link"
+msgstr "Sidelenke"
-#: ../../Zotlabs/Module/Lockview.php:144 ../../Zotlabs/Module/Lockview.php:203
-#: ../../Zotlabs/Module/Acl.php:124
-msgctxt "acl"
-msgid "Profile"
-msgstr "Profil"
+#: ../../Zotlabs/Module/Editwebpage.php:166
+msgid "Edit Webpage"
+msgstr "Endre webside"
-#: ../../Zotlabs/Module/Lockview.php:155 ../../Zotlabs/Module/Lockview.php:212
+#: ../../Zotlabs/Module/Sse_bs.php:657
+msgid "Private forum"
+msgstr ""
+
+#: ../../Zotlabs/Module/Sse_bs.php:657
#, fuzzy
-msgid "Privacy group"
-msgstr "Personverngruppe:"
+msgid "Public forum"
+msgstr "Offentlig forum:"
-#: ../../Zotlabs/Module/Lockview.php:183
-msgid "Item"
+#: ../../Zotlabs/Module/Webpages.php:67
+msgid "Import Webpage Elements"
msgstr ""
-#: ../../Zotlabs/Module/Lockview.php:230
-#, php-format
-msgid "Click to copy link to this ressource for guest %s to clipboard"
+#: ../../Zotlabs/Module/Webpages.php:68
+msgid "Import selected"
msgstr ""
-#: ../../Zotlabs/Module/Lockview.php:230
-msgid "Link copied"
+#: ../../Zotlabs/Module/Webpages.php:90
+msgid "Export Webpage Elements"
msgstr ""
-#: ../../Zotlabs/Module/Lockview.php:235
-msgid "Access"
-msgstr "Tilgang"
-
-#: ../../Zotlabs/Module/Lockview.php:236 ../../Zotlabs/Widget/Tokens.php:49
-msgid "Guest access"
+#: ../../Zotlabs/Module/Webpages.php:91
+msgid "Export selected"
msgstr ""
-#: ../../Zotlabs/Module/Lockview.php:237
-msgid "OCAP access"
-msgstr ""
+#: ../../Zotlabs/Module/Webpages.php:257
+msgid "Actions"
+msgstr "Handlinger"
-#: ../../Zotlabs/Module/Editlayout.php:128 ../../Zotlabs/Module/Layouts.php:129
-#: ../../Zotlabs/Module/Layouts.php:187
-msgid "Layout Name"
-msgstr "Layout-navn"
+#: ../../Zotlabs/Module/Webpages.php:258
+msgid "Page Link"
+msgstr "Sidelenke"
-#: ../../Zotlabs/Module/Editlayout.php:129 ../../Zotlabs/Module/Layouts.php:132
-msgid "Layout Description (Optional)"
-msgstr "Layoutens beskrivelse (valgfritt)"
+#: ../../Zotlabs/Module/Webpages.php:259
+msgid "Page Title"
+msgstr "Sidetittel"
-#: ../../Zotlabs/Module/Editlayout.php:137
-msgid "Edit Layout"
-msgstr "Endre layout"
+#: ../../Zotlabs/Module/Webpages.php:260 ../../Zotlabs/Module/Layouts.php:189
+#: ../../Zotlabs/Module/Menu.php:178 ../../Zotlabs/Module/Blocks.php:155
+msgid "Created"
+msgstr "Laget"
-#: ../../Zotlabs/Module/Photos.php:84 ../../Zotlabs/Module/Photos.php:103
-msgid "Album not found."
-msgstr "Albumet ble ikke funnet."
+#: ../../Zotlabs/Module/Webpages.php:261 ../../Zotlabs/Module/Layouts.php:190
+#: ../../Zotlabs/Module/Menu.php:179 ../../Zotlabs/Module/Blocks.php:156
+msgid "Edited"
+msgstr "Endret"
-#: ../../Zotlabs/Module/Photos.php:93
-msgid "Delete Album"
-msgstr "Slett album"
+#: ../../Zotlabs/Module/Webpages.php:289
+msgid "Invalid file type."
+msgstr ""
-#: ../../Zotlabs/Module/Photos.php:165 ../../Zotlabs/Module/Photos.php:1057
-msgid "Delete Photo"
-msgstr "Slett bilde"
+#: ../../Zotlabs/Module/Webpages.php:301
+msgid "Error opening zip file"
+msgstr ""
-#: ../../Zotlabs/Module/Photos.php:511
-#: ../../Zotlabs/Module/Viewconnections.php:23
-#: ../../Zotlabs/Module/Directory.php:72 ../../Zotlabs/Module/Directory.php:77
-#: ../../Zotlabs/Module/Display.php:27 ../../Zotlabs/Module/Search.php:24
-msgid "Public access denied."
-msgstr "Offentlig tilgang avvist."
+#: ../../Zotlabs/Module/Webpages.php:312
+msgid "Invalid folder path."
+msgstr ""
-#: ../../Zotlabs/Module/Photos.php:522
-msgid "No photos selected"
-msgstr "Ingen bilder valgt"
+#: ../../Zotlabs/Module/Webpages.php:339
+msgid "No webpage elements detected."
+msgstr ""
-#: ../../Zotlabs/Module/Photos.php:571
-msgid "Access to this item is restricted."
-msgstr "Tilgang til dette elementet er begrenset."
+#: ../../Zotlabs/Module/Webpages.php:414
+msgid "Import complete."
+msgstr ""
-#: ../../Zotlabs/Module/Photos.php:614
+#: ../../Zotlabs/Module/Impel.php:188
#, php-format
-msgid "%1$.2f MB photo storage used."
-msgstr "%1$.2f MB lagringsplass til bilder er brukt."
+msgid "%s element installed"
+msgstr "%s element installert"
-#: ../../Zotlabs/Module/Photos.php:618
+#: ../../Zotlabs/Module/Impel.php:191
#, php-format
-msgid "%1$.2f MB of %2$.2f MB photo storage used."
-msgstr "%1$.2f MB av %2$.2f MB lagringsplass til bilder er brukt."
-
-#: ../../Zotlabs/Module/Photos.php:660
-msgid "Upload Photos"
-msgstr "Last opp bilder"
-
-#: ../../Zotlabs/Module/Photos.php:664
-msgid "Enter an album name"
-msgstr "Skriv et albumnavn"
-
-#: ../../Zotlabs/Module/Photos.php:665
-msgid "or select an existing album (doubleclick)"
-msgstr "eller velg et eksisterende album (dobbeltklikk)"
-
-#: ../../Zotlabs/Module/Photos.php:666
-msgid "Create a status post for this upload"
-msgstr "Lag et statusinnlegg for denne opplastingen"
-
-#: ../../Zotlabs/Module/Photos.php:668
-msgid "Description (optional)"
-msgstr "Beskrivelse (valgritt)"
-
-#: ../../Zotlabs/Module/Photos.php:756
-msgid "Show Newest First"
-msgstr "Vis nyeste først"
-
-#: ../../Zotlabs/Module/Photos.php:758
-msgid "Show Oldest First"
-msgstr "Vis eldste først"
-
-#: ../../Zotlabs/Module/Photos.php:782 ../../Zotlabs/Module/Photos.php:1324
-#: ../../Zotlabs/Module/Embedphotos.php:171 ../../Zotlabs/Widget/Album.php:84
-#: ../../Zotlabs/Widget/Portfolio.php:91
-msgid "View Photo"
-msgstr "Vis foto"
-
-#: ../../Zotlabs/Module/Photos.php:813 ../../Zotlabs/Module/Embedphotos.php:187
-#: ../../Zotlabs/Widget/Album.php:101 ../../Zotlabs/Widget/Portfolio.php:112
-msgid "Edit Album"
-msgstr "Endre album"
-
-#: ../../Zotlabs/Module/Photos.php:815 ../../Zotlabs/Module/Photos.php:1355
-msgid "Add Photos"
-msgstr "Legg til bilder"
-
-#: ../../Zotlabs/Module/Photos.php:867
-msgid "Permission denied. Access to this item may be restricted."
-msgstr "Tillatelse avvist. Tilgang til dette elementet kan være begrenset."
-
-#: ../../Zotlabs/Module/Photos.php:869
-msgid "Photo not available"
-msgstr "Bilde er utilgjengelig"
-
-#: ../../Zotlabs/Module/Photos.php:927
-msgid "Use as profile photo"
-msgstr "Bruk som profilbilde"
-
-#: ../../Zotlabs/Module/Photos.php:928
-msgid "Use as cover photo"
-msgstr "Bruk som omslagsbilde"
-
-#: ../../Zotlabs/Module/Photos.php:935
-msgid "Private Photo"
-msgstr "Privat bilde"
-
-#: ../../Zotlabs/Module/Photos.php:946 ../../Zotlabs/Module/Cal.php:199
-#: ../../Zotlabs/Module/Cdav.php:1026
-msgid "Previous"
-msgstr "Forrige"
-
-#: ../../Zotlabs/Module/Photos.php:950
-msgid "View Full Size"
-msgstr "Vis i full størrelse"
-
-#: ../../Zotlabs/Module/Photos.php:994 ../../Zotlabs/Module/Cover_photo.php:382
-#: ../../Zotlabs/Module/Tagrm.php:137 ../../Zotlabs/Module/Admin/Addons.php:463
-#: ../../extend/addon/hzaddons/superblock/Mod_Superblock.php:90
-msgid "Remove"
-msgstr "Fjern"
-
-#: ../../Zotlabs/Module/Photos.php:1031
-msgid "Edit photo"
-msgstr "Endre bilde"
-
-#: ../../Zotlabs/Module/Photos.php:1033
-msgid "Rotate CW (right)"
-msgstr "Roter med klokka (mot høyre)"
-
-#: ../../Zotlabs/Module/Photos.php:1034
-msgid "Rotate CCW (left)"
-msgstr "Roter mot klokka (venstre)"
-
-#: ../../Zotlabs/Module/Photos.php:1037
-msgid "Move photo to album"
-msgstr ""
-
-#: ../../Zotlabs/Module/Photos.php:1038
-msgid "Enter a new album name"
-msgstr "Skriv et nytt albumnavn"
-
-#: ../../Zotlabs/Module/Photos.php:1039
-msgid "or select an existing one (doubleclick)"
-msgstr "eller velg et eksisterende album (dobbeltklikk)"
-
-#: ../../Zotlabs/Module/Photos.php:1044
-msgid "Add a Tag"
-msgstr "Legg til merkelapp"
-
-#: ../../Zotlabs/Module/Photos.php:1052
-msgid "Example: @bob, @Barbara_Jensen, @jim@example.com"
-msgstr "Eksempel: @bob, @Barbara_Jensen, @jim@example.com"
-
-#: ../../Zotlabs/Module/Photos.php:1055
-msgid "Flag as adult in album view"
-msgstr "Flag som voksent i albumvisning"
-
-#: ../../Zotlabs/Module/Photos.php:1125 ../../Zotlabs/Module/Photos.php:1137
-msgid "View all"
-msgstr "Vis alle"
-
-#: ../../Zotlabs/Module/Photos.php:1238
-msgid "Photo Tools"
-msgstr "Fotoverktøy"
-
-#: ../../Zotlabs/Module/Photos.php:1247
-msgid "In This Photo:"
-msgstr "I dette bildet:"
-
-#: ../../Zotlabs/Module/Photos.php:1252
-msgid "Map"
-msgstr "Kart"
-
-#: ../../Zotlabs/Module/Photos.php:1260
-msgctxt "noun"
-msgid "Likes"
-msgstr "Liker"
-
-#: ../../Zotlabs/Module/Photos.php:1261
-msgctxt "noun"
-msgid "Dislikes"
-msgstr "Liker ikke"
+msgid "%s element installation failed"
+msgstr "Installasjon av %s-element mislyktes"
#: ../../Zotlabs/Module/Oauth.php:45
msgid "Name is required"
@@ -6657,53 +8878,40 @@ msgstr "Navn er påkrevd"
msgid "Key and Secret are required"
msgstr "Nøkkel og hemmelighet er påkrevd"
-#: ../../Zotlabs/Module/Oauth.php:108 ../../Zotlabs/Module/Oauth.php:134
+#: ../../Zotlabs/Module/Oauth.php:109 ../../Zotlabs/Module/Oauth.php:134
#: ../../Zotlabs/Module/Oauth.php:170 ../../Zotlabs/Module/Oauth2.php:141
#: ../../Zotlabs/Module/Oauth2.php:191
msgid "Add application"
msgstr "Legg til program"
-#: ../../Zotlabs/Module/Oauth.php:111 ../../Zotlabs/Module/Oauth2.php:116
+#: ../../Zotlabs/Module/Oauth.php:112 ../../Zotlabs/Module/Oauth2.php:117
#: ../../Zotlabs/Module/Oauth2.php:144
msgid "Name of application"
msgstr "Navn på program"
-#: ../../Zotlabs/Module/Oauth.php:112 ../../Zotlabs/Module/Oauth.php:138
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:596
-#: ../../extend/addon/hzaddons/twitter/twitter.php:504
-msgid "Consumer Key"
-msgstr "Consumer Key"
-
-#: ../../Zotlabs/Module/Oauth.php:112 ../../Zotlabs/Module/Oauth.php:113
-#: ../../Zotlabs/Module/Oauth2.php:117 ../../Zotlabs/Module/Oauth2.php:145
+#: ../../Zotlabs/Module/Oauth.php:113 ../../Zotlabs/Module/Oauth.php:114
+#: ../../Zotlabs/Module/Oauth2.php:118 ../../Zotlabs/Module/Oauth2.php:145
msgid "Automatically generated - change if desired. Max length 20"
msgstr "Automatisk laget - kan endres om du vil. Største lengde 20"
-#: ../../Zotlabs/Module/Oauth.php:113 ../../Zotlabs/Module/Oauth.php:139
-#: ../../Zotlabs/Module/Oauth2.php:117 ../../Zotlabs/Module/Oauth2.php:145
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:595
-#: ../../extend/addon/hzaddons/twitter/twitter.php:505
-msgid "Consumer Secret"
-msgstr "Consumer Secret"
-
-#: ../../Zotlabs/Module/Oauth.php:114 ../../Zotlabs/Module/Oauth.php:140
-#: ../../Zotlabs/Module/Oauth2.php:118 ../../Zotlabs/Module/Oauth2.php:146
+#: ../../Zotlabs/Module/Oauth.php:115 ../../Zotlabs/Module/Oauth.php:140
+#: ../../Zotlabs/Module/Oauth2.php:119 ../../Zotlabs/Module/Oauth2.php:146
msgid "Redirect"
msgstr "Omdirigering"
-#: ../../Zotlabs/Module/Oauth.php:114 ../../Zotlabs/Module/Oauth2.php:118
+#: ../../Zotlabs/Module/Oauth.php:115 ../../Zotlabs/Module/Oauth2.php:119
#: ../../Zotlabs/Module/Oauth2.php:146
msgid ""
"Redirect URI - leave blank unless your application specifically requires this"
msgstr ""
"Omdirigerings-URI - la stå tomt hvis ikke ditt program spesifikt krever dette"
-#: ../../Zotlabs/Module/Oauth.php:115 ../../Zotlabs/Module/Oauth.php:141
+#: ../../Zotlabs/Module/Oauth.php:116 ../../Zotlabs/Module/Oauth.php:141
msgid "Icon url"
msgstr "Ikon-URL"
-#: ../../Zotlabs/Module/Oauth.php:115 ../../Zotlabs/Module/Sources.php:121
-#: ../../Zotlabs/Module/Sources.php:156 ../../Zotlabs/Module/Register.php:501
+#: ../../Zotlabs/Module/Oauth.php:116 ../../Zotlabs/Module/Register.php:501
+#: ../../Zotlabs/Module/Sources.php:121 ../../Zotlabs/Module/Sources.php:156
msgid "Optional"
msgstr "Valgfritt"
@@ -6727,9 +8935,25 @@ msgstr "Ikke noe navn"
msgid "Remove authorization"
msgstr "Fjern tillatelse"
-#: ../../Zotlabs/Module/Apps.php:51 ../../Zotlabs/Widget/Appstore.php:19
-msgid "Available Apps"
-msgstr "Tilgjengelige apper"
+#: ../../Zotlabs/Module/Network.php:110
+msgid "No such group"
+msgstr "Gruppen finnes ikke"
+
+#: ../../Zotlabs/Module/Network.php:162
+msgid "No such channel"
+msgstr "Ingen slik kanal"
+
+#: ../../Zotlabs/Module/Network.php:174 ../../Zotlabs/Module/Channel.php:246
+msgid "Search Results For:"
+msgstr "Søkeresultat for:"
+
+#: ../../Zotlabs/Module/Network.php:250
+msgid "Privacy group is empty"
+msgstr "Personverngruppen er tom"
+
+#: ../../Zotlabs/Module/Network.php:260
+msgid "Privacy group: "
+msgstr "Personverngruppe: "
#: ../../Zotlabs/Module/Apps.php:51
msgid "Installed Apps"
@@ -6743,941 +8967,928 @@ msgstr "Behandle apper"
msgid "Create Custom App"
msgstr ""
-#: ../../Zotlabs/Module/Viewconnections.php:65
-msgid "No connections."
-msgstr "Ingen forbindelser."
-
-#: ../../Zotlabs/Module/Viewconnections.php:105
-#, php-format
-msgid "Visit %s's profile [%s]"
-msgstr "Besøk %s sin profil [%s]"
-
-#: ../../Zotlabs/Module/Viewconnections.php:135
-msgid "View Connections"
-msgstr "Vis forbindelser"
-
-#: ../../Zotlabs/Module/Chanview.php:132
-msgid "toggle full screen mode"
-msgstr ""
+#: ../../Zotlabs/Module/Service_limits.php:23
+msgid "No service class restrictions found."
+msgstr "Ingen restriksjoner er funnet i tjenesteklasse."
-#: ../../Zotlabs/Module/Editpost.php:38 ../../Zotlabs/Module/Editpost.php:43
-msgid "Item is not editable"
-msgstr "Elementet kan ikke endres"
+#: ../../Zotlabs/Module/Lostpass.php:19
+msgid "No valid account found."
+msgstr "Ingen gyldig konto funnet."
-#: ../../Zotlabs/Module/Tagger.php:50
-msgid "Post not found."
-msgstr ""
+#: ../../Zotlabs/Module/Lostpass.php:33
+msgid "Password reset request issued. Check your email."
+msgstr "Forespørsel om å tilbakestille passord er mottatt. Sjekk e-posten din."
-#: ../../Zotlabs/Module/Tagger.php:121
+#: ../../Zotlabs/Module/Lostpass.php:39 ../../Zotlabs/Module/Lostpass.php:108
#, php-format
-msgid "%1$s tagged %2$s's %3$s with %4$s"
-msgstr "%1$s merket %3$s til %2$s med %4$s"
-
-#: ../../Zotlabs/Module/Common.php:14
-msgid "No channel."
-msgstr "Ingen kanal."
+msgid "Site Member (%s)"
+msgstr "Nettstedsmedlem (%s)"
-#: ../../Zotlabs/Module/Common.php:45
-msgid "No connections in common."
-msgstr "Ingen forbindelser felles."
+#: ../../Zotlabs/Module/Lostpass.php:44 ../../Zotlabs/Module/Lostpass.php:49
+#, php-format
+msgid "Password reset requested at %s"
+msgstr "Forespurt om å tilbakestille passord hos %s"
-#: ../../Zotlabs/Module/Common.php:65
-msgid "View Common Connections"
+#: ../../Zotlabs/Module/Lostpass.php:68
+msgid ""
+"Request could not be verified. (You may have previously submitted it.) "
+"Password reset failed."
msgstr ""
+"Forespørsel kunne ikke bekreftes. (Du kan ha sendt den inn tidligere.) "
+"Tilbakestilling av passord mislyktes."
-#: ../../Zotlabs/Module/Profiles.php:23 ../../Zotlabs/Module/Profiles.php:211
-#: ../../Zotlabs/Module/Profiles.php:641
-msgid "Profile not found."
-msgstr "Profilen ble ikke funnet."
-
-#: ../../Zotlabs/Module/Profiles.php:43
-msgid "Profile deleted."
-msgstr "Profilen er slettet."
-
-#: ../../Zotlabs/Module/Profiles.php:67 ../../Zotlabs/Module/Profiles.php:104
-msgid "Profile-"
-msgstr "Profil-"
-
-#: ../../Zotlabs/Module/Profiles.php:89 ../../Zotlabs/Module/Profiles.php:126
-msgid "New profile created."
-msgstr "Ny profil opprettet."
-
-#: ../../Zotlabs/Module/Profiles.php:110
-msgid "Profile unavailable to clone."
-msgstr "Profilen er utilgjengelig for klonen."
-
-#: ../../Zotlabs/Module/Profiles.php:145
-msgid "Profile unavailable to export."
-msgstr "Profilen er utilgjengelig for eksport."
-
-#: ../../Zotlabs/Module/Profiles.php:221
-msgid "Profile Name is required."
-msgstr "Profilnavn er påkrevd."
-
-#: ../../Zotlabs/Module/Profiles.php:426
-msgid "Marital Status"
-msgstr "Sivilstand"
-
-#: ../../Zotlabs/Module/Profiles.php:430
-msgid "Romantic Partner"
-msgstr "Romantisk partner"
-
-#: ../../Zotlabs/Module/Profiles.php:434 ../../Zotlabs/Module/Profiles.php:787
-msgid "Likes"
-msgstr "Liker"
-
-#: ../../Zotlabs/Module/Profiles.php:438 ../../Zotlabs/Module/Profiles.php:788
-msgid "Dislikes"
-msgstr "Liker ikke"
-
-#: ../../Zotlabs/Module/Profiles.php:442 ../../Zotlabs/Module/Profiles.php:795
-msgid "Work/Employment"
-msgstr "Arbeid/sysselsetting"
-
-#: ../../Zotlabs/Module/Profiles.php:445
-msgid "Religion"
-msgstr "Religion"
-
-#: ../../Zotlabs/Module/Profiles.php:449
-msgid "Political Views"
-msgstr "Politiske synspunkter"
+#: ../../Zotlabs/Module/Lostpass.php:92
+msgid "Your password has been reset as requested."
+msgstr "Ditt passord har blitt tilbakestilt som forespurt."
-#: ../../Zotlabs/Module/Profiles.php:453
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:74
-msgid "Gender"
-msgstr "Kjønn"
+#: ../../Zotlabs/Module/Lostpass.php:93
+msgid "Your new password is"
+msgstr "Ditt nye passord er"
-#: ../../Zotlabs/Module/Profiles.php:457
-msgid "Sexual Preference"
-msgstr "Seksuelle preferanser"
+#: ../../Zotlabs/Module/Lostpass.php:94
+msgid "Save or copy your new password - and then"
+msgstr "Lagre eller kopier ditt nye passord, og deretter kan du"
-#: ../../Zotlabs/Module/Profiles.php:461
-msgid "Homepage"
-msgstr "Hjemmeside"
+#: ../../Zotlabs/Module/Lostpass.php:95
+msgid "click here to login"
+msgstr "klikke her for å logge inn"
-#: ../../Zotlabs/Module/Profiles.php:465
-msgid "Interests"
-msgstr "Interesser"
+#: ../../Zotlabs/Module/Lostpass.php:96
+msgid ""
+"Your password may be changed from the <em>Settings</em> page after "
+"successful login."
+msgstr ""
+"Ditt passord kan endres på siden <em>Innstillinger</em> etter vellykket "
+"innlogging."
-#: ../../Zotlabs/Module/Profiles.php:469 ../../Zotlabs/Module/Connedit.php:739
-#: ../../Zotlabs/Module/Locs.php:122
-#: ../../Zotlabs/Module/Admin/Channels.php:182
-#: ../../Zotlabs/Module/Cdav.php:1375
-msgid "Address"
-msgstr "Adresse"
+#: ../../Zotlabs/Module/Lostpass.php:117
+#, php-format
+msgid "Your password has changed at %s"
+msgstr "Ditt passord er endret hos %s"
-#: ../../Zotlabs/Module/Profiles.php:573
-msgid "Profile updated."
-msgstr "Profilen er oppdatert."
+#: ../../Zotlabs/Module/Lostpass.php:130
+msgid "Forgot your Password?"
+msgstr "Glemt passord ditt?"
-#: ../../Zotlabs/Module/Profiles.php:671
-msgid "Hide my connections from viewers of this profile"
-msgstr "Skjul mine forbindelser fra besøkende som ser denne profilen"
+#: ../../Zotlabs/Module/Lostpass.php:131
+msgid ""
+"Enter your email address and submit to have your password reset. Then check "
+"your email for further instructions."
+msgstr ""
+"Skriv e-postadressen din og send inn for å tilbakestille passordet ditt. "
+"Sjekk deretter din e-post for videre instruksjoner."
-#: ../../Zotlabs/Module/Profiles.php:684
-msgid "Publish my default profile in the network directory"
-msgstr "La standardprofilen min vises i nettverkskatalonen"
+#: ../../Zotlabs/Module/Lostpass.php:132
+msgid "Email Address"
+msgstr "E-postadresse"
-#: ../../Zotlabs/Module/Profiles.php:692
-msgid "Suggest me as a potential contact to new members"
-msgstr "Foreslå meg som mulig kontakt til nye medlemmer"
+#: ../../Zotlabs/Module/Lostpass.php:133 ../../Zotlabs/Module/Pdledit.php:76
+#: ../../Zotlabs/Module/Pdledit.php:93
+msgid "Reset"
+msgstr "Tilbakestill"
-#: ../../Zotlabs/Module/Profiles.php:696
-msgid "Reveal my online status"
-msgstr "La andre se om jeg er pålogget eller ikke"
+#: ../../Zotlabs/Module/Removeme.php:35
+msgid ""
+"Channel removals are not allowed within 48 hours of changing the account "
+"password."
+msgstr ""
+"Fjerning av kanaler er ikke tillatt innen 48 timer etter endring av "
+"kontopassordet."
-#: ../../Zotlabs/Module/Profiles.php:737
-msgid "Edit Profile Details"
-msgstr "Endre profildetaljer"
+#: ../../Zotlabs/Module/Removeme.php:60 ../../Zotlabs/Module/Removeme.php:64
+#: ../../Zotlabs/Module/Settings/Channel.php:283
+msgid "Remove Channel"
+msgstr "Fjern kanal"
-#: ../../Zotlabs/Module/Profiles.php:739
-msgid "View this profile"
-msgstr "Vis denne profilen"
+#: ../../Zotlabs/Module/Removeme.php:61
+#: ../../Zotlabs/Module/Removeaccount.php:58
+#: ../../Zotlabs/Module/Changeaddr.php:78
+msgid "WARNING: "
+msgstr "ADVARSEL: "
-#: ../../Zotlabs/Module/Profiles.php:741
-msgid "Profile Tools"
-msgstr "Profilverktøy"
+#: ../../Zotlabs/Module/Removeme.php:61
+msgid "This channel will be permanently removed. "
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:742
-msgid "Change cover photo"
-msgstr "Endre omslagsbilde"
+#: ../../Zotlabs/Module/Removeme.php:61
+msgid "This action can not be undone!"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:744
-msgid "Create a new profile using these settings"
-msgstr "Lag en ny profil ved å bruke disse innstillingene"
+#: ../../Zotlabs/Module/Removeme.php:62
+#: ../../Zotlabs/Module/Removeaccount.php:59
+#: ../../Zotlabs/Module/Changeaddr.php:79
+msgid "Please enter your password for verification:"
+msgstr "Vennligst skriv ditt passord for å få bekreftelse:"
-#: ../../Zotlabs/Module/Profiles.php:745
-msgid "Clone this profile"
-msgstr "Klon denne profilen"
+#: ../../Zotlabs/Module/Import_items.php:50
+msgid "Not a zip file or zip file corrupted."
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:746
-msgid "Delete this profile"
-msgstr "Slett denne profilen"
+#: ../../Zotlabs/Module/Import_items.php:121
+msgid "Import Items"
+msgstr "Importer elementer"
-#: ../../Zotlabs/Module/Profiles.php:747
-msgid "Add profile things"
-msgstr "Legg til profilting"
+#: ../../Zotlabs/Module/Import_items.php:122
+msgid "Use this form to import existing posts and content from an export file."
+msgstr ""
+"Bruk dette skjemaet for å importere eksisterende innlegg og innhold fra en "
+"eksportfil."
-#: ../../Zotlabs/Module/Profiles.php:748
-msgid "Basic"
-msgstr "Grunnleggende"
+#: ../../Zotlabs/Module/Import_items.php:123
+#: ../../Zotlabs/Module/Import.php:606
+msgid "File to Upload"
+msgstr "Fil som skal lastes opp"
-#: ../../Zotlabs/Module/Profiles.php:750
-msgid "Relationship"
-msgstr "Forhold"
+#: ../../Zotlabs/Module/Import_items.php:136
+#: ../../Zotlabs/Module/Import.php:108
+msgid "Imported file is empty."
+msgstr "Importert fil er tom."
-#: ../../Zotlabs/Module/Profiles.php:753
-msgid "Import profile from file"
-msgstr "Importer profil fra fil"
+#: ../../Zotlabs/Module/Import_items.php:159
+msgid "Content import completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:754
-msgid "Export profile to file"
-msgstr "Eksporter profil til fil"
+#: ../../Zotlabs/Module/Import_items.php:164
+msgid "Chatroom import completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:755
-msgid "Your gender"
-msgstr "Kjønn"
+#: ../../Zotlabs/Module/Import_items.php:170
+msgid "Channel calendar import 1/2 completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:756
-msgid "Marital status"
-msgstr "Sivilstatus"
+#: ../../Zotlabs/Module/Import_items.php:176
+msgid "Channel calendar import 2/2 completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:757
-msgid "Sexual preference"
-msgstr "Legning"
+#: ../../Zotlabs/Module/Import_items.php:181
+msgid "Menu import completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:760
-msgid "Profile name"
-msgstr "Profilnavn"
+#: ../../Zotlabs/Module/Import_items.php:186
+msgid "Wiki import completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:762
-msgid "This is your default profile."
-msgstr "Dette er din standardprofil."
+#: ../../Zotlabs/Module/Import_items.php:191
+msgid "Webpages import completed"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:764
-msgid "Your full name"
-msgstr "Fullt navn"
+#: ../../Zotlabs/Module/Regdir.php:53 ../../Zotlabs/Module/Dirsearch.php:24
+msgid "This site is not a directory server"
+msgstr "Dette nettstedet er ikke en katalogtjener"
-#: ../../Zotlabs/Module/Profiles.php:765
-msgid "Short title/description"
-msgstr "Kort tittel/beskrivelse"
+#: ../../Zotlabs/Module/Notifications.php:101 ../../Zotlabs/Module/Menu.php:180
+#: ../../Zotlabs/Module/Connections.php:83
+#: ../../Zotlabs/Module/Connections.php:92
+msgid "New"
+msgstr "Nye"
-#: ../../Zotlabs/Module/Profiles.php:765
-msgid "Maximal 190 characters"
-msgstr "Maksimum 190 tegn"
+#: ../../Zotlabs/Module/Notifications.php:106
+#: ../../Zotlabs/Module/Notify.php:85
+msgid "No more system notifications."
+msgstr "Ingen flere systemvarsler."
-#: ../../Zotlabs/Module/Profiles.php:768
-msgid "Street address"
-msgstr "Gateadresse"
+#: ../../Zotlabs/Module/Notifications.php:110
+#: ../../Zotlabs/Module/Notify.php:89
+msgid "System Notifications"
+msgstr "Systemvarsler"
-#: ../../Zotlabs/Module/Profiles.php:769
-msgid "Locality/City"
-msgstr "Sted/by"
+#: ../../Zotlabs/Module/Z6trans.php:19
+msgid "Update to Hubzilla 5.0 step 2"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:770
-msgid "Region/State"
-msgstr "Region"
+#: ../../Zotlabs/Module/Z6trans.php:21
+msgid "To complete the update please run"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:771
-msgid "Postal/Zip code"
-msgstr "Postnummer"
+#: ../../Zotlabs/Module/Z6trans.php:23
+msgid "php util/z6convert.php"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:772 ../../Zotlabs/Module/Connedit.php:757
-#: ../../Zotlabs/Module/Cdav.php:1393
-msgid "Country"
-msgstr "Land"
+#: ../../Zotlabs/Module/Z6trans.php:25
+msgid "from the terminal."
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:777
-msgid "Who (if applicable)"
-msgstr "Hvem (om relevant)"
+#: ../../Zotlabs/Module/Request.php:80
+msgid "+ Repeat again"
+msgstr ""
-#: ../../Zotlabs/Module/Profiles.php:777
-msgid "Examples: cathy123, Cathy Williams, cathy@example.com"
-msgstr "Eksempler: kari123, Kari Villiamsen, kari@example.com"
+#: ../../Zotlabs/Module/Request.php:80
+#, fuzzy
+#| msgid "Remove term"
+msgid "- Remove yours"
+msgstr "Fjern begrep"
-#: ../../Zotlabs/Module/Profiles.php:778
-msgid "Since (date)"
-msgstr "Fra (dato)"
+#: ../../Zotlabs/Module/Request.php:80
+msgid "+ Add yours"
+msgstr "+ Legg til"
-#: ../../Zotlabs/Module/Profiles.php:781
-msgid "Tell us about yourself"
-msgstr "Fortell oss om deg selv"
+#: ../../Zotlabs/Module/Thing.php:146
+msgid "Thing updated"
+msgstr "Tingen er oppdatert"
-#: ../../Zotlabs/Module/Profiles.php:782
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:68
-msgid "Homepage URL"
-msgstr "Hjemmeside URL"
+#: ../../Zotlabs/Module/Thing.php:198
+msgid "Object store: failed"
+msgstr "Objektlagring: mislyktes"
-#: ../../Zotlabs/Module/Profiles.php:783
-msgid "Hometown"
-msgstr "Hjemsted"
+#: ../../Zotlabs/Module/Thing.php:202
+msgid "Thing added"
+msgstr "Ting lagt til"
-#: ../../Zotlabs/Module/Profiles.php:784
-msgid "Political views"
-msgstr "Politiske holdninger"
+#: ../../Zotlabs/Module/Thing.php:217
+#, php-format
+msgid "OBJ: %1$s %2$s %3$s"
+msgstr "OBJ: %1$s %2$s %3$s"
-#: ../../Zotlabs/Module/Profiles.php:785
-msgid "Religious views"
-msgstr "Religiøse holdninger"
+#: ../../Zotlabs/Module/Thing.php:284 ../../Zotlabs/Module/Thing.php:308
+msgid "item not found."
+msgstr "element ble ikke funnet."
-#: ../../Zotlabs/Module/Profiles.php:786
-msgid "Keywords used in directory listings"
-msgstr "Nøkkelord for bruk i katalogoppføringen"
+#: ../../Zotlabs/Module/Thing.php:343
+msgid "Edit Thing"
+msgstr "Endre ting"
-#: ../../Zotlabs/Module/Profiles.php:786
-msgid "Example: fishing photography software"
-msgstr "Eksempel: fisking fotografering programvare"
+#: ../../Zotlabs/Module/Thing.php:345 ../../Zotlabs/Module/Thing.php:399
+msgid "Select a profile"
+msgstr "Velg en profil"
-#: ../../Zotlabs/Module/Profiles.php:789
-msgid "Musical interests"
-msgstr "Musikkinteresser"
+#: ../../Zotlabs/Module/Thing.php:349 ../../Zotlabs/Module/Thing.php:402
+msgid "Post an activity"
+msgstr "Legg inn en aktivitet"
-#: ../../Zotlabs/Module/Profiles.php:790
-msgid "Books, literature"
-msgstr "Bøker, litteratur"
+#: ../../Zotlabs/Module/Thing.php:349 ../../Zotlabs/Module/Thing.php:402
+msgid "Only sends to viewers of the applicable profile"
+msgstr "Sender bare til seere av den aktuelle profilen"
-#: ../../Zotlabs/Module/Profiles.php:791
-msgid "Television"
-msgstr "TV/fjernsyn"
+#: ../../Zotlabs/Module/Thing.php:351 ../../Zotlabs/Module/Thing.php:404
+msgid "Name of thing e.g. something"
+msgstr "Navn på ting for eksempel noe"
-#: ../../Zotlabs/Module/Profiles.php:792
-msgid "Film/Dance/Culture/Entertainment"
-msgstr "Film/dans/kultur/underholdning"
+#: ../../Zotlabs/Module/Thing.php:353 ../../Zotlabs/Module/Thing.php:405
+msgid "URL of thing (optional)"
+msgstr "URL til ting (valgfritt)"
-#: ../../Zotlabs/Module/Profiles.php:793
-msgid "Hobbies/Interests"
-msgstr "Hobbier/Interesser"
+#: ../../Zotlabs/Module/Thing.php:355 ../../Zotlabs/Module/Thing.php:406
+msgid "URL for photo of thing (optional)"
+msgstr "URL til bilde av ting (valgfritt)"
-#: ../../Zotlabs/Module/Profiles.php:794
-msgid "Love/Romance"
-msgstr "Kjærlighet/romantikk"
+#: ../../Zotlabs/Module/Thing.php:397
+msgid "Add Thing to your Profile"
+msgstr "Legg til ting i din profil"
-#: ../../Zotlabs/Module/Profiles.php:796
-msgid "School/Education"
-msgstr "Skolle/utdanning"
+#: ../../Zotlabs/Module/Removeaccount.php:35
+msgid ""
+"Account removals are not allowed within 48 hours of changing the account "
+"password."
+msgstr ""
+"Sletting av kontoer er ikke tillatt innen 48 timer etter endring av "
+"kontopassordet."
-#: ../../Zotlabs/Module/Profiles.php:797
-msgid "Contact information and social networks"
-msgstr "Kontaktinformasjon og andre sosiale nettverk"
+#: ../../Zotlabs/Module/Removeaccount.php:57
+msgid "Remove This Account"
+msgstr "Slett denne kontoen"
-#: ../../Zotlabs/Module/Profiles.php:798
-msgid "My other channels"
-msgstr "Mine andre kanaler"
+#: ../../Zotlabs/Module/Removeaccount.php:58
+msgid ""
+"This account and all its channels will be completely removed from the "
+"network. "
+msgstr ""
+"Denne kontoen og alle dens kanaler vil bli fullstendig fjernet fra "
+"nettverket. "
-#: ../../Zotlabs/Module/Profiles.php:852 ../../Zotlabs/Module/Manage.php:137
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:217
-msgid "Create New"
-msgstr "Lag ny profil"
+#: ../../Zotlabs/Module/Removeaccount.php:58
+msgid "This action is permanent and can not be undone!"
+msgstr "Denne handlingen er permanent og kan ikke angres!"
-#: ../../Zotlabs/Module/Network.php:108
-msgid "No such group"
-msgstr "Gruppen finnes ikke"
+#: ../../Zotlabs/Module/Removeaccount.php:61
+#: ../../Zotlabs/Module/Settings/Account.php:113
+msgid "Remove Account"
+msgstr "Slett konto"
-#: ../../Zotlabs/Module/Network.php:160
-msgid "No such channel"
-msgstr "Ingen slik kanal"
+#: ../../Zotlabs/Module/Tokens.php:94
+#, php-format
+msgid "This channel is limited to %d tokens"
+msgstr ""
-#: ../../Zotlabs/Module/Network.php:248
-msgid "Privacy group is empty"
-msgstr "Personverngruppen er tom"
+#: ../../Zotlabs/Module/Tokens.php:100
+msgid "Name and Password are required."
+msgstr ""
-#: ../../Zotlabs/Module/Network.php:258
-msgid "Privacy group: "
-msgstr "Personverngruppe: "
+#: ../../Zotlabs/Module/Tokens.php:215
+msgid "Token saved."
+msgstr ""
-#: ../../Zotlabs/Module/Network.php:333
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:29
-#, fuzzy
-msgid "Invalid channel."
-msgstr "Ugyldig kanal"
+#: ../../Zotlabs/Module/Tokens.php:261
+msgid ""
+"Use this form to create temporary access identifiers to share things with "
+"non-members. These identities may be used in privacy groups and visitors may "
+"login using these credentials to access private content."
+msgstr ""
-#: ../../Zotlabs/Module/Invite.php:70
-msgid "Invite App"
+#: ../../Zotlabs/Module/Tokens.php:274
+msgid "Please select a role for this guest!"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:82
-msgid "Register is closed"
+#: ../../Zotlabs/Module/Tokens.php:287
+msgid "Select a role for this guest"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:116 ../../Zotlabs/Module/Invite.php:563
-msgid "Note, the invitation code is valid up to"
+#: ../../Zotlabs/Module/Tokens.php:291
+msgid "Login Name"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:129
-#, php-format
-msgid "Too many recipients for one invitation (max %d)"
+#: ../../Zotlabs/Module/Tokens.php:292
+msgid "Login Password"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:133
-msgid "No recipients for this invitation"
+#: ../../Zotlabs/Module/Tokens.php:293
+msgid "Expires (yyyy-mm-dd)"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:152
-#, php-format
-msgid "(%s) : Not a real email address"
-msgstr "(%s): Ikke en virkelig epostadresse"
+#: ../../Zotlabs/Module/Pdledit.php:27
+msgid "Layout updated."
+msgstr "Layout er oppdatert."
-#: ../../Zotlabs/Module/Invite.php:159
-#, php-format
-msgid "(%s) : Not allowed email address"
-msgstr "(%s): Ikke en tillatt epostadresse"
+#: ../../Zotlabs/Module/Pdledit.php:55 ../../Zotlabs/Module/Pdledit.php:129
+msgid "Edit System Page Description"
+msgstr "Endre beskrivelsen av systemsiden"
-#: ../../Zotlabs/Module/Invite.php:172
-#, php-format
-msgid "(%s) : email address already in use"
-msgstr "(%s): epostadressen er allerede i bruk"
+#: ../../Zotlabs/Module/Pdledit.php:76 ../../Zotlabs/Module/Pdledit.php:93
+msgid "(modified)"
+msgstr "(endret)"
-#: ../../Zotlabs/Module/Invite.php:179
-#, php-format
-msgid "(%s) : Accepted email address"
-msgstr "(%s): Godkjent epostadresse"
+#: ../../Zotlabs/Module/Pdledit.php:124
+msgid "Layout not found."
+msgstr "Layouten ble ikke funnet."
-#: ../../Zotlabs/Module/Invite.php:266
-#: ../../extend/addon/hzaddons/notifyadmin/notifyadmin.php:40
-#, php-format
-msgid "%s : Message delivery failed."
-msgstr "%s : meldingslevering feilet."
+#: ../../Zotlabs/Module/Pdledit.php:130
+msgid "Module Name:"
+msgstr "Modulnavn:"
-#: ../../Zotlabs/Module/Invite.php:271
-#, php-format
-msgid "To %s : Message delivery success."
-msgstr ""
+#: ../../Zotlabs/Module/Pdledit.php:131
+msgid "Layout Help"
+msgstr "Layout-hjelp"
-#: ../../Zotlabs/Module/Invite.php:303
-#, php-format
-msgid "%1$d mail(s) sent, %2$d mail error(s)"
+#: ../../Zotlabs/Module/Pdledit.php:132
+msgid "Edit another layout"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:328
-msgid "Invites not proposed by configuration"
+#: ../../Zotlabs/Module/Pdledit.php:133
+msgid "System layout"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:329
-msgid "Contact the site admin"
-msgstr ""
+#: ../../Zotlabs/Module/Connect.php:65 ../../Zotlabs/Module/Connect.php:118
+msgid "Continue"
+msgstr "Fortsett"
-#: ../../Zotlabs/Module/Invite.php:345
-msgid "Invites by users not enabled"
-msgstr ""
+#: ../../Zotlabs/Module/Connect.php:99
+msgid "Premium Channel Setup"
+msgstr "Premiumkanal-oppsett"
-#: ../../Zotlabs/Module/Invite.php:350
-msgid "You have no more invitations available"
-msgstr "Du har ikke flere invitasjoner tilgjengelig"
+#: ../../Zotlabs/Module/Connect.php:101
+msgid "Enable premium channel connection restrictions"
+msgstr "Slå på restriksjoner for forbindelse med premiumkanal"
-#: ../../Zotlabs/Module/Invite.php:366
-msgid "Not on xchan"
+#: ../../Zotlabs/Module/Connect.php:102
+msgid ""
+"Please enter your restrictions or conditions, such as paypal receipt, usage "
+"guidelines, etc."
msgstr ""
+"Vennligst skriv dine restriksjoner og betingelser, slik som PayPal-"
+"kvittering, retningslinjer for bruk, og så videre."
-#: ../../Zotlabs/Module/Invite.php:399
-msgid "All users invitation limit exceeded."
+#: ../../Zotlabs/Module/Connect.php:104 ../../Zotlabs/Module/Connect.php:124
+msgid ""
+"This channel may require additional steps or acknowledgement of the "
+"following conditions prior to connecting:"
msgstr ""
+"Denne kanalen kan kreve ytterligere steg og bekreftelse av følgende "
+"betingelser før tilkobling:"
-#: ../../Zotlabs/Module/Invite.php:411 ../../Zotlabs/Module/Admin/Site.php:343
-msgid "Minute(s)"
-msgstr ""
+#: ../../Zotlabs/Module/Connect.php:105
+msgid ""
+"Potential connections will then see the following text before proceeding:"
+msgstr "Potensielle forbindelser vil da se følgende tekst før de går videre:"
-#: ../../Zotlabs/Module/Invite.php:412 ../../Zotlabs/Module/Admin/Site.php:344
-msgid "Hour(s)"
+#: ../../Zotlabs/Module/Connect.php:106 ../../Zotlabs/Module/Connect.php:127
+msgid ""
+"By continuing, I certify that I have complied with any instructions provided "
+"on this page."
msgstr ""
+"Ved å fortsette bekrefter jeg at jeg har oppfylt alle instruksjoner gitt på "
+"denne siden."
-#: ../../Zotlabs/Module/Invite.php:413 ../../Zotlabs/Module/Admin/Site.php:345
-msgid "Day(s)"
-msgstr ""
+#: ../../Zotlabs/Module/Connect.php:115
+msgid "(No specific instructions have been provided by the channel owner.)"
+msgstr "(Ingen spesifikke instruksjoner er gitt av kanaleieren.)"
-#: ../../Zotlabs/Module/Invite.php:417
-msgid "Invitation expires after"
-msgstr ""
+#: ../../Zotlabs/Module/Connect.php:123
+msgid "Restricted or Premium Channel"
+msgstr "Begrenset kanal eller premiumkanal"
-#: ../../Zotlabs/Module/Invite.php:422 ../../Zotlabs/Module/Admin/Site.php:362
-#: ../../Zotlabs/Module/Admin/Site.php:384
-msgid "duration up from now"
+#: ../../Zotlabs/Module/Filestorage.php:14
+#: ../../Zotlabs/Module/Filestorage.php:53
+msgid "Deprecated!"
msgstr ""
-#: ../../Zotlabs/Module/Invite.php:518 ../../Zotlabs/Module/Invite.php:557
-msgid "Invitation"
-msgstr ""
+#: ../../Zotlabs/Module/Filestorage.php:109
+#: ../../Zotlabs/Module/Attach_edit.php:69
+msgid "File not found."
+msgstr "Filen ble ikke funnet."
-#: ../../Zotlabs/Module/Invite.php:548
-msgid "Send invitations"
-msgstr "Send invitasjoner"
+#: ../../Zotlabs/Module/Filestorage.php:157
+msgid "Permission Denied."
+msgstr "Tillatelse avvist."
-#: ../../Zotlabs/Module/Invite.php:549
-msgid "Invitations I am using"
-msgstr "Invitasjoner jeg bruker"
+#: ../../Zotlabs/Module/Filestorage.php:190
+msgid "Edit file permissions"
+msgstr "Endre filtillatelser"
-#: ../../Zotlabs/Module/Invite.php:550
-msgid "Invitations we are using"
-msgstr ""
+#: ../../Zotlabs/Module/Filestorage.php:202
+msgid "Set/edit permissions"
+msgstr "Angi/endre tillatelser"
-#: ../../Zotlabs/Module/Invite.php:551
-msgid "§ Note, the email(s) sent will be recorded in the system logs"
-msgstr ""
+#: ../../Zotlabs/Module/Filestorage.php:203
+msgid "Include all files and sub folders"
+msgstr "Inkluder alle filer og undermapper"
-#: ../../Zotlabs/Module/Invite.php:552
-msgid "Enter email addresses, one per line:"
-msgstr "Skriv e-postadresser, en per linje:"
+#: ../../Zotlabs/Module/Filestorage.php:204
+msgid "Return to file list"
+msgstr "Gå tilbake til filoversikten"
-#: ../../Zotlabs/Module/Invite.php:553
-msgid "Your message:"
-msgstr "Din melding:"
+#: ../../Zotlabs/Module/Filestorage.php:209
+msgid "Share this file"
+msgstr "Del denne filen"
-#: ../../Zotlabs/Module/Invite.php:554
-msgid "Invite template"
-msgstr ""
+#: ../../Zotlabs/Module/Filestorage.php:210
+msgid "Show URL to this file"
+msgstr "Vis URLen til denne filen"
-#: ../../Zotlabs/Module/Invite.php:556
-msgid "Subject:"
-msgstr "Emne:"
+#: ../../Zotlabs/Module/Totp_check.php:71
+#: ../../Zotlabs/Module/Admin/Account_edit.php:66
+msgid "Account not found."
+msgstr "Kontoen ble ikke funnet."
-#: ../../Zotlabs/Module/Invite.php:562
-msgid "Here you may enter personal notes to the recipient(s)"
+#: ../../Zotlabs/Module/Totp_check.php:78
+msgid "Multifactor Verification"
msgstr ""
-#: ../../Zotlabs/Module/Sources.php:41
-msgid "Failed to create source. No channel selected."
-msgstr "Mislyktes med å lage kilde. Ingen kanal er valgt."
-
-#: ../../Zotlabs/Module/Sources.php:57
-msgid "Source created."
-msgstr "Kilden er laget."
-
-#: ../../Zotlabs/Module/Sources.php:70
-msgid "Source updated."
-msgstr "Kilden er oppdatert."
-
-#: ../../Zotlabs/Module/Sources.php:99
-msgid "*"
-msgstr "*"
-
-#: ../../Zotlabs/Module/Sources.php:106
-msgid "Manage remote sources of content for your channel."
-msgstr "Håndtere eksterne innholdskilder til din kanal."
+#: ../../Zotlabs/Module/Totp_check.php:80
+msgid "Please enter the verification key from your authenticator app"
+msgstr ""
-#: ../../Zotlabs/Module/Sources.php:107 ../../Zotlabs/Module/Sources.php:117
-msgid "New Source"
-msgstr "Ny kilde"
+#: ../../Zotlabs/Module/Totp_check.php:81
+msgid "Verify"
+msgstr ""
-#: ../../Zotlabs/Module/Sources.php:118 ../../Zotlabs/Module/Sources.php:152
-msgid ""
-"Import all or selected content from the following channel into this channel "
-"and distribute it according to your channel settings."
+#: ../../Zotlabs/Module/Contactedit.php:50
+msgid "Invalid abook_id"
msgstr ""
-"Importer alt eller et utvalgt av innhold fra følgende kanal inn i denne "
-"kanalen og distribuer det i henhold til dine egne kanalinnstillinger."
-#: ../../Zotlabs/Module/Sources.php:119 ../../Zotlabs/Module/Sources.php:153
-msgid "Only import content with these words (one per line)"
-msgstr "Bare importer innhold med disse ordene (ett ord per linje)"
+#: ../../Zotlabs/Module/Contactedit.php:78 ../../Zotlabs/Module/Connedit.php:80
+#: ../../Zotlabs/Module/Defperms.php:67
+msgid "Could not access contact record."
+msgstr "Fikk ikke tilgang til kontaktinformasjonen."
-#: ../../Zotlabs/Module/Sources.php:119 ../../Zotlabs/Module/Sources.php:153
-msgid "Leave blank to import all public content"
-msgstr "La stå tomt for å importere alt offentlig innhold"
+#: ../../Zotlabs/Module/Contactedit.php:107
+#: ../../Zotlabs/Module/Connedit.php:101
+msgid "Could not locate selected profile."
+msgstr "Fant ikke valgt profil."
-#: ../../Zotlabs/Module/Sources.php:120 ../../Zotlabs/Module/Sources.php:159
-msgid "Channel Name"
-msgstr "Kanalnavn"
+#: ../../Zotlabs/Module/Contactedit.php:180
+#: ../../Zotlabs/Module/Connedit.php:215
+msgid "is now connected to"
+msgstr "er nå forbundet til"
-#: ../../Zotlabs/Module/Sources.php:121 ../../Zotlabs/Module/Sources.php:156
-msgid ""
-"Add the following categories to posts imported from this source (comma "
-"separated)"
-msgstr ""
+#: ../../Zotlabs/Module/Contactedit.php:238
+#: ../../Zotlabs/Module/Contactedit.php:387
+#: ../../Zotlabs/Module/Connedit.php:701
+msgid "Contact Tools"
+msgstr "Kontaktverktøy"
-#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
-msgid "Resend posts with this channel as author"
+#: ../../Zotlabs/Module/Contactedit.php:315
+#: ../../Zotlabs/Module/Connedit.php:622
+msgid "Approve this contact"
msgstr ""
-#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
-msgid "Copyrights may apply"
+#: ../../Zotlabs/Module/Contactedit.php:315
+#: ../../Zotlabs/Module/Connedit.php:622
+msgid "Accept contact to allow communication"
msgstr ""
-#: ../../Zotlabs/Module/Sources.php:142 ../../Zotlabs/Module/Sources.php:172
-msgid "Source not found."
-msgstr "Kilden ble ikke funnet."
-
-#: ../../Zotlabs/Module/Sources.php:149
-msgid "Edit Source"
-msgstr "Endre kilde"
-
-#: ../../Zotlabs/Module/Sources.php:150
-msgid "Delete Source"
-msgstr "Slett kilde"
+#: ../../Zotlabs/Module/Contactedit.php:348
+#: ../../Zotlabs/Module/Connedit.php:658
+msgid "Please select a role for this contact!"
+msgstr ""
-#: ../../Zotlabs/Module/Sources.php:180
-msgid "Source removed"
-msgstr "Kilden er fjernet"
+#: ../../Zotlabs/Module/Contactedit.php:368
+#: ../../Zotlabs/Module/Connedit.php:678
+msgid "This contact is unreachable from this location."
+msgstr ""
-#: ../../Zotlabs/Module/Sources.php:182
-msgid "Unable to remove source."
-msgstr "Ikke i stand til å fjerne kilde."
+#: ../../Zotlabs/Module/Contactedit.php:369
+#: ../../Zotlabs/Module/Connedit.php:679
+msgid "This contact may be unreachable from other channel locations."
+msgstr ""
-#: ../../Zotlabs/Module/Suggest.php:52
-msgid ""
-"No suggestions available. If this is a new site, please try again in 24 "
-"hours."
+#: ../../Zotlabs/Module/Contactedit.php:371
+#: ../../Zotlabs/Module/Connedit.php:681
+msgid "Location independence is not supported by their network."
msgstr ""
-"Ingen forslag tilgjengelige. Hvis dette er et nytt nettsted, vennligst prøv "
-"igjen om 24 timer."
-#: ../../Zotlabs/Module/Suggest.php:71 ../../Zotlabs/Widget/Suggestions.php:53
-msgid "Ignore/Hide"
-msgstr "Ignorer/Skjul"
+#: ../../Zotlabs/Module/Contactedit.php:381
+#, fuzzy
+msgid "View profile"
+msgstr "Vis profil"
-#: ../../Zotlabs/Module/Impel.php:188
-#, php-format
-msgid "%s element installed"
-msgstr "%s element installert"
+#: ../../Zotlabs/Module/Contactedit.php:383
+#: ../../Zotlabs/Module/Connections.php:413
+msgid "This is a group/forum channel"
+msgstr ""
-#: ../../Zotlabs/Module/Impel.php:191
-#, php-format
-msgid "%s element installation failed"
-msgstr "Installasjon av %s-element mislyktes"
+#: ../../Zotlabs/Module/Contactedit.php:394
+msgid "Select a role for this contact"
+msgstr "Velg en rolle for denne forbindelsen"
-#: ../../Zotlabs/Module/Group.php:48
-msgid "Privacy group created."
-msgstr "Personverngruppen er opprettet."
+#: ../../Zotlabs/Module/Contactedit.php:406
+#: ../../Zotlabs/Module/Connedit.php:703
+msgid "Slide to adjust your degree of friendship"
+msgstr "Flytt for å justere din grad av vennskap"
-#: ../../Zotlabs/Module/Group.php:51
-msgid "Could not create privacy group."
-msgstr "Kunne ikke opprette personverngruppen."
+#: ../../Zotlabs/Module/Contactedit.php:408
+#: ../../Zotlabs/Module/Connedit.php:705
+msgid "Custom Filter"
+msgstr "Tilpasset filter"
-#: ../../Zotlabs/Module/Group.php:83
-msgid "Privacy group updated."
-msgstr "Personverngruppen er oppdatert."
+#: ../../Zotlabs/Module/Contactedit.php:409
+#: ../../Zotlabs/Module/Connedit.php:706
+#: ../../Zotlabs/Module/Settings/Channel.php:287
+msgid "Only import posts with this text"
+msgstr "Bare importer innlegg med disse ordene"
-#: ../../Zotlabs/Module/Group.php:138 ../../Zotlabs/Module/Group.php:302
-msgid "Post to this group by default"
+#: ../../Zotlabs/Module/Contactedit.php:409
+#: ../../Zotlabs/Module/Contactedit.php:410
+#: ../../Zotlabs/Module/Connedit.php:706 ../../Zotlabs/Module/Connedit.php:707
+#: ../../Zotlabs/Module/Admin/Site.php:502
+#: ../../Zotlabs/Module/Admin/Site.php:503
+msgid ""
+"words one per line or #tags or /patterns/ or lang=xx, leave blank to import "
+"all posts"
msgstr ""
+"ord per linje eller #merkelapper eller /mønster/ eller språk lang=xx, la stå "
+"blankt for å importere alle innlegg"
-#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:303
-msgid "Add new contacts to this group by default"
-msgstr ""
+#: ../../Zotlabs/Module/Contactedit.php:410
+#: ../../Zotlabs/Module/Connedit.php:707
+#: ../../Zotlabs/Module/Settings/Channel.php:286
+msgid "Do not import posts with this text"
+msgstr "Ikke importer innlegg med denne teksten"
-#: ../../Zotlabs/Module/Group.php:147
-#, fuzzy
-msgid "Privacy group name"
-msgstr "Personverngruppens navn:"
+#: ../../Zotlabs/Module/Contactedit.php:415
+#: ../../Zotlabs/Module/Connedit.php:714
+msgid "Approve contact"
+msgstr ""
-#: ../../Zotlabs/Module/Group.php:148 ../../Zotlabs/Module/Group.php:250
-msgid "Members are visible to other channels"
-msgstr "Medlemmer er synlig for andre kanaler"
+#: ../../Zotlabs/Module/Contactedit.php:417
+#: ../../Zotlabs/Module/Connedit.php:717
+msgid "Their"
+msgstr "Deres"
-#: ../../Zotlabs/Module/Group.php:176
-msgid "Privacy group removed."
-msgstr "Personverngruppen er fjernet."
+#: ../../Zotlabs/Module/Contactedit.php:418
+#: ../../Zotlabs/Module/Connedit.php:718
+msgid "My"
+msgstr "Mine"
-#: ../../Zotlabs/Module/Group.php:179
-msgid "Unable to remove privacy group."
-msgstr "Ikke i stand til å fjerne personverngruppen."
+#: ../../Zotlabs/Module/Contactedit.php:426
+msgid "Roles"
+msgstr "Roller"
-#: ../../Zotlabs/Module/Group.php:245
-#, fuzzy, php-format
-msgid "Privacy Group: %s"
-msgstr "Personverngruppe: %s"
+#: ../../Zotlabs/Module/Contactedit.php:427
+msgid "Compare permissions"
+msgstr "Sammenlign tillatelser"
-#: ../../Zotlabs/Module/Group.php:247
-msgid "Privacy group name: "
-msgstr "Personverngruppens navn: "
+#: ../../Zotlabs/Module/Contactedit.php:428
+msgid "Permission"
+msgstr "Tillatelse"
-#: ../../Zotlabs/Module/Group.php:263
-msgid "Group members"
+#: ../../Zotlabs/Module/Contactedit.php:432
+msgid "Content filter"
msgstr ""
-#: ../../Zotlabs/Module/Group.php:265
-msgid "Not in this group"
+#: ../../Zotlabs/Module/Contactedit.php:442
+msgid "Contact updated"
msgstr ""
-#: ../../Zotlabs/Module/Group.php:297
-msgid "Click a channel to toggle membership"
+#: ../../Zotlabs/Module/Contactedit.php:442
+msgid "Contact update failed"
msgstr ""
-#: ../../Zotlabs/Module/Layouts.php:184
-msgid "Comanche page description language help"
-msgstr "Hjelp med Comanche sidebeskrivelsesspråk"
-
-#: ../../Zotlabs/Module/Layouts.php:188
-msgid "Layout Description"
-msgstr "Layout-beskrivelse"
+#: ../../Zotlabs/Module/Contactedit.php:448
+#: ../../Zotlabs/Module/Connections.php:357
+msgid "Approve connection"
+msgstr "Godkjenn forbindelse"
-#: ../../Zotlabs/Module/Layouts.php:193
-msgid "Download PDL file"
-msgstr "Last ned PDL-fil"
+#: ../../Zotlabs/Module/Contactedit.php:520
+msgid "Block status updated"
+msgstr ""
-#: ../../Zotlabs/Module/Layouts.php:196 ../../Zotlabs/Module/Pubsites.php:63
-#: ../../Zotlabs/Module/Blocks.php:164 ../../Zotlabs/Module/Webpages.php:256
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:216
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:406
-msgid "View"
-msgstr "Vis"
+#: ../../Zotlabs/Module/Contactedit.php:524
+msgid "Block failed"
+msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:54
-msgid "Name and Secret are required"
+#: ../../Zotlabs/Module/Contactedit.php:535
+msgid "Ignore status updated"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:113
-msgid "Add OAuth2 application"
+#: ../../Zotlabs/Module/Contactedit.php:539
+msgid "Ignore failed"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:119 ../../Zotlabs/Module/Oauth2.php:147
-msgid "Grant Types"
+#: ../../Zotlabs/Module/Contactedit.php:550
+msgid "Archive status updated"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:119 ../../Zotlabs/Module/Oauth2.php:120
-msgid "leave blank unless your application sepcifically requires this"
+#: ../../Zotlabs/Module/Contactedit.php:554
+msgid "Archive failed"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:120 ../../Zotlabs/Module/Oauth2.php:148
-msgid "Authorization scope"
+#: ../../Zotlabs/Module/Contactedit.php:565
+msgid "Hide status updated"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:132
-msgid "OAuth2 Application not found."
+#: ../../Zotlabs/Module/Contactedit.php:569
+msgid "Hide failed"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:147 ../../Zotlabs/Module/Oauth2.php:148
-msgid "leave blank unless your application specifically requires this"
+#: ../../Zotlabs/Module/Contactedit.php:604
+msgid "Contact removed"
msgstr ""
-#: ../../Zotlabs/Module/Oauth2.php:190
-msgid "Connected OAuth2 Apps"
+#: ../../Zotlabs/Module/Contactedit.php:608
+msgid "Delete failed"
msgstr ""
-#: ../../Zotlabs/Module/Viewsrc.php:43
-msgid "item"
+#: ../../Zotlabs/Module/Contactedit.php:619
+msgid "Refetch contact info"
msgstr ""
-#: ../../Zotlabs/Module/Cover_photo.php:86
-#: ../../Zotlabs/Module/Profile_photo.php:129
-msgid "Image uploaded but image cropping failed."
-msgstr "Bildet ble lastet opp, men beskjæring av bildet mislyktes."
+#: ../../Zotlabs/Module/Contactedit.php:623
+#: ../../Zotlabs/Module/Connedit.php:508
+#: ../../Zotlabs/Module/Admin/Accounts.php:222
+msgid "Unblock"
+msgstr "Ikke blokker lenger"
-#: ../../Zotlabs/Module/Cover_photo.php:195
-#: ../../Zotlabs/Module/Cover_photo.php:252
-msgid "Cover Photos"
-msgstr "Forsidebilder"
+#: ../../Zotlabs/Module/Contactedit.php:623
+#: ../../Zotlabs/Module/Connedit.php:508
+#: ../../Zotlabs/Module/Admin/Accounts.php:221
+msgid "Block"
+msgstr "Blokker"
-#: ../../Zotlabs/Module/Cover_photo.php:211
-#: ../../Zotlabs/Module/Profile_photo.php:185
-msgid "Image resize failed."
-msgstr "Endring av bildestørrelse mislyktes."
+#: ../../Zotlabs/Module/Contactedit.php:625
+#: ../../Zotlabs/Module/Connedit.php:511
+msgid "Block (or Unblock) all communications with this connection"
+msgstr ""
+"Blokker eller fjern blokkering av all kommunikasjon med denne forbindelsen"
-#: ../../Zotlabs/Module/Cover_photo.php:263
-#: ../../Zotlabs/Module/Profile_photo.php:350
-msgid "Image upload failed."
-msgstr "Opplasting av bildet mislyktes."
+#: ../../Zotlabs/Module/Contactedit.php:626
+#: ../../Zotlabs/Module/Connedit.php:512
+msgid "This connection is blocked!"
+msgstr "Denne forbindelsen er blokkert!"
-#: ../../Zotlabs/Module/Cover_photo.php:280
-#: ../../Zotlabs/Module/Profile_photo.php:370
-msgid "Unable to process image."
-msgstr "Kan ikke behandle bildet."
+#: ../../Zotlabs/Module/Contactedit.php:630
+#: ../../Zotlabs/Module/Connedit.php:516
+msgid "Unignore"
+msgstr "Ikke ignorer lenger"
-#: ../../Zotlabs/Module/Cover_photo.php:325
-#: ../../Zotlabs/Module/Cover_photo.php:340
-#: ../../Zotlabs/Module/Profile_photo.php:432
-#: ../../Zotlabs/Module/Profile_photo.php:497
-msgid "Photo not available."
-msgstr "Bildet er ikke tilgjengelig."
+#: ../../Zotlabs/Module/Contactedit.php:630
+#: ../../Zotlabs/Module/Connedit.php:516
+#: ../../Zotlabs/Module/Connections.php:360
+msgid "Ignore"
+msgstr "Ignorer"
-#: ../../Zotlabs/Module/Cover_photo.php:376
-msgid "Your cover photo may be visible to anybody on the internet"
-msgstr "Omslagsbildet ditt vil være synlig for enhver på internett"
+#: ../../Zotlabs/Module/Contactedit.php:632
+#: ../../Zotlabs/Module/Connedit.php:519
+msgid "Ignore (or Unignore) all inbound communications from this connection"
+msgstr ""
+"Ignorer eller fjern ignorering av all inngående kommunikasjon fra denne "
+"forbindelsen"
-#: ../../Zotlabs/Module/Cover_photo.php:378
-msgid "Upload File:"
-msgstr "Last opp fil:"
+#: ../../Zotlabs/Module/Contactedit.php:633
+#: ../../Zotlabs/Module/Connedit.php:520
+msgid "This connection is ignored!"
+msgstr "Denne forbindelsen er ignorert!"
-#: ../../Zotlabs/Module/Cover_photo.php:379
-msgid "Select a profile:"
-msgstr "Velg en profil:"
+#: ../../Zotlabs/Module/Contactedit.php:637
+#: ../../Zotlabs/Module/Connedit.php:524
+msgid "Unarchive"
+msgstr "Ikke arkiver lenger"
-#: ../../Zotlabs/Module/Cover_photo.php:380
-#, fuzzy
-msgid "Change Cover Photo"
-msgstr "Endre omslagsbilde"
+#: ../../Zotlabs/Module/Contactedit.php:637
+#: ../../Zotlabs/Module/Connedit.php:524
+msgid "Archive"
+msgstr "Arkiver"
-#: ../../Zotlabs/Module/Cover_photo.php:384
-#: ../../Zotlabs/Module/Cover_photo.php:385
-#: ../../Zotlabs/Module/Profile_photo.php:551
-#: ../../Zotlabs/Module/Profile_photo.php:552
-msgid "Use a photo from your albums"
+#: ../../Zotlabs/Module/Contactedit.php:639
+#: ../../Zotlabs/Module/Connedit.php:527
+msgid ""
+"Archive (or Unarchive) this connection - mark channel dead but keep content"
msgstr ""
+"Arkiver eller fjern arkivering av denne forbindelsen - marker kanal som død, "
+"men behold innhold"
-#: ../../Zotlabs/Module/Cover_photo.php:390
-#: ../../Zotlabs/Module/Profile_photo.php:557
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:402
-msgid "Choose a different album"
-msgstr "Velg et annet album"
+#: ../../Zotlabs/Module/Contactedit.php:640
+#: ../../Zotlabs/Module/Connedit.php:528
+msgid "This connection is archived!"
+msgstr "Denne forbindelsen er arkivert!"
-#: ../../Zotlabs/Module/Cover_photo.php:396
-msgid "Select existing photo"
-msgstr "Velg eksisterende bilde"
+#: ../../Zotlabs/Module/Contactedit.php:644
+#: ../../Zotlabs/Module/Connedit.php:532
+msgid "Unhide"
+msgstr "Ikke skjul lenger"
-#: ../../Zotlabs/Module/Cover_photo.php:413
-#: ../../Zotlabs/Module/Profile_photo.php:581
-msgid "Crop Image"
-msgstr "Beskjær bildet"
+#: ../../Zotlabs/Module/Contactedit.php:644
+#: ../../Zotlabs/Module/Connedit.php:532
+msgid "Hide"
+msgstr "Skjul"
-#: ../../Zotlabs/Module/Cover_photo.php:414
-#: ../../Zotlabs/Module/Profile_photo.php:582
-msgid "Please adjust the image cropping for optimum viewing."
-msgstr "Vennligst juster bildebeskjæringen for optimal visning."
+#: ../../Zotlabs/Module/Contactedit.php:646
+#: ../../Zotlabs/Module/Connedit.php:535
+msgid "Hide or Unhide this connection from your other connections"
+msgstr ""
+"Skjul eller fjern skjuling av denne forbindelsen fra dine andre forbindelser"
-#: ../../Zotlabs/Module/Cover_photo.php:416
-msgid "Done Editing"
-msgstr "Lagre endringer"
+#: ../../Zotlabs/Module/Contactedit.php:647
+#: ../../Zotlabs/Module/Connedit.php:536
+msgid "This connection is hidden!"
+msgstr "Denne forbindelsen er skjult!"
-#: ../../Zotlabs/Module/Chat.php:29 ../../Zotlabs/Module/Ochannel.php:32
-msgid "You must be logged in to see this page."
-msgstr "Du må være innloegget for å se denne siden."
+#: ../../Zotlabs/Module/Contactedit.php:653
+#: ../../Zotlabs/Module/Connedit.php:543
+msgid "Delete this connection"
+msgstr "Slett denne forbindelsen"
-#: ../../Zotlabs/Module/Chat.php:193
-msgid "Room not found"
-msgstr "Rommet ble ikke funnet"
+#: ../../Zotlabs/Module/Search.php:251
+#, php-format
+msgid "Items tagged with: %s"
+msgstr "Elementer merket med: %s"
-#: ../../Zotlabs/Module/Chat.php:209
-msgid "Leave Room"
-msgstr "Forlat rom"
+#: ../../Zotlabs/Module/Search.php:253
+#, php-format
+msgid "Search results for: %s"
+msgstr "Søkeresultater for: %s"
-#: ../../Zotlabs/Module/Chat.php:210
-msgid "Delete Room"
-msgstr "Slett rom"
+#: ../../Zotlabs/Module/Register.php:113
+msgid "Email address required"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:211
-msgid "I am away right now"
-msgstr "Jeg er borte akkurat nå"
+#: ../../Zotlabs/Module/Register.php:157
+msgid "No password provided"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:212
-msgid "I am online"
-msgstr "Jeg er pålogget"
+#: ../../Zotlabs/Module/Register.php:180
+msgid "Terms of Service not accepted"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:214
-msgid "Bookmark this room"
-msgstr "Bokmerk dette rommet"
+#: ../../Zotlabs/Module/Register.php:242
+msgid "Invitation code succesfully applied"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:237
-msgid "New Chatroom"
-msgstr "Nytt chatrom"
+#: ../../Zotlabs/Module/Register.php:262
+msgid "Invitation not in time or too late"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:238
-msgid "Chatroom name"
-msgstr "Romnavn"
+#: ../../Zotlabs/Module/Register.php:268
+msgid "Invitation email failed"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:239
-msgid "Expiration of chats (minutes)"
-msgstr "Chat utgår (antall minutter)"
+#: ../../Zotlabs/Module/Register.php:276
+msgid "Invitation code failed"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:255
-#, php-format
-msgid "%1$s's Chatrooms"
-msgstr "%1$s sine chatrom"
+#: ../../Zotlabs/Module/Register.php:283
+msgid "Invitations are not available"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:260
-msgid "No chatrooms available"
-msgstr "Ingen rom tilgjengelige"
+#: ../../Zotlabs/Module/Register.php:293
+msgid "Registration on this hub is by invitation only"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:261
-msgid "Add Room"
+#: ../../Zotlabs/Module/Register.php:400
+msgid "New register request"
msgstr ""
-#: ../../Zotlabs/Module/Chat.php:264
-msgid "Expiration"
-msgstr "Utløp"
+#: ../../Zotlabs/Module/Register.php:418
+msgid "Error creating dId A"
+msgstr ""
-#: ../../Zotlabs/Module/Chat.php:265
-msgid "min"
-msgstr "min"
+#: ../../Zotlabs/Module/Register.php:437
+msgid "Registration on this hub is disabled."
+msgstr "Registrering ved dette nettstedet er skrudd av."
-#: ../../Zotlabs/Module/Dreport.php:37
-msgid "Invalid message"
-msgstr "Ugyldig melding"
+#: ../../Zotlabs/Module/Register.php:446
+msgid "Why do you want to join this hub?"
+msgstr ""
-#: ../../Zotlabs/Module/Dreport.php:67
-msgid "no results"
-msgstr "ingen resultater"
+#: ../../Zotlabs/Module/Register.php:446
+msgid "This will help to review your registration"
+msgstr ""
-#: ../../Zotlabs/Module/Dreport.php:81
-msgid "channel sync processed"
-msgstr "kanalsynkronisering er behandlet"
+#: ../../Zotlabs/Module/Register.php:447
+msgid "Registration on this hub is by approval only."
+msgstr "Registrering ved dette nettstedet skjer på godkjenning."
-#: ../../Zotlabs/Module/Dreport.php:85
-msgid "queued"
-msgstr "lagt i kø"
+#: ../../Zotlabs/Module/Register.php:448
+msgid "Register at another affiliated hub in case when prefered"
+msgstr ""
-#: ../../Zotlabs/Module/Dreport.php:89
-msgid "posted"
-msgstr "lagt inn"
+#: ../../Zotlabs/Module/Register.php:461
+msgid "Registration on this hub is by invitation only."
+msgstr ""
-#: ../../Zotlabs/Module/Dreport.php:93
-msgid "accepted for delivery"
-msgstr "akseptert for levering"
+#: ../../Zotlabs/Module/Register.php:462
+msgid "Register at another affiliated hub"
+msgstr ""
-#: ../../Zotlabs/Module/Dreport.php:97
-msgid "updated"
-msgstr "oppdatert"
+#: ../../Zotlabs/Module/Register.php:476 ../../Zotlabs/Module/Siteinfo.php:29
+msgid "Terms of Service"
+msgstr "Tjenesteavtale"
-#: ../../Zotlabs/Module/Dreport.php:101
-msgid "update ignored"
-msgstr "oppdatering ignorert"
+#: ../../Zotlabs/Module/Register.php:482
+#, php-format
+msgid "I accept the %s for this website"
+msgstr "Jeg godtar %s for dette nettstedet"
-#: ../../Zotlabs/Module/Dreport.php:104
-msgid "permission denied"
-msgstr "tillatelse avvist"
+#: ../../Zotlabs/Module/Register.php:489
+#, php-format
+msgid "I am over %s years of age and accept the %s for this website"
+msgstr "Jeg er mer enn %s år gammel, og godtar %s for dette nettstedet"
-#: ../../Zotlabs/Module/Dreport.php:108
-msgid "recipient not found"
-msgstr "mottaker ble ikke funnet"
+#: ../../Zotlabs/Module/Register.php:499
+msgid "Your email address"
+msgstr "Din e-postadresse"
-#: ../../Zotlabs/Module/Dreport.php:128
-#, php-format
-msgid "Delivery report for %1$s"
-msgstr "Leveringsrapport for %1$s"
+#: ../../Zotlabs/Module/Register.php:506
+msgid "Choose a password"
+msgstr "Velg et passord"
-#: ../../Zotlabs/Module/Dreport.php:132
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:906
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:71
-msgid "Options"
-msgstr "Valg"
+#: ../../Zotlabs/Module/Register.php:507
+msgid "Please re-enter your password"
+msgstr "Vennligst skriv ditt passord en gang til"
-#: ../../Zotlabs/Module/Dreport.php:133
-msgid "Redeliver"
-msgstr ""
+#: ../../Zotlabs/Module/Register.php:509
+msgid "Please enter your invitation code"
+msgstr "Vennligst skriv din invitasjonskode"
-#: ../../Zotlabs/Module/Rbmark.php:72
-msgid "Select a bookmark folder"
-msgstr "Velg en bokmerkemappe"
+#: ../../Zotlabs/Module/Register.php:511
+msgid "Your name"
+msgstr "Navn"
-#: ../../Zotlabs/Module/Rbmark.php:80
-msgid "Save Bookmark"
-msgstr "Lagre bokmerke"
+#: ../../Zotlabs/Module/Register.php:511
+msgid "Real name is preferred"
+msgstr ""
-#: ../../Zotlabs/Module/Rbmark.php:81
-msgid "URL of bookmark"
-msgstr "URL-en til bokmerket"
+#: ../../Zotlabs/Module/Register.php:513
+#: ../../Zotlabs/Module/New_channel.php:178
+msgid "Choose a short nickname"
+msgstr "Velg et kort kallenavn"
-#: ../../Zotlabs/Module/Rbmark.php:82 ../../Zotlabs/Module/Appman.php:220
-#: ../../Zotlabs/Module/Cdav.php:1005
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:260
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:657
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:173
-msgid "Description"
-msgstr "Beskrivelse"
+#: ../../Zotlabs/Module/Register.php:513
+msgid ""
+"Your nickname will be used to create an easy to remember channel address"
+msgstr ""
-#: ../../Zotlabs/Module/Rbmark.php:86
-msgid "Or enter new bookmark folder name"
-msgstr "Eller skriv nytt navn på bokmerkemappe"
+#: ../../Zotlabs/Module/Register.php:521
+#: ../../Zotlabs/Module/Admin/Site.php:404
+msgid "Registration"
+msgstr "Registrering"
-#: ../../Zotlabs/Module/Achievements.php:38
-msgid "Some blurb about what to do when you're new here"
-msgstr "En standardtekst om hva du bør gjøre som ny her"
+#: ../../Zotlabs/Module/Register.php:529
+msgid "I have an invite code"
+msgstr ""
-#: ../../Zotlabs/Module/Sse_bs.php:631
-msgid "Private forum"
+#: ../../Zotlabs/Module/Register.php:576
+msgid ""
+"This site has exceeded the number of allowed daily account registrations."
msgstr ""
-#: ../../Zotlabs/Module/Sse_bs.php:631
-#, fuzzy
-msgid "Public forum"
-msgstr "Offentlig forum:"
+#: ../../Zotlabs/Module/Pconfig.php:33 ../../Zotlabs/Module/Pconfig.php:69
+msgid "This setting requires special processing and editing has been blocked."
+msgstr ""
+"Denne innstillingen krever spesiell behandling og redigering har blitt "
+"blokkert."
+
+#: ../../Zotlabs/Module/Pconfig.php:58
+msgid "Configuration Editor"
+msgstr "Konfigurasjonsbehandler"
+
+#: ../../Zotlabs/Module/Pconfig.php:59
+msgid ""
+"Warning: Changing some settings could render your channel inoperable. Please "
+"leave this page unless you are comfortable with and knowledgeable about how "
+"to correctly use this feature."
+msgstr ""
+"Advarsel: kanalen din kan slutte å virke ved endring av enkelte "
+"innstillinger. Vennligst forlat denne siden med mindre du er komfortabel med "
+"dette og vet hvordan du bruker denne funksjonen riktig."
#: ../../Zotlabs/Module/Appman.php:39 ../../Zotlabs/Module/Appman.php:56
msgid "App installed."
@@ -7732,119 +9943,55 @@ msgstr "Pris på app"
msgid "Location (URL) to purchase app"
msgstr "Plassering (URL) for å kjøpe app"
-#: ../../Zotlabs/Module/Directory.php:124
-msgid "No default suggestions were found."
-msgstr ""
-
-#: ../../Zotlabs/Module/Directory.php:292
-msgid "Gender: "
-msgstr "Kjønn: "
-
-#: ../../Zotlabs/Module/Directory.php:294
-msgid "Status: "
-msgstr "Status: "
-
-#: ../../Zotlabs/Module/Directory.php:296
-msgid "Homepage: "
-msgstr "Hjemmeside: "
-
-#: ../../Zotlabs/Module/Directory.php:357
-msgid "Description:"
-msgstr "Beskrivelse:"
-
-#: ../../Zotlabs/Module/Directory.php:359
-msgid "Unsafe"
+#: ../../Zotlabs/Module/Viewsrc.php:43
+msgid "item"
msgstr ""
-#: ../../Zotlabs/Module/Directory.php:362
-msgid "Spam"
-msgstr ""
+#: ../../Zotlabs/Module/Item.php:254 ../../Zotlabs/Module/Pin.php:37
+msgid "Unable to locate original post."
+msgstr "Ikke i stand til å finne opprinnelig innlegg."
-#: ../../Zotlabs/Module/Directory.php:372
-msgid "Public Forum:"
-msgstr "Offentlig forum:"
+#: ../../Zotlabs/Module/Item.php:542
+msgid "Empty post discarded."
+msgstr "Tomt innlegg forkastet."
-#: ../../Zotlabs/Module/Directory.php:375
-msgid "Keywords: "
-msgstr "Nøkkelord: "
+#: ../../Zotlabs/Module/Item.php:1005
+msgid "Duplicate post suppressed."
+msgstr "Duplikat av innlegg forhindret."
-#: ../../Zotlabs/Module/Directory.php:378
-msgid "Don't suggest"
-msgstr "Ikke foreslå"
+#: ../../Zotlabs/Module/Item.php:1155
+msgid "System error. Post not saved."
+msgstr "Systemfeil. Innlegg ble ikke lagret."
-#: ../../Zotlabs/Module/Directory.php:380
-msgid "Common connections (estimated):"
+#: ../../Zotlabs/Module/Item.php:1195
+msgid "Your comment is awaiting approval."
msgstr ""
-#: ../../Zotlabs/Module/Directory.php:430
-msgid "Global Directory"
-msgstr "Global katalog"
-
-#: ../../Zotlabs/Module/Directory.php:430
-msgid "Local Directory"
-msgstr "Lokal katalog"
-
-#: ../../Zotlabs/Module/Directory.php:436
-msgid "Finding:"
-msgstr "Finner:"
-
-#: ../../Zotlabs/Module/Directory.php:441
-msgid "next page"
-msgstr "neste side"
-
-#: ../../Zotlabs/Module/Directory.php:441
-msgid "previous page"
-msgstr "forrige side"
-
-#: ../../Zotlabs/Module/Directory.php:442
-msgid "Sort options"
-msgstr "Sorteringsvalg"
-
-#: ../../Zotlabs/Module/Directory.php:443
-msgid "Alphabetic"
-msgstr "Alfabetisk"
-
-#: ../../Zotlabs/Module/Directory.php:444
-msgid "Reverse Alphabetic"
-msgstr "Omvendt alfabetisk"
-
-#: ../../Zotlabs/Module/Directory.php:445
-msgid "Newest to Oldest"
-msgstr "Nyest til eldst"
-
-#: ../../Zotlabs/Module/Directory.php:446
-msgid "Oldest to Newest"
-msgstr "Eldst til nyest"
-
-#: ../../Zotlabs/Module/Directory.php:464
-msgid "No entries (some entries may be hidden)."
-msgstr "Ingen oppføringer (noen oppføringer kan være skjult)."
-
-#: ../../Zotlabs/Module/Service_limits.php:23
-msgid "No service class restrictions found."
-msgstr "Ingen restriksjoner er funnet i tjenesteklasse."
+#: ../../Zotlabs/Module/Item.php:1333
+msgid "Unable to obtain post information from database."
+msgstr "Ikke i stand til å få tak i informasjon om innlegg fra databasen."
-#: ../../Zotlabs/Module/Tagrm.php:48 ../../Zotlabs/Module/Tagrm.php:98
-msgid "Tag removed"
-msgstr "Merkelapp fjernet"
+#: ../../Zotlabs/Module/Item.php:1340
+#, php-format
+msgid "You have reached your limit of %1$.0f top level posts."
+msgstr "Du har nådd din grense på %1$.0f startinnlegg."
-#: ../../Zotlabs/Module/Tagrm.php:123
-msgid "Remove Item Tag"
-msgstr "Fjern merkelapp fra element"
+#: ../../Zotlabs/Module/Item.php:1347
+#, php-format
+msgid "You have reached your limit of %1$.0f webpages."
+msgstr "Du har nådd din grense på %1$.0f websider."
-#: ../../Zotlabs/Module/Tagrm.php:125
-msgid "Select a tag to remove: "
-msgstr "Velg merkelapp å fjerne: "
+#: ../../Zotlabs/Module/Achievements.php:38
+msgid "Some blurb about what to do when you're new here"
+msgstr "En standardtekst om hva du bør gjøre som ny her"
-#: ../../Zotlabs/Module/Connedit.php:80 ../../Zotlabs/Module/Defperms.php:67
-#: ../../Zotlabs/Module/Contactedit.php:78
-msgid "Could not access contact record."
-msgstr "Fikk ikke tilgang til kontaktinformasjonen."
+#: ../../Zotlabs/Module/Oexchange.php:27
+msgid "Unable to find your hub."
+msgstr "Ikke i stand til å finne hubben din."
-#: ../../Zotlabs/Module/Connedit.php:101
-#: ../../Zotlabs/Module/Contactedit.php:107
-msgid "Could not locate selected profile."
-msgstr "Fant ikke valgt profil."
+#: ../../Zotlabs/Module/Oexchange.php:41
+msgid "Post successful."
+msgstr "Innlegg vellykket."
#: ../../Zotlabs/Module/Connedit.php:171
msgid "Connection updated."
@@ -7854,11 +10001,6 @@ msgstr "Forbindelsen er oppdatert."
msgid "Failed to update connection record."
msgstr "Mislyktes med å oppdatere forbindelsesinformasjonen."
-#: ../../Zotlabs/Module/Connedit.php:215
-#: ../../Zotlabs/Module/Contactedit.php:180
-msgid "is now connected to"
-msgstr "er nå forbundet til"
-
#: ../../Zotlabs/Module/Connedit.php:320
msgid "Could not access address book record."
msgstr "Fikk ikke tilgang til informasjonen i adresseboken."
@@ -7902,101 +10044,6 @@ msgstr ""
msgid "View recent posts and comments"
msgstr "Vis nylige innlegg og kommentarer"
-#: ../../Zotlabs/Module/Connedit.php:508
-#: ../../Zotlabs/Module/Admin/Accounts.php:322
-#: ../../Zotlabs/Module/Contactedit.php:623
-msgid "Unblock"
-msgstr "Ikke blokker lenger"
-
-#: ../../Zotlabs/Module/Connedit.php:508
-#: ../../Zotlabs/Module/Admin/Accounts.php:321
-#: ../../Zotlabs/Module/Contactedit.php:623
-msgid "Block"
-msgstr "Blokker"
-
-#: ../../Zotlabs/Module/Connedit.php:511
-#: ../../Zotlabs/Module/Contactedit.php:625
-msgid "Block (or Unblock) all communications with this connection"
-msgstr ""
-"Blokker eller fjern blokkering av all kommunikasjon med denne forbindelsen"
-
-#: ../../Zotlabs/Module/Connedit.php:512
-#: ../../Zotlabs/Module/Contactedit.php:626
-msgid "This connection is blocked!"
-msgstr "Denne forbindelsen er blokkert!"
-
-#: ../../Zotlabs/Module/Connedit.php:516
-#: ../../Zotlabs/Module/Contactedit.php:630
-msgid "Unignore"
-msgstr "Ikke ignorer lenger"
-
-#: ../../Zotlabs/Module/Connedit.php:516
-#: ../../Zotlabs/Module/Connections.php:360
-#: ../../Zotlabs/Module/Contactedit.php:630
-msgid "Ignore"
-msgstr "Ignorer"
-
-#: ../../Zotlabs/Module/Connedit.php:519
-#: ../../Zotlabs/Module/Contactedit.php:632
-msgid "Ignore (or Unignore) all inbound communications from this connection"
-msgstr ""
-"Ignorer eller fjern ignorering av all inngående kommunikasjon fra denne "
-"forbindelsen"
-
-#: ../../Zotlabs/Module/Connedit.php:520
-#: ../../Zotlabs/Module/Contactedit.php:633
-msgid "This connection is ignored!"
-msgstr "Denne forbindelsen er ignorert!"
-
-#: ../../Zotlabs/Module/Connedit.php:524
-#: ../../Zotlabs/Module/Contactedit.php:637
-msgid "Unarchive"
-msgstr "Ikke arkiver lenger"
-
-#: ../../Zotlabs/Module/Connedit.php:524
-#: ../../Zotlabs/Module/Contactedit.php:637
-msgid "Archive"
-msgstr "Arkiver"
-
-#: ../../Zotlabs/Module/Connedit.php:527
-#: ../../Zotlabs/Module/Contactedit.php:639
-msgid ""
-"Archive (or Unarchive) this connection - mark channel dead but keep content"
-msgstr ""
-"Arkiver eller fjern arkivering av denne forbindelsen - marker kanal som død, "
-"men behold innhold"
-
-#: ../../Zotlabs/Module/Connedit.php:528
-#: ../../Zotlabs/Module/Contactedit.php:640
-msgid "This connection is archived!"
-msgstr "Denne forbindelsen er arkivert!"
-
-#: ../../Zotlabs/Module/Connedit.php:532
-#: ../../Zotlabs/Module/Contactedit.php:644
-msgid "Unhide"
-msgstr "Ikke skjul lenger"
-
-#: ../../Zotlabs/Module/Connedit.php:532
-#: ../../Zotlabs/Module/Contactedit.php:644
-msgid "Hide"
-msgstr "Skjul"
-
-#: ../../Zotlabs/Module/Connedit.php:535
-#: ../../Zotlabs/Module/Contactedit.php:646
-msgid "Hide or Unhide this connection from your other connections"
-msgstr ""
-"Skjul eller fjern skjuling av denne forbindelsen fra dine andre forbindelser"
-
-#: ../../Zotlabs/Module/Connedit.php:536
-#: ../../Zotlabs/Module/Contactedit.php:647
-msgid "This connection is hidden!"
-msgstr "Denne forbindelsen er skjult!"
-
-#: ../../Zotlabs/Module/Connedit.php:543
-#: ../../Zotlabs/Module/Contactedit.php:653
-msgid "Delete this connection"
-msgstr "Slett denne forbindelsen"
-
#: ../../Zotlabs/Module/Connedit.php:550
msgid "Fetch Vcard"
msgstr ""
@@ -8005,41 +10052,10 @@ msgstr ""
msgid "Fetch electronic calling card for this connection"
msgstr ""
-#: ../../Zotlabs/Module/Connedit.php:572
-#: ../../Zotlabs/Module/Contactedit.php:431
-msgid "Affinity"
-msgstr ""
-
#: ../../Zotlabs/Module/Connedit.php:575
msgid "Open Set Affinity section by default"
msgstr ""
-#: ../../Zotlabs/Module/Connedit.php:579
-#: ../../Zotlabs/Module/Contactedit.php:281
-#: ../../Zotlabs/Widget/Affinity.php:36
-msgid "Me"
-msgstr "Meg"
-
-#: ../../Zotlabs/Module/Connedit.php:580
-#: ../../Zotlabs/Module/Contactedit.php:282
-#: ../../Zotlabs/Widget/Affinity.php:37
-msgid "Family"
-msgstr "Familie"
-
-#: ../../Zotlabs/Module/Connedit.php:582
-#: ../../Zotlabs/Module/Contactedit.php:284
-#: ../../Zotlabs/Widget/Affinity.php:39
-msgid "Acquaintances"
-msgstr "Bekjente"
-
-#: ../../Zotlabs/Module/Connedit.php:583
-#: ../../Zotlabs/Module/Connections.php:97
-#: ../../Zotlabs/Module/Connections.php:111
-#: ../../Zotlabs/Module/Contactedit.php:285
-#: ../../Zotlabs/Widget/Affinity.php:40
-msgid "All"
-msgstr "Alle"
-
#: ../../Zotlabs/Module/Connedit.php:612
msgid "Filter"
msgstr ""
@@ -8048,16 +10064,6 @@ msgstr ""
msgid "Open Custom Filter section by default"
msgstr ""
-#: ../../Zotlabs/Module/Connedit.php:622
-#: ../../Zotlabs/Module/Contactedit.php:315
-msgid "Approve this contact"
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:622
-#: ../../Zotlabs/Module/Contactedit.php:315
-msgid "Accept contact to allow communication"
-msgstr ""
-
#: ../../Zotlabs/Module/Connedit.php:626
msgid "Set Affinity"
msgstr "Angi nærhet"
@@ -8070,26 +10076,6 @@ msgstr "Angi profil"
msgid "Set Affinity & Profile"
msgstr "Angi nærhet og profil"
-#: ../../Zotlabs/Module/Connedit.php:658
-#: ../../Zotlabs/Module/Contactedit.php:348
-msgid "Please select a role for this contact!"
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:678
-#: ../../Zotlabs/Module/Contactedit.php:368
-msgid "This contact is unreachable from this location."
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:679
-#: ../../Zotlabs/Module/Contactedit.php:369
-msgid "This contact may be unreachable from other channel locations."
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:681
-#: ../../Zotlabs/Module/Contactedit.php:371
-msgid "Location independence is not supported by their network."
-msgstr ""
-
#: ../../Zotlabs/Module/Connedit.php:685
#, php-format
msgid "Contact: %s"
@@ -8120,46 +10106,6 @@ msgstr ""
"Tillatelsene angitt på denne siden gjøres gjeldende for alle nye "
"forbindelser."
-#: ../../Zotlabs/Module/Connedit.php:701
-#: ../../Zotlabs/Module/Contactedit.php:238
-#: ../../Zotlabs/Module/Contactedit.php:387
-msgid "Contact Tools"
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:703
-#: ../../Zotlabs/Module/Contactedit.php:406
-msgid "Slide to adjust your degree of friendship"
-msgstr "Flytt for å justere din grad av vennskap"
-
-#: ../../Zotlabs/Module/Connedit.php:705
-#: ../../Zotlabs/Module/Contactedit.php:408
-msgid "Custom Filter"
-msgstr "Tilpasset filter"
-
-#: ../../Zotlabs/Module/Connedit.php:706
-#: ../../Zotlabs/Module/Settings/Channel.php:287
-#: ../../Zotlabs/Module/Contactedit.php:409
-msgid "Only import posts with this text"
-msgstr "Bare importer innlegg med disse ordene"
-
-#: ../../Zotlabs/Module/Connedit.php:706 ../../Zotlabs/Module/Connedit.php:707
-#: ../../Zotlabs/Module/Admin/Site.php:501
-#: ../../Zotlabs/Module/Admin/Site.php:502
-#: ../../Zotlabs/Module/Contactedit.php:409
-#: ../../Zotlabs/Module/Contactedit.php:410
-msgid ""
-"words one per line or #tags or /patterns/ or lang=xx, leave blank to import "
-"all posts"
-msgstr ""
-"ord per linje eller #merkelapper eller /mønster/ eller språk lang=xx, la stå "
-"blankt for å importere alle innlegg"
-
-#: ../../Zotlabs/Module/Connedit.php:707
-#: ../../Zotlabs/Module/Settings/Channel.php:286
-#: ../../Zotlabs/Module/Contactedit.php:410
-msgid "Do not import posts with this text"
-msgstr "Ikke importer innlegg med denne teksten"
-
#: ../../Zotlabs/Module/Connedit.php:710
msgid "Contact Pending Approval"
msgstr ""
@@ -8169,11 +10115,6 @@ msgstr ""
msgid "inherited"
msgstr "arvet"
-#: ../../Zotlabs/Module/Connedit.php:714
-#: ../../Zotlabs/Module/Contactedit.php:415
-msgid "Approve contact"
-msgstr ""
-
#: ../../Zotlabs/Module/Connedit.php:715
#, php-format
msgid ""
@@ -8183,16 +10124,6 @@ msgstr ""
"Vennligst velg profilen du ønsker å vise %s når profilen din ses på en "
"sikret måte."
-#: ../../Zotlabs/Module/Connedit.php:717
-#: ../../Zotlabs/Module/Contactedit.php:417
-msgid "Their"
-msgstr ""
-
-#: ../../Zotlabs/Module/Connedit.php:718
-#: ../../Zotlabs/Module/Contactedit.php:418
-msgid "My"
-msgstr ""
-
#: ../../Zotlabs/Module/Connedit.php:720 ../../Zotlabs/Module/Defperms.php:266
msgid "Individual Permissions"
msgstr "Individuelle tillatelser"
@@ -8234,12 +10165,6 @@ msgstr ""
msgid "Organisation"
msgstr ""
-#: ../../Zotlabs/Module/Connedit.php:734 ../../Zotlabs/Module/Cdav.php:1370
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2653
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2722
-msgid "Title"
-msgstr "Tittel"
-
#: ../../Zotlabs/Module/Connedit.php:735 ../../Zotlabs/Module/Cdav.php:1371
msgid "Phone"
msgstr ""
@@ -8252,6 +10177,12 @@ msgstr ""
msgid "Website"
msgstr ""
+#: ../../Zotlabs/Module/Connedit.php:739 ../../Zotlabs/Module/Locs.php:122
+#: ../../Zotlabs/Module/Admin/Channels.php:182
+#: ../../Zotlabs/Module/Profiles.php:469 ../../Zotlabs/Module/Cdav.php:1375
+msgid "Address"
+msgstr "Adresse"
+
#: ../../Zotlabs/Module/Connedit.php:740 ../../Zotlabs/Module/Cdav.php:1376
msgid "Note"
msgstr ""
@@ -8288,323 +10219,355 @@ msgstr ""
msgid "ZIP Code"
msgstr ""
-#: ../../Zotlabs/Module/Import.php:71
-msgid "Nothing to import."
-msgstr "Ingenting å importere."
+#: ../../Zotlabs/Module/Connedit.php:757 ../../Zotlabs/Module/Profiles.php:772
+#: ../../Zotlabs/Module/Cdav.php:1393
+msgid "Country"
+msgstr "Land"
-#: ../../Zotlabs/Module/Import.php:87 ../../Zotlabs/Module/Import.php:101
-msgid "Unable to download data from old server"
-msgstr "Ikke i stand til å laste ned data fra gammel tjener"
+#: ../../Zotlabs/Module/Defperms.php:252
+msgid "Connection Default Permissions"
+msgstr "Forbindelsens standard tillatelser"
-#: ../../Zotlabs/Module/Import.php:164
-#, php-format
-msgid "Your service plan only allows %d channels."
-msgstr "Din tjenesteplan tillater bare %d kanaler."
+#: ../../Zotlabs/Module/Defperms.php:253
+msgid "Apply these permissions automatically"
+msgstr "Bruk disse tillatelsene automatisk"
-#: ../../Zotlabs/Module/Import.php:191
-msgid "No channel. Import failed."
-msgstr "Ingen kanal. Import mislyktes."
+#: ../../Zotlabs/Module/Defperms.php:253
+msgid ""
+"If enabled, connection requests will be approved without your interaction"
+msgstr ""
-#: ../../Zotlabs/Module/Import.php:197
-msgid "Channel exists but has been marked removed on this hub. Import failed."
+#: ../../Zotlabs/Module/Defperms.php:254
+msgid "Permission role"
msgstr ""
-#: ../../Zotlabs/Module/Import.php:568
-msgid ""
-"Automatic content and files import was not possible due to API version "
-"incompatiblity. Please import content and files manually!"
+#: ../../Zotlabs/Module/Defperms.php:255
+msgid "Add permission role"
msgstr ""
-#: ../../Zotlabs/Module/Import.php:596
-msgid "You must be logged in to use this feature."
-msgstr "Du må være innlogget for å bruke denne funksjonen."
+#: ../../Zotlabs/Module/Defperms.php:260
+msgid "Automatic approval settings"
+msgstr ""
-#: ../../Zotlabs/Module/Import.php:603
-msgid "Channel Import"
+#: ../../Zotlabs/Module/Defperms.php:263
+msgid "My Settings"
+msgstr "Mine innstillinger"
+
+#: ../../Zotlabs/Module/Defperms.php:267
+msgid ""
+"Some individual permissions may have been preset or locked based on your "
+"channel type and privacy settings."
msgstr ""
-#: ../../Zotlabs/Module/Import.php:604
+#: ../../Zotlabs/Module/Suggest.php:52
msgid ""
-"Use this form to import an existing channel from a different server/hub. You "
-"may retrieve the channel identity from the old server/hub via the network or "
-"provide an export file."
+"No suggestions available. If this is a new site, please try again in 24 "
+"hours."
msgstr ""
-"Bruk dette skjemaet for å importere en eksisterende kanal fra en annen "
-"tjener/hub. Du kan hente inn kanalidentiteten fra den gamle tjeneren/huben "
-"via nettverket eller ved å bruke en eksportfil."
+"Ingen forslag tilgjengelige. Hvis dette er et nytt nettsted, vennligst prøv "
+"igjen om 24 timer."
-#: ../../Zotlabs/Module/Import.php:606
-msgid "Or provide the old server/hub details"
-msgstr "Eller oppgi detaljene fra den gamle tjeneren/hub-en"
+#: ../../Zotlabs/Module/Follow.php:75
+msgid "Connection added."
+msgstr ""
-#: ../../Zotlabs/Module/Import.php:608
-msgid "Your old identity address (xyz@example.com)"
-msgstr "Din gamle identitetsadresse (xyz@example.com)"
+#: ../../Zotlabs/Module/Regmod.php:15
+msgid "Please login."
+msgstr "Vennligst logg inn."
-#: ../../Zotlabs/Module/Import.php:609
-msgid "Your old login email address"
-msgstr "Din gamle innloggings e-postadresse"
+#: ../../Zotlabs/Module/Directory.php:124
+msgid "No default suggestions were found."
+msgstr ""
-#: ../../Zotlabs/Module/Import.php:610
-msgid "Your old login password"
-msgstr "Ditt gamle innloggingspassord"
+#: ../../Zotlabs/Module/Directory.php:292
+msgid "Gender: "
+msgstr "Kjønn: "
-#: ../../Zotlabs/Module/Import.php:611
-msgid "Import your items and files (limited by available memory)"
-msgstr ""
+#: ../../Zotlabs/Module/Directory.php:294
+msgid "Status: "
+msgstr "Status: "
-#: ../../Zotlabs/Module/Import.php:613
-msgid ""
-"For either option, please choose whether to make this hub your new primary "
-"address, or whether your old location should continue this role. You will be "
-"able to post from either location, but only one can be marked as the primary "
-"location for files, photos, and media."
-msgstr ""
-"Enten du tar det ene eller det andre valget, vennligst angi om du vil at "
-"denne hubben skal være din nye primære adresse, eller om din gamle "
-"plassering skal fortsette å ha denne rollen. Du kan lage innlegg fra den ene "
-"eller den andre plasseringen, men bare en av dem kan markeres som den "
-"primære plasseringen for filer, bilder og media."
+#: ../../Zotlabs/Module/Directory.php:296
+msgid "Homepage: "
+msgstr "Hjemmeside: "
-#: ../../Zotlabs/Module/Import.php:615
-msgid "Make this hub my primary location"
-msgstr "Gjør dette nettstedet til min primære plassering"
+#: ../../Zotlabs/Module/Directory.php:357
+msgid "Description:"
+msgstr "Beskrivelse:"
-#: ../../Zotlabs/Module/Import.php:616
-msgid "Move this channel (disable all previous locations)"
+#: ../../Zotlabs/Module/Directory.php:359
+msgid "Unsafe"
msgstr ""
-#: ../../Zotlabs/Module/Import.php:617
-msgid "Use this channel nickname instead of the one provided"
+#: ../../Zotlabs/Module/Directory.php:362
+msgid "Spam"
msgstr ""
-#: ../../Zotlabs/Module/Import.php:617
-msgid ""
-"Leave blank to keep your existing channel nickname. You will be randomly "
-"assigned a similar nickname if either name is already allocated on this site."
-msgstr ""
+#: ../../Zotlabs/Module/Directory.php:372
+msgid "Public Forum:"
+msgstr "Offentlig forum:"
-#: ../../Zotlabs/Module/Import.php:619
-msgid ""
-"This process may take several minutes to complete. Please submit the form "
-"only once and leave this page open until finished."
+#: ../../Zotlabs/Module/Directory.php:375
+msgid "Keywords: "
+msgstr "Nøkkelord: "
+
+#: ../../Zotlabs/Module/Directory.php:378
+msgid "Don't suggest"
+msgstr "Ikke foreslå"
+
+#: ../../Zotlabs/Module/Directory.php:380
+msgid "Common connections (estimated):"
msgstr ""
-"Denne prosessen kan ta flere minutter å fullføre. Vennligst send inn dette "
-"skjemaet bare en gang og la siden være åpen inntil den er ferdig."
-#: ../../Zotlabs/Module/Editwebpage.php:139
-msgid "Page link"
-msgstr "Sidelenke"
+#: ../../Zotlabs/Module/Directory.php:430
+msgid "Global Directory"
+msgstr "Global katalog"
-#: ../../Zotlabs/Module/Editwebpage.php:166
-msgid "Edit Webpage"
-msgstr "Endre webside"
+#: ../../Zotlabs/Module/Directory.php:430
+msgid "Local Directory"
+msgstr "Lokal katalog"
-#: ../../Zotlabs/Module/Filer.php:53
-msgid "Enter a folder name"
-msgstr ""
+#: ../../Zotlabs/Module/Directory.php:436
+msgid "Finding:"
+msgstr "Finner:"
-#: ../../Zotlabs/Module/Filer.php:53
-msgid "or select an existing folder (doubleclick)"
-msgstr ""
+#: ../../Zotlabs/Module/Directory.php:441
+msgid "next page"
+msgstr "neste side"
-#: ../../Zotlabs/Module/Profperm.php:35 ../../Zotlabs/Module/Profperm.php:64
-msgid "Invalid profile identifier."
-msgstr "Ugyldig profil-identifikator."
+#: ../../Zotlabs/Module/Directory.php:441
+msgid "previous page"
+msgstr "forrige side"
-#: ../../Zotlabs/Module/Profperm.php:112
-msgid "Profile Visibility Editor"
-msgstr "Endre profilsynlighet"
+#: ../../Zotlabs/Module/Directory.php:442
+msgid "Sort options"
+msgstr "Sorteringsvalg"
-#: ../../Zotlabs/Module/Profperm.php:116
-msgid "Click on a contact to add or remove."
-msgstr "Klikk på en kontakt for å legge til eller fjerne."
+#: ../../Zotlabs/Module/Directory.php:443
+msgid "Alphabetic"
+msgstr "Alfabetisk"
-#: ../../Zotlabs/Module/Profperm.php:125
-msgid "Visible To"
-msgstr "Synlig for"
+#: ../../Zotlabs/Module/Directory.php:444
+msgid "Reverse Alphabetic"
+msgstr "Omvendt alfabetisk"
-#: ../../Zotlabs/Module/Profperm.php:141
-#: ../../Zotlabs/Module/Connections.php:221
-msgid "All Connections"
-msgstr "Alle forbindelser"
+#: ../../Zotlabs/Module/Directory.php:445
+msgid "Newest to Oldest"
+msgstr "Nyest til eldst"
-#: ../../Zotlabs/Module/Cal.php:62
-msgid "Permissions denied."
-msgstr "Tillatelse avvist."
+#: ../../Zotlabs/Module/Directory.php:446
+msgid "Oldest to Newest"
+msgstr "Eldst til nyest"
-#: ../../Zotlabs/Module/Cal.php:201 ../../Zotlabs/Module/Cdav.php:1028
-msgid "Today"
-msgstr "Idag"
+#: ../../Zotlabs/Module/Directory.php:464
+msgid "No entries (some entries may be hidden)."
+msgstr "Ingen oppføringer (noen oppføringer kan være skjult)."
-#: ../../Zotlabs/Module/Dircensor.php:61
-msgid "Entry censored"
+#: ../../Zotlabs/Module/Profile_photo.php:544
+msgid ""
+"This profile photo will be visible to anybody on the internet and may be "
+"distributed to other websites."
msgstr ""
+"Dette profilbildet vil være synlig for alle besøkende, og kan bli gjort "
+"tilgjengelig på andre nettsteder."
-#: ../../Zotlabs/Module/Dircensor.php:64
-msgid "Entry OK"
+#: ../../Zotlabs/Module/Profile_photo.php:544
+msgid ""
+"This profile photo will be visible only to channels with permission to view "
+"this profile."
msgstr ""
-#: ../../Zotlabs/Module/Acl.php:371
-msgid "network"
-msgstr "nettverk"
-
-#: ../../Zotlabs/Module/Pconfig.php:33 ../../Zotlabs/Module/Pconfig.php:69
-msgid "This setting requires special processing and editing has been blocked."
+#: ../../Zotlabs/Module/Profile_photo.php:546
+msgid "Use Photo for Profile"
msgstr ""
-"Denne innstillingen krever spesiell behandling og redigering har blitt "
-"blokkert."
-#: ../../Zotlabs/Module/Pconfig.php:58
-msgid "Configuration Editor"
-msgstr "Konfigurasjonsbehandler"
+#: ../../Zotlabs/Module/Profile_photo.php:546
+msgid "Change Profile Photo"
+msgstr "Endre profilbilde"
-#: ../../Zotlabs/Module/Pconfig.php:59
-msgid ""
-"Warning: Changing some settings could render your channel inoperable. Please "
-"leave this page unless you are comfortable with and knowledgeable about how "
-"to correctly use this feature."
-msgstr ""
-"Advarsel: kanalen din kan slutte å virke ved endring av enkelte "
-"innstillinger. Vennligst forlat denne siden med mindre du er komfortabel med "
-"dette og vet hvordan du bruker denne funksjonen riktig."
+#: ../../Zotlabs/Module/Profile_photo.php:548
+msgid "Reset to default"
+msgstr "Tilbakestill til standard"
-#: ../../Zotlabs/Module/Xchan.php:10
-msgid "Xchan Lookup"
-msgstr "Xchan oppslag"
+#: ../../Zotlabs/Module/Profile_photo.php:562
+msgid "Select existing"
+msgstr "Velg eksisterende bilde"
-#: ../../Zotlabs/Module/Xchan.php:13
-msgid "Lookup xchan beginning with (or webbie): "
-msgstr "Slå opp xchan som begynner med (eller webbie): "
+#: ../../Zotlabs/Module/Profile_photo.php:584
+msgid "Done editing"
+msgstr "Lagre endringer"
-#: ../../Zotlabs/Module/Register.php:113
-msgid "Email address required"
+#: ../../Zotlabs/Module/Authorize.php:17
+msgid "Unknown App"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:157
-msgid "No password provided"
+#: ../../Zotlabs/Module/Authorize.php:29
+msgid "Authorize"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:180
-msgid "Terms of Service not accepted"
+#: ../../Zotlabs/Module/Authorize.php:30
+#, php-format
+msgid "Do you authorize the app %s to access your channel data?"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:242
-msgid "Invitation code succesfully applied"
-msgstr ""
+#: ../../Zotlabs/Module/Authorize.php:33
+#: ../../Zotlabs/Module/Admin/Accounts.php:219
+msgid "Deny"
+msgstr "Avslå"
-#: ../../Zotlabs/Module/Register.php:262
-msgid "Invitation not in time or too late"
-msgstr ""
+#: ../../Zotlabs/Module/Viewconnections.php:65
+msgid "No connections."
+msgstr "Ingen forbindelser."
-#: ../../Zotlabs/Module/Register.php:268
-msgid "Invitation email failed"
-msgstr ""
+#: ../../Zotlabs/Module/Viewconnections.php:105
+#, php-format
+msgid "Visit %s's profile [%s]"
+msgstr "Besøk %s sin profil [%s]"
-#: ../../Zotlabs/Module/Register.php:276
-msgid "Invitation code failed"
-msgstr ""
+#: ../../Zotlabs/Module/Viewconnections.php:135
+msgid "View Connections"
+msgstr "Vis forbindelser"
-#: ../../Zotlabs/Module/Register.php:283
-msgid "Invitations are not available"
-msgstr ""
+#: ../../Zotlabs/Module/Api.php:76 ../../Zotlabs/Module/Api.php:97
+msgid "Authorize application connection"
+msgstr "Tillat programforbindelse"
-#: ../../Zotlabs/Module/Register.php:293
-msgid "Registration on this hub is by invitation only"
+#: ../../Zotlabs/Module/Api.php:77
+msgid "Return to your app and insert this Security Code:"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:400
-msgid "New register request"
-msgstr ""
+#: ../../Zotlabs/Module/Api.php:87
+msgid "Please login to continue."
+msgstr "Vennligst logg inn for å fortsette."
-#: ../../Zotlabs/Module/Register.php:418
-msgid "Error creating dId A"
+#: ../../Zotlabs/Module/Api.php:99
+msgid ""
+"Do you want to authorize this application to access your posts and contacts, "
+"and/or create new posts for you?"
msgstr ""
+"Vil du tillate dette programmet å få tilgang til dine innlegg og kontakter, "
+"og/eller lage nye innlegg for deg?"
-#: ../../Zotlabs/Module/Register.php:437
-msgid "Registration on this hub is disabled."
-msgstr "Registrering ved dette nettstedet er skrudd av."
+#: ../../Zotlabs/Module/Pdledit_gui.php:119
+#, fuzzy
+msgid "Layout not found"
+msgstr "Layouten ble ikke funnet."
-#: ../../Zotlabs/Module/Register.php:446
-msgid "Why do you want to join this hub?"
+#: ../../Zotlabs/Module/Pdledit_gui.php:127
+msgid "This template does not support pdledi_gui (no content regions defined)"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:446
-msgid "This will help to review your registration"
+#: ../../Zotlabs/Module/Pdledit_gui.php:404
+msgid "Main page content"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:447
-msgid "Registration on this hub is by approval only."
-msgstr "Registrering ved dette nettstedet skjer på godkjenning."
-
-#: ../../Zotlabs/Module/Register.php:448
-msgid "Register at another affiliated hub in case when prefered"
+#: ../../Zotlabs/Module/Pdledit_gui.php:405
+msgid "The main page content can not be edited!"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:461
-msgid "Registration on this hub is by invitation only."
-msgstr ""
+#: ../../Zotlabs/Module/Home.php:112
+#, php-format
+msgid "Welcome to %s"
+msgstr "Velkommen til %s"
-#: ../../Zotlabs/Module/Register.php:462
-msgid "Register at another affiliated hub"
-msgstr ""
+#: ../../Zotlabs/Module/Tagrm.php:48 ../../Zotlabs/Module/Tagrm.php:98
+msgid "Tag removed"
+msgstr "Merkelapp fjernet"
-#: ../../Zotlabs/Module/Register.php:476 ../../Zotlabs/Module/Siteinfo.php:29
-msgid "Terms of Service"
-msgstr "Tjenesteavtale"
+#: ../../Zotlabs/Module/Tagrm.php:123
+msgid "Remove Item Tag"
+msgstr "Fjern merkelapp fra element"
-#: ../../Zotlabs/Module/Register.php:482
-#, php-format
-msgid "I accept the %s for this website"
-msgstr "Jeg godtar %s for dette nettstedet"
+#: ../../Zotlabs/Module/Tagrm.php:125
+msgid "Select a tag to remove: "
+msgstr "Velg merkelapp å fjerne: "
-#: ../../Zotlabs/Module/Register.php:489
-#, php-format
-msgid "I am over %s years of age and accept the %s for this website"
-msgstr "Jeg er mer enn %s år gammel, og godtar %s for dette nettstedet"
+#: ../../Zotlabs/Module/Layouts.php:129 ../../Zotlabs/Module/Layouts.php:187
+#: ../../Zotlabs/Module/Editlayout.php:128
+msgid "Layout Name"
+msgstr "Layout-navn"
-#: ../../Zotlabs/Module/Register.php:499
-msgid "Your email address"
-msgstr "Din e-postadresse"
+#: ../../Zotlabs/Module/Layouts.php:132 ../../Zotlabs/Module/Editlayout.php:129
+msgid "Layout Description (Optional)"
+msgstr "Layoutens beskrivelse (valgfritt)"
-#: ../../Zotlabs/Module/Register.php:506
-msgid "Choose a password"
-msgstr "Velg et passord"
+#: ../../Zotlabs/Module/Layouts.php:184
+msgid "Comanche page description language help"
+msgstr "Hjelp med Comanche sidebeskrivelsesspråk"
-#: ../../Zotlabs/Module/Register.php:507
-msgid "Please re-enter your password"
-msgstr "Vennligst skriv ditt passord en gang til"
+#: ../../Zotlabs/Module/Layouts.php:188
+msgid "Layout Description"
+msgstr "Layout-beskrivelse"
-#: ../../Zotlabs/Module/Register.php:509
-msgid "Please enter your invitation code"
-msgstr "Vennligst skriv din invitasjonskode"
+#: ../../Zotlabs/Module/Layouts.php:193
+msgid "Download PDL file"
+msgstr "Last ned PDL-fil"
-#: ../../Zotlabs/Module/Register.php:511
-msgid "Your name"
-msgstr "Navn"
+#: ../../Zotlabs/Module/Common.php:14
+msgid "No channel."
+msgstr "Ingen kanal."
-#: ../../Zotlabs/Module/Register.php:511
-msgid "Real name is preferred"
+#: ../../Zotlabs/Module/Common.php:45
+msgid "No connections in common."
+msgstr "Ingen forbindelser felles."
+
+#: ../../Zotlabs/Module/Common.php:65
+msgid "View Common Connections"
msgstr ""
-#: ../../Zotlabs/Module/Register.php:513
+#: ../../Zotlabs/Module/Locs.php:27 ../../Zotlabs/Module/Locs.php:65
+msgid "Location not found."
+msgstr "Plassering er ikke funnet."
+
+#: ../../Zotlabs/Module/Locs.php:74
+msgid "Location lookup failed."
+msgstr "Oppslag på plassering mislyktes."
+
+#: ../../Zotlabs/Module/Locs.php:78
msgid ""
-"Your nickname will be used to create an easy to remember channel address"
+"Please select another location to become primary before removing the primary "
+"location."
msgstr ""
+"Vennligst velg en annen plassering som primær før du sletter gjeldende "
+"primære plassering."
-#: ../../Zotlabs/Module/Register.php:521
-#: ../../Zotlabs/Module/Admin/Site.php:403
-msgid "Registration"
-msgstr "Registrering"
+#: ../../Zotlabs/Module/Locs.php:106
+msgid "Syncing locations"
+msgstr "Synkroniserer plasseringer"
-#: ../../Zotlabs/Module/Register.php:529
-msgid "I have an invite code"
-msgstr ""
+#: ../../Zotlabs/Module/Locs.php:115
+msgid "No locations found."
+msgstr "Ingen plasseringer ble funnet."
-#: ../../Zotlabs/Module/Register.php:576
+#: ../../Zotlabs/Module/Locs.php:120
+msgid "Manage Channel Locations"
+msgstr "Håndter kanalplasseringer"
+
+#: ../../Zotlabs/Module/Locs.php:124 ../../Zotlabs/Module/Menu.php:177
+msgid "Drop"
+msgstr "Slett"
+
+#: ../../Zotlabs/Module/Locs.php:126
+msgid "Sync Now"
+msgstr "Synkroniser nå"
+
+#: ../../Zotlabs/Module/Locs.php:127
+msgid "Please wait several minutes between consecutive operations."
+msgstr "Vennligst vent flere minutter mellom hver etterfølgende operasjon."
+
+#: ../../Zotlabs/Module/Locs.php:128
msgid ""
-"This site has exceeded the number of allowed daily account registrations."
+"When possible, drop a location by logging into that website/hub and removing "
+"your channel."
msgstr ""
+"Når mulig, fjern en plassering ved å logge inn på det nettstedet eller den "
+"hub-en og fjern din kanal."
+
+#: ../../Zotlabs/Module/Locs.php:129
+msgid "Use this form to drop the location if the hub is no longer operating."
+msgstr ""
+"Bruk dette skjemaet for å fjerne plasseringen hvis huben ikke er i drift "
+"lenger."
#: ../../Zotlabs/Module/Pubsites.php:27
msgid "Public Hubs"
@@ -8652,998 +10615,823 @@ msgstr "Vurderinger"
msgid "Rate"
msgstr "Vurder"
-#: ../../Zotlabs/Module/Thing.php:139
-msgid "Thing updated"
-msgstr "Tingen er oppdatert"
-
-#: ../../Zotlabs/Module/Thing.php:191
-msgid "Object store: failed"
-msgstr "Objektlagring: mislyktes"
-
-#: ../../Zotlabs/Module/Thing.php:195
-msgid "Thing added"
-msgstr "Ting lagt til"
-
-#: ../../Zotlabs/Module/Thing.php:210
-#, php-format
-msgid "OBJ: %1$s %2$s %3$s"
-msgstr "OBJ: %1$s %2$s %3$s"
-
-#: ../../Zotlabs/Module/Thing.php:277 ../../Zotlabs/Module/Thing.php:301
-msgid "item not found."
-msgstr "element ble ikke funnet."
-
-#: ../../Zotlabs/Module/Thing.php:336
-msgid "Edit Thing"
-msgstr "Endre ting"
-
-#: ../../Zotlabs/Module/Thing.php:338 ../../Zotlabs/Module/Thing.php:394
-msgid "Select a profile"
-msgstr "Velg en profil"
-
-#: ../../Zotlabs/Module/Thing.php:342 ../../Zotlabs/Module/Thing.php:397
-msgid "Post an activity"
-msgstr "Legg inn en aktivitet"
-
-#: ../../Zotlabs/Module/Thing.php:342 ../../Zotlabs/Module/Thing.php:397
-msgid "Only sends to viewers of the applicable profile"
-msgstr "Sender bare til seere av den aktuelle profilen"
-
-#: ../../Zotlabs/Module/Thing.php:344 ../../Zotlabs/Module/Thing.php:399
-msgid "Name of thing e.g. something"
-msgstr "Navn på ting for eksempel noe"
-
-#: ../../Zotlabs/Module/Thing.php:346 ../../Zotlabs/Module/Thing.php:400
-msgid "URL of thing (optional)"
-msgstr "URL til ting (valgfritt)"
-
-#: ../../Zotlabs/Module/Thing.php:348 ../../Zotlabs/Module/Thing.php:401
-msgid "URL for photo of thing (optional)"
-msgstr "URL til bilde av ting (valgfritt)"
-
-#: ../../Zotlabs/Module/Thing.php:392
-msgid "Add Thing to your Profile"
-msgstr "Legg til ting i din profil"
-
-#: ../../Zotlabs/Module/Home.php:105
-#, php-format
-msgid "Welcome to %s"
-msgstr "Velkommen til %s"
-
-#: ../../Zotlabs/Module/Blocks.php:154
-msgid "Block Title"
-msgstr "Byggeklossens tittel"
-
-#: ../../Zotlabs/Module/Defperms.php:111
-#: ../../Zotlabs/Module/Settings/Channel.php:151
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:163
-#: ../../extend/addon/hzaddons/piwik/piwik.php:116
-#: ../../extend/addon/hzaddons/twitter/twitter.php:493
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:108
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:82
-#: ../../extend/addon/hzaddons/logrot/logrot.php:54
-#: ../../extend/addon/hzaddons/xmpp/xmpp.php:54
-#: ../../extend/addon/hzaddons/faces/faces.php:291
-#: ../../extend/addon/hzaddons/msgfooter/msgfooter.php:54
-msgid "Settings updated."
-msgstr "Innstillinger oppdatert."
-
-#: ../../Zotlabs/Module/Defperms.php:252
-msgid "Connection Default Permissions"
-msgstr "Forbindelsens standard tillatelser"
-
-#: ../../Zotlabs/Module/Defperms.php:253
-msgid "Apply these permissions automatically"
-msgstr "Bruk disse tillatelsene automatisk"
-
-#: ../../Zotlabs/Module/Defperms.php:253
+#: ../../Zotlabs/Module/Changeaddr.php:35
msgid ""
-"If enabled, connection requests will be approved without your interaction"
+"Channel name changes are not allowed within 48 hours of changing the account "
+"password."
msgstr ""
-#: ../../Zotlabs/Module/Defperms.php:254
-msgid "Permission role"
+#: ../../Zotlabs/Module/Changeaddr.php:77
+msgid "Change channel nickname/address"
msgstr ""
-#: ../../Zotlabs/Module/Defperms.php:255
-msgid "Add permission role"
+#: ../../Zotlabs/Module/Changeaddr.php:78
+msgid "Any/all connections on other networks will be lost!"
msgstr ""
-#: ../../Zotlabs/Module/Defperms.php:260
-msgid "Automatic approval settings"
+#: ../../Zotlabs/Module/Changeaddr.php:80
+msgid "New channel address"
msgstr ""
-#: ../../Zotlabs/Module/Defperms.php:263
-msgid "My Settings"
-msgstr "Mine innstillinger"
-
-#: ../../Zotlabs/Module/Defperms.php:267
-msgid ""
-"Some individual permissions may have been preset or locked based on your "
-"channel type and privacy settings."
+#: ../../Zotlabs/Module/Changeaddr.php:81
+msgid "Rename Channel"
msgstr ""
-#: ../../Zotlabs/Module/Lostpass.php:19
-msgid "No valid account found."
-msgstr "Ingen gyldig konto funnet."
-
-#: ../../Zotlabs/Module/Lostpass.php:33
-msgid "Password reset request issued. Check your email."
-msgstr "Forespørsel om å tilbakestille passord er mottatt. Sjekk e-posten din."
-
-#: ../../Zotlabs/Module/Lostpass.php:39 ../../Zotlabs/Module/Lostpass.php:108
-#, php-format
-msgid "Site Member (%s)"
-msgstr "Nettstedsmedlem (%s)"
-
-#: ../../Zotlabs/Module/Lostpass.php:44 ../../Zotlabs/Module/Lostpass.php:49
-#, php-format
-msgid "Password reset requested at %s"
-msgstr "Forespurt om å tilbakestille passord hos %s"
+#: ../../Zotlabs/Module/Menu.php:68
+msgid "Unable to update menu."
+msgstr "Ikke i stand til å oppdatere meny."
-#: ../../Zotlabs/Module/Lostpass.php:68
-msgid ""
-"Request could not be verified. (You may have previously submitted it.) "
-"Password reset failed."
-msgstr ""
-"Forespørsel kunne ikke bekreftes. (Du kan ha sendt den inn tidligere.) "
-"Tilbakestilling av passord mislyktes."
+#: ../../Zotlabs/Module/Menu.php:79
+msgid "Unable to create menu."
+msgstr "Ikke i stand til å lage meny."
-#: ../../Zotlabs/Module/Lostpass.php:92
-msgid "Your password has been reset as requested."
-msgstr "Ditt passord har blitt tilbakestilt som forespurt."
+#: ../../Zotlabs/Module/Menu.php:161 ../../Zotlabs/Module/Menu.php:174
+msgid "Menu Name"
+msgstr "Menynavn"
-#: ../../Zotlabs/Module/Lostpass.php:93
-msgid "Your new password is"
-msgstr "Ditt nye passord er"
+#: ../../Zotlabs/Module/Menu.php:161
+msgid "Unique name (not visible on webpage) - required"
+msgstr "Unikt navn (ikke synlig på websiden) - påkrevet"
-#: ../../Zotlabs/Module/Lostpass.php:94
-msgid "Save or copy your new password - and then"
-msgstr "Lagre eller kopier ditt nye passord, og deretter kan du"
+#: ../../Zotlabs/Module/Menu.php:162 ../../Zotlabs/Module/Menu.php:175
+msgid "Menu Title"
+msgstr "Menytittel"
-#: ../../Zotlabs/Module/Lostpass.php:95
-msgid "click here to login"
-msgstr "klikke her for å logge inn"
+#: ../../Zotlabs/Module/Menu.php:162
+msgid "Visible on webpage - leave empty for no title"
+msgstr "Synlig på websiden - la stå tomt for ingen tittel"
-#: ../../Zotlabs/Module/Lostpass.php:96
-msgid ""
-"Your password may be changed from the <em>Settings</em> page after "
-"successful login."
-msgstr ""
-"Ditt passord kan endres på siden <em>Innstillinger</em> etter vellykket "
-"innlogging."
+#: ../../Zotlabs/Module/Menu.php:163
+msgid "Allow Bookmarks"
+msgstr "Tillat bokmerker"
-#: ../../Zotlabs/Module/Lostpass.php:117
-#, php-format
-msgid "Your password has changed at %s"
-msgstr "Ditt passord er endret hos %s"
+#: ../../Zotlabs/Module/Menu.php:163 ../../Zotlabs/Module/Menu.php:222
+msgid "Menu may be used to store saved bookmarks"
+msgstr "Menyen kan brukes til å lagre lagrede bokmerker"
-#: ../../Zotlabs/Module/Lostpass.php:130
-msgid "Forgot your Password?"
-msgstr "Glemt passord ditt?"
+#: ../../Zotlabs/Module/Menu.php:164 ../../Zotlabs/Module/Menu.php:225
+msgid "Submit and proceed"
+msgstr "Send inn og fortsett"
-#: ../../Zotlabs/Module/Lostpass.php:131
-msgid ""
-"Enter your email address and submit to have your password reset. Then check "
-"your email for further instructions."
-msgstr ""
-"Skriv e-postadressen din og send inn for å tilbakestille passordet ditt. "
-"Sjekk deretter din e-post for videre instruksjoner."
+#: ../../Zotlabs/Module/Menu.php:181
+msgid "Bookmarks allowed"
+msgstr "Bokmerker tillatt"
-#: ../../Zotlabs/Module/Lostpass.php:132
-msgid "Email Address"
-msgstr "E-postadresse"
+#: ../../Zotlabs/Module/Menu.php:183
+msgid "Delete this menu"
+msgstr "Slett denne menyen"
-#: ../../Zotlabs/Module/Lostpass.php:133 ../../Zotlabs/Module/Pdledit.php:76
-#: ../../Zotlabs/Module/Pdledit.php:93
-msgid "Reset"
-msgstr "Tilbakestill"
+#: ../../Zotlabs/Module/Menu.php:184 ../../Zotlabs/Module/Menu.php:219
+msgid "Edit menu contents"
+msgstr "Endre menyinnholdet"
-#: ../../Zotlabs/Module/Locs.php:27 ../../Zotlabs/Module/Locs.php:65
-msgid "Location not found."
-msgstr "Plassering er ikke funnet."
+#: ../../Zotlabs/Module/Menu.php:185
+msgid "Edit this menu"
+msgstr "Endre denne menyen"
-#: ../../Zotlabs/Module/Locs.php:74
-msgid "Location lookup failed."
-msgstr "Oppslag på plassering mislyktes."
+#: ../../Zotlabs/Module/Menu.php:201
+msgid "Menu could not be deleted."
+msgstr "Menyen kunne ikke bli slettet."
-#: ../../Zotlabs/Module/Locs.php:78
-msgid ""
-"Please select another location to become primary before removing the primary "
-"location."
-msgstr ""
-"Vennligst velg en annen plassering som primær før du sletter gjeldende "
-"primære plassering."
+#: ../../Zotlabs/Module/Menu.php:209 ../../Zotlabs/Module/Mitem.php:31
+msgid "Menu not found."
+msgstr "Menyen ble ikke funnet."
-#: ../../Zotlabs/Module/Locs.php:106
-msgid "Syncing locations"
-msgstr "Synkroniserer plasseringer"
+#: ../../Zotlabs/Module/Menu.php:214
+msgid "Edit Menu"
+msgstr "Endre meny"
-#: ../../Zotlabs/Module/Locs.php:115
-msgid "No locations found."
-msgstr "Ingen plasseringer ble funnet."
+#: ../../Zotlabs/Module/Menu.php:218
+msgid "Add or remove entries to this menu"
+msgstr "Legg til eller fjern punkter i denne menyen"
-#: ../../Zotlabs/Module/Locs.php:120
-msgid "Manage Channel Locations"
-msgstr "Håndter kanalplasseringer"
+#: ../../Zotlabs/Module/Menu.php:220
+msgid "Menu name"
+msgstr "Menynavn"
-#: ../../Zotlabs/Module/Locs.php:123
-#: ../../extend/addon/hzaddons/workflow/workflow.php:288
-msgid "Primary"
-msgstr "Primær"
+#: ../../Zotlabs/Module/Menu.php:220
+msgid "Must be unique, only seen by you"
+msgstr "Må være unik, ses bare av deg"
-#: ../../Zotlabs/Module/Locs.php:126
-msgid "Sync Now"
-msgstr "Synkroniser nå"
+#: ../../Zotlabs/Module/Menu.php:221
+msgid "Menu title"
+msgstr "Menytittel"
-#: ../../Zotlabs/Module/Locs.php:127
-msgid "Please wait several minutes between consecutive operations."
-msgstr "Vennligst vent flere minutter mellom hver etterfølgende operasjon."
+#: ../../Zotlabs/Module/Menu.php:221
+msgid "Menu title as seen by others"
+msgstr "Menytittelen andre ser"
-#: ../../Zotlabs/Module/Locs.php:128
-msgid ""
-"When possible, drop a location by logging into that website/hub and removing "
-"your channel."
-msgstr ""
-"Når mulig, fjern en plassering ved å logge inn på det nettstedet eller den "
-"hub-en og fjern din kanal."
+#: ../../Zotlabs/Module/Menu.php:222
+msgid "Allow bookmarks"
+msgstr "Tillat bokmerker"
-#: ../../Zotlabs/Module/Locs.php:129
-msgid "Use this form to drop the location if the hub is no longer operating."
-msgstr ""
-"Bruk dette skjemaet for å fjerne plasseringen hvis huben ikke er i drift "
-"lenger."
+#: ../../Zotlabs/Module/Menu.php:232 ../../Zotlabs/Module/Xchan.php:41
+#: ../../Zotlabs/Module/Mitem.php:134
+msgid "Not found."
+msgstr "Ikke funnet."
-#: ../../Zotlabs/Module/Totp_check.php:71
-#: ../../Zotlabs/Module/Admin/Account_edit.php:61
-#, fuzzy
-msgid "Account not found."
+#: ../../Zotlabs/Module/Admin/Accounts.php:71
+msgid "Account not found"
msgstr "Kontoen ble ikke funnet"
-#: ../../Zotlabs/Module/Totp_check.php:78
-msgid "Multifactor Verification"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Accounts.php:92
+#, php-format
+msgid "Account '%s' blocked"
+msgstr "Kontoen '%s' blokkert"
-#: ../../Zotlabs/Module/Totp_check.php:80
-msgid "Please enter the verification key from your authenticator app"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Accounts.php:100
+#, php-format
+msgid "Account '%s' unblocked"
+msgstr "Kontoen '%s' er ikke blokkert lenger"
-#: ../../Zotlabs/Module/Totp_check.php:81
-msgid "Verify"
+#: ../../Zotlabs/Module/Admin/Accounts.php:140
+msgid "Unverified"
msgstr ""
-#: ../../Zotlabs/Module/Page.php:174
-msgid ""
-"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
-"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
-"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
-"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
-"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
-"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+#: ../../Zotlabs/Module/Admin/Accounts.php:143
+msgid "Expired"
msgstr ""
-"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
-"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
-"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
-"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
-"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
-"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
-#: ../../Zotlabs/Module/Vote.php:40
-msgid "Poll not found."
-msgstr "Fant ikke spørreskjema."
+#: ../../Zotlabs/Module/Admin/Accounts.php:207
+#: ../../Zotlabs/Module/Admin/Themes.php:138
+#: ../../Zotlabs/Module/Admin/Themes.php:172
+#: ../../Zotlabs/Module/Admin/Addons.php:124
+#: ../../Zotlabs/Module/Admin/Addons.php:191
+#: ../../Zotlabs/Module/Admin/Channels.php:167
+#: ../../Zotlabs/Module/Admin/Logs.php:83
+#: ../../Zotlabs/Module/Admin/Site.php:399
+#: ../../Zotlabs/Module/Admin/Security.php:106
+#: ../../Zotlabs/Module/Admin.php:142
+msgid "Administration"
+msgstr "Administrasjon"
-#: ../../Zotlabs/Module/Vote.php:69
-msgid "Invalid response."
+#: ../../Zotlabs/Module/Admin/Accounts.php:210
+msgid "Show verified registrations"
msgstr ""
-#: ../../Zotlabs/Module/Vote.php:125
-msgid "Response submitted. Updates may not appear instantly."
+#: ../../Zotlabs/Module/Admin/Accounts.php:210
+msgid "Show all registrations"
msgstr ""
-#: ../../Zotlabs/Module/Profile_photo.php:269
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:299
-msgid ""
-"Shift-reload the page or clear browser cache if the new photo does not "
-"display immediately."
+#: ../../Zotlabs/Module/Admin/Accounts.php:212
+msgid "Select toggle"
msgstr ""
-"Hold nede Shift-knappen og last siden på nytt eller tøm nettleserens "
-"mellomlager hvis det nye bildet ikke vises umiddelbart."
-#: ../../Zotlabs/Module/Profile_photo.php:544
-msgid ""
-"This profile photo will be visible to anybody on the internet and may be "
-"distributed to other websites."
+#: ../../Zotlabs/Module/Admin/Accounts.php:213
+msgid "Deny selected"
msgstr ""
-"Dette profilbildet vil være synlig for enhver besøkende, og kan bli gjort tilg"
-"jengelig på andre nettsteder."
-#: ../../Zotlabs/Module/Profile_photo.php:544
-msgid ""
-"This profile photo will be visible only to channels with permission to view "
-"this profile."
+#: ../../Zotlabs/Module/Admin/Accounts.php:214
+msgid "Approve selected"
msgstr ""
-#: ../../Zotlabs/Module/Profile_photo.php:546
-msgid "Use Photo for Profile"
+#: ../../Zotlabs/Module/Admin/Accounts.php:215
+msgid "All registrations"
msgstr ""
-#: ../../Zotlabs/Module/Profile_photo.php:546
-msgid "Change Profile Photo"
-msgstr "Endre profilbilde"
-
-#: ../../Zotlabs/Module/Profile_photo.php:548
-msgid "Reset to default"
-msgstr "Sett tilbake til standard"
+#: ../../Zotlabs/Module/Admin/Accounts.php:215
+msgid "Verified registrations waiting for approval"
+msgstr ""
-#: ../../Zotlabs/Module/Profile_photo.php:562
-msgid "Select existing"
-msgstr "Velg eksisterende bilde"
+#: ../../Zotlabs/Module/Admin/Accounts.php:216
+msgid "Request date"
+msgstr "Dato for forespørsel"
-#: ../../Zotlabs/Module/Profile_photo.php:584
-msgid "Done editing"
-msgstr "Lagre endringer"
+#: ../../Zotlabs/Module/Admin/Accounts.php:216
+msgid "Requests"
+msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:67
-msgid "Import Webpage Elements"
+#: ../../Zotlabs/Module/Admin/Accounts.php:217
+msgid "No registrations available"
msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:68
-msgid "Import selected"
+#: ../../Zotlabs/Module/Admin/Accounts.php:217
+msgid "No verified registrations available"
msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:91
-msgid "Export Webpage Elements"
+#: ../../Zotlabs/Module/Admin/Accounts.php:223
+msgid "Verified"
msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:92
-msgid "Export selected"
+#: ../../Zotlabs/Module/Admin/Accounts.php:224
+msgid "Not yet verified"
msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:258
-msgid "Actions"
-msgstr "Handlinger"
+#: ../../Zotlabs/Module/Admin/Accounts.php:229
+msgid "ID"
+msgstr "ID"
-#: ../../Zotlabs/Module/Webpages.php:259
-msgid "Page Link"
-msgstr "Sidelenke"
+#: ../../Zotlabs/Module/Admin/Accounts.php:231
+msgid "All channels"
+msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:260
-msgid "Page Title"
-msgstr "Sidetittel"
+#: ../../Zotlabs/Module/Admin/Accounts.php:232
+msgid "Register date"
+msgstr "Registreringsdato"
-#: ../../Zotlabs/Module/Webpages.php:290
-msgid "Invalid file type."
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Accounts.php:233
+msgid "Last login"
+msgstr "Siste innlogging"
-#: ../../Zotlabs/Module/Webpages.php:302
-msgid "Error opening zip file"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Accounts.php:234
+msgid "Expires"
+msgstr "Utløper"
-#: ../../Zotlabs/Module/Webpages.php:313
-msgid "Invalid folder path."
+#: ../../Zotlabs/Module/Admin/Accounts.php:235
+#: ../../Zotlabs/Module/Admin/Account_edit.php:77
+msgid "Service class"
msgstr ""
-#: ../../Zotlabs/Module/Webpages.php:340
-msgid "No webpage elements detected."
+#: ../../Zotlabs/Module/Admin/Accounts.php:237
+msgid ""
+"Selected accounts will be deleted!\\n\\nEverything these accounts had posted "
+"on this site will be permanently deleted!\\n\\nAre you sure?"
msgstr ""
+"Valgte kontoer vil bli slettet!\\n\\nAlt disse kontoene har lagt inn på "
+"dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil "
+"slette disse valgte kontoene?"
-#: ../../Zotlabs/Module/Webpages.php:415
-msgid "Import complete."
+#: ../../Zotlabs/Module/Admin/Accounts.php:238
+msgid ""
+"The account {0} will be deleted!\\n\\nEverything this account has posted on "
+"this site will be permanently deleted!\\n\\nAre you sure?"
msgstr ""
+"Kontoen {0} vl bli slettet!\\n\\nAlt denne kontoen har lagt inn på dette "
+"nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette "
+"denne kontoen?"
-#: ../../Zotlabs/Module/Connections.php:58
-#: ../../Zotlabs/Module/Connections.php:116
-msgid "Active"
-msgstr "Aktiv"
-
-#: ../../Zotlabs/Module/Connections.php:63
-#: ../../Zotlabs/Module/Connections.php:185
-#: ../../Zotlabs/Module/Connections.php:296
-msgid "Blocked"
-msgstr "Blokkert"
-
-#: ../../Zotlabs/Module/Connections.php:68
-#: ../../Zotlabs/Module/Connections.php:192
-#: ../../Zotlabs/Module/Connections.php:295
-msgid "Ignored"
-msgstr "Ignorert"
-
-#: ../../Zotlabs/Module/Connections.php:73
-#: ../../Zotlabs/Module/Connections.php:206
-#: ../../Zotlabs/Module/Connections.php:294
-msgid "Hidden"
-msgstr "Skjult"
+#: ../../Zotlabs/Module/Admin/Accounts.php:247
+msgid "Message"
+msgstr "Melding"
-#: ../../Zotlabs/Module/Connections.php:78
-#: ../../Zotlabs/Module/Connections.php:199
-msgid "Archived/Unreachable"
-msgstr "Arkivert/utilgjengelig"
+#: ../../Zotlabs/Module/Admin/Accounts.php:366
+#, php-format
+msgid "%s account blocked/unblocked"
+msgid_plural "%s account blocked/unblocked"
+msgstr[0] "%s konto blokkert/ikke blokkert lenger"
+msgstr[1] "%s kontoer blokkert/ikke blokkert lenger"
-#: ../../Zotlabs/Module/Connections.php:161
-msgid "Active Connections"
-msgstr "Aktive forbindelser"
+#: ../../Zotlabs/Module/Admin/Accounts.php:389
+#, php-format
+msgid "%s account deleted"
+msgid_plural "%s accounts deleted"
+msgstr[0] "%s konto slettet"
+msgstr[1] "%s kontoer slettet"
-#: ../../Zotlabs/Module/Connections.php:164
-msgid "Show active connections"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:66
+msgid "Max queueworker threads"
msgstr ""
-#: ../../Zotlabs/Module/Connections.php:168
-#: ../../Zotlabs/Widget/Notifications.php:96
-msgid "New Connections"
-msgstr "Nye forbindelser"
-
-#: ../../Zotlabs/Module/Connections.php:171
-msgid "Show pending (new) connections"
-msgstr "Vis ventende (nye) forbindelser"
-
-#: ../../Zotlabs/Module/Connections.php:188
-msgid "Only show blocked connections"
-msgstr "Vis bare forbindelser som er blokkert"
-
-#: ../../Zotlabs/Module/Connections.php:195
-msgid "Only show ignored connections"
-msgstr "Vis bare ignorerte forbindelser"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:68
+msgid "Minimum 4, default 4"
+msgstr ""
-#: ../../Zotlabs/Module/Connections.php:202
-msgid "Only show archived/unreachable connections"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:78
+msgid "Assume workers dead after"
msgstr ""
-#: ../../Zotlabs/Module/Connections.php:209
-msgid "Only show hidden connections"
-msgstr "Vis bare skjulte forbindelser"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:80
+msgid "Minimum 120, default 300 seconds"
+msgstr ""
-#: ../../Zotlabs/Module/Connections.php:224
-msgid "Show all connections"
-msgstr "Vis alle forbindelser"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:92
+msgid "Pause before starting next task"
+msgstr ""
-#: ../../Zotlabs/Module/Connections.php:292
-msgid "Pending approval"
-msgstr "Venter på godkjenning"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:94
+msgid "Minimum 100, default 100 microseconds"
+msgstr ""
-#: ../../Zotlabs/Module/Connections.php:293
-msgid "Archived"
-msgstr "Arkivert"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:103
+msgid "Automatically adjust pause before starting next task"
+msgstr ""
-#: ../../Zotlabs/Module/Connections.php:297
-msgid "Not connected at this location"
+#: ../../Zotlabs/Module/Admin/Queueworker.php:112
+msgid "Queueworker Settings"
msgstr ""
-#: ../../Zotlabs/Module/Connections.php:336
-#, php-format
-msgid "%1$s [%2$s]"
-msgstr "%1$s [%2$s]"
+#: ../../Zotlabs/Module/Admin/Themes.php:27
+msgid "Theme settings updated."
+msgstr "Temainnstillinger er oppdatert."
-#: ../../Zotlabs/Module/Connections.php:337
-msgid "Edit connection"
-msgstr "Endre forbindelse"
+#: ../../Zotlabs/Module/Admin/Themes.php:62
+msgid "No themes found."
+msgstr "Ingen temaer er funnet."
-#: ../../Zotlabs/Module/Connections.php:339
-msgid "Delete connection"
-msgstr "Slett forbindelse"
+#: ../../Zotlabs/Module/Admin/Themes.php:111
+#: ../../Zotlabs/Module/Admin/Addons.php:93
+msgid "Disable"
+msgstr "Skru av"
-#: ../../Zotlabs/Module/Connections.php:345
-msgid "Channel address"
-msgstr "Kanaladresse"
+#: ../../Zotlabs/Module/Admin/Themes.php:113
+#: ../../Zotlabs/Module/Admin/Addons.php:96
+msgid "Enable"
+msgstr "Skru på"
-#: ../../Zotlabs/Module/Connections.php:350
-msgid "Call"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Themes.php:132
+msgid "Screenshot"
+msgstr "Skjermbilde"
-#: ../../Zotlabs/Module/Connections.php:352
-msgid "Status"
-msgstr "Status"
+#: ../../Zotlabs/Module/Admin/Themes.php:140
+#: ../../Zotlabs/Module/Admin/Addons.php:126
+msgid "Toggle"
+msgstr "Skru av og på"
-#: ../../Zotlabs/Module/Connections.php:355
-msgid "Connected"
-msgstr "Forbundet"
+#: ../../Zotlabs/Module/Admin/Themes.php:150
+#: ../../Zotlabs/Module/Admin/Addons.php:134
+msgid "Author: "
+msgstr "Forfatter: "
-#: ../../Zotlabs/Module/Connections.php:357
-#: ../../Zotlabs/Module/Contactedit.php:448
-msgid "Approve connection"
-msgstr "Godkjenn forbindelse"
+#: ../../Zotlabs/Module/Admin/Themes.php:151
+#: ../../Zotlabs/Module/Admin/Addons.php:135
+msgid "Maintainer: "
+msgstr "Vedlikeholder: "
-#: ../../Zotlabs/Module/Connections.php:359
-msgid "Ignore connection"
-msgstr "Ignorer forbindelse"
+#: ../../Zotlabs/Module/Admin/Themes.php:178
+msgid "[Experimental]"
+msgstr "[Eksperimentelt]"
-#: ../../Zotlabs/Module/Connections.php:361
-msgid "Recent activity"
-msgstr "Nylig aktivitet"
+#: ../../Zotlabs/Module/Admin/Themes.php:179
+msgid "[Unsupported]"
+msgstr "[Ingen støtte]"
-#: ../../Zotlabs/Module/Connections.php:367
-msgid "Connect at this location"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Addons.php:72
+#, php-format
+msgid "Plugin %s disabled."
+msgstr "Tilleggsfunksjonen %s er avskrudd."
-#: ../../Zotlabs/Module/Connections.php:377 ../../Zotlabs/Widget/Follow.php:27
+#: ../../Zotlabs/Module/Admin/Addons.php:77
#, php-format
-msgid "You have %1$.0f of %2$.0f allowed connections."
-msgstr "Du har %1$.0f av %2$.0f tillate forbindelser."
+msgid "Plugin %s enabled."
+msgstr "Tilleggsfunksjonen %s er påskrudd."
-#: ../../Zotlabs/Module/Connections.php:405
-msgid "Search your connections"
-msgstr "Søk blant dine forbindelser"
+#: ../../Zotlabs/Module/Admin/Addons.php:136
+msgid "Minimum project version: "
+msgstr "Minimum prosjektversjon: "
-#: ../../Zotlabs/Module/Connections.php:406
-msgid "Contact search"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Addons.php:137
+msgid "Maximum project version: "
+msgstr "Maksimum prosjektversjon: "
-#: ../../Zotlabs/Module/Connections.php:413
-#: ../../Zotlabs/Module/Contactedit.php:383
-msgid "This is a group/forum channel"
+#: ../../Zotlabs/Module/Admin/Addons.php:138
+msgid "Minimum PHP version: "
+msgstr "Minimum PHP-versjon: "
+
+#: ../../Zotlabs/Module/Admin/Addons.php:139
+msgid "Compatible Server Roles: "
msgstr ""
-#: ../../Zotlabs/Module/Connect.php:65 ../../Zotlabs/Module/Connect.php:118
-msgid "Continue"
-msgstr "Fortsett"
+#: ../../Zotlabs/Module/Admin/Addons.php:140
+msgid "Requires: "
+msgstr "Krever: "
-#: ../../Zotlabs/Module/Connect.php:99
-msgid "Premium Channel Setup"
-msgstr "Premiumkanal-oppsett"
+#: ../../Zotlabs/Module/Admin/Addons.php:141
+#: ../../Zotlabs/Module/Admin/Addons.php:197
+msgid "Disabled - version incompatibility"
+msgstr "Skrudd av - versjonsinkompatibilitet"
-#: ../../Zotlabs/Module/Connect.php:101
-msgid "Enable premium channel connection restrictions"
-msgstr "Slå på restriksjoner for forbindelse med premiumkanal"
+#: ../../Zotlabs/Module/Admin/Features.php:56
+#, php-format
+msgid "Lock feature %s"
+msgstr "Lås funksjon %s"
-#: ../../Zotlabs/Module/Connect.php:102
-msgid ""
-"Please enter your restrictions or conditions, such as paypal receipt, usage "
-"guidelines, etc."
-msgstr ""
-"Vennligst skriv dine restriksjoner og betingelser, slik som PayPal-"
-"kvittering, retningslinjer for bruk, og så videre."
+#: ../../Zotlabs/Module/Admin/Features.php:65
+msgid "Manage Additional Features"
+msgstr "Håndter tilleggsfunksjoner"
-#: ../../Zotlabs/Module/Connect.php:104 ../../Zotlabs/Module/Connect.php:124
-msgid ""
-"This channel may require additional steps or acknowledgement of the "
-"following conditions prior to connecting:"
-msgstr ""
-"Denne kanalen kan kreve ytterligere steg og bekreftelse av følgende "
-"betingelser før tilkobling:"
+#: ../../Zotlabs/Module/Admin/Profs.php:90
+msgid "New Profile Field"
+msgstr "Nytt profilfelt"
-#: ../../Zotlabs/Module/Connect.php:105
-msgid ""
-"Potential connections will then see the following text before proceeding:"
-msgstr "Potensielle forbindelser vil da se følgende tekst før de går videre:"
+#: ../../Zotlabs/Module/Admin/Profs.php:91
+#: ../../Zotlabs/Module/Admin/Profs.php:111
+msgid "Field nickname"
+msgstr "Feltets kallenavn"
-#: ../../Zotlabs/Module/Connect.php:106 ../../Zotlabs/Module/Connect.php:127
-msgid ""
-"By continuing, I certify that I have complied with any instructions provided "
-"on this page."
-msgstr ""
-"Ved å fortsette bekrefter jeg at jeg har oppfylt alle instruksjoner gitt på "
-"denne siden."
+#: ../../Zotlabs/Module/Admin/Profs.php:91
+#: ../../Zotlabs/Module/Admin/Profs.php:111
+msgid "System name of field"
+msgstr "Systemnavnet til feltet"
-#: ../../Zotlabs/Module/Connect.php:115
-msgid "(No specific instructions have been provided by the channel owner.)"
-msgstr "(Ingen spesifikke instruksjoner er gitt av kanaleieren.)"
+#: ../../Zotlabs/Module/Admin/Profs.php:92
+#: ../../Zotlabs/Module/Admin/Profs.php:112
+msgid "Input type"
+msgstr "Inndata-type"
-#: ../../Zotlabs/Module/Connect.php:123
-msgid "Restricted or Premium Channel"
-msgstr "Begrenset kanal eller premiumkanal"
+#: ../../Zotlabs/Module/Admin/Profs.php:93
+#: ../../Zotlabs/Module/Admin/Profs.php:113
+msgid "Field Name"
+msgstr "Feltnavn"
-#: ../../Zotlabs/Module/Affinity.php:35
-msgid "Affinity Tool settings updated."
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:93
+#: ../../Zotlabs/Module/Admin/Profs.php:113
+msgid "Label on profile pages"
+msgstr "Merkelapp på profilsider"
-#: ../../Zotlabs/Module/Affinity.php:54
-msgid ""
-"The numbers below represent the minimum and maximum slider default positions "
-"for your network/stream page as a percentage."
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:94
+#: ../../Zotlabs/Module/Admin/Profs.php:114
+msgid "Help text"
+msgstr "Hjelpetekst"
-#: ../../Zotlabs/Module/Affinity.php:61
-msgid "Default maximum affinity level"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:94
+#: ../../Zotlabs/Module/Admin/Profs.php:114
+msgid "Additional info (optional)"
+msgstr "Tilleggsinformasjon (valgfritt)"
-#: ../../Zotlabs/Module/Affinity.php:61
-#, fuzzy
-msgid "0-99 default 99"
-msgstr "Standard"
+#: ../../Zotlabs/Module/Admin/Profs.php:104
+msgid "Field definition not found"
+msgstr "Feltdefinisjonen ble ikke funnet"
-#: ../../Zotlabs/Module/Affinity.php:67
-msgid "Default minimum affinity level"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:110
+msgid "Edit Profile Field"
+msgstr "Endre profilfelt"
-#: ../../Zotlabs/Module/Affinity.php:67
-#, fuzzy
-msgid "0-99 - default 0"
-msgstr "Standard"
+#: ../../Zotlabs/Module/Admin/Profs.php:170
+msgid "Basic Profile Fields"
+msgstr "Grunnleggende profilfelter"
-#: ../../Zotlabs/Module/Affinity.php:73
-msgid "Persistent affinity levels"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:171
+msgid "Advanced Profile Fields"
+msgstr "Utvidede profilfelter"
-#: ../../Zotlabs/Module/Affinity.php:73
-msgid ""
-"If disabled the max and min levels will be reset to default after page reload"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Profs.php:171
+msgid "(In addition to basic fields)"
+msgstr "(I tillegg til grunnleggende felt)"
-#: ../../Zotlabs/Module/Affinity.php:81
-msgid "Affinity Tool Settings"
+#: ../../Zotlabs/Module/Admin/Profs.php:173
+msgid "All available fields"
+msgstr "Alle tilgjengelige felt"
+
+#: ../../Zotlabs/Module/Admin/Profs.php:174
+msgid "Custom Fields"
msgstr ""
-#: ../../Zotlabs/Module/Siteinfo.php:22
-msgid "About this site"
-msgstr "Om dette nettstedet "
+#: ../../Zotlabs/Module/Admin/Profs.php:178
+msgid "Create Custom Field"
+msgstr "Legg til egendefinert felt"
-#: ../../Zotlabs/Module/Siteinfo.php:23
-#, fuzzy
-msgid "Site Name"
-msgstr "Nettstedets navn"
+#: ../../Zotlabs/Module/Admin/Channels.php:28
+#: ../../Zotlabs/Module/Admin/Channels.php:80
+msgid "Channel not found"
+msgstr "Kanalen ble ikke funnet"
-#: ../../Zotlabs/Module/Siteinfo.php:25 ../../Zotlabs/Module/Admin/Site.php:414
-msgid "Site Information"
-msgstr "Nettstedsinformasjon"
+#: ../../Zotlabs/Module/Admin/Channels.php:48
+#, php-format
+msgid "%s channel censored/uncensored"
+msgid_plural "%s channels censored/uncensored"
+msgstr[0] "%s kanal er sensurert/ikke sensurert lenger"
+msgstr[1] "%s kanaler er sensurert/ikke sensurert lenger"
-#: ../../Zotlabs/Module/Siteinfo.php:27
-msgid "Administrator"
-msgstr "Administrator"
+#: ../../Zotlabs/Module/Admin/Channels.php:57
+#, php-format
+msgid "%s channel code allowed/disallowed"
+msgid_plural "%s channels code allowed/disallowed"
+msgstr[0] "%s kanal med kode tillatt/ikke tillatt"
+msgstr[1] "%s kanaler med kode tillatt/ikke tillatt"
-#: ../../Zotlabs/Module/Siteinfo.php:30
-msgid "Software and Project information"
-msgstr "Program- og prosjektinformasjon"
+#: ../../Zotlabs/Module/Admin/Channels.php:63
+#, php-format
+msgid "%s channel deleted"
+msgid_plural "%s channels deleted"
+msgstr[0] "%s kanal slettet"
+msgstr[1] "%s kanaler slettet"
-#: ../../Zotlabs/Module/Siteinfo.php:31
-msgid "This site is powered by $Projectname"
-msgstr "Dette nettstedet drives av $Projectname"
+#: ../../Zotlabs/Module/Admin/Channels.php:90
+#, php-format
+msgid "Channel '%s' deleted"
+msgstr "Kanalen '%s' er slettet"
-#: ../../Zotlabs/Module/Siteinfo.php:32
-#, fuzzy
-#| msgid ""
-#| "Federated and decentralised networking and identity services provided by "
-#| "Zot"
-msgid ""
-"Federated and decentralised networking and identity services provided by"
-msgstr "Fødererte og desentraliserte nettverks- og identitetstjenester via Zot"
+#: ../../Zotlabs/Module/Admin/Channels.php:109
+#, php-format
+msgid "Channel '%s' censored"
+msgstr "Kanalen '%s' er sensurert"
-#: ../../Zotlabs/Module/Siteinfo.php:35
-msgid "Additional federated transport protocols:"
-msgstr "Øvrige fødererte transportprotokoller:"
+#: ../../Zotlabs/Module/Admin/Channels.php:109
+#, php-format
+msgid "Channel '%s' uncensored"
+msgstr "Kanalen '%s' er ikke sensurert lenger"
-#: ../../Zotlabs/Module/Siteinfo.php:37
+#: ../../Zotlabs/Module/Admin/Channels.php:120
#, php-format
-msgid "Version %s"
-msgstr "Versjon %s"
+msgid "Channel '%s' code allowed"
+msgstr "Kanal '%s' kode tillatt"
-#: ../../Zotlabs/Module/Siteinfo.php:38
-msgid "Project homepage"
-msgstr "Prosjektets hjemmeside"
+#: ../../Zotlabs/Module/Admin/Channels.php:120
+#, php-format
+msgid "Channel '%s' code disallowed"
+msgstr "Kanal '%s' kode ikke tillatt"
-#: ../../Zotlabs/Module/Siteinfo.php:39
-msgid "Developer homepage"
-msgstr "Utviklers hjemmeside"
+#: ../../Zotlabs/Module/Admin/Channels.php:170
+msgid "select all"
+msgstr "velg alle"
-#: ../../Zotlabs/Module/Siteinfo.php:42 ../../Zotlabs/Module/Admin.php:149
-msgid "Active addons"
-msgstr "Aktive tillegg"
+#: ../../Zotlabs/Module/Admin/Channels.php:172
+msgid "Censor"
+msgstr "Sensurer"
-#: ../../Zotlabs/Module/Siteinfo.php:43
-#, fuzzy
-#| msgid "Block Title"
-msgid "Blocked sites"
-msgstr "Byggeklossens tittel"
+#: ../../Zotlabs/Module/Admin/Channels.php:173
+msgid "Uncensor"
+msgstr "Ikke sensurer lenger"
-#: ../../Zotlabs/Module/Import_progress.php:40
-msgid "Item sync completed!"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Channels.php:174
+msgid "Allow Code"
+msgstr "Tillat kode"
-#: ../../Zotlabs/Module/Import_progress.php:62
-#: ../../Zotlabs/Module/Import_progress.php:111
-msgid "Import host does not seem to be online or compatible"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Channels.php:175
+msgid "Disallow Code"
+msgstr "Ikke tillat kode"
-#: ../../Zotlabs/Module/Import_progress.php:73
-msgid "Item sync completed but no items were found!"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Channels.php:180
+msgid "UID"
+msgstr "UID"
-#: ../../Zotlabs/Module/Import_progress.php:90
-msgid "File sync completed!"
+#: ../../Zotlabs/Module/Admin/Channels.php:184
+msgid ""
+"Selected channels will be deleted!\\n\\nEverything that was posted in these "
+"channels on this site will be permanently deleted!\\n\\nAre you sure?"
msgstr ""
+"Valgte kanaler vil bli slettet!\\n\\nAlt innhold som er lagt inn i disse "
+"kanalene på dette nettstedet vil bli slettet for alltid!\\n\\nEr du sikker "
+"på at du vil slette disse kanalene med alt innhold?"
-#: ../../Zotlabs/Module/Import_progress.php:122
-msgid "File sync completed but no files were found!"
+#: ../../Zotlabs/Module/Admin/Channels.php:185
+msgid ""
+"The channel {0} will be deleted!\\n\\nEverything that was posted in this "
+"channel on this site will be permanently deleted!\\n\\nAre you sure?"
msgstr ""
+"Kanalen {0} vil bli slettet!\\n\\nAlt innhold som er lagt inn i denne "
+"kanalen på dettet nettstedet vil bli slettet for alltid!\\n\\nEr du sikker "
+"på at du vil slette denne kanalen med alt innhold?"
-#: ../../Zotlabs/Module/Import_progress.php:137
-msgid "Channel clone status"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:34
+#, php-format
+msgid "Password changed for account %d."
msgstr ""
-#: ../../Zotlabs/Module/Import_progress.php:138
-msgid "Item sync status"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:51
+msgid "Account settings updated."
msgstr ""
-#: ../../Zotlabs/Module/Import_progress.php:139
-msgid "File sync status"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:73
+msgid "Account Edit"
msgstr ""
-#: ../../Zotlabs/Module/Import_progress.php:146
-msgid "Channel cloning completed!"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:74
+msgid "New Password"
msgstr ""
-#: ../../Zotlabs/Module/Import_progress.php:147
-msgid "Resume"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:75
+msgid "New Password again"
msgstr ""
-#: ../../Zotlabs/Module/Import_progress.php:148
-msgid "Only resume if sync stalled!"
+#: ../../Zotlabs/Module/Admin/Account_edit.php:76
+msgid "Account language (for emails)"
msgstr ""
-#: ../../Zotlabs/Module/Dirsearch.php:24 ../../Zotlabs/Module/Regdir.php:53
-msgid "This site is not a directory server"
-msgstr "Dette nettstedet er ikke en katalogtjener"
-
-#: ../../Zotlabs/Module/Dirsearch.php:32
-msgid "This directory server requires an access token"
-msgstr "Denne katalogtjeneren krever en tilgangsnøkkel (access token)"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:19
+msgid "Update has been marked successful"
+msgstr "Oppdateringen har blitt merket som en suksess"
-#: ../../Zotlabs/Module/Filestorage.php:14
-#: ../../Zotlabs/Module/Filestorage.php:53
-msgid "Deprecated!"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:32
+#, php-format
+msgid "Verification of update %s failed. Check system logs."
msgstr ""
-#: ../../Zotlabs/Module/Filestorage.php:109
-#: ../../Zotlabs/Module/Attach_edit.php:69
-msgid "File not found."
-msgstr "Filen ble ikke funnet."
-
-#: ../../Zotlabs/Module/Filestorage.php:157
-msgid "Permission Denied."
-msgstr "Tillatelse avvist."
-
-#: ../../Zotlabs/Module/Filestorage.php:190
-msgid "Edit file permissions"
-msgstr "Endre filtillatelser"
-
-#: ../../Zotlabs/Module/Filestorage.php:202
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:268
-msgid "Set/edit permissions"
-msgstr "Angi/endre tillatelser"
-
-#: ../../Zotlabs/Module/Filestorage.php:203
-msgid "Include all files and sub folders"
-msgstr "Inkluder alle filer og undermapper"
-
-#: ../../Zotlabs/Module/Filestorage.php:204
-msgid "Return to file list"
-msgstr "Gå tilbake til filoversikten"
-
-#: ../../Zotlabs/Module/Filestorage.php:209
-msgid "Share this file"
-msgstr "Del denne filen"
-
-#: ../../Zotlabs/Module/Filestorage.php:210
-msgid "Show URL to this file"
-msgstr "Vis URLen til denne filen"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:35
+#: ../../Zotlabs/Module/Admin/Dbsync.php:62
+#, php-format
+msgid "Update %s was successfully applied."
+msgstr "Oppdatering %s ble gjennomført med suksess."
-#: ../../Zotlabs/Module/Pdledit_gui.php:119
-#, fuzzy
-msgid "Layout not found"
-msgstr "Layouten ble ikke funnet."
+#: ../../Zotlabs/Module/Admin/Dbsync.php:39
+#, php-format
+msgid "Verifying update %s did not return a status. Unknown if it succeeded."
+msgstr ""
-#: ../../Zotlabs/Module/Pdledit_gui.php:127
-msgid "This template does not support pdledi_gui (no content regions defined)"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:42
+#, php-format
+msgid "Update %s does not contain a verification function."
msgstr ""
-#: ../../Zotlabs/Module/Pdledit_gui.php:404
-msgid "Main page content"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:46
+#: ../../Zotlabs/Module/Admin/Dbsync.php:69
+#, php-format
+msgid "Update function %s could not be found."
+msgstr "Oppdatering av funksjon %s kunne ikke finnes."
+
+#: ../../Zotlabs/Module/Admin/Dbsync.php:59
+#, php-format
+msgid "Executing update procedure %s failed. Check system logs."
msgstr ""
-#: ../../Zotlabs/Module/Pdledit_gui.php:405
-msgid "The main page content can not be edited!"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:66
+#, php-format
+msgid ""
+"Update %s did not return a status. It cannot be determined if it was "
+"successful."
msgstr ""
-#: ../../Zotlabs/Module/Pdledit.php:27
-msgid "Layout updated."
-msgstr "Layout er oppdatert."
+#: ../../Zotlabs/Module/Admin/Dbsync.php:87
+msgid "Failed Updates"
+msgstr "Mislykkede oppdateringer"
-#: ../../Zotlabs/Module/Pdledit.php:55 ../../Zotlabs/Module/Pdledit.php:129
-msgid "Edit System Page Description"
-msgstr "Endre beskrivelsen av systemsiden"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:89
+msgid "Mark success (if update was manually applied)"
+msgstr "Marker suksess (hvis oppdateringen ble gjennomført manuelt)"
-#: ../../Zotlabs/Module/Pdledit.php:76 ../../Zotlabs/Module/Pdledit.php:93
-msgid "(modified)"
-msgstr "(endret)"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:90
+msgid "Attempt to verify this update if a verification procedure exists"
+msgstr ""
-#: ../../Zotlabs/Module/Pdledit.php:124
-msgid "Layout not found."
-msgstr "Layouten ble ikke funnet."
+#: ../../Zotlabs/Module/Admin/Dbsync.php:91
+msgid "Attempt to execute this update step automatically"
+msgstr "Prøv å gjennomføre dette oppdateringstrinnet automatisk"
-#: ../../Zotlabs/Module/Pdledit.php:130
-msgid "Module Name:"
-msgstr "Modulnavn:"
+#: ../../Zotlabs/Module/Admin/Dbsync.php:96
+msgid "No failed updates."
+msgstr "Ingen mislykkede oppdateringer."
-#: ../../Zotlabs/Module/Pdledit.php:131
-msgid "Layout Help"
-msgstr "Layout-hjelp"
+#: ../../Zotlabs/Module/Admin/Logs.php:29
+msgid "Log settings updated."
+msgstr "Logginnstillinger er oppdatert."
-#: ../../Zotlabs/Module/Pdledit.php:132
-msgid "Edit another layout"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Logs.php:86
+msgid "Clear"
+msgstr "Tøm"
-#: ../../Zotlabs/Module/Pdledit.php:133
-msgid "System layout"
-msgstr ""
+#: ../../Zotlabs/Module/Admin/Logs.php:92
+msgid "Debugging"
+msgstr "Feilsøking"
-#: ../../Zotlabs/Module/Moderate.php:70
-#, fuzzy
-#| msgid "Account approved."
-msgid "Item approved"
-msgstr "Konto godkjent."
+#: ../../Zotlabs/Module/Admin/Logs.php:93
+msgid "Log file"
+msgstr "Loggfil"
-#: ../../Zotlabs/Module/Removeaccount.php:35
+#: ../../Zotlabs/Module/Admin/Logs.php:93
msgid ""
-"Account removals are not allowed within 48 hours of changing the account "
-"password."
+"Must be writable by web server. Relative to your top-level webserver "
+"directory."
msgstr ""
-"Sletting av kontoer er ikke tillatt innen 48 timer etter endring av "
-"kontopassordet."
-#: ../../Zotlabs/Module/Removeaccount.php:57
-msgid "Remove This Account"
-msgstr "Slett denne kontoen"
+#: ../../Zotlabs/Module/Admin/Logs.php:94
+msgid "Log level"
+msgstr "Loggnivå"
-#: ../../Zotlabs/Module/Removeaccount.php:58
-#: ../../Zotlabs/Module/Removeme.php:61 ../../Zotlabs/Module/Changeaddr.php:78
-msgid "WARNING: "
-msgstr "ADVARSEL: "
+#: ../../Zotlabs/Module/Admin/Queue.php:45
+msgid "Queue Statistics"
+msgstr "Køstatistikk"
-#: ../../Zotlabs/Module/Removeaccount.php:58
-msgid ""
-"This account and all its channels will be completely removed from the "
-"network. "
-msgstr ""
-"Denne kontoen og alle dens kanaler vil bli fullstendig fjernet fra "
-"nettverket. "
+#: ../../Zotlabs/Module/Admin/Queue.php:46
+msgid "Total Entries"
+msgstr "Totalt antall oppføringer"
-#: ../../Zotlabs/Module/Removeaccount.php:58
-msgid "This action is permanent and can not be undone!"
-msgstr "Denne handlingen er permanent og kan ikke angres!"
+#: ../../Zotlabs/Module/Admin/Queue.php:48
+msgid "Destination URL"
+msgstr "Mål-URL"
-#: ../../Zotlabs/Module/Removeaccount.php:59
-#: ../../Zotlabs/Module/Removeme.php:62 ../../Zotlabs/Module/Changeaddr.php:79
-msgid "Please enter your password for verification:"
-msgstr "Vennligst skriv ditt passord for å få bekreftelse:"
+#: ../../Zotlabs/Module/Admin/Queue.php:49
+msgid "Mark hub permanently offline"
+msgstr "Merk hub som permanent offline"
-#: ../../Zotlabs/Module/Removeaccount.php:61
-#: ../../Zotlabs/Module/Settings/Account.php:113
-msgid "Remove Account"
-msgstr "Slett konto"
+#: ../../Zotlabs/Module/Admin/Queue.php:50
+msgid "Retry delivery to this hub"
+msgstr ""
-#: ../../Zotlabs/Module/Regmod.php:15
-msgid "Please login."
-msgstr "Vennligst logg inn."
+#: ../../Zotlabs/Module/Admin/Queue.php:51
+msgid "Empty queue for this hub"
+msgstr "Tøm køen for denne hubben"
+
+#: ../../Zotlabs/Module/Admin/Queue.php:52
+msgid "Last known contact"
+msgstr "Siste kjente kontakt"
-#: ../../Zotlabs/Module/Admin/Site.php:112
+#: ../../Zotlabs/Module/Admin/Site.php:114
msgid "Invalid input"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:132
+#: ../../Zotlabs/Module/Admin/Site.php:134
msgid "Errors"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:220
+#: ../../Zotlabs/Module/Admin/Site.php:221
msgid "Site settings updated."
msgstr "Nettstedsinnstillinger er oppdatert."
-#: ../../Zotlabs/Module/Admin/Site.php:243
+#: ../../Zotlabs/Module/Admin/Site.php:244
#: ../../Zotlabs/Module/Settings/Display.php:117
#, php-format
msgid "%s - (Incompatible)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:250
+#: ../../Zotlabs/Module/Admin/Site.php:251
msgid "mobile"
msgstr "mobil"
-#: ../../Zotlabs/Module/Admin/Site.php:252
+#: ../../Zotlabs/Module/Admin/Site.php:253
msgid "experimental"
msgstr "eksperimentell"
-#: ../../Zotlabs/Module/Admin/Site.php:254
+#: ../../Zotlabs/Module/Admin/Site.php:255
msgid "unsupported"
msgstr "ikke støttet"
-#: ../../Zotlabs/Module/Admin/Site.php:307
+#: ../../Zotlabs/Module/Admin/Site.php:308
msgid "Yes - with approval"
msgstr "Ja - med godkjenning"
-#: ../../Zotlabs/Module/Admin/Site.php:315
+#: ../../Zotlabs/Module/Admin/Site.php:316
msgid "My site is not a public server"
msgstr "Mitt nettsted er ikke en offentlig tjeneste"
-#: ../../Zotlabs/Module/Admin/Site.php:316
+#: ../../Zotlabs/Module/Admin/Site.php:317
msgid "My site has paid access only"
msgstr "Mitt nettsted gir kun tilgang mot betaling"
-#: ../../Zotlabs/Module/Admin/Site.php:317
+#: ../../Zotlabs/Module/Admin/Site.php:318
msgid "My site has free access only"
msgstr "Mitt nettsted har kun gratis tilgang"
-#: ../../Zotlabs/Module/Admin/Site.php:318
+#: ../../Zotlabs/Module/Admin/Site.php:319
msgid "My site offers free accounts with optional paid upgrades"
msgstr ""
"Mitt nettsted tilbyr gratis konto med valgfri oppgradering til betalt "
"tjeneste"
-#: ../../Zotlabs/Module/Admin/Site.php:336
+#: ../../Zotlabs/Module/Admin/Site.php:337
msgid "Default permission role for new accounts"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:336
+#: ../../Zotlabs/Module/Admin/Site.php:337
msgid ""
"This role will be used for the first channel created after registration."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:346
-msgid "Week(s)"
+#: ../../Zotlabs/Module/Admin/Site.php:344 ../../Zotlabs/Module/Invite.php:394
+msgid "Minute(s)"
+msgstr ""
+
+#: ../../Zotlabs/Module/Admin/Site.php:345 ../../Zotlabs/Module/Invite.php:395
+msgid "Hour(s)"
+msgstr ""
+
+#: ../../Zotlabs/Module/Admin/Site.php:346 ../../Zotlabs/Module/Invite.php:396
+msgid "Day(s)"
msgstr ""
#: ../../Zotlabs/Module/Admin/Site.php:347
-msgid "Month(s)"
+msgid "Week(s)"
msgstr ""
#: ../../Zotlabs/Module/Admin/Site.php:348
+msgid "Month(s)"
+msgstr ""
+
+#: ../../Zotlabs/Module/Admin/Site.php:349
msgid "Year(s)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:356
+#: ../../Zotlabs/Module/Admin/Site.php:357
msgid "Register verification delay"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:359
+#: ../../Zotlabs/Module/Admin/Site.php:360
msgid "Time to wait before a registration can be verified"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:378
+#: ../../Zotlabs/Module/Admin/Site.php:363
+#: ../../Zotlabs/Module/Admin/Site.php:385 ../../Zotlabs/Module/Invite.php:405
+msgid "duration up from now"
+msgstr ""
+
+#: ../../Zotlabs/Module/Admin/Site.php:379
msgid "Register verification expiration time"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:381
+#: ../../Zotlabs/Module/Admin/Site.php:382
msgid "Time before an unverified registration will expire"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:398
-#: ../../Zotlabs/Module/Admin/Accounts.php:307
-#: ../../Zotlabs/Module/Admin/Security.php:106
-#: ../../Zotlabs/Module/Admin/Channels.php:167
-#: ../../Zotlabs/Module/Admin/Addons.php:346
-#: ../../Zotlabs/Module/Admin/Addons.php:444
-#: ../../Zotlabs/Module/Admin/Themes.php:138
-#: ../../Zotlabs/Module/Admin/Themes.php:172
-#: ../../Zotlabs/Module/Admin/Logs.php:83 ../../Zotlabs/Module/Admin.php:142
-msgid "Administration"
-msgstr "Administrasjon"
-
-#: ../../Zotlabs/Module/Admin/Site.php:401 ../../Zotlabs/Widget/Admin.php:27
-msgid "Site"
-msgstr "Nettsted"
-
-#: ../../Zotlabs/Module/Admin/Site.php:404
+#: ../../Zotlabs/Module/Admin/Site.php:405
msgid "File upload"
msgstr "Last opp fil"
-#: ../../Zotlabs/Module/Admin/Site.php:405
+#: ../../Zotlabs/Module/Admin/Site.php:406
msgid "Policies"
msgstr "Retningslinjer"
-#: ../../Zotlabs/Module/Admin/Site.php:410
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:593
-msgid "Site name"
-msgstr "Nettstedets navn"
-
-#: ../../Zotlabs/Module/Admin/Site.php:412
+#: ../../Zotlabs/Module/Admin/Site.php:413
msgid "Banner/Logo"
msgstr "Banner/Logo"
-#: ../../Zotlabs/Module/Admin/Site.php:412
+#: ../../Zotlabs/Module/Admin/Site.php:413
msgid "Unfiltered HTML/CSS/JS is allowed"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:413
+#: ../../Zotlabs/Module/Admin/Site.php:414
msgid "Administrator Information"
msgstr "Administratorinformasjon"
-#: ../../Zotlabs/Module/Admin/Site.php:413
+#: ../../Zotlabs/Module/Admin/Site.php:414
msgid ""
"Contact information for site administrators. Displayed on siteinfo page. "
"BBCode can be used here"
@@ -9651,17 +11439,21 @@ msgstr ""
"Kontaktinformasjon til nettstedsadministratorer. Vises på siteinfo-siden. "
"BBCode kan brukes her"
-#: ../../Zotlabs/Module/Admin/Site.php:414
+#: ../../Zotlabs/Module/Admin/Site.php:415 ../../Zotlabs/Module/Siteinfo.php:25
+msgid "Site Information"
+msgstr "Nettstedsinformasjon"
+
+#: ../../Zotlabs/Module/Admin/Site.php:415
msgid ""
"Publicly visible description of this site. Displayed on siteinfo page. "
"BBCode can be used here"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:415
+#: ../../Zotlabs/Module/Admin/Site.php:416
msgid "System theme"
msgstr "Systemtema"
-#: ../../Zotlabs/Module/Admin/Site.php:415
+#: ../../Zotlabs/Module/Admin/Site.php:416
msgid ""
"Default system theme - may be over-ridden by user profiles - <a href='#' "
"id='cnftheme'>change theme settings</a>"
@@ -9669,19 +11461,19 @@ msgstr ""
"Standard systemtema - kan overstyres av brukerprofiler - <a href='#' "
"id='cnftheme'>endre temainnstillinger</a>"
-#: ../../Zotlabs/Module/Admin/Site.php:418
+#: ../../Zotlabs/Module/Admin/Site.php:419
msgid "Allow Feeds as Connections"
msgstr "Tillat strømmer som forbindelser"
-#: ../../Zotlabs/Module/Admin/Site.php:418
+#: ../../Zotlabs/Module/Admin/Site.php:419
msgid "(Heavy system resource usage)"
msgstr "(Tung bruk av systemressurser)"
-#: ../../Zotlabs/Module/Admin/Site.php:419
+#: ../../Zotlabs/Module/Admin/Site.php:420
msgid "Maximum image size"
msgstr "Største bildestørrelse"
-#: ../../Zotlabs/Module/Admin/Site.php:419
+#: ../../Zotlabs/Module/Admin/Site.php:420
msgid ""
"Maximum size in bytes of uploaded images. Default is 0, which means no "
"limits."
@@ -9689,122 +11481,122 @@ msgstr ""
"Største størrelse i bytes for opplastede bilder. Standard er 0, som betyr "
"ubegrenset."
-#: ../../Zotlabs/Module/Admin/Site.php:420
+#: ../../Zotlabs/Module/Admin/Site.php:421
msgid "Minimum age"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:420
+#: ../../Zotlabs/Module/Admin/Site.php:421
msgid "Minimum age (in years) for who may register on this site."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:421
+#: ../../Zotlabs/Module/Admin/Site.php:422
msgid "Which best describes the types of account offered by this hub?"
msgstr ""
"Hvilket alternativ beskriver best hva slags kontotype som tilbys av dette "
"nettstedet/denne hubben?"
-#: ../../Zotlabs/Module/Admin/Site.php:421
+#: ../../Zotlabs/Module/Admin/Site.php:422
msgid "This is displayed on the public server site list."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:427
+#: ../../Zotlabs/Module/Admin/Site.php:428
msgid "Register text"
msgstr "Registreringstekst"
-#: ../../Zotlabs/Module/Admin/Site.php:429
+#: ../../Zotlabs/Module/Admin/Site.php:430
msgid "This text will be displayed prominently at the registration page"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:433
+#: ../../Zotlabs/Module/Admin/Site.php:434
msgid "Does this site allow new member registration?"
msgstr "Tillater dette nettstedet registrering av nye medlemmer?"
-#: ../../Zotlabs/Module/Admin/Site.php:440
+#: ../../Zotlabs/Module/Admin/Site.php:441
msgid "Configure the registration open days/hours"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:442
+#: ../../Zotlabs/Module/Admin/Site.php:443
msgid "Empty or '-:-' value will keep registration open 24/7 (default)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:443
+#: ../../Zotlabs/Module/Admin/Site.php:444
msgid ""
"Weekdays and hours must be separated by colon ':', From-To ranges with a "
"dash `-` example: 1:800-1200"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:444
+#: ../../Zotlabs/Module/Admin/Site.php:445
msgid ""
"Weekday:Hour pairs must be separated by space ' ' example: 1:900-1700 "
"2:900-1700"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:445
+#: ../../Zotlabs/Module/Admin/Site.php:446
msgid ""
"From-To ranges must be separated by comma ',' example: 1:800-1200,1300-1700 "
"or 1-2,4-5:900-1700"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:446
+#: ../../Zotlabs/Module/Admin/Site.php:447
msgid "Advanced examples:"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:446
+#: ../../Zotlabs/Module/Admin/Site.php:447
#: ../../Zotlabs/Module/Settings/Channel.php:184
msgid "or"
msgstr "eller"
-#: ../../Zotlabs/Module/Admin/Site.php:447
+#: ../../Zotlabs/Module/Admin/Site.php:448
msgid "Check your configuration"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:451
+#: ../../Zotlabs/Module/Admin/Site.php:452
msgid "Max account registrations per day"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:453
+#: ../../Zotlabs/Module/Admin/Site.php:454
msgid "Unlimited if zero or no value - default 50"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:457
+#: ../../Zotlabs/Module/Admin/Site.php:458
msgid "Max account registrations from same IP"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:459
+#: ../../Zotlabs/Module/Admin/Site.php:460
msgid "Unlimited if zero or no value - default 3"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:465
+#: ../../Zotlabs/Module/Admin/Site.php:466
msgid "Auto channel create"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:467
+#: ../../Zotlabs/Module/Admin/Site.php:468
msgid ""
"If disabled the channel will be created in a separate step during the "
"registration process"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:471
+#: ../../Zotlabs/Module/Admin/Site.php:472
msgid "Require invite code"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:476
+#: ../../Zotlabs/Module/Admin/Site.php:477
msgid "Allow invite code"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:481
+#: ../../Zotlabs/Module/Admin/Site.php:482
msgid "Require email address"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:483
+#: ../../Zotlabs/Module/Admin/Site.php:484
msgid "The provided email address will be verified (recommended)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:487
+#: ../../Zotlabs/Module/Admin/Site.php:488
msgid "Abandon account after x days"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:489
+#: ../../Zotlabs/Module/Admin/Site.php:490
msgid ""
"Will not waste system resources polling external sites for abandonded "
"accounts. Enter 0 for no time limit."
@@ -9812,22 +11604,22 @@ msgstr ""
"Vil ikke kaste bort systemressurser på å spørre eksterne nettsteder etter "
"forlatte kontoer. Skriv 0 for å ikke sette noen tidsgrense."
-#: ../../Zotlabs/Module/Admin/Site.php:494
+#: ../../Zotlabs/Module/Admin/Site.php:495
msgid "Site homepage to show visitors (default: login box)"
msgstr ""
"Nettstedets hjemmeside som vises til besøkende (standard: innloggingsboks)"
-#: ../../Zotlabs/Module/Admin/Site.php:494
+#: ../../Zotlabs/Module/Admin/Site.php:495
msgid ""
"example: 'pubstream' to show public stream, 'page/sys/home' to show a system "
"webpage called 'home' or 'include:home.html' to include a file."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:495
+#: ../../Zotlabs/Module/Admin/Site.php:496
msgid "Preserve site homepage URL"
msgstr "Bevar URL-en til nettstedets hjemmeside"
-#: ../../Zotlabs/Module/Admin/Site.php:495
+#: ../../Zotlabs/Module/Admin/Site.php:496
msgid ""
"Present the site homepage in a frame at the original location instead of "
"redirecting"
@@ -9835,11 +11627,11 @@ msgstr ""
"Presenter hjemmesiden til nettstedet i en ramme fra den opprinnelige "
"plasseringen i stedet for å omdirigere"
-#: ../../Zotlabs/Module/Admin/Site.php:496
+#: ../../Zotlabs/Module/Admin/Site.php:497
msgid "Allowed friend domains"
msgstr "Tillatte vennedomener"
-#: ../../Zotlabs/Module/Admin/Site.php:496
+#: ../../Zotlabs/Module/Admin/Site.php:497
msgid ""
"Comma separated list of domains which are allowed to establish friendships "
"with this site. Wildcards are accepted. Empty to allow any domains"
@@ -9847,66 +11639,56 @@ msgstr ""
"Kommaseparert liste over domener som har lov til å etablere vennskap med "
"dette nettstedet. Jokertegn er akseptert. Tøm for å tillate alle domener"
-#: ../../Zotlabs/Module/Admin/Site.php:497
+#: ../../Zotlabs/Module/Admin/Site.php:498
msgid "Force publish"
msgstr "Tving publisering"
-#: ../../Zotlabs/Module/Admin/Site.php:497
-#, fuzzy
-#| msgid ""
-#| "Check to force all profiles on this site to be listed in the site "
-#| "directory."
+#: ../../Zotlabs/Module/Admin/Site.php:498
msgid ""
"Check to force all profiles on this site to be listed in the site directory"
msgstr ""
"Kryss av for å tvinge alle profiler på dette nettstedet til å bli oppført i "
-"nettstedet sin katalog."
+"nettstedet sin katalog"
-#: ../../Zotlabs/Module/Admin/Site.php:498
+#: ../../Zotlabs/Module/Admin/Site.php:499
msgid "Enable public stream"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:498
-#, fuzzy
-#| msgid ""
-#| "Import and allow access to public content pulled from other sites. "
-#| "Warning: this content is unmoderated."
+#: ../../Zotlabs/Module/Admin/Site.php:499
msgid "Enable the public stream. Warning: this content is unmoderated"
msgstr ""
"Importer og gi tilgang til offentlig innhold trukket inn fra andre "
-"nettsteder. Advarsel: dette innholdet er ikke moderert."
+"nettsteder. Advarsel: dette innholdet er ikke moderert"
-#: ../../Zotlabs/Module/Admin/Site.php:499
-#, fuzzy
-#| msgid "My site is not a public server"
+#: ../../Zotlabs/Module/Admin/Site.php:500
msgid "Site only public stream"
-msgstr "Mitt nettsted er ikke en offentlig tjeneste"
+msgstr "Offentlig strøm kun fra nettstedet"
-#: ../../Zotlabs/Module/Admin/Site.php:499
+#: ../../Zotlabs/Module/Admin/Site.php:500
msgid "Restrict the public stream to content originating at this site"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:500
+#: ../../Zotlabs/Module/Admin/Site.php:501
msgid "Allow anybody on the internet to access the public streams"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:500
+#: ../../Zotlabs/Module/Admin/Site.php:501
msgid "Disable to require authentication before viewing"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:501
+#: ../../Zotlabs/Module/Admin/Site.php:502
msgid "Only import Public stream posts with this text"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:502
+#: ../../Zotlabs/Module/Admin/Site.php:503
msgid "Do not import Public stream posts with this text"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:505
+#: ../../Zotlabs/Module/Admin/Site.php:506
msgid "Login on Homepage"
msgstr "Logg inn på hjemmesiden"
-#: ../../Zotlabs/Module/Admin/Site.php:505
+#: ../../Zotlabs/Module/Admin/Site.php:506
msgid ""
"Present a login box to visitors on the home page if no other content has "
"been configured."
@@ -9914,68 +11696,68 @@ msgstr ""
"Presenter en innloggingsboks til besøkende på hjemmesiden hvis ikke noe "
"annet innhold har blitt konfigurert."
-#: ../../Zotlabs/Module/Admin/Site.php:506
+#: ../../Zotlabs/Module/Admin/Site.php:507
msgid "Enable context help"
msgstr "Slå på kontekstuell hjelp"
-#: ../../Zotlabs/Module/Admin/Site.php:506
+#: ../../Zotlabs/Module/Admin/Site.php:507
msgid ""
"Display contextual help for the current page when the help button is pressed."
msgstr ""
"Vis kontekstuell hjelp for den gjeldende siden når du klikker på "
"hjelpeknappen."
-#: ../../Zotlabs/Module/Admin/Site.php:508
+#: ../../Zotlabs/Module/Admin/Site.php:509
msgid "Reply-to email address for system generated email."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:509
+#: ../../Zotlabs/Module/Admin/Site.php:510
msgid "Sender (From) email address for system generated email."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:510
+#: ../../Zotlabs/Module/Admin/Site.php:511
msgid "Name of email sender for system generated email."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:512
+#: ../../Zotlabs/Module/Admin/Site.php:513
msgid "Directory Server URL"
msgstr "Katalogtjener URL"
-#: ../../Zotlabs/Module/Admin/Site.php:512
+#: ../../Zotlabs/Module/Admin/Site.php:513
msgid "Default directory server"
msgstr "Standard katalogtjener"
-#: ../../Zotlabs/Module/Admin/Site.php:514
+#: ../../Zotlabs/Module/Admin/Site.php:515
msgid "Enable SSE Notifications"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:514
+#: ../../Zotlabs/Module/Admin/Site.php:515
msgid ""
"If disabled, traditional polling will be used. Warning: this setting might "
"not be suited for shared hosting"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:516
+#: ../../Zotlabs/Module/Admin/Site.php:517
msgid "Proxy user"
msgstr "Brukernavn mellomtjener"
-#: ../../Zotlabs/Module/Admin/Site.php:517
+#: ../../Zotlabs/Module/Admin/Site.php:518
msgid "Proxy URL"
msgstr "Mellomtjener URL"
-#: ../../Zotlabs/Module/Admin/Site.php:518
+#: ../../Zotlabs/Module/Admin/Site.php:519
msgid "Network timeout"
msgstr "Nettverk tidsavbrudd"
-#: ../../Zotlabs/Module/Admin/Site.php:518
+#: ../../Zotlabs/Module/Admin/Site.php:519
msgid "Value is in seconds. Set to 0 for unlimited (not recommended)."
msgstr "Verdien i sekunder. Skriv 0 for ubegrenset (ikke anbefalt)."
-#: ../../Zotlabs/Module/Admin/Site.php:519
+#: ../../Zotlabs/Module/Admin/Site.php:520
msgid "Delivery interval"
msgstr "Leveringsinterval"
-#: ../../Zotlabs/Module/Admin/Site.php:519
+#: ../../Zotlabs/Module/Admin/Site.php:520
msgid ""
"Delay background delivery processes by this many seconds to reduce system "
"load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 "
@@ -9985,11 +11767,11 @@ msgstr ""
"redusere systembelastningen. Anbefaling: 4-5 for delte tjenere, 2-3 for "
"virtuelle tjenere, 0-1 for større dedikerte tjenere."
-#: ../../Zotlabs/Module/Admin/Site.php:520
+#: ../../Zotlabs/Module/Admin/Site.php:521
msgid "Deliveries per process"
msgstr "Leveranser per prosess"
-#: ../../Zotlabs/Module/Admin/Site.php:520
+#: ../../Zotlabs/Module/Admin/Site.php:521
msgid ""
"Number of deliveries to attempt in a single operating system process. Adjust "
"if necessary to tune system performance. Recommend: 1-5."
@@ -9997,11 +11779,11 @@ msgstr ""
"Antall leveranser som forsøkes i en enkelt operativsystemprosess. Juster om "
"nødvendig for å fininnstille systemets yteevne. Anbefaling: 1-5."
-#: ../../Zotlabs/Module/Admin/Site.php:522
+#: ../../Zotlabs/Module/Admin/Site.php:523
msgid "Poll interval"
msgstr "Spørreintervall"
-#: ../../Zotlabs/Module/Admin/Site.php:522
+#: ../../Zotlabs/Module/Admin/Site.php:523
msgid ""
"Delay background polling processes by this many seconds to reduce system "
"load. If 0, use delivery interval."
@@ -10009,22 +11791,22 @@ msgstr ""
"Forsink spørreprosessene i bakgrunnen med dette antall sekunder for å "
"redusere systembelastningen. Hvis 0, bruk dette leveringsintervallet."
-#: ../../Zotlabs/Module/Admin/Site.php:523
+#: ../../Zotlabs/Module/Admin/Site.php:524
msgid "Path to ImageMagick convert program"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:523
+#: ../../Zotlabs/Module/Admin/Site.php:524
msgid ""
"If set, use this program to generate photo thumbnails for huge images ( > "
"4000 pixels in either dimension), otherwise memory exhaustion may occur. "
"Example: /usr/bin/convert"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:524
+#: ../../Zotlabs/Module/Admin/Site.php:525
msgid "Maximum Load Average"
msgstr "Største belastningsgjennomsnitt"
-#: ../../Zotlabs/Module/Admin/Site.php:524
+#: ../../Zotlabs/Module/Admin/Site.php:525
msgid ""
"Maximum system load before delivery and poll processes are deferred - "
"default 50."
@@ -10032,316 +11814,50 @@ msgstr ""
"Største systembelastning før leverings- og spørreprosesser blir utsatt - "
"standard 50."
-#: ../../Zotlabs/Module/Admin/Site.php:525
+#: ../../Zotlabs/Module/Admin/Site.php:526
msgid "Expiration period in days for imported (grid/network) content"
msgstr "Antall dager før importert innhold (nettet/nettverk) utgår"
-#: ../../Zotlabs/Module/Admin/Site.php:525
+#: ../../Zotlabs/Module/Admin/Site.php:526
msgid "0 for no expiration of imported content"
msgstr "0 dersom importert innhold ikke skal utgå"
-#: ../../Zotlabs/Module/Admin/Site.php:526
+#: ../../Zotlabs/Module/Admin/Site.php:527
msgid ""
"Do not expire any posts which have comments less than this many days ago"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:527
+#: ../../Zotlabs/Module/Admin/Site.php:528
msgid ""
"Public servers: Optional landing (marketing) webpage for new registrants"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:527
+#: ../../Zotlabs/Module/Admin/Site.php:528
#, php-format
msgid "Create this page first. Default is %s/register"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:528
+#: ../../Zotlabs/Module/Admin/Site.php:529
msgid "Page to display after creating a new channel"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:528
+#: ../../Zotlabs/Module/Admin/Site.php:529
msgid "Default: profiles"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:529
+#: ../../Zotlabs/Module/Admin/Site.php:530
msgid "Optional: site location"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:529
+#: ../../Zotlabs/Module/Admin/Site.php:530
msgid "Region or country"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Site.php:614
#: ../../Zotlabs/Module/Admin/Site.php:615
+#: ../../Zotlabs/Module/Admin/Site.php:616
msgid "Invalid 24h time value (hhmm/hmm)"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Dbsync.php:19
-msgid "Update has been marked successful"
-msgstr "Oppdateringen har blitt merket som en suksess"
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:32
-#, php-format
-msgid "Verification of update %s failed. Check system logs."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:35
-#: ../../Zotlabs/Module/Admin/Dbsync.php:62
-#, php-format
-msgid "Update %s was successfully applied."
-msgstr "Oppdatering %s ble gjennomført med suksess."
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:39
-#, php-format
-msgid "Verifying update %s did not return a status. Unknown if it succeeded."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:42
-#, php-format
-msgid "Update %s does not contain a verification function."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:46
-#: ../../Zotlabs/Module/Admin/Dbsync.php:69
-#, php-format
-msgid "Update function %s could not be found."
-msgstr "Oppdatering av funksjon %s kunne ikke finnes."
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:59
-#, php-format
-msgid "Executing update procedure %s failed. Check system logs."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:66
-#, php-format
-msgid ""
-"Update %s did not return a status. It cannot be determined if it was "
-"successful."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:87
-msgid "Failed Updates"
-msgstr "Mislykkede oppdateringer"
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:89
-msgid "Mark success (if update was manually applied)"
-msgstr "Marker suksess (hvis oppdateringen ble gjennomført manuelt)"
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:90
-msgid "Attempt to verify this update if a verification procedure exists"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:91
-msgid "Attempt to execute this update step automatically"
-msgstr "Prøv å gjennomføre dette oppdateringstrinnet automatisk"
-
-#: ../../Zotlabs/Module/Admin/Dbsync.php:96
-msgid "No failed updates."
-msgstr "Ingen mislykkede oppdateringer."
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:128
-#, php-format
-msgid "%s account blocked/unblocked"
-msgid_plural "%s account blocked/unblocked"
-msgstr[0] "%s konto blokkert/ikke blokkert lenger"
-msgstr[1] "%s kontoer blokkert/ikke blokkert lenger"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:135
-#, php-format
-msgid "%s account deleted"
-msgid_plural "%s accounts deleted"
-msgstr[0] "%s konto slettet"
-msgstr[1] "%s kontoer slettet"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:171
-msgid "Account not found"
-msgstr "Kontoen ble ikke funnet"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:192
-#, php-format
-msgid "Account '%s' blocked"
-msgstr "Kontoen '%s' blokkert"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:200
-#, php-format
-msgid "Account '%s' unblocked"
-msgstr "Kontoen '%s' er ikke blokkert lenger"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:240
-msgid "Unverified"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:243
-msgid "Expired"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:308
-#: ../../Zotlabs/Module/Admin/Accounts.php:327
-#: ../../Zotlabs/Module/Admin.php:97 ../../Zotlabs/Widget/Admin.php:28
-msgid "Accounts"
-msgstr "Kontoer"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:310
-msgid "Show verified registrations"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:310
-msgid "Show all registrations"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:312
-msgid "Select toggle"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:313
-msgid "Deny selected"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:314
-msgid "Approve selected"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:315
-msgid "All registrations"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:315
-msgid "Verified registrations waiting for approval"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:316
-msgid "Request date"
-msgstr "Dato for forespørsel"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:316
-msgid "Requests"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:317
-msgid "No registrations available"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:317
-msgid "No verified registrations available"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:319
-#: ../../Zotlabs/Module/Authorize.php:33
-msgid "Deny"
-msgstr "Avslå"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:323
-msgid "Verified"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:324
-msgid "Not yet verified"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:329
-msgid "ID"
-msgstr "ID"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:331
-msgid "All channels"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:332
-msgid "Register date"
-msgstr "Registreringsdato"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:333
-msgid "Last login"
-msgstr "Siste innlogging"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:334
-msgid "Expires"
-msgstr "Utløper"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:335
-#: ../../Zotlabs/Module/Admin/Account_edit.php:72
-msgid "Service class"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:337
-msgid ""
-"Selected accounts will be deleted!\\n\\nEverything these accounts had posted "
-"on this site will be permanently deleted!\\n\\nAre you sure?"
-msgstr ""
-"Valgte kontoer vil bli slettet!\\n\\nAlt disse kontoene har lagt inn på "
-"dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil "
-"slette disse valgte kontoene?"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:338
-msgid ""
-"The account {0} will be deleted!\\n\\nEverything this account has posted on "
-"this site will be permanently deleted!\\n\\nAre you sure?"
-msgstr ""
-"Kontoen {0} vl bli slettet!\\n\\nAlt denne kontoen har lagt inn på dette "
-"nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette "
-"denne kontoen?"
-
-#: ../../Zotlabs/Module/Admin/Accounts.php:347
-msgid "Message"
-msgstr "Melding"
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:66
-msgid "Max queueworker threads"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:68
-msgid "Minimum 4, default 4"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:78
-msgid "Assume workers dead after"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:80
-msgid "Minimum 120, default 300 seconds"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:92
-msgid "Pause before starting next task"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:94
-msgid "Minimum 100, default 100 microseconds"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:103
-msgid "Automatically adjust pause before starting next task"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Queueworker.php:112
-msgid "Queueworker Settings"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:29
-#, php-format
-msgid "Password changed for account %d."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:46
-msgid "Account settings updated."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:68
-msgid "Account Edit"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:69
-msgid "New Password"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:70
-msgid "New Password again"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Account_edit.php:71
-msgid "Account language (for emails)"
-msgstr ""
-
#: ../../Zotlabs/Module/Admin/Security.php:97
msgid ""
"By default, unfiltered HTML is allowed in embedded media. This is inherently "
@@ -10373,11 +11889,6 @@ msgstr ""
"Alt annet innebygget innhold vil bli filtrert, <strong>med mindre</strong> "
"innebygget innhold fra den aktuelle siden eksplisitt er blokkert."
-#: ../../Zotlabs/Module/Admin/Security.php:107
-#: ../../Zotlabs/Widget/Admin.php:30
-msgid "Security"
-msgstr "Sikkerhet"
-
#: ../../Zotlabs/Module/Admin/Security.php:109
msgid "Block public"
msgstr "Blokker offentlig tilgang"
@@ -10511,395 +12022,109 @@ msgid ""
"https://example.tld"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Channels.php:28
-#: ../../Zotlabs/Module/Admin/Channels.php:80
-msgid "Channel not found"
-msgstr "Kanalen ble ikke funnet"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:48
-#, php-format
-msgid "%s channel censored/uncensored"
-msgid_plural "%s channels censored/uncensored"
-msgstr[0] "%s kanal er sensurert/ikke sensurert lenger"
-msgstr[1] "%s kanaler er sensurert/ikke sensurert lenger"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:57
-#, php-format
-msgid "%s channel code allowed/disallowed"
-msgid_plural "%s channels code allowed/disallowed"
-msgstr[0] "%s kanal med kode tillatt/ikke tillatt"
-msgstr[1] "%s kanaler med kode tillatt/ikke tillatt"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:63
-#, php-format
-msgid "%s channel deleted"
-msgid_plural "%s channels deleted"
-msgstr[0] "%s kanal slettet"
-msgstr[1] "%s kanaler slettet"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:90
-#, php-format
-msgid "Channel '%s' deleted"
-msgstr "Kanalen '%s' er slettet"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:109
-#, php-format
-msgid "Channel '%s' censored"
-msgstr "Kanalen '%s' er sensurert"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:109
-#, php-format
-msgid "Channel '%s' uncensored"
-msgstr "Kanalen '%s' er ikke sensurert lenger"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:120
-#, php-format
-msgid "Channel '%s' code allowed"
-msgstr "Kanal '%s' kode tillatt"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:120
+#: ../../Zotlabs/Module/Manage.php:130 ../../Zotlabs/Module/New_channel.php:148
#, php-format
-msgid "Channel '%s' code disallowed"
-msgstr "Kanal '%s' kode ikke tillatt"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:170
-msgid "select all"
-msgstr "velg alle"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:172
-msgid "Censor"
-msgstr "Sensurer"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:173
-msgid "Uncensor"
-msgstr "Ikke sensurer lenger"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:174
-msgid "Allow Code"
-msgstr "Tillat kode"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:175
-msgid "Disallow Code"
-msgstr "Ikke tillat kode"
-
-#: ../../Zotlabs/Module/Admin/Channels.php:180
-msgid "UID"
-msgstr "UID"
+msgid "You have created %1$.0f of %2$.0f allowed channels."
+msgstr "Du har laget %1$.0f av %2$.0f tillatte kanaler."
-#: ../../Zotlabs/Module/Admin/Channels.php:184
-msgid ""
-"Selected channels will be deleted!\\n\\nEverything that was posted in these "
-"channels on this site will be permanently deleted!\\n\\nAre you sure?"
-msgstr ""
-"Valgte kanaler vil bli slettet!\\n\\nAlt innhold som er lagt inn i disse "
-"kanalene på dette nettstedet vil bli slettet for alltid!\\n\\nEr du sikker "
-"på at du vil slette disse kanalene med alt innhold?"
+#: ../../Zotlabs/Module/Manage.php:137
+msgid "Create a new channel"
+msgstr "Lag en ny kanal"
-#: ../../Zotlabs/Module/Admin/Channels.php:185
-msgid ""
-"The channel {0} will be deleted!\\n\\nEverything that was posted in this "
-"channel on this site will be permanently deleted!\\n\\nAre you sure?"
-msgstr ""
-"Kanalen {0} vil bli slettet!\\n\\nAlt innhold som er lagt inn i denne "
-"kanalen på dettet nettstedet vil bli slettet for alltid!\\n\\nEr du sikker "
-"på at du vil slette denne kanalen med alt innhold?"
+#: ../../Zotlabs/Module/Manage.php:163
+msgid "Current Channel"
+msgstr "Gjeldende kanal"
-#: ../../Zotlabs/Module/Admin/Features.php:56
-#, php-format
-msgid "Lock feature %s"
-msgstr "Lås funksjon %s"
+#: ../../Zotlabs/Module/Manage.php:165
+msgid "Switch to one of your channels by selecting it."
+msgstr "Bytt til en av dine kanaler ved å velge den."
-#: ../../Zotlabs/Module/Admin/Features.php:64
-msgid "Manage Additional Features"
-msgstr "Håndter tilleggsfunksjoner"
+#: ../../Zotlabs/Module/Manage.php:166
+msgid "Default Channel"
+msgstr "Standardkanal"
-#: ../../Zotlabs/Module/Admin/Addons.php:294
-#, php-format
-msgid "Plugin %s disabled."
-msgstr "Tilleggsfunksjonen %s er avskrudd."
+#: ../../Zotlabs/Module/Manage.php:167
+msgid "Make Default"
+msgstr "Gjør til standard"
-#: ../../Zotlabs/Module/Admin/Addons.php:299
+#: ../../Zotlabs/Module/Manage.php:170
#, php-format
-msgid "Plugin %s enabled."
-msgstr "Tilleggsfunksjonen %s er påskrudd."
-
-#: ../../Zotlabs/Module/Admin/Addons.php:315
-#: ../../Zotlabs/Module/Admin/Themes.php:111
-msgid "Disable"
-msgstr "Skru av"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:318
-#: ../../Zotlabs/Module/Admin/Themes.php:113
-msgid "Enable"
-msgstr "Skru på"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:347
-#: ../../Zotlabs/Module/Admin/Addons.php:445 ../../Zotlabs/Widget/Admin.php:32
-msgid "Addons"
-msgstr "Tillegg"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:348
-#: ../../Zotlabs/Module/Admin/Themes.php:140
-msgid "Toggle"
-msgstr "Skru av og på"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:356
-#: ../../Zotlabs/Module/Admin/Themes.php:150
-msgid "Author: "
-msgstr "Forfatter: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:357
-#: ../../Zotlabs/Module/Admin/Themes.php:151
-msgid "Maintainer: "
-msgstr "Vedlikeholder: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:358
-msgid "Minimum project version: "
-msgstr "Minimum prosjektversjon: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:359
-msgid "Maximum project version: "
-msgstr "Maksimum prosjektversjon: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:360
-msgid "Minimum PHP version: "
-msgstr "Minimum PHP-versjon: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:361
-msgid "Compatible Server Roles: "
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:362
-msgid "Requires: "
-msgstr "Krever: "
-
-#: ../../Zotlabs/Module/Admin/Addons.php:363
-#: ../../Zotlabs/Module/Admin/Addons.php:450
-msgid "Disabled - version incompatibility"
-msgstr "Skrudd av - versjonsinkompatibilitet"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:419
-msgid "Enter the public git repository URL of the addon repo."
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:420
-msgid "Addon repo git URL"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:421
-msgid "Custom repo name"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:421
-#, fuzzy
-msgid "(optional)"
-msgstr "Valgfritt"
-
-#: ../../Zotlabs/Module/Admin/Addons.php:422
-msgid "Download Addon Repo"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:429
-msgid "Install new repo"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:453
-msgid "Manage Repos"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin/Addons.php:454
-msgid "Installed Addon Repositories"
-msgstr ""
+msgid "%d new introductions"
+msgstr "%d nye introduksjoner"
-#: ../../Zotlabs/Module/Admin/Addons.php:455
-msgid "Install a New Addon Repository"
-msgstr ""
+#: ../../Zotlabs/Module/Manage.php:172
+msgid "Delegated Channel"
+msgstr "Delegert kanal"
-#: ../../Zotlabs/Module/Admin/Addons.php:462
-msgid "Switch branch"
+#: ../../Zotlabs/Module/Email_resend.php:12
+#: ../../Zotlabs/Module/Email_validation.php:25
+msgid "Token verification failed."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Queue.php:45
-msgid "Queue Statistics"
-msgstr "Køstatistikk"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:46
-msgid "Total Entries"
-msgstr "Totalt antall oppføringer"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:47
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2668
-msgid "Priority"
-msgstr "Prioritet"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:48
-msgid "Destination URL"
-msgstr "Mål-URL"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:49
-msgid "Mark hub permanently offline"
-msgstr "Merk hub som permanent offline"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:50
-msgid "Retry delivery to this hub"
+#: ../../Zotlabs/Module/Email_resend.php:30
+msgid "Email verification resent"
msgstr ""
-#: ../../Zotlabs/Module/Admin/Queue.php:51
-msgid "Empty queue for this hub"
-msgstr "Tøm køen for denne hubben"
-
-#: ../../Zotlabs/Module/Admin/Queue.php:52
-msgid "Last known contact"
-msgstr "Siste kjente kontakt"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:90
-msgid "New Profile Field"
-msgstr "Nytt profilfelt"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:91
-#: ../../Zotlabs/Module/Admin/Profs.php:111
-msgid "Field nickname"
-msgstr "Feltets kallenavn"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:91
-#: ../../Zotlabs/Module/Admin/Profs.php:111
-msgid "System name of field"
-msgstr "Systemnavnet til feltet"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:92
-#: ../../Zotlabs/Module/Admin/Profs.php:112
-msgid "Input type"
-msgstr "Inndata-type"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:93
-#: ../../Zotlabs/Module/Admin/Profs.php:113
-msgid "Field Name"
-msgstr "Feltnavn"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:93
-#: ../../Zotlabs/Module/Admin/Profs.php:113
-msgid "Label on profile pages"
-msgstr "Merkelapp på profilsider"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:94
-#: ../../Zotlabs/Module/Admin/Profs.php:114
-msgid "Help text"
-msgstr "Hjelpetekst"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:94
-#: ../../Zotlabs/Module/Admin/Profs.php:114
-msgid "Additional info (optional)"
-msgstr "Tilleggsinformasjon (valgfritt)"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:104
-msgid "Field definition not found"
-msgstr "Feltdefinisjonen ble ikke funnet"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:110
-msgid "Edit Profile Field"
-msgstr "Endre profilfelt"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:169 ../../Zotlabs/Widget/Admin.php:36
-msgid "Profile Fields"
-msgstr "Profilfelter"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:170
-msgid "Basic Profile Fields"
-msgstr "Grunnleggende profilfelter"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:171
-msgid "Advanced Profile Fields"
-msgstr "Utvidede profilfelter"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:171
-msgid "(In addition to basic fields)"
-msgstr "(I tillegg til grunnleggende felt)"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:173
-msgid "All available fields"
-msgstr "Alle tilgjengelige felt"
-
-#: ../../Zotlabs/Module/Admin/Profs.php:174
-msgid "Custom Fields"
+#: ../../Zotlabs/Module/Email_resend.php:33
+msgid "Unable to resend email verification message."
msgstr ""
-#: ../../Zotlabs/Module/Admin/Profs.php:178
-msgid "Create Custom Field"
-msgstr "Legg til egendefinert felt"
-
-#: ../../Zotlabs/Module/Admin/Themes.php:27
-msgid "Theme settings updated."
-msgstr "Temainnstillinger er oppdatert."
-
-#: ../../Zotlabs/Module/Admin/Themes.php:62
-msgid "No themes found."
-msgstr "Ingen temaer er funnet."
-
-#: ../../Zotlabs/Module/Admin/Themes.php:132
-msgid "Screenshot"
-msgstr "Skjermbilde"
-
-#: ../../Zotlabs/Module/Admin/Themes.php:139
-#: ../../Zotlabs/Module/Admin/Themes.php:173 ../../Zotlabs/Widget/Admin.php:33
-msgid "Themes"
-msgstr "Utseende"
-
-#: ../../Zotlabs/Module/Admin/Themes.php:178
-msgid "[Experimental]"
-msgstr "[Eksperimentelt]"
+#: ../../Zotlabs/Module/Dreport.php:43
+msgid "Invalid message"
+msgstr "Ugyldig melding"
-#: ../../Zotlabs/Module/Admin/Themes.php:179
-msgid "[Unsupported]"
-msgstr "[Ingen støtte]"
+#: ../../Zotlabs/Module/Dreport.php:73
+msgid "no results"
+msgstr "ingen resultater"
-#: ../../Zotlabs/Module/Admin/Logs.php:29
-msgid "Log settings updated."
-msgstr "Logginnstillinger er oppdatert."
+#: ../../Zotlabs/Module/Dreport.php:87
+msgid "channel sync processed"
+msgstr "kanalsynkronisering er behandlet"
-#: ../../Zotlabs/Module/Admin/Logs.php:84 ../../Zotlabs/Widget/Admin.php:54
-#: ../../Zotlabs/Widget/Admin.php:64
-msgid "Logs"
-msgstr "Logger"
+#: ../../Zotlabs/Module/Dreport.php:91
+msgid "queued"
+msgstr "lagt i kø"
-#: ../../Zotlabs/Module/Admin/Logs.php:86
-msgid "Clear"
-msgstr "Tøm"
+#: ../../Zotlabs/Module/Dreport.php:95
+msgid "posted"
+msgstr "lagt inn"
-#: ../../Zotlabs/Module/Admin/Logs.php:92
-msgid "Debugging"
-msgstr "Feilsøking"
+#: ../../Zotlabs/Module/Dreport.php:99
+msgid "accepted for delivery"
+msgstr "akseptert for levering"
-#: ../../Zotlabs/Module/Admin/Logs.php:93
-msgid "Log file"
-msgstr "Loggfil"
+#: ../../Zotlabs/Module/Dreport.php:103
+msgid "updated"
+msgstr "oppdatert"
-#: ../../Zotlabs/Module/Admin/Logs.php:93
-msgid ""
-"Must be writable by web server. Relative to your top-level webserver "
-"directory."
-msgstr ""
+#: ../../Zotlabs/Module/Dreport.php:107
+msgid "update ignored"
+msgstr "oppdatering ignorert"
-#: ../../Zotlabs/Module/Admin/Logs.php:94
-msgid "Log level"
-msgstr "Loggnivå"
+#: ../../Zotlabs/Module/Dreport.php:110
+msgid "permission denied"
+msgstr "tillatelse avvist"
-#: ../../Zotlabs/Module/Attach_edit.php:118
-msgid "Can not copy folder into itself."
-msgstr ""
+#: ../../Zotlabs/Module/Dreport.php:114
+msgid "recipient not found"
+msgstr "mottaker ble ikke funnet"
-#: ../../Zotlabs/Module/Attach_edit.php:131
+#: ../../Zotlabs/Module/Dreport.php:134
#, php-format
-msgid "Can not move folder \"%s\" into itself."
+msgid "Delivery report for %1$s"
+msgstr "Leveringsrapport for %1$s"
+
+#: ../../Zotlabs/Module/Dreport.php:139
+msgid "Redeliver"
msgstr ""
-#: ../../Zotlabs/Module/Pin.php:36 ../../Zotlabs/Module/Item.php:477
-msgid "Unable to locate original post."
-msgstr "Ikke i stand til å finne opprinnelig innlegg."
+#: ../../Zotlabs/Module/Chatsvc.php:131
+msgid "Away"
+msgstr "Borte"
-#: ../../Zotlabs/Module/Subthread.php:112 ../../Zotlabs/Module/Like.php:456
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:1693
-msgid "status"
-msgstr "status"
+#: ../../Zotlabs/Module/Chatsvc.php:136
+msgid "Online"
+msgstr "Online"
#: ../../Zotlabs/Module/Subthread.php:125
#, php-format
@@ -10911,358 +12136,135 @@ msgstr "%1$s følger %2$s sin %3$s"
msgid "%1$s stopped following %2$s's %3$s"
msgstr "%1$s stopped å følge %2$s sin %3$s"
-#: ../../Zotlabs/Module/Attach.php:68
-msgid "Item not available."
-msgstr "Elementet er ikke tilgjengelig."
-
-#: ../../Zotlabs/Module/Removeme.php:35
-msgid ""
-"Channel removals are not allowed within 48 hours of changing the account "
-"password."
-msgstr ""
-"Fjerning av kanaler er ikke tillatt innen 48 timer etter endring av "
-"kontopassordet."
-
-#: ../../Zotlabs/Module/Removeme.php:60 ../../Zotlabs/Module/Removeme.php:64
-#: ../../Zotlabs/Module/Settings/Channel.php:283
-msgid "Remove Channel"
-msgstr "Fjern kanal"
-
-#: ../../Zotlabs/Module/Removeme.php:61
-msgid "This channel will be permanently removed. "
-msgstr ""
-
-#: ../../Zotlabs/Module/Removeme.php:61
-msgid "This action can not be undone!"
-msgstr ""
-
-#: ../../Zotlabs/Module/Rmagic.php:46
-msgid "Authentication failed."
-msgstr "Autentisering mislyktes."
-
-#: ../../Zotlabs/Module/Item.php:763
-msgid "Empty post discarded."
-msgstr "Tomt innlegg forkastet."
-
-#: ../../Zotlabs/Module/Item.php:1207
-msgid "Duplicate post suppressed."
-msgstr "Duplikat av innlegg forhindret."
-
-#: ../../Zotlabs/Module/Item.php:1350
-msgid "System error. Post not saved."
-msgstr "Systemfeil. Innlegg ble ikke lagret."
-
-#: ../../Zotlabs/Module/Item.php:1384
-msgid "Your comment is awaiting approval."
-msgstr ""
-
-#: ../../Zotlabs/Module/Item.php:1520
-msgid "Unable to obtain post information from database."
-msgstr "Ikke i stand til å få tak i informasjon om innlegg fra databasen."
-
-#: ../../Zotlabs/Module/Item.php:1527
-#, php-format
-msgid "You have reached your limit of %1$.0f top level posts."
-msgstr "Du har nådd din grense på %1$.0f startinnlegg."
-
-#: ../../Zotlabs/Module/Item.php:1534
-#, php-format
-msgid "You have reached your limit of %1$.0f webpages."
-msgstr "Du har nådd din grense på %1$.0f websider."
-
-#: ../../Zotlabs/Module/Display.php:340
-msgid "Article"
-msgstr ""
-
-#: ../../Zotlabs/Module/Display.php:384
-msgid "Item has been removed."
-msgstr ""
-
-#: ../../Zotlabs/Module/Mitem.php:63
-msgid "Unable to create element."
-msgstr "Klarer ikke å lage element."
-
-#: ../../Zotlabs/Module/Mitem.php:87
-msgid "Unable to update menu element."
-msgstr "Ikke i stand til å oppdatere menyelement."
-
-#: ../../Zotlabs/Module/Mitem.php:103
-msgid "Unable to add menu element."
-msgstr "Ikke i stand til å legge til menyelement."
-
-#: ../../Zotlabs/Module/Mitem.php:167 ../../Zotlabs/Module/Mitem.php:246
-msgid "Menu Item Permissions"
-msgstr "Menyelement Tillatelser"
-
-#: ../../Zotlabs/Module/Mitem.php:168 ../../Zotlabs/Module/Mitem.php:247
-msgid "(click to open/close)"
-msgstr "(klikk for å åpne/lukke)"
-
-#: ../../Zotlabs/Module/Mitem.php:174 ../../Zotlabs/Module/Mitem.php:191
-msgid "Link Name"
-msgstr "Lenkenavn"
-
-#: ../../Zotlabs/Module/Mitem.php:175 ../../Zotlabs/Module/Mitem.php:255
-msgid "Link or Submenu Target"
-msgstr "Lenke- eller undermeny-mål"
-
-#: ../../Zotlabs/Module/Mitem.php:175
-msgid "Enter URL of the link or select a menu name to create a submenu"
-msgstr "Skriv URL-en til lenken eller velg et menynavn for å lage en undermeny"
-
-#: ../../Zotlabs/Module/Mitem.php:176 ../../Zotlabs/Module/Mitem.php:256
-msgid "Use magic-auth if available"
-msgstr "Bruk magic-autent hvis mulig"
-
-#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:257
-msgid "Open link in new window"
-msgstr "Åpne lenke i nytt vindu"
-
-#: ../../Zotlabs/Module/Mitem.php:178 ../../Zotlabs/Module/Mitem.php:258
-msgid "Order in list"
-msgstr "Ordne i liste"
-
-#: ../../Zotlabs/Module/Mitem.php:178 ../../Zotlabs/Module/Mitem.php:258
-msgid "Higher numbers will sink to bottom of listing"
-msgstr "Høyere tall vil synke mot bunnen av listen"
-
-#: ../../Zotlabs/Module/Mitem.php:179
-msgid "Submit and finish"
-msgstr "Send inn og avslutt"
-
-#: ../../Zotlabs/Module/Mitem.php:180
-msgid "Submit and continue"
-msgstr "Send inn og fortsett"
-
-#: ../../Zotlabs/Module/Mitem.php:189
-msgid "Menu:"
-msgstr "Meny:"
-
-#: ../../Zotlabs/Module/Mitem.php:192
-msgid "Link Target"
-msgstr "Lenkemål"
-
-#: ../../Zotlabs/Module/Mitem.php:195
-msgid "Edit menu"
-msgstr "Endre meny"
-
-#: ../../Zotlabs/Module/Mitem.php:198
-msgid "Edit element"
-msgstr "Endre element"
-
-#: ../../Zotlabs/Module/Mitem.php:199
-msgid "Drop element"
-msgstr "Slett element"
-
-#: ../../Zotlabs/Module/Mitem.php:200
-msgid "New element"
-msgstr "Nytt element"
-
-#: ../../Zotlabs/Module/Mitem.php:201
-msgid "Edit this menu container"
-msgstr "Endre denne menybeholderen"
-
-#: ../../Zotlabs/Module/Mitem.php:202
-msgid "Add menu element"
-msgstr "Legg til menyelement"
-
-#: ../../Zotlabs/Module/Mitem.php:203
-msgid "Delete this menu item"
-msgstr "Slett dette menyelementet"
+#: ../../Zotlabs/Module/Cal.php:62
+msgid "Permissions denied."
+msgstr "Tillatelse avvist."
-#: ../../Zotlabs/Module/Mitem.php:204
-msgid "Edit this menu item"
-msgstr "Endre dette menyelementet"
+#: ../../Zotlabs/Module/Cal.php:161
+#: ../../Zotlabs/Module/Channel_calendar.php:380
+#: ../../Zotlabs/Module/Cdav.php:935
+#, fuzzy
+msgid "Link to source"
+msgstr "Lenke til kilde"
-#: ../../Zotlabs/Module/Mitem.php:222
-msgid "Menu item not found."
-msgstr "Menyelement ble ikke funnet."
+#: ../../Zotlabs/Module/Cal.php:199 ../../Zotlabs/Module/Photos.php:948
+#: ../../Zotlabs/Module/Cdav.php:1026
+msgid "Previous"
+msgstr "Forrige"
-#: ../../Zotlabs/Module/Mitem.php:235
-msgid "Menu item deleted."
-msgstr "Menyelement slettet."
+#: ../../Zotlabs/Module/Cal.php:200 ../../Zotlabs/Module/Photos.php:957
+#: ../../Zotlabs/Module/Setup.php:278 ../../Zotlabs/Module/Cdav.php:1027
+msgid "Next"
+msgstr "Neste"
-#: ../../Zotlabs/Module/Mitem.php:237
-msgid "Menu item could not be deleted."
-msgstr "Menyelement kunne ikke bli slettet."
+#: ../../Zotlabs/Module/Cal.php:201 ../../Zotlabs/Module/Cdav.php:1028
+msgid "Today"
+msgstr "Idag"
-#: ../../Zotlabs/Module/Mitem.php:244
-msgid "Edit Menu Element"
-msgstr "Endre menyelement"
+#: ../../Zotlabs/Module/Bookmarks.php:62
+msgid "Bookmark added"
+msgstr "Bokmerke lagt til"
-#: ../../Zotlabs/Module/Mitem.php:254
-msgid "Link text"
-msgstr "Lenketekst"
+#: ../../Zotlabs/Module/Bookmarks.php:101
+msgid "My Connections Bookmarks"
+msgstr "Mine forbindelsers bokmerker"
-#: ../../Zotlabs/Module/Email_validation.php:37
-msgid "Email Verification Required"
+#: ../../Zotlabs/Module/Affinity.php:35
+msgid "Affinity Tool settings updated."
msgstr ""
-#: ../../Zotlabs/Module/Email_validation.php:38
-#, php-format
+#: ../../Zotlabs/Module/Affinity.php:54
msgid ""
-"A verification token was sent to your email address [%s]. Enter that token "
-"here to complete the account verification step. Please allow a few minutes "
-"for delivery, and check your spam folder if you do not see the message."
+"The numbers below represent the minimum and maximum slider default positions "
+"for your network/stream page as a percentage."
msgstr ""
-"En verifikasjonskode ble sendt til epostadressen din [%s]. Skriv inn koden "
-"du mottok her for å fullføre kontoverifikasjonen. Det kan ta noen minutter "
-"før du mottar koden. Sjekk også at eposten ikke har havnet i mappen for "
-"søppelpost om du ikke ser den etter en stund."
-#: ../../Zotlabs/Module/Email_validation.php:39
-msgid "Resend Email"
+#: ../../Zotlabs/Module/Affinity.php:61
+msgid "Default maximum affinity level"
msgstr ""
-#: ../../Zotlabs/Module/Email_validation.php:42
-msgid "Validation token"
-msgstr ""
+#: ../../Zotlabs/Module/Affinity.php:61
+#, fuzzy
+msgid "0-99 default 99"
+msgstr "Standard"
-#: ../../Zotlabs/Module/Uexport.php:108
-msgid "No content available for year"
+#: ../../Zotlabs/Module/Affinity.php:67
+msgid "Default minimum affinity level"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:171
-msgid "Export Channel"
-msgstr "Eksporter kanal"
-
-#: ../../Zotlabs/Module/Uexport.php:173
-msgid "Export channel"
-msgstr "Eksporter kanal"
-
-#: ../../Zotlabs/Module/Uexport.php:174
-msgid ""
-"This will export your identity and social graph into a file which can be "
-"used to import your channel to a new hub."
-msgstr ""
+#: ../../Zotlabs/Module/Affinity.php:67
+#, fuzzy
+msgid "0-99 - default 0"
+msgstr "Standard"
-#: ../../Zotlabs/Module/Uexport.php:177
-msgid "Export content"
+#: ../../Zotlabs/Module/Affinity.php:73
+msgid "Persistent affinity levels"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:178
+#: ../../Zotlabs/Module/Affinity.php:73
msgid ""
-"This will export your posts, direct messages, articles and cards per month "
-"stored into a zip file per year. Months with no posts will be dismissed."
-msgstr ""
-"Dette vil eksportere dine innlegg, direktemeldinger, artikler og kort, en "
-"fil for hver måned, pakket i en zip fil for hvert år. Måneder uten innhold "
-"blir ignorert."
-
-#: ../../Zotlabs/Module/Uexport.php:180
-msgid "Export wikis"
-msgstr ""
-
-#: ../../Zotlabs/Module/Uexport.php:181
-msgid "This will export your wikis and wiki pages."
-msgstr ""
-
-#: ../../Zotlabs/Module/Uexport.php:183
-msgid "Export webpages"
-msgstr ""
-
-#: ../../Zotlabs/Module/Uexport.php:184
-msgid "This will export your webpages and menus."
-msgstr ""
-
-#: ../../Zotlabs/Module/Uexport.php:186
-msgid "Export channel calendar"
+"If disabled the max and min levels will be reset to default after page reload"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:187
-msgid ""
-"This will export your channel calendar events and associated items. CalDAV "
-"calendars are not included."
+#: ../../Zotlabs/Module/Affinity.php:81
+msgid "Affinity Tool Settings"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:189
-msgid "Export chatrooms"
+#: ../../Zotlabs/Module/Admin.php:98
+msgid "Blocked accounts"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:190
-msgid "This will export your chatrooms. Chat history is dismissed."
+#: ../../Zotlabs/Module/Admin.php:99
+msgid "Expired accounts"
msgstr ""
-#: ../../Zotlabs/Module/Uexport.php:192
-#, php-format
-msgid ""
-"This export can be imported or restored by visiting <a href=\"%1$s\">%2$s</"
-"a> on any site containing your channel."
+#: ../../Zotlabs/Module/Admin.php:100
+msgid "Expiring accounts"
msgstr ""
-#: ../../Zotlabs/Module/Notifications.php:106
-#: ../../Zotlabs/Module/Notify.php:85
-msgid "No more system notifications."
-msgstr "Ingen flere systemvarsler."
-
-#: ../../Zotlabs/Module/Notifications.php:110
-#: ../../Zotlabs/Module/Notify.php:89
-msgid "System Notifications"
-msgstr "Systemvarsler"
-
-#: ../../Zotlabs/Module/Notifications.php:111
-msgid "Mark all seen"
-msgstr "Merk alle som sett"
-
-#: ../../Zotlabs/Module/Sharedwithme.php:106
-msgid "Files: shared with me"
-msgstr "Filer: delt med meg"
-
-#: ../../Zotlabs/Module/Sharedwithme.php:108
-msgid "NEW"
-msgstr "NY"
-
-#: ../../Zotlabs/Module/Sharedwithme.php:111
-msgid "Remove all files"
-msgstr "Fjern alle filer"
-
-#: ../../Zotlabs/Module/Sharedwithme.php:112
-msgid "Remove this file"
-msgstr "Fjern denne filen"
+#: ../../Zotlabs/Module/Admin.php:124
+msgid "Message queues"
+msgstr "Meldingskøer"
-#: ../../Zotlabs/Module/Tokens.php:94
-#, php-format
-msgid "This channel is limited to %d tokens"
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:138
+msgid "Your software should be updated"
+msgstr "Programvaren bør oppdateres"
-#: ../../Zotlabs/Module/Tokens.php:100
-msgid "Name and Password are required."
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:143
+msgid "Summary"
+msgstr "Sammendrag"
-#: ../../Zotlabs/Module/Tokens.php:215
-msgid "Token saved."
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:146
+msgid "Registered accounts"
+msgstr "Registrerte kontoer"
-#: ../../Zotlabs/Module/Tokens.php:261
-msgid ""
-"Use this form to create temporary access identifiers to share things with "
-"non-members. These identities may be used in privacy groups and visitors may "
-"login using these credentials to access private content."
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:147
+msgid "Pending registrations"
+msgstr "Ventende registreringer"
-#: ../../Zotlabs/Module/Tokens.php:274
-msgid "Please select a role for this guest!"
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:148
+msgid "Registered channels"
+msgstr "Registrerte kanaler"
-#: ../../Zotlabs/Module/Tokens.php:287
-msgid "Select a role for this guest"
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:149 ../../Zotlabs/Module/Siteinfo.php:42
+msgid "Active addons"
+msgstr "Aktive tillegg"
-#: ../../Zotlabs/Module/Tokens.php:291
-msgid "Login Name"
-msgstr ""
+#: ../../Zotlabs/Module/Admin.php:150
+msgid "Version"
+msgstr "Versjon"
-#: ../../Zotlabs/Module/Tokens.php:292
-msgid "Login Password"
+#: ../../Zotlabs/Module/Admin.php:151
+msgid "Repository version (master)"
msgstr ""
-#: ../../Zotlabs/Module/Tokens.php:293
-msgid "Expires (yyyy-mm-dd)"
+#: ../../Zotlabs/Module/Admin.php:152
+msgid "Repository version (dev)"
msgstr ""
#: ../../Zotlabs/Module/Permcats.php:59
msgid "Contact role deleted."
-msgstr ""
+msgstr "Kontaktrollen ble slettet."
#: ../../Zotlabs/Module/Permcats.php:86
msgid "Permission category name is required."
@@ -11270,735 +12272,428 @@ msgstr ""
#: ../../Zotlabs/Module/Permcats.php:102 ../../Zotlabs/Module/Permcats.php:154
msgid "Contact role saved."
-msgstr ""
+msgstr "Kontaktrollen ble lagret."
#: ../../Zotlabs/Module/Permcats.php:204
msgid "Role to assign affected contacts and default role to"
-msgstr ""
+msgstr "Ny rolle for berørte forbindelser og standardrolle"
#: ../../Zotlabs/Module/Permcats.php:204
msgid "Role to assign affected contacts to"
-msgstr ""
+msgstr "Ny rolle for berørte forbindelser"
#: ../../Zotlabs/Module/Permcats.php:234
msgid "Assign this role to"
-msgstr ""
+msgstr "Bruk denne rollen for"
#: ../../Zotlabs/Module/Permcats.php:236
msgid "All my contacts"
-msgstr ""
+msgstr "Alle mine forbindelser"
#: ../../Zotlabs/Module/Permcats.php:247
msgid "Automatically assign this role to new contacts"
-msgstr ""
+msgstr "Bruk denne rollen for nye kontakter automatisk"
#: ../../Zotlabs/Module/Permcats.php:249
msgid "Role name"
-msgstr ""
+msgstr "Navn"
#: ../../Zotlabs/Module/Permcats.php:249
msgid "System role - not editable"
-msgstr ""
+msgstr "Systemrolle - kan ikke redigeres"
#: ../../Zotlabs/Module/Permcats.php:250
msgid "Deleting"
-msgstr ""
+msgstr "Sletter kanalrolle"
#: ../../Zotlabs/Module/Permcats.php:255
msgid "Role Permissions"
-msgstr ""
+msgstr "Tillatelser"
#: ../../Zotlabs/Module/Permcats.php:256
msgid ""
"Some permissions may be inherited from your <a href=\"settings\">channel "
"role</a>, which have higher priority than contact role settings."
msgstr ""
+"Noen tillatelser kan være arvet fra din <a href=\"settings\">kanalrole</a>, "
+"som vil ha høyere prioritet enn innstillingene for kontaktrollen."
-#: ../../Zotlabs/Module/Settings/Channel.php:106
-#: ../../Zotlabs/Module/Settings/Channel.php:218
-msgid "Please select a channel role"
-msgstr "Velg en kanalrolle"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:195
-msgid "Your channel address is"
-msgstr "Din kanaladresse er"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:198
-msgid "Your files/photos are accessible via WebDAV at"
-msgstr "Dine filer og foto er tilgjengelig via WebDAV på"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:229
-msgid "Channel Settings"
-msgstr "Kanalinnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:236
-msgid "Basic Settings"
-msgstr "Grunninnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:237
-msgid "Channel timezone:"
-msgstr "Tidssone for kanalen:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:238
-msgid "Default post location:"
-msgstr "Standard plassering for innlegg:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:238
-msgid "Geographical location to display on your posts"
-msgstr "Geografisk plassering som vises på dine innlegg"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:239
-msgid "Use browser location"
-msgstr "Bruk nettleserplassering"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:240
-msgid "Adult content"
-msgstr "Voksent innhold"
+#: ../../Zotlabs/Module/Attach_edit.php:118
+msgid "Can not copy folder into itself."
+msgstr "Kan ikke kopiere mappe inn i seg selv."
-#: ../../Zotlabs/Module/Settings/Channel.php:240
-msgid "This channel frequently or regularly publishes adult content"
+#: ../../Zotlabs/Module/Attach_edit.php:131
+#, php-format
+msgid "Can not move folder \"%s\" into itself."
msgstr ""
-"Denne kanalen vil ofte, eller regelmessig poste innlegg med voksent innhold"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:241
-msgid "Maximum Friend Requests/Day:"
-msgstr "Maksimalt antall venneforespørsler per dag:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:241
-msgid "May reduce spam activity"
-msgstr "Kan redusere søppelpostaktivitet"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:243
-msgid "By default post a status message when:"
-msgstr "Legg inn en statusmelding når du:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:244
-msgid "accepting a friend request"
-msgstr "aksepterer en venneforespørsel"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:245
-msgid "joining a forum/community"
-msgstr "blir med i et forum/miljø"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:246
-msgid "making an <em>interesting</em> profile change"
-msgstr "gjør en <em>interessant</em> profilendring"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:247
-msgid "Send a notification email when:"
-msgstr "Send en varsel-e-post når:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:248
-msgid "You receive a connection request"
-msgstr "Du har mottatt en forespørsel om forbindelse"
-#: ../../Zotlabs/Module/Settings/Channel.php:249
-msgid "Your connections are confirmed"
-msgstr "Dine forbindelser er bekreftet"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:250
-msgid "Someone writes on your profile wall"
-msgstr "Noen skriver på din profilvegg"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:251
-msgid "Someone writes a followup comment"
-msgstr "Noen skriver en oppfølgende kommentar"
+#: ../../Zotlabs/Module/Xchan.php:10
+msgid "Xchan Lookup"
+msgstr "Xchan oppslag"
-#: ../../Zotlabs/Module/Settings/Channel.php:252
-msgid "You receive a private message"
-msgstr "Du mottar en privat melding"
+#: ../../Zotlabs/Module/Xchan.php:13
+msgid "Lookup xchan beginning with (or webbie): "
+msgstr "Slå opp xchan som begynner med (eller webbie): "
-#: ../../Zotlabs/Module/Settings/Channel.php:253
-msgid "You receive a friend suggestion"
-msgstr "Du mottok et venneforslag"
+#: ../../Zotlabs/Module/Group.php:48
+msgid "Privacy group created."
+msgstr "Personverngruppen er opprettet."
-#: ../../Zotlabs/Module/Settings/Channel.php:254
-msgid "You are tagged in a post"
-msgstr "Du merkes i et innlegg"
+#: ../../Zotlabs/Module/Group.php:51
+msgid "Could not create privacy group."
+msgstr "Kunne ikke opprette personverngruppen."
-#: ../../Zotlabs/Module/Settings/Channel.php:255
-msgid "You are poked/prodded/etc. in a post"
-msgstr "Du ble prikket/oppildnet/og så vider i et innlegg"
+#: ../../Zotlabs/Module/Group.php:83
+msgid "Privacy group updated."
+msgstr "Personverngruppen er oppdatert."
-#: ../../Zotlabs/Module/Settings/Channel.php:256
-msgid "Someone likes your post/comment"
+#: ../../Zotlabs/Module/Group.php:138 ../../Zotlabs/Module/Group.php:302
+msgid "Post to this group by default"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:257
-msgid "Show visual notifications including:"
-msgstr "Vis visuelle varslinger om:"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:258
-msgid "Unseen stream activity"
-msgstr "Ny aktivitet i nettverksstrømmen"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:259
-msgid "Unseen channel activity"
-msgstr "Ny kanalaktivitet"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:260
-msgid "Unseen private messages"
-msgstr "Nye private meldinger"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:260
-#: ../../Zotlabs/Module/Settings/Channel.php:265
-#: ../../Zotlabs/Module/Settings/Channel.php:266
-#: ../../Zotlabs/Module/Settings/Channel.php:267
-msgid "Recommended"
-msgstr "Anbefalt"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:261
-msgid "Upcoming events"
-msgstr "Kommende hendelser"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:262
-msgid "Events today"
-msgstr "Hendelser idag"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:263
-msgid "Upcoming birthdays"
-msgstr "Kommende fødselsdager"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:263
-msgid "Not available in all themes"
-msgstr "Ikke tilgjengelig i alle temaer"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:264
-msgid "System (personal) notifications"
-msgstr "System (personlige) varslinger"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:265
-msgid "System info messages"
-msgstr "System infomeldinger"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:266
-msgid "System critical alerts"
-msgstr "System kritiske varsel"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:267
-msgid "New connections"
-msgstr "Nye forbindelser"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:268
-msgid "System Registrations"
-msgstr "Systemregistreringer"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:269
-msgid "Unseen shared files"
-msgstr "Nye delte filer"
+#: ../../Zotlabs/Module/Group.php:139 ../../Zotlabs/Module/Group.php:303
+msgid "Add new contacts to this group by default"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:270
-msgid "Unseen public stream activity"
-msgstr "Ny aktivitet i den offentlige strømmen"
+#: ../../Zotlabs/Module/Group.php:147
+msgid "Privacy group name"
+msgstr "Personverngruppens navn"
-#: ../../Zotlabs/Module/Settings/Channel.php:271
-msgid "Unseen likes and dislikes"
-msgstr "Nye liker/ikke liker"
+#: ../../Zotlabs/Module/Group.php:148 ../../Zotlabs/Module/Group.php:250
+msgid "Members are visible to other channels"
+msgstr "Medlemmer er synlig for andre kanaler"
-#: ../../Zotlabs/Module/Settings/Channel.php:272
-msgid "Unseen forum posts"
-msgstr "Ny forumpost"
+#: ../../Zotlabs/Module/Group.php:176
+msgid "Privacy group removed."
+msgstr "Personverngruppen er fjernet."
-#: ../../Zotlabs/Module/Settings/Channel.php:273
-msgid "Email notification hub (hostname)"
-msgstr "Hub for epostvarsler (tjenernavn)"
+#: ../../Zotlabs/Module/Group.php:179
+msgid "Unable to remove privacy group."
+msgstr "Ikke i stand til å fjerne personverngruppen."
-#: ../../Zotlabs/Module/Settings/Channel.php:273
+#: ../../Zotlabs/Module/Group.php:245
#, php-format
-msgid ""
-"If your channel is mirrored to multiple hubs, set this to your preferred "
-"location. This will prevent duplicate email notifications. Example: %s"
-msgstr ""
-"Hvis kanalen din er klonet til flere tjenere kan du sette dette til den du vil"
-" skal sende deg epostvarsler. Dette vil forhindre duplikate epostvarsler. Ekse"
-"mpel: %s"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:274
-msgid "Show new wall posts, private messages and connections under Notices"
-msgstr "Vis nye veggposter, private meldinger og forbindelser under varsler"
+msgid "Privacy Group: %s"
+msgstr "Personverngruppe: %s"
-#: ../../Zotlabs/Module/Settings/Channel.php:275
-msgid "Mark all notices of the thread read if a notice is clicked"
-msgstr "Merk alle varsler for en tråd som lest når du klikker på ett varsel"
+#: ../../Zotlabs/Module/Group.php:247
+msgid "Privacy group name: "
+msgstr "Personverngruppens navn: "
-#: ../../Zotlabs/Module/Settings/Channel.php:275
-msgid "If no, only the clicked notice will be marked read"
+#: ../../Zotlabs/Module/Group.php:263
+msgid "Group members"
msgstr ""
-"Om du velger \"Nei\", blir kun det varslet du klikker på merket som lest"
-#: ../../Zotlabs/Module/Settings/Channel.php:276
-msgid ""
-"Desktop notifications are unavailable because the required browser "
-"permission has not been granted"
+#: ../../Zotlabs/Module/Group.php:265
+msgid "Not in this group"
msgstr ""
-"Skriverbordsvarsler er ikke tilgjengelig fordi nettstedet ikke har de nødvendi"
-"ge nettlesertillatelsene"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:277
-msgid "Grant permission"
-msgstr "Gi tilgang"
-#: ../../Zotlabs/Module/Settings/Channel.php:278
-msgid "Notify me of events this many days in advance"
-msgstr "Varsle meg om hendelser dette antall dager på forhånd"
-
-#: ../../Zotlabs/Module/Settings/Channel.php:278
-msgid "Must be greater than 0"
-msgstr "Må være større enn 0"
+#: ../../Zotlabs/Module/Group.php:297
+msgid "Click a channel to toggle membership"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:281
-msgid "Default photo upload folder"
-msgstr "Standard mappe for opplasting av bilder"
+#: ../../Zotlabs/Module/Help.php:61
+msgid "Documentation Search"
+msgstr "Søk i dokumentasjon"
-#: ../../Zotlabs/Module/Settings/Channel.php:281
-#: ../../Zotlabs/Module/Settings/Channel.php:282
-msgid "%Y - current year, %m - current month"
-msgstr "%Y - nåværende år, %m - nåværende måned"
+#: ../../Zotlabs/Module/Help.php:170
+msgid "Not Found"
+msgstr "Ikke funnet"
-#: ../../Zotlabs/Module/Settings/Channel.php:282
-msgid "Default file upload folder"
-msgstr "Standard mappe for opplasting av filer"
+#: ../../Zotlabs/Module/Help.php:223
+msgid "$Projectname Documentation"
+msgstr "$Projectname dokumentasjon"
-#: ../../Zotlabs/Module/Settings/Channel.php:284
-msgid "Remove this channel."
-msgstr "Fjern denne kanalen."
+#: ../../Zotlabs/Module/Help.php:234
+msgid "Contents"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:285
-msgid "Expire other channel content after this many days"
-msgstr "Annet kanal innhold utløper etter så mange dager"
+#: ../../Zotlabs/Module/Help.php:241
+msgid "Members"
+msgstr "Medlemmer"
-#: ../../Zotlabs/Module/Settings/Channel.php:285
-msgid "0 or blank to use the website limit."
-msgstr "0 eller la være tomt for å bruke grensen til nettstedet."
+#: ../../Zotlabs/Module/Help.php:242
+msgid "Administrators"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:285
-#, php-format
-msgid "This website expires after %d days."
+#: ../../Zotlabs/Module/Help.php:243
+msgid "Developers"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:285
-msgid "This website does not expire imported content."
-msgstr "Dette nettstedet sletter ikke importert innhold."
+#: ../../Zotlabs/Module/Help.php:244
+msgid "Tutorials"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Channel.php:285
-msgid "The website limit takes precedence if lower than your limit."
-msgstr "Nettstedets grense vil gjelde dersom den er lavere enn din grense."
+#: ../../Zotlabs/Module/Help.php:262
+msgid "Help:"
+msgstr "Hjelp:"
-#: ../../Zotlabs/Module/Settings/Channel.php:286
-#: ../../Zotlabs/Module/Settings/Channel.php:287
-msgid ""
-"Words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - "
-"leave blank to import all posts"
+#: ../../Zotlabs/Module/Tagger.php:52
+msgid "Post not found."
msgstr ""
-"Ett ord per linje eller #emneknagger, $kategorier, /mønster/, lang=xx, lang!"
-"=xx - la være tomt for å importere alle innlegg"
-
-#: ../../Zotlabs/Module/Settings/Featured.php:25
-msgid "No feature settings configured"
-msgstr "Ingen funksjonsinnstillinger er konfigurert"
-#: ../../Zotlabs/Module/Settings/Featured.php:34
-msgid "Addon Settings"
-msgstr "Instillinger for tillegg"
+#: ../../Zotlabs/Module/Tagger.php:83
+msgid "comment"
+msgstr "kommentar"
-#: ../../Zotlabs/Module/Settings/Featured.php:35
-msgid "Please save/submit changes to any panel before opening another."
-msgstr "Husk å lagre endringene i ett panel før du åpner andre."
+#: ../../Zotlabs/Module/Tagger.php:123
+#, php-format
+msgid "%1$s tagged %2$s's %3$s with %4$s"
+msgstr "%1$s merket %3$s til %2$s med %4$s"
-#: ../../Zotlabs/Module/Settings/Editor.php:40
-msgid "Editor Settings"
-msgstr "Instillinger for redigering"
+#: ../../Zotlabs/Module/Invite.php:70
+msgid "Invite App"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Photos.php:40
-msgid "Photos Settings"
+#: ../../Zotlabs/Module/Invite.php:82
+msgid "Register is closed"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Profiles.php:41
-msgid "Default profile for new contacts"
+#: ../../Zotlabs/Module/Invite.php:116 ../../Zotlabs/Module/Invite.php:546
+msgid "Note, the invitation code is valid up to"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Profiles.php:49
-msgid "Profiles Settings"
+#: ../../Zotlabs/Module/Invite.php:129
+#, php-format
+msgid "Too many recipients for one invitation (max %d)"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Network.php:42
-#: ../../Zotlabs/Module/Settings/Channel_home.php:46
-msgid "Max height of content (in pixels)"
-msgstr "Maks høyde for innhold (i piksler)"
+#: ../../Zotlabs/Module/Invite.php:133
+msgid "No recipients for this invitation"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Network.php:44
-#: ../../Zotlabs/Module/Settings/Channel_home.php:48
-msgid "Click to expand content exceeding this height"
-msgstr "Klikk for å vise hele innlegg som overskrider denne grensen"
+#: ../../Zotlabs/Module/Invite.php:152
+#, php-format
+msgid "(%s) : Not a real email address"
+msgstr "(%s): Ikke en virkelig epostadresse"
-#: ../../Zotlabs/Module/Settings/Network.php:59
-msgid "Stream Settings"
-msgstr "Instillinger for tidslinjen"
+#: ../../Zotlabs/Module/Invite.php:159
+#, php-format
+msgid "(%s) : Not allowed email address"
+msgstr "(%s): Ikke en tillatt epostadresse"
-#: ../../Zotlabs/Module/Settings/Directory.php:40
-msgid "Directory Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Invite.php:172
+#, php-format
+msgid "(%s) : email address already in use"
+msgstr "(%s): epostadressen er allerede i bruk"
-#: ../../Zotlabs/Module/Settings/Channel_home.php:61
-msgid "Personal menu to display in your channel pages"
-msgstr "Personlig meny som kan vises på dine kanalsider"
+#: ../../Zotlabs/Module/Invite.php:179
+#, php-format
+msgid "(%s) : Accepted email address"
+msgstr "(%s): Godkjent epostadresse"
-#: ../../Zotlabs/Module/Settings/Channel_home.php:88
-msgid "Channel Home Settings"
+#: ../../Zotlabs/Module/Invite.php:271
+#, php-format
+msgid "To %s : Message delivery success."
msgstr ""
-#: ../../Zotlabs/Module/Settings/Calendar.php:40
-msgid "Calendar Settings"
+#: ../../Zotlabs/Module/Invite.php:303
+#, php-format
+msgid "%1$d mail(s) sent, %2$d mail error(s)"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Conversation.php:42
-msgid "Conversation Settings"
+#: ../../Zotlabs/Module/Invite.php:328
+msgid "Invites not proposed by configuration"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Features.php:45
-msgid "Additional Features"
-msgstr "Ekstra funksjoner"
-
-#: ../../Zotlabs/Module/Settings/Connections.php:40
-msgid "Connections Settings"
+#: ../../Zotlabs/Module/Invite.php:329
+msgid "Contact the site admin"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Multifactor.php:23
-#, fuzzy
-#| msgid "Name is required"
-msgid "Password is required"
-msgstr "Navn er påkrevd"
-
-#: ../../Zotlabs/Module/Settings/Multifactor.php:29
-msgid "The provided password is not correct"
+#: ../../Zotlabs/Module/Invite.php:345
+msgid "Invites by users not enabled"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Multifactor.php:68
-msgid "Account Multi-Factor Authentication"
-msgstr "Tofaktorautentisering"
+#: ../../Zotlabs/Module/Invite.php:350
+msgid "You have no more invitations available"
+msgstr "Du har ikke flere invitasjoner tilgjengelig"
-#: ../../Zotlabs/Module/Settings/Multifactor.php:69
-msgid ""
-"This is your generated secret. It may be used in some cases if the QR image "
-"cannot be read. Please store it in a safe place."
+#: ../../Zotlabs/Module/Invite.php:360
+msgid "Not on xchan"
msgstr ""
-"Dette er en generert sikkerhetskode. Den kan være nyttig i enkelte tilfeller "
-"hvor QR koden ikke kan leses. Lagre den et sikkert sted."
-
-#: ../../Zotlabs/Module/Settings/Multifactor.php:70
-msgid "Please enter the code from your authenticator app"
-msgstr "Skriv inn koden fra din autentiseringsapp"
-#: ../../Zotlabs/Module/Settings/Multifactor.php:71
-msgid "You will only be able to enable MFA if the test passes"
+#: ../../Zotlabs/Module/Invite.php:400
+msgid "Invitation expires after"
msgstr ""
-"Flerfaktorautentisering vil kun bli slått på dersom denne testen lykkes"
-#: ../../Zotlabs/Module/Settings/Multifactor.php:75
-msgid "Congratulations, the provided code was correct"
+#: ../../Zotlabs/Module/Invite.php:501 ../../Zotlabs/Module/Invite.php:540
+msgid "Invitation"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Multifactor.php:76
-msgid "Incorrect code"
-msgstr ""
+#: ../../Zotlabs/Module/Invite.php:531
+msgid "Send invitations"
+msgstr "Send invitasjoner"
-#: ../../Zotlabs/Module/Settings/Multifactor.php:79
-#, fuzzy
-#| msgid "Failed authentication"
-msgid "Enable Multi-Factor Authentication"
-msgstr "Mislykket autentisering"
+#: ../../Zotlabs/Module/Invite.php:532
+msgid "Invitations I am using"
+msgstr "Invitasjoner jeg bruker"
-#: ../../Zotlabs/Module/Settings/Multifactor.php:81
-msgid "Logging in will require you to be in possession of your smartphone"
+#: ../../Zotlabs/Module/Invite.php:533
+msgid "Invitations we are using"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Multifactor.php:84
-#, fuzzy
-#| msgid "Your new password is"
-msgid "Your account password"
-msgstr "Ditt nye passord er"
-
-#: ../../Zotlabs/Module/Settings/Multifactor.php:86
-msgid "Test"
-msgstr "Sjekk koden"
-
-#: ../../Zotlabs/Module/Settings/Events.php:40
-msgid "Events Settings"
+#: ../../Zotlabs/Module/Invite.php:534
+msgid "§ Note, the email(s) sent will be recorded in the system logs"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Account.php:23
-msgid "Not valid email."
-msgstr "Ikke gyldig e-post."
-
-#: ../../Zotlabs/Module/Settings/Account.php:26
-msgid "Protected email address. Cannot change to that email."
-msgstr "Beskyttet e-postadresse. Kan ikke endre til den e-postadressen."
-
-#: ../../Zotlabs/Module/Settings/Account.php:35
-msgid "System failure storing new email. Please try again."
-msgstr "Systemfeil ved lagring av ny e-post. Vennligst prøv igjen."
-
-#: ../../Zotlabs/Module/Settings/Account.php:53
-msgid "Password verification failed."
-msgstr "Passordbekreftelsen mislyktes."
-
-#: ../../Zotlabs/Module/Settings/Account.php:60
-msgid "Passwords do not match. Password unchanged."
-msgstr "Passordene stemmer ikke overens. Passord uforandret."
+#: ../../Zotlabs/Module/Invite.php:535
+msgid "Enter email addresses, one per line:"
+msgstr "Skriv e-postadresser, en per linje:"
-#: ../../Zotlabs/Module/Settings/Account.php:64
-msgid "Empty passwords are not allowed. Password unchanged."
-msgstr "Tomme passord er ikke tillatt. Passord uforandret."
+#: ../../Zotlabs/Module/Invite.php:536
+msgid "Your message:"
+msgstr "Din melding:"
-#: ../../Zotlabs/Module/Settings/Account.php:78
-msgid "Password changed."
-msgstr "Passord endret."
+#: ../../Zotlabs/Module/Invite.php:537
+msgid "Invite template"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Account.php:80
-msgid "Password update failed. Please try again."
-msgstr "Passord oppdatering mislyktes. Vennligst prøv igjen."
+#: ../../Zotlabs/Module/Invite.php:539
+msgid "Subject:"
+msgstr "Emne:"
-#: ../../Zotlabs/Module/Settings/Account.php:105
-msgid "Account Settings"
-msgstr "Kontoinnstillinger"
+#: ../../Zotlabs/Module/Invite.php:545
+msgid "Here you may enter personal notes to the recipient(s)"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Account.php:106
-msgid "Current Password"
-msgstr "Nåværende passord"
+#: ../../Zotlabs/Module/Siteinfo.php:22
+msgid "About this site"
+msgstr "Om dette nettstedet "
-#: ../../Zotlabs/Module/Settings/Account.php:107
-msgid "Enter New Password"
-msgstr "Skriv nytt passord"
+#: ../../Zotlabs/Module/Siteinfo.php:23
+#, fuzzy
+msgid "Site Name"
+msgstr "Nettstedets navn"
-#: ../../Zotlabs/Module/Settings/Account.php:108
-msgid "Confirm New Password"
-msgstr "Bekreft nytt passord"
+#: ../../Zotlabs/Module/Siteinfo.php:27
+msgid "Administrator"
+msgstr "Administrator"
-#: ../../Zotlabs/Module/Settings/Account.php:108
-msgid "Leave password fields blank unless changing"
-msgstr "La passordfeltene stå blanke om det ikke skal endres"
+#: ../../Zotlabs/Module/Siteinfo.php:30
+msgid "Software and Project information"
+msgstr "Program- og prosjektinformasjon"
-#: ../../Zotlabs/Module/Settings/Account.php:110
-msgid "Multi-Factor Authentication"
-msgstr "Flerfaktorautentisering"
+#: ../../Zotlabs/Module/Siteinfo.php:31
+msgid "This site is powered by $Projectname"
+msgstr "Dette nettstedet drives av $Projectname"
-#: ../../Zotlabs/Module/Settings/Account.php:111
-msgid "DId2 or Email Address:"
-msgstr "DId2 eller epostadresse:"
+#: ../../Zotlabs/Module/Siteinfo.php:32
+#, fuzzy
+#| msgid ""
+#| "Federated and decentralised networking and identity services provided by "
+#| "Zot"
+msgid ""
+"Federated and decentralised networking and identity services provided by"
+msgstr "Fødererte og desentraliserte nettverks- og identitetstjenester via Zot"
-#: ../../Zotlabs/Module/Settings/Account.php:114
-msgid "Remove this account including all its channels"
-msgstr "Slett denne kontoen inkludert alle dens kanaler"
+#: ../../Zotlabs/Module/Siteinfo.php:35
+msgid "Additional federated transport protocols:"
+msgstr "Øvrige fødererte transportprotokoller:"
-#: ../../Zotlabs/Module/Settings/Privacy.php:49
-msgid "Privacy settings updated."
-msgstr "Personverninnstillingene ble oppdatert."
+#: ../../Zotlabs/Module/Siteinfo.php:37
+#, php-format
+msgid "Version %s"
+msgstr "Versjon %s"
-#: ../../Zotlabs/Module/Settings/Privacy.php:67
-msgid "Only those you specifically allow"
-msgstr "Bare de du spesifikt tillater"
+#: ../../Zotlabs/Module/Siteinfo.php:38
+msgid "Project homepage"
+msgstr "Prosjektets hjemmeside"
-#: ../../Zotlabs/Module/Settings/Privacy.php:68
-msgid "Approved connections"
-msgstr "Godkjente forbindelser"
+#: ../../Zotlabs/Module/Siteinfo.php:39
+msgid "Developer homepage"
+msgstr "Utviklers hjemmeside"
-#: ../../Zotlabs/Module/Settings/Privacy.php:69
-msgid "Any connections"
-msgstr "Enhver forbindelse"
+#: ../../Zotlabs/Module/Siteinfo.php:43
+#, fuzzy
+#| msgid "Block Title"
+msgid "Blocked sites"
+msgstr "Byggeklossens tittel"
-#: ../../Zotlabs/Module/Settings/Privacy.php:70
-msgid "Anybody on this website"
-msgstr "Enhver ved dette nettstedet"
+#: ../../Zotlabs/Module/Attach.php:68
+msgid "Item not available."
+msgstr "Elementet er ikke tilgjengelig."
-#: ../../Zotlabs/Module/Settings/Privacy.php:71
-msgid "Anybody in this network"
-msgstr "Enhver i dette nettverket"
+#: ../../Zotlabs/Module/Uexport.php:108
+msgid "No content available for year"
+msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:72
-msgid "Anybody authenticated"
-msgstr "Enhver som er autentisert"
+#: ../../Zotlabs/Module/Uexport.php:171
+msgid "Export Channel"
+msgstr "Eksporter kanal"
-#: ../../Zotlabs/Module/Settings/Privacy.php:73
-msgid "Anybody on the internet"
-msgstr "Enhver på Internett"
+#: ../../Zotlabs/Module/Uexport.php:173
+msgid "Export channel"
+msgstr "Eksporter kanal"
-#: ../../Zotlabs/Module/Settings/Privacy.php:83
+#: ../../Zotlabs/Module/Uexport.php:174
msgid ""
-"Advise: set to \"Anybody on the internet\" and use privacy groups to "
-"restrict access"
-msgstr ""
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:122
-msgid "Privacy Settings"
-msgstr "Personverninnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:127
-msgid "Advanced configuration"
+"This will export your identity and social graph into a file which can be "
+"used to import your channel to a new hub."
msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:129
-msgid "Proceed with caution"
+#: ../../Zotlabs/Module/Uexport.php:177
+msgid "Export content"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:130
+#: ../../Zotlabs/Module/Uexport.php:178
msgid ""
-"Changing advanced configuration settings can impact your, and your contacts "
-"channels functionality and security."
+"This will export your posts, direct messages, articles and cards per month "
+"stored into a zip file per year. Months with no posts will be dismissed."
msgstr ""
+"Dette vil eksportere dine innlegg, direktemeldinger, artikler og kort, en "
+"fil for hver måned, pakket i en zip fil for hvert år. Måneder uten innhold "
+"blir ignorert."
-#: ../../Zotlabs/Module/Settings/Privacy.php:131
-msgid "Accept the risk and continue"
+#: ../../Zotlabs/Module/Uexport.php:180
+msgid "Export wikis"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:133
-msgid "Automatically approve new contacts"
-msgstr "Automatisk godkjenn nye kontakter"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:134
-msgid "Opt-out of search engine indexing"
-msgstr "Ikke la søkemotorer indeksere denne kanalen"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:135
-msgid "Group actor"
+#: ../../Zotlabs/Module/Uexport.php:181
+msgid "This will export your wikis and wiki pages."
msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:135
-msgid "Allow this channel to act as a forum"
+#: ../../Zotlabs/Module/Uexport.php:183
+msgid "Export webpages"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Privacy.php:136
-msgid "Accept all messages which mention you"
-msgstr "Godta alle meldinger som nevner deg"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:136
-msgid "This setting bypasses normal permissions"
-msgstr "Denne instillingen overstyrer vanlig tilgangskontroll"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:137
-msgid "Accept unsolicited comments for moderation"
-msgstr "Godta kommentarer fra fremmede for moderering"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:137
-msgid "Otherwise they will be silently dropped"
-msgstr "Ellers vil de avvises uten varsel"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:138
-msgid "Enable OCAP access"
-msgstr "Slå på OCAP tilgangskontroll"
-
-#: ../../Zotlabs/Module/Settings/Privacy.php:138
-msgid "Grant limited posts the right to access linked private media"
-msgstr "Gi begrensede innlegg tilgang til private media som er lenket fra dem"
-
-#: ../../Zotlabs/Module/Settings/Display.php:126
-#, php-format
-msgid "%s - (Experimental)"
-msgstr "%s - (Eksperimentelt)"
-
-#: ../../Zotlabs/Module/Settings/Display.php:183
-msgid "Display Settings"
-msgstr "Visningsinnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Display.php:184
-msgid "Theme Settings"
-msgstr "Temainnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Display.php:185
-msgid "Custom Theme Settings"
-msgstr "Tilpassede temainnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Display.php:186
-msgid "Content Settings"
-msgstr "Innholdsinnstillinger"
-
-#: ../../Zotlabs/Module/Settings/Display.php:192
-msgid "Display Theme:"
-msgstr "Visningstema:"
-
-#: ../../Zotlabs/Module/Settings/Display.php:193
-msgid "Select scheme"
-msgstr "Velg skjema"
-
-#: ../../Zotlabs/Module/Settings/Display.php:195
-msgid "Preload images before rendering the page"
-msgstr "Last inn bildene før gjengivelsen av siden"
-
-#: ../../Zotlabs/Module/Settings/Display.php:195
-msgid ""
-"The subjective page load time will be longer but the page will be ready when "
-"displayed"
+#: ../../Zotlabs/Module/Uexport.php:184
+msgid "This will export your webpages and menus."
msgstr ""
-"Den personlige opplevelsen av lastetiden vil være lenger, men siden vil være "
-"klar når den vises"
-
-#: ../../Zotlabs/Module/Settings/Display.php:196
-msgid "Enable user zoom on mobile devices"
-msgstr "Skru på brukerstyrt zoom på mobile enheter"
-
-#: ../../Zotlabs/Module/Settings/Display.php:197
-msgid "Update browser every xx seconds"
-msgstr "Oppdater nettleser hvert xx sekunder"
-
-#: ../../Zotlabs/Module/Settings/Display.php:197
-msgid "Minimum of 10 seconds, no maximum"
-msgstr "Minimum 10 sekunder, ikke noe maksimum"
-
-#: ../../Zotlabs/Module/Settings/Display.php:198
-msgid "Maximum number of conversations to load at any time:"
-msgstr "Maksimalt antall samtaler å laste samtidig:"
-#: ../../Zotlabs/Module/Settings/Display.php:198
-msgid "Maximum of 30 items"
+#: ../../Zotlabs/Module/Uexport.php:186
+msgid "Export channel calendar"
msgstr ""
-#: ../../Zotlabs/Module/Settings/Display.php:199
-msgid "Show emoticons (smilies) as images"
-msgstr "Vis emoticons (smilefjes) som bilder"
-
-#: ../../Zotlabs/Module/Settings/Display.php:200
-msgid "Link post titles to source"
-msgstr "Lenk innleggets tittel til kilden"
-
-#: ../../Zotlabs/Module/Settings/Display.php:202
-#: ../../Zotlabs/Widget/Newmember.php:82
-msgid "New Member Links"
-msgstr "Lenker for nye medlemmer"
-
-#: ../../Zotlabs/Module/Settings/Display.php:202
-msgid "Display new member quick links menu"
-msgstr "Vis hurtiglenker for nye medlemmer"
-
-#: ../../Zotlabs/Module/Settings/Manage.php:41
-msgid "Channel Manager Settings"
+#: ../../Zotlabs/Module/Uexport.php:187
+msgid ""
+"This will export your channel calendar events and associated items. CalDAV "
+"calendars are not included."
msgstr ""
-#: ../../Zotlabs/Module/Apporder.php:47
-msgid "Change Order of Pinned Navbar Apps"
+#: ../../Zotlabs/Module/Uexport.php:189
+msgid "Export chatrooms"
msgstr ""
-#: ../../Zotlabs/Module/Apporder.php:47
-msgid "Change Order of App Tray Apps"
+#: ../../Zotlabs/Module/Uexport.php:190
+msgid "This will export your chatrooms. Chat history is dismissed."
msgstr ""
-#: ../../Zotlabs/Module/Apporder.php:48
+#: ../../Zotlabs/Module/Uexport.php:192
+#, php-format
msgid ""
-"Use arrows to move the corresponding app left (top) or right (bottom) in the "
-"navbar"
-msgstr ""
-
-#: ../../Zotlabs/Module/Apporder.php:48
-msgid "Use arrows to move the corresponding app up or down in the app tray"
+"This export can be imported or restored by visiting <a href=\"%1$s\">%2$s</"
+"a> on any site containing your channel."
msgstr ""
-#: ../../Zotlabs/Module/Like.php:111
+#: ../../Zotlabs/Module/Like.php:99
msgid "Like/Dislike"
msgstr "Liker/Liker ikke"
-#: ../../Zotlabs/Module/Like.php:117
+#: ../../Zotlabs/Module/Like.php:105
msgid "This action is restricted to members."
msgstr "Denne handlingen er begrenset til medlemmer."
-#: ../../Zotlabs/Module/Like.php:118
+#: ../../Zotlabs/Module/Like.php:106
msgid ""
"Please <a href=\"rmagic\">login with your $Projectname ID</a> or <a "
"href=\"register\">register as a new $Projectname member</a> to continue."
@@ -12007,3639 +12702,2821 @@ msgstr ""
"href=\"register\">registrer deg som et nytt $Projectname-medlem</a> for å "
"fortsette."
-#: ../../Zotlabs/Module/Like.php:171 ../../Zotlabs/Module/Like.php:197
-#: ../../Zotlabs/Module/Like.php:230
+#: ../../Zotlabs/Module/Like.php:159 ../../Zotlabs/Module/Like.php:185
+#: ../../Zotlabs/Module/Like.php:218
msgid "Invalid request."
msgstr "Ugyldig forespørsel."
-#: ../../Zotlabs/Module/Like.php:212
+#: ../../Zotlabs/Module/Like.php:200
msgid "thing"
msgstr "ting"
-#: ../../Zotlabs/Module/Like.php:253
+#: ../../Zotlabs/Module/Like.php:241
msgid "Channel unavailable."
msgstr "Kanalen er utilgjengelig."
-#: ../../Zotlabs/Module/Like.php:289
+#: ../../Zotlabs/Module/Like.php:277
msgid "Previous action reversed."
msgstr "Forrige handling er omgjort."
-#: ../../Zotlabs/Module/Like.php:453
+#: ../../Zotlabs/Module/Like.php:444
#, fuzzy
#| msgid "Profile"
msgid "profile"
msgstr "Profil"
-#: ../../Zotlabs/Module/Like.php:487
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:2248
-#, php-format
-msgid "%1$s is attending %2$s's %3$s"
-msgstr "%1$s deltar på %2$ss %3$s"
-
-#: ../../Zotlabs/Module/Like.php:489
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:2250
-#, php-format
-msgid "%1$s is not attending %2$s's %3$s"
-msgstr "%1$s deltar ikke på %2$ss %3$s"
-
-#: ../../Zotlabs/Module/Like.php:491
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:2252
-#, php-format
-msgid "%1$s may attend %2$s's %3$s"
-msgstr "%1$s deltar kanskje på %2$ss %3$s"
-
-#: ../../Zotlabs/Module/Like.php:615
+#: ../../Zotlabs/Module/Like.php:611
msgid "Action completed."
msgstr "Handling ferdig."
-#: ../../Zotlabs/Module/Like.php:616
+#: ../../Zotlabs/Module/Like.php:612
msgid "Thank you."
msgstr "Tusen takk."
-#: ../../Zotlabs/Module/Follow.php:75
-msgid "Connection added."
-msgstr ""
-
-#: ../../Zotlabs/Module/Chatsvc.php:131
-msgid "Away"
-msgstr "Borte"
-
-#: ../../Zotlabs/Module/Chatsvc.php:136
-msgid "Online"
-msgstr "Online"
-
-#: ../../Zotlabs/Module/Admin.php:98
-msgid "Blocked accounts"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin.php:99
-msgid "Expired accounts"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin.php:100
-msgid "Expiring accounts"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin.php:124
-msgid "Message queues"
-msgstr "Meldingskøer"
-
-#: ../../Zotlabs/Module/Admin.php:138
-msgid "Your software should be updated"
-msgstr "Programvaren bør oppdateres"
-
-#: ../../Zotlabs/Module/Admin.php:143
-msgid "Summary"
-msgstr "Sammendrag"
-
-#: ../../Zotlabs/Module/Admin.php:146
-msgid "Registered accounts"
-msgstr "Registrerte kontoer"
-
-#: ../../Zotlabs/Module/Admin.php:147
-msgid "Pending registrations"
-msgstr "Ventende registreringer"
-
-#: ../../Zotlabs/Module/Admin.php:148
-msgid "Registered channels"
-msgstr "Registrerte kanaler"
-
-#: ../../Zotlabs/Module/Admin.php:150
-msgid "Version"
-msgstr "Versjon"
-
-#: ../../Zotlabs/Module/Admin.php:151
-msgid "Repository version (master)"
-msgstr ""
-
-#: ../../Zotlabs/Module/Admin.php:152
-msgid "Repository version (dev)"
-msgstr ""
-
-#: ../../Zotlabs/Module/Authorize.php:17
-msgid "Unknown App"
-msgstr ""
-
-#: ../../Zotlabs/Module/Authorize.php:29
-msgid "Authorize"
-msgstr ""
-
-#: ../../Zotlabs/Module/Authorize.php:30
-#, php-format
-msgid "Do you authorize the app %s to access your channel data?"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:22
-msgid "This page is available only to site members"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:28
-#: ../../Zotlabs/Widget/Channel_activities.php:29
-msgid "Welcome"
-msgstr "Velkommen"
-
-#: ../../Zotlabs/Module/Go.php:30
-msgid "What would you like to do?"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:32
-msgid ""
-"Please bookmark this page if you would like to return to it in the future"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:36
-msgid "Upload a profile photo"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:37
-msgid "Upload a cover photo"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:38
-msgid "Edit your default profile"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:39 ../../Zotlabs/Widget/Newmember.php:48
-msgid "View friend suggestions"
-msgstr "Vis venneforslag"
-
-#: ../../Zotlabs/Module/Go.php:40
-msgid "View the channel directory"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:41
-msgid "View/edit your channel settings"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:42
-msgid "View the site or project documentation"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:43
-msgid "Visit your channel homepage"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:44
-msgid ""
-"View your connections and/or add somebody whose address you already know"
-msgstr ""
-
-#: ../../Zotlabs/Module/Go.php:45
-msgid ""
-"View your personal stream (this may be empty until you add some connections)"
-msgstr ""
+#: ../../Zotlabs/Module/Blocks.php:97 ../../Zotlabs/Module/Blocks.php:153
+#: ../../Zotlabs/Module/Editblock.php:113
+msgid "Block Name"
+msgstr "Byggeklossens navn"
-#: ../../Zotlabs/Module/Go.php:53
-msgid "View the public stream. Warning: this content is not moderated"
-msgstr ""
+#: ../../Zotlabs/Module/Blocks.php:154
+msgid "Block Title"
+msgstr "Byggeklossens tittel"
-#: ../../Zotlabs/Module/Manage.php:137
-msgid "Create a new channel"
-msgstr "Lag en ny kanal"
+#: ../../Zotlabs/Module/Mitem.php:63
+msgid "Unable to create element."
+msgstr "Klarer ikke å lage element."
-#: ../../Zotlabs/Module/Manage.php:163
-msgid "Current Channel"
-msgstr "Gjeldende kanal"
+#: ../../Zotlabs/Module/Mitem.php:87
+msgid "Unable to update menu element."
+msgstr "Ikke i stand til å oppdatere menyelement."
-#: ../../Zotlabs/Module/Manage.php:165
-msgid "Switch to one of your channels by selecting it."
-msgstr "Bytt til en av dine kanaler ved å velge den."
+#: ../../Zotlabs/Module/Mitem.php:103
+msgid "Unable to add menu element."
+msgstr "Ikke i stand til å legge til menyelement."
-#: ../../Zotlabs/Module/Manage.php:166
-msgid "Default Channel"
-msgstr "Standardkanal"
+#: ../../Zotlabs/Module/Mitem.php:167 ../../Zotlabs/Module/Mitem.php:244
+msgid "Menu Item Permissions"
+msgstr "Menyelement Tillatelser"
-#: ../../Zotlabs/Module/Manage.php:167
-msgid "Make Default"
-msgstr "Gjør til standard"
+#: ../../Zotlabs/Module/Mitem.php:168 ../../Zotlabs/Module/Mitem.php:245
+msgid "(click to open/close)"
+msgstr "(klikk for å åpne/lukke)"
-#: ../../Zotlabs/Module/Manage.php:170
-#, php-format
-msgid "%d new introductions"
-msgstr "%d nye introduksjoner"
+#: ../../Zotlabs/Module/Mitem.php:174 ../../Zotlabs/Module/Mitem.php:191
+msgid "Link Name"
+msgstr "Lenkenavn"
-#: ../../Zotlabs/Module/Manage.php:172
-msgid "Delegated Channel"
-msgstr "Delegert kanal"
+#: ../../Zotlabs/Module/Mitem.php:175 ../../Zotlabs/Module/Mitem.php:253
+msgid "Link or Submenu Target"
+msgstr "Lenke- eller undermeny-mål"
-#: ../../Zotlabs/Module/Search.php:251
-#, php-format
-msgid "Items tagged with: %s"
-msgstr "Elementer merket med: %s"
+#: ../../Zotlabs/Module/Mitem.php:175
+msgid "Enter URL of the link or select a menu name to create a submenu"
+msgstr "Skriv URL-en til lenken eller velg et menynavn for å lage en undermeny"
-#: ../../Zotlabs/Module/Search.php:253
-#, php-format
-msgid "Search results for: %s"
-msgstr "Søkeresultater for: %s"
+#: ../../Zotlabs/Module/Mitem.php:176 ../../Zotlabs/Module/Mitem.php:254
+msgid "Use magic-auth if available"
+msgstr "Bruk magic-autent hvis mulig"
-#: ../../Zotlabs/Module/Changeaddr.php:35
-msgid ""
-"Channel name changes are not allowed within 48 hours of changing the account "
-"password."
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:177 ../../Zotlabs/Module/Mitem.php:255
+msgid "Open link in new window"
+msgstr "Åpne lenke i nytt vindu"
-#: ../../Zotlabs/Module/Changeaddr.php:77
-msgid "Change channel nickname/address"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:178 ../../Zotlabs/Module/Mitem.php:256
+msgid "Order in list"
+msgstr "Ordne i liste"
-#: ../../Zotlabs/Module/Changeaddr.php:78
-msgid "Any/all connections on other networks will be lost!"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:178 ../../Zotlabs/Module/Mitem.php:256
+msgid "Higher numbers will sink to bottom of listing"
+msgstr "Høyere tall vil synke mot bunnen av listen"
-#: ../../Zotlabs/Module/Changeaddr.php:80
-msgid "New channel address"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:179
+msgid "Submit and finish"
+msgstr "Send inn og avslutt"
-#: ../../Zotlabs/Module/Changeaddr.php:81
-msgid "Rename Channel"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:180
+msgid "Submit and continue"
+msgstr "Send inn og fortsett"
-#: ../../Zotlabs/Module/Cdav.php:819
-msgid "Calendar entries imported."
-msgstr "Kalenderhendelsene er importert."
+#: ../../Zotlabs/Module/Mitem.php:189
+msgid "Menu:"
+msgstr "Meny:"
-#: ../../Zotlabs/Module/Cdav.php:821
-msgid "No calendar entries found."
-msgstr "Ingen kalenderhendelser funnet."
+#: ../../Zotlabs/Module/Mitem.php:192
+msgid "Link Target"
+msgstr "Lenkemål"
-#: ../../Zotlabs/Module/Cdav.php:1000
-msgid "Event title"
-msgstr "Tittel på hendelse"
+#: ../../Zotlabs/Module/Mitem.php:195
+msgid "Edit menu"
+msgstr "Endre meny"
-#: ../../Zotlabs/Module/Cdav.php:1001
-msgid "Start date and time"
-msgstr "Startdato og tidspunkt"
+#: ../../Zotlabs/Module/Mitem.php:198
+msgid "Edit element"
+msgstr "Endre element"
-#: ../../Zotlabs/Module/Cdav.php:1002
-msgid "End date and time"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:199
+msgid "Drop element"
+msgstr "Slett element"
-#: ../../Zotlabs/Module/Cdav.php:1003
-#, fuzzy
-msgid "Timezone:"
-msgstr "Tidssone"
+#: ../../Zotlabs/Module/Mitem.php:200
+msgid "New element"
+msgstr "Nytt element"
-#: ../../Zotlabs/Module/Cdav.php:1029
-#, fuzzy
-msgid "Month"
-msgstr "måned"
+#: ../../Zotlabs/Module/Mitem.php:201
+msgid "Edit this menu container"
+msgstr "Endre denne menybeholderen"
-#: ../../Zotlabs/Module/Cdav.php:1030
-#, fuzzy
-msgid "Week"
-msgstr "uke"
+#: ../../Zotlabs/Module/Mitem.php:202
+msgid "Add menu element"
+msgstr "Legg til menyelement"
-#: ../../Zotlabs/Module/Cdav.php:1031
-#, fuzzy
-msgid "Day"
-msgstr "dag"
+#: ../../Zotlabs/Module/Mitem.php:203
+msgid "Delete this menu item"
+msgstr "Slett dette menyelementet"
-#: ../../Zotlabs/Module/Cdav.php:1032
-msgid "List month"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:204
+msgid "Edit this menu item"
+msgstr "Endre dette menyelementet"
-#: ../../Zotlabs/Module/Cdav.php:1033
-msgid "List week"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:220
+msgid "Menu item not found."
+msgstr "Menyelement ble ikke funnet."
-#: ../../Zotlabs/Module/Cdav.php:1034
-msgid "List day"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:233
+msgid "Menu item deleted."
+msgstr "Menyelement slettet."
-#: ../../Zotlabs/Module/Cdav.php:1042
-msgid "More"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:235
+msgid "Menu item could not be deleted."
+msgstr "Menyelement kunne ikke bli slettet."
-#: ../../Zotlabs/Module/Cdav.php:1043
-msgid "Less"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:242
+msgid "Edit Menu Element"
+msgstr "Endre menyelement"
-#: ../../Zotlabs/Module/Cdav.php:1045
-msgid "Select calendar"
-msgstr ""
+#: ../../Zotlabs/Module/Mitem.php:252
+msgid "Link text"
+msgstr "Lenketekst"
-#: ../../Zotlabs/Module/Cdav.php:1046 ../../Zotlabs/Widget/Cdav.php:149
-msgid "Channel Calendars"
+#: ../../Zotlabs/Module/Dircensor.php:61
+msgid "Entry censored"
msgstr ""
-#: ../../Zotlabs/Module/Cdav.php:1046 ../../Zotlabs/Widget/Cdav.php:135
-#: ../../Zotlabs/Widget/Cdav.php:149
-msgid "CalDAV Calendars"
+#: ../../Zotlabs/Module/Dircensor.php:64
+msgid "Entry OK"
msgstr ""
-#: ../../Zotlabs/Module/Cdav.php:1048
-msgid "Delete all"
+#: ../../Zotlabs/Module/Apporder.php:47
+msgid "Change Order of Pinned Navbar Apps"
msgstr ""
-#: ../../Zotlabs/Module/Cdav.php:1051
-msgid "Sorry! Editing of recurrent events is not yet implemented."
+#: ../../Zotlabs/Module/Apporder.php:47
+msgid "Change Order of App Tray Apps"
msgstr ""
-#: ../../Zotlabs/Module/Cdav.php:1052
+#: ../../Zotlabs/Module/Apporder.php:48
msgid ""
-"Could not fetch calendar resource. The selected calendar might be disabled."
-msgstr ""
-
-#: ../../Zotlabs/Module/Cdav.php:1452
-msgid "Default Calendar"
-msgstr ""
-
-#: ../../Zotlabs/Module/Cdav.php:1463
-msgid "Default Addressbook"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:50
-msgid "Invalid abook_id"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:381
-#, fuzzy
-msgid "View profile"
-msgstr "Vis profil"
-
-#: ../../Zotlabs/Module/Contactedit.php:394
-msgid "Select a role for this contact"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:395
-#: ../../Zotlabs/Widget/Permcats.php:92
-msgid "Contact roles"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:426
-msgid "Roles"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:427
-msgid "Compare permissions"
+"Use arrows to move the corresponding app left (top) or right (bottom) in the "
+"navbar"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:428
-msgid "Permission"
+#: ../../Zotlabs/Module/Apporder.php:48
+msgid "Use arrows to move the corresponding app up or down in the app tray"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:429
-#: ../../Zotlabs/Widget/Privacygroups.php:54
-#, fuzzy
-msgid "Privacy groups"
-msgstr "Personverngrupper"
-
-#: ../../Zotlabs/Module/Contactedit.php:432
-msgid "Content filter"
+#: ../../Zotlabs/Module/New_channel.php:160
+msgid "Your real name is recommended."
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:442
-msgid "Contact updated"
+#: ../../Zotlabs/Module/New_channel.php:161
+msgid ""
+"Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation "
+"Group\""
msgstr ""
+"Eksempel: \"Ola Nordmann\", \"Lisa og hestene hennes\", \"Fotball\", "
+"\"Sykkelgruppa\""
-#: ../../Zotlabs/Module/Contactedit.php:442
-msgid "Contact update failed"
+#: ../../Zotlabs/Module/New_channel.php:166
+msgid ""
+"This will be used to create a unique network address (like an email address)."
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:491
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:417
-#: ../../extend/addon/hzaddons/pubcrawl/pubcrawl.php:1085
-msgid "Refresh succeeded"
+#: ../../Zotlabs/Module/New_channel.php:168
+msgid "Allowed characters are a-z 0-9, - and _"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:494
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:412
-#: ../../extend/addon/hzaddons/pubcrawl/pubcrawl.php:1078
+#: ../../Zotlabs/Module/New_channel.php:176
#, fuzzy
-#| msgid "Refresh"
-msgid "Refresh failed"
-msgstr "Forny"
-
-#: ../../Zotlabs/Module/Contactedit.php:520
-msgid "Block status updated"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:524
-msgid "Block failed"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:535
-msgid "Ignore status updated"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:539
-msgid "Ignore failed"
-msgstr ""
-
-#: ../../Zotlabs/Module/Contactedit.php:550
-msgid "Archive status updated"
-msgstr ""
+msgid "Channel name"
+msgstr "Kanalnavn"
-#: ../../Zotlabs/Module/Contactedit.php:554
-msgid "Archive failed"
-msgstr ""
+#: ../../Zotlabs/Module/New_channel.php:179
+#: ../../Zotlabs/Module/Settings/Channel.php:234
+msgid "Channel role"
+msgstr "Kanalrolle"
-#: ../../Zotlabs/Module/Contactedit.php:565
-msgid "Hide status updated"
+#: ../../Zotlabs/Module/New_channel.php:182
+msgid "Create a Channel"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:569
-msgid "Hide failed"
+#: ../../Zotlabs/Module/New_channel.php:183
+msgid ""
+"A channel is a unique network identity. It can represent a person (social "
+"network profile), a forum (group), a business or celebrity page, a newsfeed, "
+"and many other things."
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:604
-msgid "Contact removed"
+#: ../../Zotlabs/Module/New_channel.php:184
+msgid ""
+"or <a href=\"import\">import an existing channel</a> from another location."
msgstr ""
+"eller <a href=\"import\">importer en eksisterende kanal</a> fra et annet "
+"sted."
-#: ../../Zotlabs/Module/Contactedit.php:608
-msgid "Delete failed"
+#: ../../Zotlabs/Module/New_channel.php:189
+msgid "Validate"
msgstr ""
-#: ../../Zotlabs/Module/Contactedit.php:618
-#: ../../Zotlabs/Widget/Affinity.php:60
-msgid "Refresh"
-msgstr "Forny"
+#: ../../Zotlabs/Module/Photos.php:84 ../../Zotlabs/Module/Photos.php:103
+msgid "Album not found."
+msgstr "Albumet ble ikke funnet."
-#: ../../Zotlabs/Module/Contactedit.php:619
-msgid "Refetch contact info"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:93
+msgid "Delete Album"
+msgstr "Slett album"
-#: ../../Zotlabs/Module/Profile.php:106
-msgid "vcard"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:165 ../../Zotlabs/Module/Photos.php:1058
+msgid "Delete Photo"
+msgstr "Slett bilde"
-#: ../../Zotlabs/Widget/Suggestions.php:58
-msgid "Suggestions"
-msgstr "Forslag"
+#: ../../Zotlabs/Module/Photos.php:522
+msgid "No photos selected"
+msgstr "Ingen bilder valgt"
-#: ../../Zotlabs/Widget/Suggestions.php:59
-msgid "See more..."
-msgstr "Se mer..."
+#: ../../Zotlabs/Module/Photos.php:573
+msgid "Access to this item is restricted."
+msgstr "Tilgang til dette elementet er begrenset."
-#: ../../Zotlabs/Widget/Pinned.php:95
-msgid "Share This"
-msgstr "Del dette"
+#: ../../Zotlabs/Module/Photos.php:616
+#, php-format
+msgid "%1$.2f MB photo storage used."
+msgstr "%1$.2f MB lagringsplass til bilder er brukt."
-#: ../../Zotlabs/Widget/Pinned.php:118 ../../Zotlabs/Widget/Pinned.php:119
+#: ../../Zotlabs/Module/Photos.php:620
#, php-format
-msgid "View %s's profile - %s"
-msgstr "Vis %s sin profil - %s"
+msgid "%1$.2f MB of %2$.2f MB photo storage used."
+msgstr "%1$.2f MB av %2$.2f MB lagringsplass til bilder er brukt."
-#: ../../Zotlabs/Widget/Pinned.php:152
-msgid "Don't show"
-msgstr "Ikke vis"
+#: ../../Zotlabs/Module/Photos.php:662
+msgid "Upload Photos"
+msgstr "Last opp bilder"
-#: ../../Zotlabs/Widget/Privacygroups.php:45
-msgid "Add new group"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:666
+msgid "Enter an album name"
+msgstr "Skriv et albumnavn"
-#: ../../Zotlabs/Widget/Savedsearch.php:81
-msgid "Remove term"
-msgstr "Fjern begrep"
+#: ../../Zotlabs/Module/Photos.php:667
+msgid "or select an existing album (doubleclick)"
+msgstr "eller velg et eksisterende album (dobbeltklikk)"
-#: ../../Zotlabs/Widget/Photo_rand.php:63 ../../Zotlabs/Widget/Photo.php:54
-msgid "photo/image"
-msgstr "foto/bilde"
+#: ../../Zotlabs/Module/Photos.php:668
+msgid "Create a status post for this upload"
+msgstr "Lag et statusinnlegg for denne opplastingen"
-#: ../../Zotlabs/Widget/Notes.php:39
-msgid "Read mode"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:670
+msgid "Description (optional)"
+msgstr "Beskrivelse (valgritt)"
-#: ../../Zotlabs/Widget/Notes.php:40
-msgid "Edit mode"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:758
+msgid "Show Newest First"
+msgstr "Vis nyeste først"
-#: ../../Zotlabs/Widget/Notes.php:41
-msgid "Editing"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:760
+msgid "Show Oldest First"
+msgstr "Vis eldste først"
-#: ../../Zotlabs/Widget/Notes.php:42
-msgid "Saving"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:817 ../../Zotlabs/Module/Photos.php:1349
+msgid "Add Photos"
+msgstr "Legg til bilder"
-#: ../../Zotlabs/Widget/Notes.php:43
-msgid "Saved"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:869
+msgid "Permission denied. Access to this item may be restricted."
+msgstr "Tillatelse avvist. Tilgang til dette elementet kan være begrenset."
-#: ../../Zotlabs/Widget/Suggestedchats.php:36
-msgid "Suggested Chatrooms"
-msgstr "Foreslåtte chatrom"
+#: ../../Zotlabs/Module/Photos.php:871
+msgid "Photo not available"
+msgstr "Bilde er utilgjengelig"
-#: ../../Zotlabs/Widget/Bookmarkedchats.php:25
-msgid "Bookmarked Chatrooms"
-msgstr "Bokmerkede chatrom"
+#: ../../Zotlabs/Module/Photos.php:929
+msgid "Use as profile photo"
+msgstr "Bruk som profilbilde"
-#: ../../Zotlabs/Widget/Tasklist.php:31
-msgid "Tasks"
-msgstr "Oppgaver"
+#: ../../Zotlabs/Module/Photos.php:930
+msgid "Use as cover photo"
+msgstr "Bruk som omslagsbilde"
-#: ../../Zotlabs/Widget/Messages.php:32
-msgid "Public and restricted messages"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:937
+msgid "Private Photo"
+msgstr "Privat bilde"
-#: ../../Zotlabs/Widget/Messages.php:33
-msgid "Direct messages"
-msgstr "Direktemeldinger"
+#: ../../Zotlabs/Module/Photos.php:952
+msgid "View Full Size"
+msgstr "Vis i full størrelse"
-#: ../../Zotlabs/Widget/Messages.php:34
-msgid "Starred messages"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:1032
+msgid "Edit photo"
+msgstr "Endre bilde"
-#: ../../Zotlabs/Widget/Messages.php:35
-#: ../../Zotlabs/Widget/Notifications.php:116
-#: ../../Zotlabs/Widget/Notifications.php:117
-msgid "Notices"
-msgstr "Varsel"
+#: ../../Zotlabs/Module/Photos.php:1034
+msgid "Rotate CW (right)"
+msgstr "Roter med klokka (mot høyre)"
-#: ../../Zotlabs/Widget/Messages.php:37
-msgid "No messages"
-msgstr "Ingen meldinger"
+#: ../../Zotlabs/Module/Photos.php:1035
+msgid "Rotate CCW (left)"
+msgstr "Roter mot klokka (venstre)"
-#: ../../Zotlabs/Widget/Messages.php:38
-msgid "Unseen"
-msgstr "Ulest"
+#: ../../Zotlabs/Module/Photos.php:1038
+msgid "Move photo to album"
+msgstr ""
-#: ../../Zotlabs/Widget/Messages.php:39
-#: ../../Zotlabs/Widget/Notifications.php:34
-#: ../../Zotlabs/Widget/Notifications.php:54
-#: ../../Zotlabs/Widget/Notifications.php:73
-#: ../../Zotlabs/Widget/Notifications.php:134
-#: ../../Zotlabs/Widget/Notifications.php:167
-msgid "Filter by name or address"
-msgstr "Filtrer etter navn eller adresse"
+#: ../../Zotlabs/Module/Photos.php:1039
+msgid "Enter a new album name"
+msgstr "Skriv et nytt albumnavn"
-#: ../../Zotlabs/Widget/Cover_photo.php:74
-msgid "Click to show more"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:1040
+msgid "or select an existing one (doubleclick)"
+msgstr "eller velg et eksisterende album (dobbeltklikk)"
-#: ../../Zotlabs/Widget/Activity.php:55
-#, fuzzy
-msgctxt "widget"
-msgid "Activity"
-msgstr "aktivitet"
+#: ../../Zotlabs/Module/Photos.php:1045
+msgid "Add a Tag"
+msgstr "Legg til merkelapp"
-#: ../../Zotlabs/Widget/Appstore.php:16
-msgid "App Collections"
-msgstr "Appsamlinger"
+#: ../../Zotlabs/Module/Photos.php:1053
+msgid "Example: @bob, @Barbara_Jensen, @jim@example.com"
+msgstr "Eksempel: @bob, @Barbara_Jensen, @jim@example.com"
-#: ../../Zotlabs/Widget/Appstore.php:18
-msgid "Installed apps"
-msgstr "Installerte apper"
+#: ../../Zotlabs/Module/Photos.php:1056
+msgid "Flag as adult in album view"
+msgstr "Flag som voksent i albumvisning"
-#: ../../Zotlabs/Widget/Hq_controls.php:23
-msgid "Toggle post editor"
-msgstr "Vis redigering av innlegg"
+#: ../../Zotlabs/Module/Photos.php:1075
+msgid "I like this (toggle)"
+msgstr "Jeg liker dette (skru av og på)"
-#: ../../Zotlabs/Widget/Hq_controls.php:33
-msgid "Toggle personal notes"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:1076
+msgid "I don't like this (toggle)"
+msgstr "Jeg liker ikke dette (skru av og på)"
-#: ../../Zotlabs/Widget/Hq_controls.php:43
-msgid "Channel activities"
-msgstr ""
+#: ../../Zotlabs/Module/Photos.php:1232
+msgid "Photo Tools"
+msgstr "Fotoverktøy"
-#: ../../Zotlabs/Widget/Archive.php:49
-msgid "Archives"
-msgstr "Arkiv"
+#: ../../Zotlabs/Module/Photos.php:1241
+msgid "In This Photo:"
+msgstr "I dette bildet:"
-#: ../../Zotlabs/Widget/Channel_activities.php:46
-#, fuzzy
-#| msgid "Recent activity"
-msgid "No recent activities"
-msgstr "Nylig aktivitet"
+#: ../../Zotlabs/Module/Photos.php:1246
+msgid "Map"
+msgstr "Kart"
-#: ../../Zotlabs/Widget/Channel_activities.php:214
+#: ../../Zotlabs/Module/Photos.php:1254
msgctxt "noun"
-msgid "new connection"
-msgid_plural "new connections"
-msgstr[0] "ny forbindelse"
-msgstr[1] "nye forbindelser"
+msgid "Likes"
+msgstr "Liker"
-#: ../../Zotlabs/Widget/Channel_activities.php:220
+#: ../../Zotlabs/Module/Photos.php:1255
msgctxt "noun"
-msgid "notice"
-msgid_plural "notices"
-msgstr[0] "varsel"
-msgstr[1] "varsel"
+msgid "Dislikes"
+msgstr "Liker ikke"
-#: ../../Zotlabs/Widget/Settings_menu.php:37
-msgid "Account settings"
-msgstr "Kontoinnstillinger"
+#: ../../Zotlabs/Module/Ochannel.php:32 ../../Zotlabs/Module/Chat.php:29
+msgid "You must be logged in to see this page."
+msgstr "Du må være innloegget for å se denne siden."
-#: ../../Zotlabs/Widget/Settings_menu.php:43
-msgid "Channel settings"
-msgstr "Kanalinnstillinger"
+#: ../../Zotlabs/Module/Hcard.php:37 ../../Zotlabs/Module/Channel.php:150
+#: ../../Zotlabs/Module/Profile.php:62
+msgid "Posts and comments"
+msgstr "Innlegg og kommentarer"
-#: ../../Zotlabs/Widget/Settings_menu.php:49
-msgid "Privacy settings"
-msgstr "Personverninnstillinger"
+#: ../../Zotlabs/Module/Hcard.php:44 ../../Zotlabs/Module/Channel.php:157
+#: ../../Zotlabs/Module/Profile.php:69
+msgid "Only posts"
+msgstr "Kun innlegg"
-#: ../../Zotlabs/Widget/Settings_menu.php:56
-msgid "Display settings"
-msgstr "Visningsinnstillinger"
+#: ../../Zotlabs/Module/Channel.php:231
+msgid "Insufficient permissions. Request redirected to profile page."
+msgstr "Utilstrekkelig tillatelse. Forespørsel omdirigert til profilsiden."
-#: ../../Zotlabs/Widget/Settings_menu.php:63
-msgid "Manage locations"
+#: ../../Zotlabs/Module/Profile.php:106
+msgid "vcard"
msgstr ""
-#: ../../Zotlabs/Widget/Rating.php:59
-msgid "Rating Tools"
-msgstr "Vurderingsverktøy"
-
-#: ../../Zotlabs/Widget/Rating.php:63 ../../Zotlabs/Widget/Rating.php:65
-msgid "Rate Me"
-msgstr "Vurder meg"
-
-#: ../../Zotlabs/Widget/Rating.php:68
-msgid "View Ratings"
-msgstr "Vis vurderinger"
-
-#: ../../Zotlabs/Widget/Appcategories.php:49
-msgid "App Categories"
-msgstr "Appkategorier"
-
-#: ../../Zotlabs/Widget/Activity_filter.php:44
-#: ../../Zotlabs/Widget/Notifications.php:62
-msgid "Direct Messages"
-msgstr "Direktemeldinger"
-
-#: ../../Zotlabs/Widget/Activity_filter.php:48
-msgid "Show direct (private) messages"
-msgstr "Vis direktemeldinger (private meldinger)"
-
-#: ../../Zotlabs/Widget/Activity_filter.php:53
-#: ../../Zotlabs/Widget/Notifications.php:81
-msgid "Events"
-msgstr "Hendelser"
-
-#: ../../Zotlabs/Widget/Activity_filter.php:57
-msgid "Show posts that include events"
-msgstr ""
+#: ../../Zotlabs/Module/Moderate.php:70
+#, fuzzy
+#| msgid "Account approved."
+msgid "Item approved"
+msgstr "Konto godkjent."
-#: ../../Zotlabs/Widget/Activity_filter.php:63
-msgid "Polls"
-msgstr "Spørreskjema"
+#: ../../Zotlabs/Module/Sources.php:41
+msgid "Failed to create source. No channel selected."
+msgstr "Mislyktes med å lage kilde. Ingen kanal er valgt."
-#: ../../Zotlabs/Widget/Activity_filter.php:67
-msgid "Show posts that include polls"
-msgstr "Vis innlegg som inneholder spørreskjema"
+#: ../../Zotlabs/Module/Sources.php:57
+msgid "Source created."
+msgstr "Kilden er laget."
-#: ../../Zotlabs/Widget/Activity_filter.php:90
-#, php-format
-msgid "Show posts related to the %s privacy group"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:70
+msgid "Source updated."
+msgstr "Kilden er oppdatert."
-#: ../../Zotlabs/Widget/Activity_filter.php:99
-msgid "Show my privacy groups"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:99
+msgid "*"
+msgstr "*"
-#: ../../Zotlabs/Widget/Activity_filter.php:123
-msgid "Show posts to this forum"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:106
+msgid "Manage remote sources of content for your channel."
+msgstr "Håndtere eksterne innholdskilder til din kanal."
-#: ../../Zotlabs/Widget/Activity_filter.php:134
-msgid "Show forums"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:107 ../../Zotlabs/Module/Sources.php:117
+msgid "New Source"
+msgstr "Ny kilde"
-#: ../../Zotlabs/Widget/Activity_filter.php:148
-msgid "Starred Posts"
+#: ../../Zotlabs/Module/Sources.php:118 ../../Zotlabs/Module/Sources.php:152
+msgid ""
+"Import all or selected content from the following channel into this channel "
+"and distribute it according to your channel settings."
msgstr ""
+"Importer alt eller et utvalgt av innhold fra følgende kanal inn i denne "
+"kanalen og distribuer det i henhold til dine egne kanalinnstillinger."
-#: ../../Zotlabs/Widget/Activity_filter.php:152
-msgid "Show posts that I have starred"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:119 ../../Zotlabs/Module/Sources.php:153
+msgid "Only import content with these words (one per line)"
+msgstr "Bare importer innhold med disse ordene (ett ord per linje)"
-#: ../../Zotlabs/Widget/Activity_filter.php:163
-msgid "Personal Posts"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:119 ../../Zotlabs/Module/Sources.php:153
+msgid "Leave blank to import all public content"
+msgstr "La stå tomt for å importere alt offentlig innhold"
-#: ../../Zotlabs/Widget/Activity_filter.php:167
-msgid "Show posts that mention or involve me"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:120 ../../Zotlabs/Module/Sources.php:159
+msgid "Channel Name"
+msgstr "Kanalnavn"
-#: ../../Zotlabs/Widget/Activity_filter.php:190
-#, php-format
-msgid "Show posts that I have filed to %s"
+#: ../../Zotlabs/Module/Sources.php:121 ../../Zotlabs/Module/Sources.php:156
+msgid ""
+"Add the following categories to posts imported from this source (comma "
+"separated)"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_filter.php:200
-msgid "Show filed post categories"
+#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
+msgid "Resend posts with this channel as author"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_filter.php:214
-msgid "Panel search"
+#: ../../Zotlabs/Module/Sources.php:122 ../../Zotlabs/Module/Sources.php:157
+msgid "Copyrights may apply"
msgstr ""
-#: ../../Zotlabs/Widget/Activity_filter.php:224
-msgid "Filter by name"
-msgstr "Filtrer etter navn"
+#: ../../Zotlabs/Module/Sources.php:142 ../../Zotlabs/Module/Sources.php:172
+msgid "Source not found."
+msgstr "Kilden ble ikke funnet."
-#: ../../Zotlabs/Widget/Activity_filter.php:239
-msgid "Remove active filter"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:149
+msgid "Edit Source"
+msgstr "Endre kilde"
-#: ../../Zotlabs/Widget/Activity_filter.php:255
-msgid "Stream Filters"
-msgstr "Filtere for tidslinjen"
+#: ../../Zotlabs/Module/Sources.php:150
+msgid "Delete Source"
+msgstr "Slett kilde"
-#: ../../Zotlabs/Widget/Notifications.php:24
-msgid "New network activity notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:180
+msgid "Source removed"
+msgstr "Kilden er fjernet"
-#: ../../Zotlabs/Widget/Notifications.php:27
-msgid "Network stream"
-msgstr ""
+#: ../../Zotlabs/Module/Sources.php:182
+msgid "Unable to remove source."
+msgstr "Ikke i stand til å fjerne kilde."
-#: ../../Zotlabs/Widget/Notifications.php:30
-#: ../../Zotlabs/Widget/Notifications.php:69
-msgid "Mark all notifications read"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:23 ../../Zotlabs/Module/Profiles.php:211
+#: ../../Zotlabs/Module/Profiles.php:641
+msgid "Profile not found."
+msgstr "Profilen ble ikke funnet."
-#: ../../Zotlabs/Widget/Notifications.php:33
-#: ../../Zotlabs/Widget/Notifications.php:53
-#: ../../Zotlabs/Widget/Notifications.php:72
-#: ../../Zotlabs/Widget/Notifications.php:166
-msgid "Show new posts only"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:43
+msgid "Profile deleted."
+msgstr "Profilen er slettet."
-#: ../../Zotlabs/Widget/Notifications.php:44
-msgid "New home activity notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:67 ../../Zotlabs/Module/Profiles.php:104
+msgid "Profile-"
+msgstr "Profil-"
-#: ../../Zotlabs/Widget/Notifications.php:47
-msgid "Home stream"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:89 ../../Zotlabs/Module/Profiles.php:126
+msgid "New profile created."
+msgstr "Ny profil opprettet."
-#: ../../Zotlabs/Widget/Notifications.php:50
-msgid "Mark all notifications seen"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:110
+msgid "Profile unavailable to clone."
+msgstr "Profilen er utilgjengelig for klonen."
-#: ../../Zotlabs/Widget/Notifications.php:63
-msgid "New direct messages notifications"
-msgstr "Varsel om nye direktemeldinger"
+#: ../../Zotlabs/Module/Profiles.php:145
+msgid "Profile unavailable to export."
+msgstr "Profilen er utilgjengelig for eksport."
-#: ../../Zotlabs/Widget/Notifications.php:66
-msgid "Direct messages stream"
-msgstr "Strøm for direktemeldinger"
+#: ../../Zotlabs/Module/Profiles.php:221
+msgid "Profile Name is required."
+msgstr "Profilnavn er påkrevd."
-#: ../../Zotlabs/Widget/Notifications.php:82
-msgid "New events notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:426
+msgid "Marital Status"
+msgstr "Sivilstand"
-#: ../../Zotlabs/Widget/Notifications.php:85
-msgid "View events"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:430
+msgid "Romantic Partner"
+msgstr "Romantisk partner"
-#: ../../Zotlabs/Widget/Notifications.php:88
-msgid "Mark all events seen"
-msgstr "Merk alle hendelser som sett"
+#: ../../Zotlabs/Module/Profiles.php:434 ../../Zotlabs/Module/Profiles.php:787
+msgid "Likes"
+msgstr "Liker"
-#: ../../Zotlabs/Widget/Notifications.php:97
-msgid "New connections notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:438 ../../Zotlabs/Module/Profiles.php:788
+msgid "Dislikes"
+msgstr "Liker ikke"
-#: ../../Zotlabs/Widget/Notifications.php:100
-msgid "View all connections"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:442 ../../Zotlabs/Module/Profiles.php:795
+msgid "Work/Employment"
+msgstr "Arbeid/sysselsetting"
-#: ../../Zotlabs/Widget/Notifications.php:109
-msgid "New files notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:445
+msgid "Religion"
+msgstr "Religion"
-#: ../../Zotlabs/Widget/Notifications.php:120
-msgid "View all notices"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:449
+msgid "Political Views"
+msgstr "Politiske synspunkter"
-#: ../../Zotlabs/Widget/Notifications.php:123
-msgid "Mark all notices seen"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:457
+msgid "Sexual Preference"
+msgstr "Seksuelle preferanser"
-#: ../../Zotlabs/Widget/Notifications.php:144
-msgid "Registrations"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:461
+msgid "Homepage"
+msgstr "Hjemmeside"
-#: ../../Zotlabs/Widget/Notifications.php:145
-msgid "New registrations notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:465
+msgid "Interests"
+msgstr "Interesser"
-#: ../../Zotlabs/Widget/Notifications.php:155
-msgid "New public stream notifications"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:573
+msgid "Profile updated."
+msgstr "Profilen er oppdatert."
-#: ../../Zotlabs/Widget/Notifications.php:158
-msgid "Public stream"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:671
+msgid "Hide my connections from viewers of this profile"
+msgstr "Skjul mine forbindelser fra besøkende som ser denne profilen"
-#: ../../Zotlabs/Widget/Notifications.php:174
-msgid "Sorry, you have got no notifications at the moment"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:684
+msgid "Publish my default profile in the network directory"
+msgstr "La standardprofilen min vises i nettverkskatalogen"
-#: ../../Zotlabs/Widget/Tokens.php:41
-msgid "Add new guest"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:692
+msgid "Suggest me as a potential contact to new members"
+msgstr "Foreslå meg som mulig kontakt til nye medlemmer"
-#: ../../Zotlabs/Widget/Permcats.php:43
-msgid "Add new role"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:696
+msgid "Reveal my online status"
+msgstr "La andre se om jeg er pålogget eller ikke"
-#: ../../Zotlabs/Widget/Permcats.php:93
-msgid "Role members"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:737
+msgid "Edit Profile Details"
+msgstr "Endre profildetaljer"
-#: ../../Zotlabs/Widget/Newmember.php:38
-msgid "Profile Creation"
-msgstr "Oppretting av profil"
+#: ../../Zotlabs/Module/Profiles.php:739
+msgid "View this profile"
+msgstr "Vis denne profilen"
-#: ../../Zotlabs/Widget/Newmember.php:40
-msgid "Upload profile photo"
-msgstr "Last opp profilbilde"
+#: ../../Zotlabs/Module/Profiles.php:741
+msgid "Profile Tools"
+msgstr "Profilverktøy"
-#: ../../Zotlabs/Widget/Newmember.php:41
-msgid "Upload cover photo"
-msgstr "Last opp omslagsbilde"
+#: ../../Zotlabs/Module/Profiles.php:742
+msgid "Change cover photo"
+msgstr "Endre omslagsbilde"
-#: ../../Zotlabs/Widget/Newmember.php:45
-msgid "Find and Connect with others"
-msgstr "Finn andre"
+#: ../../Zotlabs/Module/Profiles.php:744
+msgid "Create a new profile using these settings"
+msgstr "Lag en ny profil ved å bruke disse innstillingene"
-#: ../../Zotlabs/Widget/Newmember.php:47
-msgid "View the directory"
-msgstr "Se i katalogen"
+#: ../../Zotlabs/Module/Profiles.php:745
+msgid "Clone this profile"
+msgstr "Klon denne profilen"
-#: ../../Zotlabs/Widget/Newmember.php:49
-msgid "Manage your connections"
-msgstr "Behandle forbindelser"
+#: ../../Zotlabs/Module/Profiles.php:746
+msgid "Delete this profile"
+msgstr "Slett denne profilen"
-#: ../../Zotlabs/Widget/Newmember.php:52
-msgid "Communicate"
-msgstr "Kommuniser"
+#: ../../Zotlabs/Module/Profiles.php:747
+msgid "Add profile things"
+msgstr "Legg til profilting"
-#: ../../Zotlabs/Widget/Newmember.php:54
-msgid "View your channel homepage"
-msgstr "Vis kanalens hjemmeside"
+#: ../../Zotlabs/Module/Profiles.php:748
+msgid "Basic"
+msgstr "Grunnleggende"
-#: ../../Zotlabs/Widget/Newmember.php:55
-msgid "View your network stream"
-msgstr "Vis nettverksstrømmen"
+#: ../../Zotlabs/Module/Profiles.php:750
+msgid "Relationship"
+msgstr "Forhold"
-#: ../../Zotlabs/Widget/Newmember.php:61
-msgid "Documentation"
-msgstr "Dokumentasjon"
+#: ../../Zotlabs/Module/Profiles.php:753
+msgid "Import profile from file"
+msgstr "Importer profil fra fil"
-#: ../../Zotlabs/Widget/Newmember.php:64
-msgid "Missing Features?"
-msgstr "Noe som mangler?"
+#: ../../Zotlabs/Module/Profiles.php:754
+msgid "Export profile to file"
+msgstr "Eksporter profil til fil"
-#: ../../Zotlabs/Widget/Newmember.php:66
-msgid "Pin apps to navigation bar"
-msgstr "Fest apper til navigasjonslinjen"
+#: ../../Zotlabs/Module/Profiles.php:755
+msgid "Your gender"
+msgstr "Kjønn"
-#: ../../Zotlabs/Widget/Newmember.php:67
-msgid "Install more apps"
-msgstr "Legg til flere apper"
+#: ../../Zotlabs/Module/Profiles.php:756
+msgid "Marital status"
+msgstr "Sivilstatus"
-#: ../../Zotlabs/Widget/Newmember.php:78
-msgid "View public stream"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:757
+msgid "Sexual preference"
+msgstr "Legning"
-#: ../../Zotlabs/Widget/Follow.php:34
-msgid "Add New Connection"
-msgstr "Legg til ny forbindelse"
+#: ../../Zotlabs/Module/Profiles.php:760
+msgid "Profile name"
+msgstr "Profilnavn"
-#: ../../Zotlabs/Widget/Follow.php:35
-msgid "Enter channel address"
-msgstr "Skriv kanaladressen"
+#: ../../Zotlabs/Module/Profiles.php:762
+msgid "This is your default profile."
+msgstr "Dette er din standardprofil."
-#: ../../Zotlabs/Widget/Follow.php:36
-msgid "Examples: bob@example.com, https://example.com/barbara"
-msgstr "Eksempel: ola@eksempel.no, https://eksempel.no/kari"
+#: ../../Zotlabs/Module/Profiles.php:764
+msgid "Your full name"
+msgstr "Fullt navn"
-#: ../../Zotlabs/Widget/Admin.php:28 ../../Zotlabs/Widget/Admin.php:66
-msgid "Member registrations waiting for confirmation"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:765
+msgid "Short title/description"
+msgstr "Kort tittel/beskrivelse"
-#: ../../Zotlabs/Widget/Admin.php:34
-msgid "Inspect queue"
-msgstr "Inspiser kø"
+#: ../../Zotlabs/Module/Profiles.php:765
+msgid "Maximal 190 characters"
+msgstr "Maksimum 190 tegn"
-#: ../../Zotlabs/Widget/Admin.php:35
-msgid "Queueworker"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:768
+msgid "Street address"
+msgstr "Gateadresse"
-#: ../../Zotlabs/Widget/Admin.php:37
-msgid "DB updates"
-msgstr "Databaseoppdateringer"
+#: ../../Zotlabs/Module/Profiles.php:769
+msgid "Locality/City"
+msgstr "Sted/by"
-#: ../../Zotlabs/Widget/Admin.php:62
-msgid "Addon Features"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:770
+msgid "Region/State"
+msgstr "Region"
-#: ../../Zotlabs/Widget/Chatroom_list.php:26
-msgid "Overview"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:771
+msgid "Postal/Zip code"
+msgstr "Postnummer"
-#: ../../Zotlabs/Widget/Cdav.php:41
-msgid "Select Channel"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:777
+msgid "Who (if applicable)"
+msgstr "Hvem (om relevant)"
-#: ../../Zotlabs/Widget/Cdav.php:46
-msgid "Read-write"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:777
+msgid "Examples: cathy123, Cathy Williams, cathy@example.com"
+msgstr "Eksempler: kari123, Kari Villiamsen, kari@example.com"
-#: ../../Zotlabs/Widget/Cdav.php:47
-msgid "Read-only"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:778
+msgid "Since (date)"
+msgstr "Fra (dato)"
-#: ../../Zotlabs/Widget/Cdav.php:133
-msgid "Channel Calendar"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:781
+msgid "Tell us about yourself"
+msgstr "Fortell oss om deg selv"
-#: ../../Zotlabs/Widget/Cdav.php:137
-msgid "Shared CalDAV Calendars"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:783
+msgid "Hometown"
+msgstr "Hjemsted"
-#: ../../Zotlabs/Widget/Cdav.php:141
-msgid "Share this calendar"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:784
+msgid "Political views"
+msgstr "Politiske holdninger"
-#: ../../Zotlabs/Widget/Cdav.php:143
-msgid "Calendar name and color"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:785
+msgid "Religious views"
+msgstr "Religiøse holdninger"
-#: ../../Zotlabs/Widget/Cdav.php:145
-msgid "Create new CalDAV calendar"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:786
+msgid "Keywords used in directory listings"
+msgstr "Nøkkelord for bruk i katalogoppføringen"
-#: ../../Zotlabs/Widget/Cdav.php:147
-msgid "Calendar Name"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:786
+msgid "Example: fishing photography software"
+msgstr "Eksempel: fisking fotografering programvare"
-#: ../../Zotlabs/Widget/Cdav.php:148
-msgid "Calendar Tools"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:789
+msgid "Musical interests"
+msgstr "Musikkinteresser"
-#: ../../Zotlabs/Widget/Cdav.php:150
-msgid "Import calendar"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:790
+msgid "Books, literature"
+msgstr "Bøker, litteratur"
-#: ../../Zotlabs/Widget/Cdav.php:151
-msgid "Select a calendar to import to"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:791
+msgid "Television"
+msgstr "TV/fjernsyn"
-#: ../../Zotlabs/Widget/Cdav.php:178
-msgid "Addressbooks"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:792
+msgid "Film/Dance/Culture/Entertainment"
+msgstr "Film/dans/kultur/underholdning"
-#: ../../Zotlabs/Widget/Cdav.php:180
-msgid "Addressbook name"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:793
+msgid "Hobbies/Interests"
+msgstr "Hobbier/Interesser"
-#: ../../Zotlabs/Widget/Cdav.php:182
-msgid "Create new addressbook"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:794
+msgid "Love/Romance"
+msgstr "Kjærlighet/romantikk"
-#: ../../Zotlabs/Widget/Cdav.php:183
-msgid "Addressbook Name"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:796
+msgid "School/Education"
+msgstr "Skolle/utdanning"
-#: ../../Zotlabs/Widget/Cdav.php:185
-msgid "Addressbook Tools"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:797
+msgid "Contact information and social networks"
+msgstr "Kontaktinformasjon og andre sosiale nettverk"
-#: ../../Zotlabs/Widget/Cdav.php:186
-msgid "Import addressbook"
-msgstr ""
+#: ../../Zotlabs/Module/Profiles.php:798
+msgid "My other channels"
+msgstr "Mine andre kanaler"
-#: ../../Zotlabs/Widget/Cdav.php:187
-msgid "Select an addressbook to import to"
-msgstr ""
+#: ../../Zotlabs/Module/Sharedwithme.php:106
+msgid "Files: shared with me"
+msgstr "Filer: delt med meg"
-#: ../../Zotlabs/Widget/Activity_order.php:96
-msgid "Commented Date"
-msgstr "Sist kommentert"
+#: ../../Zotlabs/Module/Sharedwithme.php:108
+msgid "NEW"
+msgstr "NY"
-#: ../../Zotlabs/Widget/Activity_order.php:100
-msgid "Order by last commented date"
-msgstr "Sorter etter dato for siste kommentar"
+#: ../../Zotlabs/Module/Sharedwithme.php:111
+msgid "Remove all files"
+msgstr "Fjern alle filer"
-#: ../../Zotlabs/Widget/Activity_order.php:103
-msgid "Posted Date"
-msgstr "Innleggsdato"
+#: ../../Zotlabs/Module/Sharedwithme.php:112
+msgid "Remove this file"
+msgstr "Fjern denne filen"
-#: ../../Zotlabs/Widget/Activity_order.php:107
-msgid "Order by last posted date"
-msgstr "Sorter etter dato innlegg ble postet"
+#: ../../Zotlabs/Module/Rbmark.php:72
+msgid "Select a bookmark folder"
+msgstr "Velg en bokmerkemappe"
-#: ../../Zotlabs/Widget/Activity_order.php:110
-msgid "Date Unthreaded"
-msgstr "Utrådet"
+#: ../../Zotlabs/Module/Rbmark.php:80
+msgid "Save Bookmark"
+msgstr "Lagre bokmerke"
-#: ../../Zotlabs/Widget/Activity_order.php:114
-msgid "Order unthreaded by date"
-msgstr "Sorter innlegg og kommentarer uavhengig av hverandre etter dato"
+#: ../../Zotlabs/Module/Rbmark.php:81
+msgid "URL of bookmark"
+msgstr "URL-en til bokmerket"
-#: ../../Zotlabs/Widget/Activity_order.php:129
-msgid "Stream Order"
-msgstr "Sortering av innlegg"
+#: ../../Zotlabs/Module/Rbmark.php:86
+msgid "Or enter new bookmark folder name"
+msgstr "Eller skriv nytt navn på bokmerkemappe"
-#: ../../Zotlabs/Widget/Chatroom_members.php:17
-msgid "Chat Members"
+#: ../../Zotlabs/Module/Lockview.php:101
+msgid "Remote privacy information not available"
msgstr ""
-#: ../../util/nconfig.php:34
-msgid "Source channel not found."
-msgstr "Fant ikke kildekanalen."
+#: ../../Zotlabs/Module/Lockview.php:144 ../../Zotlabs/Module/Lockview.php:203
+#: ../../Zotlabs/Module/Acl.php:124
+msgctxt "acl"
+msgid "Profile"
+msgstr "Profil"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:123
-msgid "generic profile image"
-msgstr ""
+#: ../../Zotlabs/Module/Lockview.php:155 ../../Zotlabs/Module/Lockview.php:212
+#, fuzzy
+msgid "Privacy group"
+msgstr "Personverngruppe:"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:124
-msgid "random geometric pattern"
+#: ../../Zotlabs/Module/Lockview.php:183
+msgid "Item"
msgstr ""
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:125
-msgid "monster face"
+#: ../../Zotlabs/Module/Lockview.php:230
+#, php-format
+msgid "Click to copy link to this ressource for guest %s to clipboard"
msgstr ""
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:126
-msgid "computer generated face"
+#: ../../Zotlabs/Module/Lockview.php:230
+msgid "Link copied"
msgstr ""
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:127
-msgid "retro arcade style face"
-msgstr ""
+#: ../../Zotlabs/Module/Lockview.php:235
+msgid "Access"
+msgstr "Tilgang"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:128
-msgid "Hub default profile photo"
+#: ../../Zotlabs/Module/Lockview.php:237
+msgid "OCAP access"
msgstr ""
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:143
-msgid "Information"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:62
+msgid "Event can not end before it has started."
+msgstr "Hendelsen kan ikke slutte før den starter."
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:143
-msgid ""
-"Libravatar addon is installed, too. Please disable Libravatar addon or this "
-"Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if "
-"nothing was found at Libravatar."
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:64
+#: ../../Zotlabs/Module/Channel_calendar.php:72
+#: ../../Zotlabs/Module/Channel_calendar.php:87
+msgid "Unable to generate preview."
+msgstr "Klarer ikke å lage forhåndsvisning."
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:150
-#: ../../extend/addon/hzaddons/xmpp/xmpp.php:43
-#: ../../extend/addon/hzaddons/msgfooter/msgfooter.php:46
-msgid "Save Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:70
+msgid "Event title and start time are required."
+msgstr "Hendelsestittel og starttidspunkt er påkrevd."
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:151
-msgid "Default avatar image"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:85
+#: ../../Zotlabs/Module/Channel_calendar.php:214
+msgid "Event not found."
+msgstr "Hendelsen ble ikke funnet."
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:151
-msgid "Select default avatar image if none was found at Gravatar. See README"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:358
+msgid "Edit event"
+msgstr "Endre hendelse"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:152
-msgid "Rating of images"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:360
+msgid "Delete event"
+msgstr "Slett hendelse"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:152
-msgid "Select the appropriate avatar rating for your site. See README"
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:394
+msgid "calendar"
+msgstr "kalender"
-#: ../../extend/addon/hzaddons/gravatar/gravatar.php:165
-msgid "Gravatar settings updated."
-msgstr ""
+#: ../../Zotlabs/Module/Channel_calendar.php:485
+msgid "Failed to remove event"
+msgstr "Mislyktes med å slette hendelse"
-#: ../../extend/addon/hzaddons/sendzid/Mod_Sendzid.php:21
-msgid "Send your identity to all websites"
+#: ../../Zotlabs/Module/Go.php:22
+msgid "This page is available only to site members"
msgstr ""
-#: ../../extend/addon/hzaddons/sendzid/Mod_Sendzid.php:29
-msgid "Send ZID"
+#: ../../Zotlabs/Module/Go.php:30
+msgid "What would you like to do?"
msgstr ""
-#: ../../extend/addon/hzaddons/ldapauth/ldapauth.php:101
-msgid "An account has been created for you."
+#: ../../Zotlabs/Module/Go.php:32
+msgid ""
+"Please bookmark this page if you would like to return to it in the future"
msgstr ""
-#: ../../extend/addon/hzaddons/ldapauth/ldapauth.php:108
-msgid "Authentication successful but rejected: account creation is disabled."
+#: ../../Zotlabs/Module/Go.php:36
+msgid "Upload a profile photo"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:259
-msgid "DB Cleanup Failure"
+#: ../../Zotlabs/Module/Go.php:37
+msgid "Upload a cover photo"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:692
-msgid "[cart] Item Added"
+#: ../../Zotlabs/Module/Go.php:38
+msgid "Edit your default profile"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1107
-msgid "Order already checked out."
+#: ../../Zotlabs/Module/Go.php:40
+msgid "View the channel directory"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1416
-msgid "Drop database tables when uninstalling."
+#: ../../Zotlabs/Module/Go.php:41
+msgid "View/edit your channel settings"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1423
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:129
-msgid "Cart Settings"
+#: ../../Zotlabs/Module/Go.php:42
+msgid "View the site or project documentation"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1435
-#: ../../extend/addon/hzaddons/cart/cart.php:1438
-msgid "Shop"
+#: ../../Zotlabs/Module/Go.php:43
+msgid "Visit your channel homepage"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1458
-#: ../../extend/addon/hzaddons/faces/Mod_Faces.php:64
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:36
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:52
-msgid "Profile Unavailable."
-msgstr "Profilen er ikke tilgjengelig."
-
-#: ../../extend/addon/hzaddons/cart/cart.php:1494
-#: ../../extend/addon/hzaddons/cart/myshop.php:113
-msgid "Order Not Found"
+#: ../../Zotlabs/Module/Go.php:44
+msgid ""
+"View your connections and/or add somebody whose address you already know"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1598
-msgid "You must be logged into the Grid to shop."
+#: ../../Zotlabs/Module/Go.php:45
+msgid ""
+"View your personal stream (this may be empty until you add some connections)"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1609
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:486
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:456
-#: ../../extend/addon/hzaddons/cart/myshop.php:37
-#: ../../extend/addon/hzaddons/cart/manual_payments.php:93
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:81
-msgid "Invalid channel"
-msgstr "Ugyldig kanal"
-
-#: ../../extend/addon/hzaddons/cart/cart.php:1631
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:417
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:392
-#: ../../extend/addon/hzaddons/cart/manual_payments.php:68
-msgid "Order not found."
+#: ../../Zotlabs/Module/Go.php:53
+msgid "View the public stream. Warning: this content is not moderated"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/cart.php:1647
-#, fuzzy
-msgid "Access denied."
-msgstr "Ingen tilgang"
+#: ../../Zotlabs/Module/Chat.php:193
+msgid "Room not found"
+msgstr "Rommet ble ikke funnet"
-#: ../../extend/addon/hzaddons/cart/cart.php:1699
-#: ../../extend/addon/hzaddons/cart/cart.php:1842
-msgid "No Order Found"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:209
+msgid "Leave Room"
+msgstr "Forlat rom"
-#: ../../extend/addon/hzaddons/cart/cart.php:1708
-msgid "An unknown error has occurred Please start again."
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:210
+msgid "Delete Room"
+msgstr "Slett rom"
-#: ../../extend/addon/hzaddons/cart/cart.php:1851
-msgid "Requirements not met."
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:211
+msgid "I am away right now"
+msgstr "Jeg er borte akkurat nå"
-#: ../../extend/addon/hzaddons/cart/cart.php:1851
-msgid "Review your order and complete any needed requirements."
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:212
+msgid "I am online"
+msgstr "Jeg er pålogget"
-#: ../../extend/addon/hzaddons/cart/cart.php:1877
-msgid "Invalid Payment Type. Please start again."
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:214
+msgid "Bookmark this room"
+msgstr "Bokmerk dette rommet"
-#: ../../extend/addon/hzaddons/cart/cart.php:1884
-msgid "Order not found"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:237
+msgid "New Chatroom"
+msgstr "Nytt chatrom"
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:70
-msgid "Enable Order/Item Options"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:238
+msgid "Chatroom name"
+msgstr "Romnavn"
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:333
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:357
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:433
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:457
-msgid "Label"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:239
+msgid "Expiration of chats (minutes)"
+msgstr "Chat utgår (antall minutter)"
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:336
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:360
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:436
-#: ../../extend/addon/hzaddons/cart/submodules/orderoptions.php:460
-msgid "Instructions"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:255
+#, php-format
+msgid "%1$s's Chatrooms"
+msgstr "%1$s sine chatrom"
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:151
-msgid "Enable Subscription Management Module"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:260
+msgid "No chatrooms available"
+msgstr "Ingen rom tilgjengelige"
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:223
-msgid ""
-"Cannot include subscription items with different terms in the same order."
+#: ../../Zotlabs/Module/Chat.php:261
+msgid "Add Room"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:372
-msgid "Select Subscription to Edit"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:264
+msgid "Expiration"
+msgstr "Utløp"
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:380
-msgid "Edit Subscriptions"
-msgstr ""
+#: ../../Zotlabs/Module/Chat.php:265
+msgid "min"
+msgstr "min"
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:414
-msgid "Subscription SKU"
+#: ../../Zotlabs/Module/Regate.php:85
+msgid "Email resent"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:419
-msgid "Catalog Description"
+#: ../../Zotlabs/Module/Regate.php:85
+msgid "Email resend failed"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:423
-msgid "Subscription available for purchase."
+#: ../../Zotlabs/Module/Regate.php:110
+msgid "Verification successful"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:428
-msgid "Maximum active subscriptions to this item per account."
+#: ../../Zotlabs/Module/Regate.php:154
+msgid "Account successfull created"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:431
-msgid "Subscription price."
+#: ../../Zotlabs/Module/Regate.php:212
+msgid "Channel successfull created"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:435
-msgid "Quantity"
+#: ../../Zotlabs/Module/Regate.php:218
+msgid "Automatic channel creation failed. Please create a channel."
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/subscriptions.php:439
-msgid "Term"
+#: ../../Zotlabs/Module/Regate.php:230
+msgid "Account creation error"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:61
-msgid "Enable Manual Cart Module"
+#: ../../Zotlabs/Module/Regate.php:242
+msgid "Verify failed"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:172
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:164
-msgid "New Sku"
+#: ../../Zotlabs/Module/Regate.php:247
+msgid "Token verification failed"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:208
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:199
-msgid "Cannot save edits to locked item."
+#: ../../Zotlabs/Module/Regate.php:252
+msgid "Request not inside time frame"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:252
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:649
-msgid "Changes Locked"
+#: ../../Zotlabs/Module/Regate.php:258 ../../Zotlabs/Module/Regate.php:288
+msgid "Identity unknown"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:256
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:653
-msgid "Item available for purchase."
+#: ../../Zotlabs/Module/Regate.php:264
+msgid "dId2 mistaken"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:263
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:660
-#: ../../extend/addon/hzaddons/cart/widgets/catalogitem.php:57
-msgid "Price"
+#: ../../Zotlabs/Module/Regate.php:292
+msgid "Your Registration ID"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/manualcat.php:266
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:663
-msgid "Photo URL"
+#: ../../Zotlabs/Module/Regate.php:305 ../../Zotlabs/Module/Regate.php:397
+#: ../../Zotlabs/Module/Regate.php:429
+msgid "Registration verification"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:86
-msgid "Enable Paypal Button Module (API-v2)"
+#: ../../Zotlabs/Module/Regate.php:312 ../../Zotlabs/Module/Regate.php:434
+msgid "Hold on, you can start verification in"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:96
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:93
-msgid "Use Production Key"
+#: ../../Zotlabs/Module/Regate.php:313
+msgid "Please remember your verification token for ID"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:103
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:100
-msgid "Paypal Sandbox Client Key"
+#: ../../Zotlabs/Module/Regate.php:315
+msgid "Token validity"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:110
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:107
-msgid "Paypal Sandbox Secret Key"
+#: ../../Zotlabs/Module/Regate.php:351
+msgid "Resend email"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:116
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:113
-msgid "Paypal Production Client Key"
+#: ../../Zotlabs/Module/Regate.php:356
+msgid "Registration status"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:123
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:120
-msgid "Paypal Production Secret Key"
+#: ../../Zotlabs/Module/Regate.php:359
+msgid "Verification successful!"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:271
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:252
-msgid "Paypal button payments are not enabled."
+#: ../../Zotlabs/Module/Regate.php:360
+msgid "Your login ID is"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbuttonV2.php:289
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:270
+#: ../../Zotlabs/Module/Regate.php:361
msgid ""
-"Paypal button payments are not properly configured. Please choose another "
-"payment option."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/paypalbutton.php:85
-msgid "Enable Paypal Button Module"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:65
-msgid "Enable Hubzilla Services Module"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:248
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:335
-msgid "SKU not found."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:301
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:305
-msgid "Invalid Activation Directive."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:376
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:380
-msgid "Invalid Deactivation Directive."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:566
-msgid "Add to this privacy group"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:582
-msgid "Set user service class"
+"After your account has been approved by our administrator you will be able "
+"to login with your login ID and your provided password."
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:609
-msgid "You must be using a local account to purchase this service."
+#: ../../Zotlabs/Module/Regate.php:373
+msgid "Registration request revoked"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:667
-msgid "Add buyer to privacy group"
+#: ../../Zotlabs/Module/Regate.php:374
+msgid "Sorry for any inconvience. Thank you for your response."
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:672
-msgid "Add buyer as connection"
+#: ../../Zotlabs/Module/Regate.php:398
+msgid "Please enter your verification token for ID"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:680
-#: ../../extend/addon/hzaddons/cart/submodules/hzservices.php:722
-msgid "Set Service Class"
+#: ../../Zotlabs/Module/Regate.php:399 ../../Zotlabs/Module/Regate.php:426
+msgid "Please check your email!"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/myshop.php:30
-#, fuzzy
-msgid "Access Denied."
-msgstr "Ingen tilgang"
-
-#: ../../extend/addon/hzaddons/cart/myshop.php:145
-#: ../../extend/addon/hzaddons/cart/myshop.php:181
-#: ../../extend/addon/hzaddons/cart/myshop.php:215
-#: ../../extend/addon/hzaddons/cart/myshop.php:265
-#: ../../extend/addon/hzaddons/cart/myshop.php:300
-#: ../../extend/addon/hzaddons/cart/myshop.php:323
-msgid "Access Denied"
-msgstr "Ingen tilgang"
-
-#: ../../extend/addon/hzaddons/cart/myshop.php:190
-#: ../../extend/addon/hzaddons/cart/myshop.php:224
-#: ../../extend/addon/hzaddons/cart/myshop.php:275
-#: ../../extend/addon/hzaddons/cart/myshop.php:333
-#, fuzzy
-msgid "Invalid Item"
-msgstr "Ugyldig element."
-
-#: ../../extend/addon/hzaddons/cart/manual_payments.php:7
-msgid "Error: order mismatch. Please try again."
+#: ../../Zotlabs/Module/Regate.php:409
+msgid "Verification token"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/manual_payments.php:61
-msgid "Manual payments are not enabled."
+#: ../../Zotlabs/Module/Regate.php:420
+msgid "ID expired"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/manual_payments.php:77
-msgid "Finished"
+#: ../../Zotlabs/Module/Regate.php:435
+msgid "You will require the verification token for ID"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:58
-msgid "Enable Test Catalog"
+#: ../../Zotlabs/Module/Regate.php:444
+msgid "Unknown or expired ID"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:70
-msgid "Enable Manual Payments"
+#: ../../Zotlabs/Module/Regate.php:455
+msgid "dId2 malformed"
msgstr ""
-#: ../../extend/addon/hzaddons/cart/Settings/Cart.php:90
-msgid "Base Merchant Currency"
-msgstr ""
+#: ../../Zotlabs/Module/Editlayout.php:137
+msgid "Edit Layout"
+msgstr "Endre layout"
-#: ../../extend/addon/hzaddons/articles/Mod_Article_edit.php:127
-msgid "Edit Article"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:180
+msgid "$Projectname Server - Setup"
+msgstr "$Projectname-tjener - oppsett"
-#: ../../extend/addon/hzaddons/articles/articles.php:51
-msgid "View Articles"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:184
+msgid "Could not connect to database."
+msgstr "Fikk ikke kontakt med databasen."
-#: ../../extend/addon/hzaddons/articles/Mod_Articles.php:119
-msgid "Add Article"
+#: ../../Zotlabs/Module/Setup.php:188
+msgid ""
+"Could not connect to specified site URL. Possible SSL certificate or DNS "
+"issue."
msgstr ""
+"Fikk ikke kontakt med det angitte nettstedets URL. Problemet kan muligens "
+"skyldes SSL-sertifikatet eller DNS."
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:81
-msgid "Hubzilla File Storage Import"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:195
+msgid "Could not create table."
+msgstr "Kunne ikke lage tabellen."
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:82
-msgid "This will import all your cloud files from another server."
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:201
+msgid "Your site database has been installed."
+msgstr "Databasen til ditt nettsted har blitt installert."
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:83
-msgid "Hubzilla Server base URL"
+#: ../../Zotlabs/Module/Setup.php:207
+msgid ""
+"You may need to import the file \"install/schema_xxx.sql\" manually using a "
+"database client."
msgstr ""
+"Du må kanskje importere filen \"install/schmea_xxx.sql\" manuelt ved å bruke "
+"en databaseklient."
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:84
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:138
-msgid "Since modified date yyyy-mm-dd"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:208 ../../Zotlabs/Module/Setup.php:277
+#: ../../Zotlabs/Module/Setup.php:802
+msgid "Please see the file \"install/INSTALL.txt\"."
+msgstr "Vennligst les filen \"install/INSTALL.txt\"."
-#: ../../extend/addon/hzaddons/hzfiles/hzfiles.php:85
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:139
-msgid "Until modified date yyyy-mm-dd"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:274
+msgid "System check"
+msgstr "Systemsjekk"
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:125
-msgid "View Larger"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:279
+msgid "Check again"
+msgstr "Sjekk igjen"
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:148
-msgid "Tile Server URL"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:300
+msgid "Database connection"
+msgstr "Databaseforbindelse"
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:148
+#: ../../Zotlabs/Module/Setup.php:301
msgid ""
-"A list of <a href=\"http://wiki.openstreetmap.org/wiki/TMS\" "
-"target=\"_blank\">public tile servers</a>"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:149
-msgid "Nominatim (reverse geocoding) Server URL"
+"In order to install $Projectname we need to know how to connect to your "
+"database."
msgstr ""
+"For å installere $Projectname må du oppgi hvordan din database kan kontaktes."
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:149
+#: ../../Zotlabs/Module/Setup.php:302
msgid ""
-"A list of <a href=\"http://wiki.openstreetmap.org/wiki/Nominatim\" "
-"target=\"_blank\">Nominatim servers</a>"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:150
-msgid "Default zoom"
+"Please contact your hosting provider or site administrator if you have "
+"questions about these settings."
msgstr ""
+"Vennligst kontakt din nettstedstilbyder eller nettstedsadministrator hvis du "
+"har spørsmål om disse innstillingene."
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:150
+#: ../../Zotlabs/Module/Setup.php:303
msgid ""
-"The default zoom level. (1:world, 18:highest, also depends on tile server)"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:151
-msgid "Include marker on map"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openstreetmap/openstreetmap.php:151
-msgid "Include a marker on the map."
+"The database you specify below should already exist. If it does not, please "
+"create it before continuing."
msgstr ""
+"Databasen du oppgir nedenfor må finnes på forhånd. Hvis den ikke finnes, "
+"vennligst lag den før du fortsetter."
-#: ../../extend/addon/hzaddons/hsse/Mod_Hsse.php:15
-msgid "WYSIWYG status editor"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:307
+msgid "Database Server Name"
+msgstr "Navn på databasetjener"
-#: ../../extend/addon/hzaddons/hsse/Mod_Hsse.php:24
-msgid "WYSIWYG Status App"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:307
+msgid "Default is 127.0.0.1"
+msgstr "Standard er 127.0.0.1"
-#: ../../extend/addon/hzaddons/hsse/Mod_Hsse.php:32
-msgid "WYSIWYG Status"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:308
+msgid "Database Port"
+msgstr "Databaseport"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:106
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:179
-msgid "Network error"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:308
+msgid "Communication port number - use 0 for default"
+msgstr "Kommunikasjonsportnummer - bruk 0 for standard"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:110
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:183
-msgid "API error"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:309
+msgid "Database Login Name"
+msgstr "Database innloggingsnavn"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:114
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:187
-msgid "Unknown issue"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:310
+msgid "Database Login Password"
+msgstr "Database innloggingspassord"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:152
-msgid "Unable to retrieve email address from remote identity provider"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:311
+msgid "Database Name"
+msgstr "Databasenavn"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:171
-msgid "Unable to login using email address "
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:312
+msgid "Database Type"
+msgstr "Databasetype"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:211
-msgid "Social Authentication using your social media account"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:314 ../../Zotlabs/Module/Setup.php:354
+msgid "Site administrator email address"
+msgstr "E-postadressen til administrator ved nettstedet"
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:215
+#: ../../Zotlabs/Module/Setup.php:314 ../../Zotlabs/Module/Setup.php:354
msgid ""
-"This app enables one or more social provider sign-in buttons on the login "
-"page."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:233
-msgid "Add an identity provider"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:260
-#, fuzzy
-msgid "Enable "
-msgstr "Skru på"
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:267
-msgid "Key"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:267
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:272
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:288
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:299
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:308
-msgid "Word"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:272
-msgid "Secret"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:308
-msgid "Add a custom provider"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:328
-msgid "Remove an identity provider"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:338
-msgid "Social authentication"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:380
-msgid "Error while saving provider settings"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:403
-msgid "Custom provider already exists"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/socialauth/Mod_SocialAuth.php:420
-msgid "Social authentication settings saved."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/pumpio.php:152
-msgid "You are now authenticated to pumpio."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/pumpio.php:153
-msgid "return to the featured settings page"
-msgstr "tilbake til funksjonsinnstillinger"
-
-#: ../../extend/addon/hzaddons/pumpio/pumpio.php:168
-msgid "Post to Pump.io"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:40
-msgid "Pump.io Settings saved."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:71
-msgid "Pump.io servername"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:71
-msgid "Without \"http://\" or \"https://\""
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:75
-msgid "Pump.io username"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:75
-msgid "Without the servername"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:86
-msgid "You are not authenticated to pumpio"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:88
-msgid "(Re-)Authenticate your pump.io connection"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:92
-msgid "Post to pump.io by default"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:96
-msgid "Should posts be public"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:100
-msgid "Mirror all public posts"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pumpio/Mod_Pumpio.php:110
-msgid "Pump.io Crosspost Connector"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/superblock/superblock.php:355
-msgid "Block Completely"
-msgstr "Blokker helt"
-
-#: ../../extend/addon/hzaddons/superblock/Mod_Superblock.php:62
-msgid "superblock settings updated"
-msgstr "innstillingene til superblokk ble oppdatert"
-
-#: ../../extend/addon/hzaddons/superblock/Mod_Superblock.php:86
-msgid "Currently blocked"
-msgstr "Blokkert for øyeblikket"
-
-#: ../../extend/addon/hzaddons/superblock/Mod_Superblock.php:88
-msgid "No channels currently blocked"
-msgstr "Ingen kanaler er blokkert i øyeblikket"
-
-#: ../../extend/addon/hzaddons/gallery/gallery.php:43
-#: ../../extend/addon/hzaddons/gallery/Mod_Gallery.php:135
-msgid "Gallery"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/gallery/gallery.php:46
-msgid "Photo Gallery"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/randpost/randpost.php:99
-msgid "You're welcome."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/randpost/randpost.php:100
-msgid "Ah shucks..."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/randpost/randpost.php:101
-msgid "Don't mention it."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/randpost/randpost.php:102
-msgid "&lt;blush&gt;"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:51
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:129
-msgid "System defaults:"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:55
-msgid "Preferred Clipart IDs"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:55
-msgid "List of preferred clipart ids. These will be shown first."
+"Your account email address must match this in order to use the web admin "
+"panel."
msgstr ""
+"Din konto sin e-postadresse må være lik denne for å kunne bruke web-"
+"administrasjonspanelet."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:56
-msgid "Default Search Term"
-msgstr "Standard søkeord"
-
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:56
-msgid "The default search term. These will be shown second."
-msgstr "Standard søkeord. Disse vil vises som nummer to."
+#: ../../Zotlabs/Module/Setup.php:315 ../../Zotlabs/Module/Setup.php:356
+msgid "Website URL"
+msgstr "Nettstedets URL"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:57
-msgid "Return After"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:315 ../../Zotlabs/Module/Setup.php:356
+msgid "Please use SSL (https) URL if available."
+msgstr "Vennligst bruk SSL (https) URL hvis tilgjengelig."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:57
-msgid "Page to load after image selection."
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:316 ../../Zotlabs/Module/Setup.php:358
+msgid "Please select a default timezone for your website"
+msgstr "Vennligst velg en standard tidssone for ditt nettsted"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:60
-msgid "Profile List"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:343
+msgid "Site settings"
+msgstr "Nettstedets innstillinger"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:62
-msgid "Order of Preferred"
+#: ../../Zotlabs/Module/Setup.php:397
+msgid "PHP version 8.0 or greater is required."
msgstr ""
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:62
-msgid "Sort order of preferred clipart ids."
+#: ../../Zotlabs/Module/Setup.php:398
+msgid "PHP version"
msgstr ""
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:63
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:69
-msgid "Newest first"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:414
+msgid "Could not find a command line version of PHP in the web server PATH."
+msgstr "Fant ikke en kommandolinjeversjon av PHP i webtjenerens sti (PATH)."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:66
-msgid "As entered"
+#: ../../Zotlabs/Module/Setup.php:415
+msgid ""
+"If you don't have a command line version of PHP installed on server, you "
+"will not be able to run background polling via cron."
msgstr ""
+"Hvis du ikke har en kommandolinjeversjon av PHP installert på tjeneren, så "
+"vil du ikke kunne kjøre bakgrunnshenting via cron."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:68
-msgid "Order of other"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:419
+msgid "PHP executable path"
+msgstr "PHP-kjørefilens sti"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:68
-msgid "Sort order of other clipart ids."
+#: ../../Zotlabs/Module/Setup.php:419
+msgid ""
+"Enter full path to php executable. You can leave this blank to continue the "
+"installation."
msgstr ""
+"Skriv full sti til kjørefilen for PHP. Du kan la denne stå blank for å "
+"fortsette installasjonen."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:70
-msgid "Most downloaded first"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:424
+msgid "Command line PHP"
+msgstr "Kommandolinje PHP"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:71
-msgid "Most liked first"
+#: ../../Zotlabs/Module/Setup.php:434
+msgid ""
+"Unable to check command line PHP, as shell_exec() is disabled. This is "
+"required."
msgstr ""
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:73
-msgid "Preferred IDs Message"
+#: ../../Zotlabs/Module/Setup.php:438
+msgid ""
+"The command line version of PHP on your system does not have "
+"\"register_argc_argv\" enabled."
msgstr ""
+"Kommandolinjeversjonen av PHP på ditt system har ikke \"register_argc_argv\" "
+"påskrudd."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:73
-msgid "Message to display above preferred results."
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:439
+msgid "This is required for message delivery to work."
+msgstr "Dette er påkrevd for at meldingslevering skal virke."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:79
-msgid "Uploaded by: "
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:442
+msgid "PHP register_argc_argv"
+msgstr "PHP register_argc_argv"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:79
-msgid "Drawn by: "
+#: ../../Zotlabs/Module/Setup.php:462
+msgid ""
+"This is not sufficient to upload larger images or files. You should be able "
+"to upload at least 4 MB at once."
msgstr ""
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:183
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:195
-msgid "Use this image"
+#: ../../Zotlabs/Module/Setup.php:464
+#, php-format
+msgid ""
+"Your max allowed total upload size is set to %s. Maximum size of one file to "
+"upload is set to %s. You are allowed to upload up to %d files at once."
msgstr ""
+"Den største totale opplastingsstørrelsen du er tillatt er satt til %s. "
+"Filstørrelsen på en enkelt fil er satt til å maksimalt være %s. Du har lov "
+"til å laste opp inntil %d filer samtidig."
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:193
-msgid "Or select from a free OpenClipart.org image:"
+#: ../../Zotlabs/Module/Setup.php:470
+msgid "You can adjust these settings in the server php.ini file."
msgstr ""
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:196
-msgid "Search Term"
-msgstr "Søkeord"
+#: ../../Zotlabs/Module/Setup.php:472
+msgid "PHP upload limits"
+msgstr "PHP opplastingsgrenser"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:233
-msgid "Unknown error. Please try again later."
+#: ../../Zotlabs/Module/Setup.php:495
+msgid ""
+"Error: the \"openssl_pkey_new\" function on this system is not able to "
+"generate encryption keys"
msgstr ""
+"Feil: \"openssl_pkey_new\"-funksjonen på dette systemet er ikke i stand til "
+"å lage krypteringsnøkler"
-#: ../../extend/addon/hzaddons/openclipatar/openclipatar.php:309
-msgid "Profile photo updated successfully."
+#: ../../Zotlabs/Module/Setup.php:496
+msgid ""
+"If running under Windows, please see \"http://www.php.net/manual/en/"
+"openssl.installation.php\"."
msgstr ""
+"Ved kjøring på Windows, vennligst se \"http://www.php.net/manual/en/"
+"openssl.installation.php\"."
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:94
-msgid "Hubzilla Directory Stats"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:499
+msgid "Generate encryption keys"
+msgstr "Lag krypteringsnøkler"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:95
-msgid "Total Hubs"
+#: ../../Zotlabs/Module/Setup.php:503
+msgid "Error: the sodium encryption library is not installed."
msgstr ""
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:97
-msgid "Hubzilla Hubs"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:505
+#, fuzzy
+#| msgid "Generate encryption keys"
+msgid "Generate ed25519 encryption keys"
+msgstr "Lag krypteringsnøkler"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:99
-msgid "Friendica Hubs"
+#: ../../Zotlabs/Module/Setup.php:510
+msgid ""
+"Error: one of \"bcmath\" or \"gmp\" (bigmath library) extensions are "
+"required."
msgstr ""
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:101
-msgid "Diaspora Pods"
+#: ../../Zotlabs/Module/Setup.php:512
+msgid "Bigmath library (either bcmath or gmp)"
msgstr ""
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:103
-msgid "Hubzilla Channels"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:529
+msgid "libCurl PHP module"
+msgstr "libCurl PHP-modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:105
-msgid "Friendica Channels"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:530
+msgid "GD graphics PHP module"
+msgstr "GD graphics PHP-modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:107
-msgid "Diaspora Channels"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:531
+msgid "OpenSSL PHP module"
+msgstr "OpenSSL PHP-modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:109
-msgid "Aged 35 and above"
+#: ../../Zotlabs/Module/Setup.php:532
+msgid "PDO database PHP module"
msgstr ""
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:111
-msgid "Aged 34 and under"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:533
+msgid "mb_string PHP module"
+msgstr "mb_string PHP-modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:113
-msgid "Average Age"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:534
+msgid "xml PHP module"
+msgstr "xml PHP modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:115
-msgid "Known Chatrooms"
+#: ../../Zotlabs/Module/Setup.php:535
+msgid "zip PHP module"
msgstr ""
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:117
-msgid "Known Tags"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:536
+#, fuzzy
+#| msgid "xml PHP module"
+msgid "intl PHP module"
+msgstr "xml PHP modul"
-#: ../../extend/addon/hzaddons/dirstats/dirstats.php:119
-msgid ""
-"Please note Diaspora and Friendica statistics are merely those **this "
-"directory** is aware of, and not all those known in the network. This also "
-"applies to chatrooms,"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:540 ../../Zotlabs/Module/Setup.php:542
+msgid "Apache mod_rewrite module"
+msgstr "Apache mod_rewrite-modul"
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:61
+#: ../../Zotlabs/Module/Setup.php:540
msgid ""
-"Please contact your site administrator.<br />The provided API URL is not "
-"valid."
-msgstr ""
-"Den oppgitte URLen for APIet er ikke gyldig. Kontakt serverens administrator."
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:98
-msgid "We could not contact the GNU social API with the Path you entered."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:130
-msgid "GNU social settings updated."
+"Error: Apache webserver mod-rewrite module is required but not installed."
msgstr ""
+"Feil: Apache web-tjenerens mod-rewrite-modul er påkrevd, men ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:179
-msgid "Globally Available GNU social OAuthKeys"
+#: ../../Zotlabs/Module/Setup.php:546 ../../Zotlabs/Module/Setup.php:549
+msgid "exec"
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:181
+#: ../../Zotlabs/Module/Setup.php:546
msgid ""
-"There are preconfigured OAuth key pairs for some GNU social servers "
-"available. If you are using one of them, please use these credentials.<br /"
-">If not feel free to connect to any other GNU social instance (see below)."
+"Error: exec is required but is either not installed or has been disabled in "
+"php.ini"
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:196
-msgid "Provide your own OAuth Credentials"
+#: ../../Zotlabs/Module/Setup.php:552 ../../Zotlabs/Module/Setup.php:555
+msgid "shell_exec"
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:198
+#: ../../Zotlabs/Module/Setup.php:552
msgid ""
-"No consumer key pair for GNU social found. Register your Hubzilla Account as "
-"an desktop client on your GNU social account, copy the consumer key pair "
-"here and enter the API base root.<br />Before you register your own OAuth "
-"key pair ask the administrator if there is already a key pair for this "
-"Hubzilla installation at your favourite GNU social installation."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:202
-msgid "OAuth Consumer Key"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:206
-msgid "OAuth Consumer Secret"
+"Error: shell_exec is required but is either not installed or has been "
+"disabled in php.ini"
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:210
-msgid "Base API Path"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:560
+msgid "Error: libCURL PHP module required but not installed."
+msgstr "Feil: libCURL PHP-modul er påkrevd, men er ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:210
-msgid "Remember the trailing /"
+#: ../../Zotlabs/Module/Setup.php:564
+msgid ""
+"Error: GD PHP module with JPEG support or ImageMagick graphics library "
+"required but not installed."
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:214
-msgid "GNU social application name"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:568
+msgid "Error: openssl PHP module required but not installed."
+msgstr "Feil: openssl PHP-modul er påkrevd, men er ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:237
+#: ../../Zotlabs/Module/Setup.php:574
msgid ""
-"To connect to your GNU social account click the button below to get a "
-"security code from GNU social which you have to copy into the input box "
-"below and submit the form. Only your <strong>public</strong> posts will be "
-"posted to GNU social."
+"Error: PDO database PHP module missing a driver for either mysql or pgsql."
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:239
-msgid "Log in with GNU social"
+#: ../../Zotlabs/Module/Setup.php:579
+msgid "Error: PDO database PHP module required but not installed."
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:242
-msgid "Copy the security code from GNU social here"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:583
+msgid "Error: mb_string PHP module required but not installed."
+msgstr "Feil: mb_string PHP-modul er påkrevd, men er ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:252
-msgid "Cancel Connection Process"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:587
+msgid "Error: xml PHP module required for DAV but not installed."
+msgstr "Feil: XML PHP modul er påkrevet for DAV, men den er ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:254
-msgid "Current GNU social API is"
+#: ../../Zotlabs/Module/Setup.php:591
+msgid "Error: zip PHP module required but not installed."
msgstr ""
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:258
-msgid "Cancel GNU social Connection"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:595
+#, fuzzy
+#| msgid "Error: openssl PHP module required but not installed."
+msgid "Error: intl PHP module required but not installed."
+msgstr "Feil: openssl PHP-modul er påkrevd, men er ikke installert."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:270
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:145
-msgid "Currently connected to: "
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:614 ../../Zotlabs/Module/Setup.php:623
+msgid ".htconfig.php is writable"
+msgstr ".htconfig.php kan skrives til"
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:275
+#: ../../Zotlabs/Module/Setup.php:619
msgid ""
-"<strong>Note</strong>: Due your privacy settings (<em>Hide your profile "
-"details from unknown viewers?</em>) the link potentially included in public "
-"postings relayed to GNU social will lead the visitor to a blank page "
-"informing the visitor that the access to your profile has been restricted."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:280
-msgid "Post to GNU social by default"
+"The web installer needs to be able to create a file called \".htconfig.php\" "
+"in the top folder of your web server and it is unable to do so."
msgstr ""
+"Web-installasjonen må kunne lage en fil kalt \".htconfig.php\" i "
+"toppkatalogen til web-tjeneren din, men dette får den ikke til."
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:280
+#: ../../Zotlabs/Module/Setup.php:620
msgid ""
-"If enabled your public postings will be posted to the associated GNU-social "
-"account by default"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:289
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:169
-msgid "Clear OAuth configuration"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/Mod_Statusnet.php:301
-msgid "GNU-Social Crosspost Connector"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:145
-msgid "Post to GNU social"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:594
-msgid "API URL"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/statusnet/statusnet.php:597
-msgid "Application name"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/ljpost.php:49
-msgid "Post to Livejournal"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/ljpost.php:127
-msgid "Posted by"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/ljpost.php:134
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:67
-#: ../../extend/addon/hzaddons/dwpost/dwpost.php:134
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:381
-#: ../../extend/addon/hzaddons/wppost/wppost.php:175
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:96
-msgid "Source"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:53
-msgid "Livejournal username"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:57
-msgid "Livejournal password"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:61
-msgid "Post to Livejournal by default"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:65
-msgid "Send wall-to-wall posts to Livejournal"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:69
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:63
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:92
-msgid "Add link to original post"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ljpost/Mod_Ljpost.php:77
-msgid "Livejournal Crosspost Connector"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pageheader/Mod_Pageheader.php:22
-msgid "pageheader Settings saved."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pageheader/Mod_Pageheader.php:41
-msgid "Message to display on every page on this server"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pageheader/Mod_Pageheader.php:49
-msgid "Page Header"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/rainbowtag/Mod_Rainbowtag.php:22
-msgid "Rainbow Tag App"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/rainbowtag/Mod_Rainbowtag.php:23
-msgid "Add some colour to tag clouds"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/rainbowtag/Mod_Rainbowtag.php:30
-msgid "Rainbow Tag"
+"This is most often a permission setting, as the web server may not be able "
+"to write files in your folder - even if you can."
msgstr ""
+"Dette er oftest tillatelsesinnstilling, ettersom webtjeneren kanskje kan "
+"skrive til filer i din mappe - selv om du kan."
-#: ../../extend/addon/hzaddons/visage/Mod_Visage.php:23
-msgid "Recent Channel/Profile Viewers"
+#: ../../Zotlabs/Module/Setup.php:621
+msgid "Please see install/INSTALL.txt for additional information."
msgstr ""
-#: ../../extend/addon/hzaddons/visage/Mod_Visage.php:34
-msgid "No entries."
+#: ../../Zotlabs/Module/Setup.php:637
+msgid ""
+"This software uses the Smarty3 template engine to render its web views. "
+"Smarty3 compiles templates to PHP to speed up rendering."
msgstr ""
-#: ../../extend/addon/hzaddons/testdrive/testdrive.php:104
+#: ../../Zotlabs/Module/Setup.php:638
#, php-format
-msgid "Your account on %s will expire in a few days."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/testdrive/testdrive.php:105
-msgid "Your test account is about to expire."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:21
-msgid "nofed Settings saved."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:40
-msgid "Federate posts by default"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nofed/Mod_Nofed.php:48
-msgid "No Federation"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nofed/nofed.php:47
-msgid "Federate"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:51
-msgid "Your Webbie:"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:54
-msgid "Fontsize (px):"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:68
-msgid "Link:"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:70
-msgid "Like us on Hubzilla"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/likebanner/likebanner.php:72
-msgid "Embed:"
+msgid ""
+"In order to store these compiled templates, the web server needs to have "
+"write access to the directory %s under the top level web folder."
msgstr ""
-#: ../../extend/addon/hzaddons/piwik/piwik.php:85
+#: ../../Zotlabs/Module/Setup.php:639 ../../Zotlabs/Module/Setup.php:660
msgid ""
-"This website is tracked using the <a href='http://www.piwik.org'>Piwik</a> "
-"analytics tool."
+"Please ensure that the user that your web server runs as (e.g. www-data) has "
+"write access to this folder."
msgstr ""
+"Vennligst sikre at brukeren som din web-tjeneste kjører som (for eksempel "
+"www-data) har skrivetilgang til denne katalogen."
-#: ../../extend/addon/hzaddons/piwik/piwik.php:88
+#: ../../Zotlabs/Module/Setup.php:640
#, php-format
msgid ""
-"If you do not want that your visits are logged this way you <a href='%s'>can "
-"set a cookie to prevent Piwik from tracking further visits of the site</a> "
-"(opt-out)."
+"Note: as a security measure, you should give the web server write access to "
+"%s only--not the template files (.tpl) that it contains."
msgstr ""
+"Merknad: som et sikkerhetstiltak bør du bare gi webtjerenn skrivetilgang til "
+"%s - ikke til malfilene (.tpl) som den inneholder."
-#: ../../extend/addon/hzaddons/piwik/piwik.php:96
-msgid "Piwik Base URL"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:643
+#, php-format
+msgid "%s is writable"
+msgstr "%s kan skrives til"
-#: ../../extend/addon/hzaddons/piwik/piwik.php:96
+#: ../../Zotlabs/Module/Setup.php:659
msgid ""
-"Absolute path to your Piwik installation. (without protocol (http/s), with "
-"trailing slash)"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/piwik/piwik.php:97
-msgid "Site ID"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/piwik/piwik.php:98
-msgid "Show opt-out cookie link?"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/piwik/piwik.php:99
-msgid "Asynchronous tracking"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/piwik/piwik.php:100
-msgid "Enable frontend JavaScript error tracking"
+"This software uses the store directory to save uploaded files. The web "
+"server needs to have write access to the store directory under the top level "
+"web folder"
msgstr ""
-#: ../../extend/addon/hzaddons/piwik/piwik.php:100
-msgid "This feature requires Piwik >= 2.2.0"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:663
+msgid "store is writable"
+msgstr "lageret kan skrives til"
-#: ../../extend/addon/hzaddons/startpage/Mod_Startpage.php:60
-msgid "Page to load after login"
+#: ../../Zotlabs/Module/Setup.php:695
+msgid ""
+"SSL certificate cannot be validated. Fix certificate or disable https access "
+"to this site."
msgstr ""
+"SSL-sertifikatet kan ikke kontrolleres. Fiks sertifikatet eller skru av "
+"https tilgang til dette nettstedet."
-#: ../../extend/addon/hzaddons/startpage/Mod_Startpage.php:60
+#: ../../Zotlabs/Module/Setup.php:696
msgid ""
-"Examples: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy "
-"collection), &quot;channel&quot; or &quot;notifications/system&quot; (leave "
-"blank for default network page (grid)."
+"If you have https access to your website or allow connections to TCP port "
+"443 (the https: port), you MUST use a browser-valid certificate. You MUST "
+"NOT use self-signed certificates!"
msgstr ""
+"Hvis du har HTTPS-tilgang til ditt nettsted eller tillater forbindelser til "
+"TCP port 443 (HTTPS-porten), så MÅ du bruke nettlesergodkjent sertifkater. "
+"Du MÅ IKKE bruke egensignert sertifikater!"
-#: ../../extend/addon/hzaddons/startpage/Mod_Startpage.php:68
-msgid "Startpage"
+#: ../../Zotlabs/Module/Setup.php:697
+msgid ""
+"This restriction is incorporated because public posts from you may for "
+"example contain references to images on your own hub."
msgstr ""
+"Denne begrensningen er tatt inn fordi offentlige innlegg fra deg kan for "
+"eksempel inneholde referanser til bilder på din egen hub."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:65
-msgid "Twitter settings updated."
+#: ../../Zotlabs/Module/Setup.php:698
+msgid ""
+"If your certificate is not recognized, members of other sites (who may "
+"themselves have valid certificates) will get a warning message on their own "
+"site complaining about security issues."
msgstr ""
+"Hvis sertifikatet ditt ikke gjenkjennes, så vil medlemmer på andre "
+"nettsteder (som selv kan ha godkjente sertifikater) få en beskjed med en "
+"advarsel på deres eget nettsted som klager over sikkerhetsproblemer."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:101
+#: ../../Zotlabs/Module/Setup.php:699
msgid ""
-"No consumer key pair for Twitter found. Please contact your site "
-"administrator."
+"This can cause usability issues elsewhere (not just on your own site) so we "
+"must insist on this requirement."
msgstr ""
+"Dette kan gi problemer med brukervennlighet (ikke bare på ditt eget "
+"nettsted), så vi må insistere på dette kravet."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:123
+#: ../../Zotlabs/Module/Setup.php:700
msgid ""
-"At this Hubzilla instance the Twitter plugin was enabled but you have not "
-"yet connected your account to your Twitter account. To do so click the "
-"button below to get a PIN from Twitter which you have to copy into the input "
-"box below and submit the form. Only your <strong>public</strong> posts will "
-"be posted to Twitter."
+"Providers are available that issue free certificates which are browser-valid."
msgstr ""
+"Det finnes tilbydere som utsteder gratis sertifikater som er gyldige i "
+"nettlesere."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:125
-msgid "Log in with Twitter"
+#: ../../Zotlabs/Module/Setup.php:701
+msgid ""
+"If you are confident that the certificate is valid and signed by a trusted "
+"authority, check to see if you have failed to install an intermediate cert. "
+"These are not normally required by browsers, but are required for server-to-"
+"server communications."
msgstr ""
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:128
-msgid "Copy the PIN from Twitter here"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:703
+msgid "SSL certificate validation"
+msgstr "SSL sertifikat-kontroll"
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:150
+#: ../../Zotlabs/Module/Setup.php:709
msgid ""
-"<strong>Note:</strong> Due your privacy settings (<em>Hide your profile "
-"details from unknown viewers?</em>) the link potentially included in public "
-"postings relayed to Twitter will lead the visitor to a blank page informing "
-"the visitor that the access to your profile has been restricted."
+"Url rewrite in .htaccess is not working. Check your server "
+"configuration.Test: "
msgstr ""
+"URL omskriving (rewrite) i .htaccess virker ikke. Sjekk konfigurasjonen til "
+"tjeneren din. Test: "
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:155
-msgid "Twitter post length"
-msgstr ""
+#: ../../Zotlabs/Module/Setup.php:712
+msgid "Url rewrite is working"
+msgstr "URL rewrite virker"
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:155
-msgid "Maximum tweet length"
+#: ../../Zotlabs/Module/Setup.php:725
+msgid ""
+"The database configuration file \".htconfig.php\" could not be written. "
+"Please use the enclosed text to create a configuration file in your web "
+"server root."
msgstr ""
+"Databasekonfigurasjonsfilen \".htconfig.php\" kunne ikke skrives. Vennligst "
+"bruk den medfølgende teksten for å lage en konfigurasjonsfil i toppkatalogen "
+"av din web-tjener."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:160
-msgid "Send public postings to Twitter by default"
+#: ../../Zotlabs/Module/Setup.php:800
+msgid "<h1>What next?</h1>"
msgstr ""
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:160
+#: ../../Zotlabs/Module/Setup.php:801
msgid ""
-"If enabled your public postings will be posted to the associated Twitter "
-"account by default"
+"IMPORTANT: You will need to [manually] setup a scheduled task for the poller."
msgstr ""
+"VIKTIG: Du må [manuelt] sette opp en automatisert tidfestet oppgave til "
+"bakgrunnshenteren."
-#: ../../extend/addon/hzaddons/twitter/Mod_Twitter.php:179
-msgid "Twitter Crosspost Connector"
+#: ../../Zotlabs/Module/Chanview.php:132
+msgid "toggle full screen mode"
msgstr ""
-#: ../../extend/addon/hzaddons/twitter/twitter.php:109
-msgid "Post to Twitter"
-msgstr ""
+#: ../../Zotlabs/Module/Profperm.php:35 ../../Zotlabs/Module/Profperm.php:66
+msgid "Invalid profile identifier."
+msgstr "Ugyldig profil-identifikator."
-#: ../../extend/addon/hzaddons/twitter/twitter.php:502
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:95
-msgid "Submit Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Profperm.php:114
+msgid "Profile Visibility Editor"
+msgstr "Endre profilsynlighet"
-#: ../../extend/addon/hzaddons/fuzzloc/Mod_Fuzzloc.php:22
-msgid "Fuzzloc Settings updated."
-msgstr ""
+#: ../../Zotlabs/Module/Profperm.php:118
+msgid "Click on a contact to add or remove."
+msgstr "Klikk på en kontakt for å legge til eller fjerne."
-#: ../../extend/addon/hzaddons/fuzzloc/Mod_Fuzzloc.php:38
-msgid "Minimum offset in meters"
-msgstr ""
+#: ../../Zotlabs/Module/Profperm.php:127
+msgid "Visible To"
+msgstr "Synlig for"
-#: ../../extend/addon/hzaddons/fuzzloc/Mod_Fuzzloc.php:42
-msgid "Maximum offset in meters"
-msgstr ""
+#: ../../Zotlabs/Module/Profperm.php:143
+#: ../../Zotlabs/Module/Connections.php:221
+msgid "All Connections"
+msgstr "Alle forbindelser"
-#: ../../extend/addon/hzaddons/fuzzloc/Mod_Fuzzloc.php:51
-msgid "Fuzzy Location"
+#: ../../Zotlabs/Module/Lang.php:20
+msgid "Language App"
msgstr ""
-#: ../../extend/addon/hzaddons/diaspora/import_diaspora.php:18
-msgid "No username found in import file."
-msgstr "Ingen brukernavn ble funnet i importfilen."
-
-#: ../../extend/addon/hzaddons/diaspora/import_diaspora.php:140
-msgid "Import completed."
-msgstr "Import ferdig."
+#: ../../Zotlabs/Module/Lang.php:80
+msgid "Select an alternate language"
+msgstr "Velg et annet språk"
-#: ../../extend/addon/hzaddons/diaspora/Receiver.php:1632
-#, php-format
-msgid "%1$s dislikes %2$s's %3$s"
-msgstr ""
+#: ../../Zotlabs/Module/Rpost.php:117 ../../Zotlabs/Module/Editpost.php:114
+msgid "Edit post"
+msgstr "Endre innlegg"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:43
-msgid "Diaspora Protocol Settings updated."
-msgstr ""
+#: ../../Zotlabs/Module/Editpost.php:38 ../../Zotlabs/Module/Editpost.php:43
+msgid "Item is not editable"
+msgstr "Elementet kan ikke endres"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:52
-msgid ""
-"The diaspora protocol does not support location independence. Connections "
-"you make within that network may be unreachable from alternate channel "
-"locations."
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:58
+#: ../../Zotlabs/Module/Connections.php:116
+msgid "Active"
+msgstr "Aktiv"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:80
-msgid "Prevent your hashtags from being redirected to other sites"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:63
+#: ../../Zotlabs/Module/Connections.php:185
+#: ../../Zotlabs/Module/Connections.php:296
+msgid "Blocked"
+msgstr "Blokkert"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:84
-msgid "Sign and forward posts and comments with no existing Diaspora signature"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:68
+#: ../../Zotlabs/Module/Connections.php:192
+#: ../../Zotlabs/Module/Connections.php:295
+msgid "Ignored"
+msgstr "Ignorert"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:89
-msgid "Followed hashtags (comma separated, do not include the #)"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:73
+#: ../../Zotlabs/Module/Connections.php:206
+#: ../../Zotlabs/Module/Connections.php:294
+msgid "Hidden"
+msgstr "Skjult"
-#: ../../extend/addon/hzaddons/diaspora/Mod_Diaspora.php:98
-msgid "Diaspora Protocol"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:78
+#: ../../Zotlabs/Module/Connections.php:199
+msgid "Archived/Unreachable"
+msgstr "Arkivert/utilgjengelig"
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:81
-msgid ""
-"Please install the statistics addon to be able to configure a diaspora relay"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:161
+msgid "Active Connections"
+msgstr "Aktive forbindelser"
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:91
-msgid "Diaspora Relay Handle"
+#: ../../Zotlabs/Module/Connections.php:164
+msgid "Show active connections"
msgstr ""
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:91
-msgid "Address of a diaspora relay. Example: relay@diasporarelay.tld"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:171
+msgid "Show pending (new) connections"
+msgstr "Vis ventende (nye) forbindelser"
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:111
-msgid "Diaspora relay could not be imported"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:188
+msgid "Only show blocked connections"
+msgstr "Vis bare forbindelser som er blokkert"
-#: ../../extend/addon/hzaddons/diaspora/diaspora.php:1103
-msgid "No subject"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:195
+msgid "Only show ignored connections"
+msgstr "Vis bare ignorerte forbindelser"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:106
-msgid "Photos imported"
+#: ../../Zotlabs/Module/Connections.php:202
+msgid "Only show archived/unreachable connections"
msgstr ""
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:129
-msgid "Redmatrix Photo Album Import"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:209
+msgid "Only show hidden connections"
+msgstr "Vis bare skjulte forbindelser"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:130
-msgid "This will import all your Redmatrix photo albums to this channel."
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:224
+msgid "Show all connections"
+msgstr "Vis alle forbindelser"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:131
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:121
-msgid "Redmatrix Server base URL"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:292
+msgid "Pending approval"
+msgstr "Venter på godkjenning"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:132
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:122
-msgid "Redmatrix Login Username"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:293
+msgid "Archived"
+msgstr "Arkivert"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:133
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:123
-msgid "Redmatrix Login Password"
+#: ../../Zotlabs/Module/Connections.php:297
+msgid "Not connected at this location"
msgstr ""
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:134
-msgid "Import just this album"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:336
+#, php-format
+msgid "%1$s [%2$s]"
+msgstr "%1$s [%2$s]"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:134
-msgid "Leave blank to import all albums"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:337
+msgid "Edit connection"
+msgstr "Endre forbindelse"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:135
-msgid "Maximum count to import"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:339
+msgid "Delete connection"
+msgstr "Slett forbindelse"
-#: ../../extend/addon/hzaddons/redphotos/redphotos.php:135
-msgid "0 or blank to import all available"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:345
+msgid "Channel address"
+msgstr "Kanaladresse"
-#: ../../extend/addon/hzaddons/skeleton/Mod_Skeleton.php:38
-msgid "Some setting"
+#: ../../Zotlabs/Module/Connections.php:350
+msgid "Call"
msgstr ""
-#: ../../extend/addon/hzaddons/skeleton/Mod_Skeleton.php:38
-msgid "A setting"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:352
+msgid "Status"
+msgstr "Status"
-#: ../../extend/addon/hzaddons/skeleton/Mod_Skeleton.php:46
-msgid "Skeleton Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:355
+msgid "Connected"
+msgstr "Forbundet"
-#: ../../extend/addon/hzaddons/qrator/qrator.php:48
-msgid "QR code"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:359
+msgid "Ignore connection"
+msgstr "Ignorer forbindelse"
-#: ../../extend/addon/hzaddons/qrator/qrator.php:63
-msgid "QR Generator"
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:361
+msgid "Recent activity"
+msgstr "Nylig aktivitet"
-#: ../../extend/addon/hzaddons/qrator/qrator.php:64
-msgid "Enter some text"
+#: ../../Zotlabs/Module/Connections.php:367
+msgid "Connect at this location"
msgstr ""
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:26
-msgid "Dreamwidth Crosspost Connector Settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Connections.php:405
+msgid "Search your connections"
+msgstr "Søk blant dine forbindelser"
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:51
-msgid "Dreamwidth username"
+#: ../../Zotlabs/Module/Connections.php:406
+msgid "Contact search"
msgstr ""
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:55
-msgid "Dreamwidth password"
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:819
+msgid "Calendar entries imported."
+msgstr "Kalenderhendelsene er importert."
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:59
-msgid "Post to Dreamwidth by default"
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:821
+msgid "No calendar entries found."
+msgstr "Ingen kalenderhendelser funnet."
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:67
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:96
-msgid "Link description (default:"
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1000
+msgid "Event title"
+msgstr "Tittel på hendelse"
-#: ../../extend/addon/hzaddons/dwpost/Mod_Dwpost.php:75
-msgid "Dreamwidth Crosspost Connector"
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1001
+msgid "Start date and time"
+msgstr "Startdato og tidspunkt"
-#: ../../extend/addon/hzaddons/dwpost/dwpost.php:49
-msgid "Post to Dreamwidth"
+#: ../../Zotlabs/Module/Cdav.php:1002
+msgid "End date and time"
msgstr ""
-#: ../../extend/addon/hzaddons/upload_limits/upload_limits.php:25
-msgid "Show Upload Limits"
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1003
+#, fuzzy
+msgid "Timezone:"
+msgstr "Tidssone"
-#: ../../extend/addon/hzaddons/upload_limits/upload_limits.php:27
-msgid "Hubzilla configured maximum size: "
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1029
+#, fuzzy
+msgid "Month"
+msgstr "måned"
-#: ../../extend/addon/hzaddons/upload_limits/upload_limits.php:28
-msgid "PHP upload_max_filesize: "
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1030
+#, fuzzy
+msgid "Week"
+msgstr "uke"
-#: ../../extend/addon/hzaddons/upload_limits/upload_limits.php:29
-msgid "PHP post_max_size (must be larger than upload_max_filesize): "
-msgstr ""
+#: ../../Zotlabs/Module/Cdav.php:1031
+#, fuzzy
+msgid "Day"
+msgstr "dag"
-#: ../../extend/addon/hzaddons/nsabait/Mod_Nsabait.php:23
-msgid "NSA Bait App"
+#: ../../Zotlabs/Module/Cdav.php:1032
+msgid "List month"
msgstr ""
-#: ../../extend/addon/hzaddons/nsabait/Mod_Nsabait.php:25
-msgid "Make yourself a political target."
+#: ../../Zotlabs/Module/Cdav.php:1033
+msgid "List week"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:27
-msgid "No server specified"
+#: ../../Zotlabs/Module/Cdav.php:1034
+msgid "List day"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:72
-msgid "Posts imported"
+#: ../../Zotlabs/Module/Cdav.php:1042
+msgid "More"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:112
-msgid "Files imported"
+#: ../../Zotlabs/Module/Cdav.php:1043
+msgid "Less"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:134
-msgid ""
-"This will import all your conversations and cloud files from a cloned "
-"channel on another server. This may take a while if you have lots of posts "
-"and or files."
+#: ../../Zotlabs/Module/Cdav.php:1045
+msgid "Select calendar"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:135
-msgid "Include posts"
+#: ../../Zotlabs/Module/Cdav.php:1048
+msgid "Delete all"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:135
-msgid "Conversations, Articles, Cards, and other posted content"
+#: ../../Zotlabs/Module/Cdav.php:1051
+msgid "Sorry! Editing of recurrent events is not yet implemented."
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:136
-msgid "Include files"
+#: ../../Zotlabs/Module/Cdav.php:1052
+msgid ""
+"Could not fetch calendar resource. The selected calendar might be disabled."
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:136
-msgid "Files, Photos and other cloud storage"
+#: ../../Zotlabs/Module/Cdav.php:1452
+msgid "Default Calendar"
msgstr ""
-#: ../../extend/addon/hzaddons/content_import/Mod_content_import.php:137
-msgid "Original Server base URL"
+#: ../../Zotlabs/Module/Cdav.php:1463
+msgid "Default Addressbook"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:24
-msgid "Friendica Crosspost Connector Settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Dirsearch.php:32
+msgid "This directory server requires an access token"
+msgstr "Denne katalogtjeneren krever en tilgangsnøkkel (access token)"
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:47
-msgid "Send public postings to Friendica by default"
+#: ../../Zotlabs/Module/Oauth2.php:54
+msgid "Name and Secret are required"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:51
-msgid "Friendica API Path"
+#: ../../Zotlabs/Module/Oauth2.php:114
+msgid "Add OAuth2 application"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:51
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:65
-msgid "https://{sitename}/api"
+#: ../../Zotlabs/Module/Oauth2.php:120 ../../Zotlabs/Module/Oauth2.php:147
+msgid "Grant Types"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:55
-msgid "Friendica login name"
+#: ../../Zotlabs/Module/Oauth2.php:120 ../../Zotlabs/Module/Oauth2.php:121
+msgid "leave blank unless your application sepcifically requires this"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:59
-msgid "Friendica password"
+#: ../../Zotlabs/Module/Oauth2.php:121 ../../Zotlabs/Module/Oauth2.php:148
+msgid "Authorization scope"
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/Mod_Rtof.php:67
-msgid "Friendica Crosspost Connector"
+#: ../../Zotlabs/Module/Oauth2.php:132
+msgid "OAuth2 Application not found."
msgstr ""
-#: ../../extend/addon/hzaddons/rtof/rtof.php:51
-msgid "Post to Friendica"
+#: ../../Zotlabs/Module/Oauth2.php:147 ../../Zotlabs/Module/Oauth2.php:148
+msgid "leave blank unless your application specifically requires this"
msgstr ""
-#: ../../extend/addon/hzaddons/notifyadmin/notifyadmin.php:34
-msgid "New registration"
+#: ../../Zotlabs/Module/Oauth2.php:190
+msgid "Connected OAuth2 Apps"
msgstr ""
-#: ../../extend/addon/hzaddons/notifyadmin/notifyadmin.php:42
+#: ../../Zotlabs/Module/Settings/Display.php:126
#, php-format
-msgid "Message sent to %s. New account registration: %s"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:119
-msgid "Redmatrix File Storage Import"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/redfiles/redfiles.php:120
-msgid "This will import all your Redmatrix cloud files to this channel."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:19
-msgid "Send test email"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:50
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:50
-msgid "No recipients found."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:66
-msgid "Mail sent."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:68
-msgid "Sending of mail failed."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:77
-msgid "Mail Test"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/mailtest/mailtest.php:96
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:93
-msgid "Message subject"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:57
-msgid "Errors encountered deleting database table "
-msgstr ""
+msgid "%s - (Experimental)"
+msgstr "%s - (Eksperimentelt)"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:96
-msgid "Drop tables when uninstalling?"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:182
+msgid "Display Settings"
+msgstr "Visningsinnstillinger"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:96
-msgid ""
-"If checked, the Rendezvous database tables will be deleted when the plugin "
-"is uninstalled."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:183
+msgid "Theme Settings"
+msgstr "Temainnstillinger"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:97
-msgid "Mapbox Access Token"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:184
+msgid "Custom Theme Settings"
+msgstr "Tilpassede temainnstillinger"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:97
-msgid ""
-"If you enter a Mapbox access token, it will be used to retrieve map tiles "
-"from Mapbox instead of the default OpenStreetMap tile server."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:185
+msgid "Content Settings"
+msgstr "Innholdsinnstillinger"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:162
-msgid "Rendezvous"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:191
+msgid "Display Theme:"
+msgstr "Visningstema:"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:167
-msgid ""
-"This identity has been deleted by another member due to inactivity. Please "
-"press the \"New identity\" button or refresh the page to register a new "
-"identity. You may use the same name."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:192
+msgid "Select scheme"
+msgstr "Velg skjema"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:168
-msgid "Welcome to Rendezvous!"
+#: ../../Zotlabs/Module/Settings/Display.php:194
+msgid "Threaded conversation view"
msgstr ""
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:169
-msgid ""
-"Enter your name to join this rendezvous. To begin sharing your location with "
-"the other members, tap the GPS control. When your location is discovered, a "
-"red dot will appear and others will be able to see you on the map."
+#: ../../Zotlabs/Module/Settings/Display.php:194
+msgid "Display replies below their parent message (default yes)"
msgstr ""
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:171
-msgid "Let's meet here"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:195
+msgid "Enable user zoom on mobile devices"
+msgstr "Skru på brukerstyrt zoom på mobile enheter"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:174
-msgid "New marker"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:196
+msgid "Update browser every xx seconds"
+msgstr "Oppdater nettleser hvert xx sekunder"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:175
-msgid "Edit marker"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:196
+msgid "Minimum of 10 seconds, no maximum"
+msgstr "Minimum 10 sekunder, ikke noe maksimum"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:176
-msgid "New identity"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:197
+msgid "Maximum number of conversations to load at any time:"
+msgstr "Maksimalt antall samtaler å laste samtidig:"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:177
-msgid "Delete marker"
+#: ../../Zotlabs/Module/Settings/Display.php:197
+msgid "Maximum of 30 items"
msgstr ""
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:178
-msgid "Delete member"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:198
+msgid "Show emoticons (smilies) as images"
+msgstr "Vis emoticons (smilefjes) som bilder"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:179
-msgid "Edit proximity alert"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:199
+msgid "Link post titles to source"
+msgstr "Lenk innleggets tittel til kilden"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:180
-msgid ""
-"A proximity alert will be issued when this member is within a certain radius "
-"of you.<br><br>Enter a radius in meters (0 to disable):"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Display.php:201
+msgid "Display new member quick links menu"
+msgstr "Vis hurtiglenker for nye medlemmer"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:180
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:185
-msgid "distance"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Network.php:42
+#: ../../Zotlabs/Module/Settings/Channel_home.php:46
+msgid "Max height of content (in pixels)"
+msgstr "Maks høyde for innhold (i piksler)"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:181
-msgid "Proximity alert distance (meters)"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Network.php:44
+#: ../../Zotlabs/Module/Settings/Channel_home.php:48
+msgid "Click to expand content exceeding this height"
+msgstr "Klikk for å vise hele innlegg som overskrider denne grensen"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:182
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:184
-msgid ""
-"A proximity alert will be issued when you are within a certain radius of the "
-"marker location.<br><br>Enter a radius in meters (0 to disable):"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Network.php:59
+msgid "Stream Settings"
+msgstr "Instillinger for tidslinjen"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:183
-msgid "Marker proximity alert"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel_home.php:61
+msgid "Personal menu to display in your channel pages"
+msgstr "Personlig meny som kan vises på dine kanalsider"
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:186
-msgid "Reminder note"
+#: ../../Zotlabs/Module/Settings/Channel_home.php:88
+msgid "Channel Home Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:187
-msgid ""
-"Enter a note to be displayed when you are within the specified proximity..."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:23
+msgid "Not valid email."
+msgstr "Ikke gyldig e-post."
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:199
-msgid "Add new rendezvous"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:26
+msgid "Protected email address. Cannot change to that email."
+msgstr "Beskyttet e-postadresse. Kan ikke endre til den e-postadressen."
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:200
-msgid ""
-"Create a new rendezvous and share the access link with those you wish to "
-"invite to the group. Those who open the link become members of the "
-"rendezvous. They can view other member locations, add markers to the map, or "
-"share their own locations with the group."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:35
+msgid "System failure storing new email. Please try again."
+msgstr "Systemfeil ved lagring av ny e-post. Vennligst prøv igjen."
-#: ../../extend/addon/hzaddons/rendezvous/rendezvous.php:232
-msgid "You have no rendezvous. Press the button above to create a rendezvous!"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:53
+msgid "Password verification failed."
+msgstr "Passordbekreftelsen mislyktes."
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:24
-msgid "Channel is required."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:60
+msgid "Passwords do not match. Password unchanged."
+msgstr "Passordene stemmer ikke overens. Passord uforandret."
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:38
-msgid "Hubzilla Crosspost Connector Settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:64
+msgid "Empty passwords are not allowed. Password unchanged."
+msgstr "Tomme passord er ikke tillatt. Passord uforandret."
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:61
-msgid "Send public postings to Hubzilla channel by default"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:78
+msgid "Password changed."
+msgstr "Passord endret."
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:65
-msgid "Hubzilla API Path"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:80
+msgid "Password update failed. Please try again."
+msgstr "Passord oppdatering mislyktes. Vennligst prøv igjen."
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:69
-msgid "Hubzilla login name"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:105
+msgid "Account Settings"
+msgstr "Kontoinnstillinger"
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:73
-msgid "Hubzilla channel name"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:106
+msgid "Current Password"
+msgstr "Nåværende passord"
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:77
-msgid "Hubzilla password"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:107
+msgid "Enter New Password"
+msgstr "Skriv nytt passord"
-#: ../../extend/addon/hzaddons/redred/Mod_Redred.php:85
-msgid "Hubzilla Crosspost Connector"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:108
+msgid "Confirm New Password"
+msgstr "Bekreft nytt passord"
-#: ../../extend/addon/hzaddons/redred/redred.php:50
-msgid "Post to Hubzilla"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:108
+msgid "Leave password fields blank unless changing"
+msgstr "La passordfeltene stå blanke om det ikke skal endres"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:224
-msgid "Workflow user."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:110
+msgid "Multi-Factor Authentication"
+msgstr "Flerfaktorautentisering"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:275
-msgid "This channel"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:111
+msgid "DId2 or Email Address:"
+msgstr "DId2 eller epostadresse:"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:330
-msgid "Create New Workflow Item"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Account.php:114
+msgid "Remove this account including all its channels"
+msgstr "Slett denne kontoen inkludert alle dens kanaler"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:564
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1466
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1485
-msgid "Workflow"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Editor.php:40
+msgid "Editor Settings"
+msgstr "Instillinger for redigering"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1454
-msgid "No Workflows Available"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Features.php:45
+msgid "Additional Features"
+msgstr "Ekstra funksjoner"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1484
-msgid "Add item to which workflow"
+#: ../../Zotlabs/Module/Settings/Directory.php:40
+msgid "Directory Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1544
-#: ../../extend/addon/hzaddons/workflow/workflow.php:1663
-msgid "Create Workflow Item"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Featured.php:25
+msgid "No feature settings configured"
+msgstr "Ingen funksjonsinnstillinger er konfigurert"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2632
-msgid "Link"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Featured.php:34
+msgid "Addon Settings"
+msgstr "Instillinger for tillegg"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2634
-msgid "Web link."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Featured.php:35
+msgid "Please save/submit changes to any panel before opening another."
+msgstr "Husk å lagre endringene i ett panel før du åpner andre."
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2655
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2724
-msgid "Brief description or title"
+#: ../../Zotlabs/Module/Settings/Manage.php:41
+msgid "Channel Manager Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2663
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2732
-msgid "Notes and Info"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:49
+msgid "Privacy settings updated."
+msgstr "Personverninnstillingene ble oppdatert."
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2670
-msgid "Used to order links"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:67
+msgid "Only those you specifically allow"
+msgstr "Bare de du spesifikt tillater"
-#: ../../extend/addon/hzaddons/workflow/workflow.php:2730
-msgid "Body"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:68
+msgid "Approved connections"
+msgstr "Godkjente forbindelser"
-#: ../../extend/addon/hzaddons/workflow/Settings/Mod_WorkflowSettings.php:101
-msgid "Workflow Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:69
+msgid "Any connections"
+msgstr "Enhver forbindelse"
-#: ../../extend/addon/hzaddons/workflow/Settings/WorkflowSettingsUtil.php:145
-#, fuzzy
-#| msgid "Editor Settings"
-msgid "Workflow settings"
-msgstr "Instillinger for redigering"
+#: ../../Zotlabs/Module/Settings/Privacy.php:70
+msgid "Anybody on this website"
+msgstr "Enhver ved dette nettstedet"
-#: ../../extend/addon/hzaddons/nsfw/nsfw.php:153
-msgid "Possible adult content"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:71
+msgid "Anybody in this network"
+msgstr "Enhver i dette nettverket"
-#: ../../extend/addon/hzaddons/nsfw/nsfw.php:168
-#, php-format
-msgid "%s - view"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:72
+msgid "Anybody authenticated"
+msgstr "Enhver som er autentisert"
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:22
-msgid "NSFW Settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:73
+msgid "Anybody on the internet"
+msgstr "Enhver på Internett"
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:42
+#: ../../Zotlabs/Module/Settings/Privacy.php:83
msgid ""
-"This app looks in posts for the words/text you specify below, and collapses "
-"any content containing those keywords so it is not displayed at "
-"inappropriate times, such as sexual innuendo that may be improper in a work "
-"setting. It is polite and recommended to tag any content containing nudity "
-"with #NSFW. This filter can also match any other word/text you specify, and "
-"can thereby be used as a general purpose content filter."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:47
-msgid "Comma separated list of keywords to hide"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:47
-msgid "Word, /regular-expression/, lang=xx, lang!=xx"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/nsfw/Mod_Nsfw.php:56
-msgid "NSFW"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/tictac/tictac.php:21
-msgid "Three Dimensional Tic-Tac-Toe"
+"Advise: set to \"Anybody on the internet\" and use privacy groups to "
+"restrict access"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:54
-msgid "3D Tic-Tac-Toe"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:122
+msgid "Privacy Settings"
+msgstr "Personverninnstillinger"
-#: ../../extend/addon/hzaddons/tictac/tictac.php:59
-msgid "New game"
+#: ../../Zotlabs/Module/Settings/Privacy.php:127
+msgid "Advanced configuration"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:60
-msgid "New game with handicap"
+#: ../../Zotlabs/Module/Settings/Privacy.php:129
+msgid "Proceed with caution"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:61
+#: ../../Zotlabs/Module/Settings/Privacy.php:130
msgid ""
-"Three dimensional tic-tac-toe is just like the traditional game except that "
-"it is played on multiple levels simultaneously. "
+"Changing advanced configuration settings can impact your, and your contacts "
+"channels functionality and security."
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:62
-msgid ""
-"In this case there are three levels. You win by getting three in a row on "
-"any level, as well as up, down, and diagonally across the different levels."
+#: ../../Zotlabs/Module/Settings/Privacy.php:131
+msgid "Accept the risk and continue"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:64
-msgid ""
-"The handicap game disables the center position on the middle level because "
-"the player claiming this square often has an unfair advantage."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:133
+msgid "Automatically approve new contacts"
+msgstr "Automatisk godkjenn nye kontakter"
-#: ../../extend/addon/hzaddons/tictac/tictac.php:183
-msgid "You go first..."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:134
+msgid "Opt-out of search engine indexing"
+msgstr "Ikke la søkemotorer indeksere denne kanalen"
-#: ../../extend/addon/hzaddons/tictac/tictac.php:188
-msgid "I'm going first this time..."
+#: ../../Zotlabs/Module/Settings/Privacy.php:135
+msgid "Group actor"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:194
-msgid "You won!"
+#: ../../Zotlabs/Module/Settings/Privacy.php:135
+msgid "Allow this channel to act as a forum"
msgstr ""
-#: ../../extend/addon/hzaddons/tictac/tictac.php:200
-#: ../../extend/addon/hzaddons/tictac/tictac.php:225
-msgid "\"Cat\" game!"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:136
+msgid "Accept all messages which mention you"
+msgstr "Godta alle meldinger som nevner deg"
-#: ../../extend/addon/hzaddons/tictac/tictac.php:223
-msgid "I won!"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:136
+msgid "This setting bypasses normal permissions"
+msgstr "Denne instillingen overstyrer vanlig tilgangskontroll"
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:27
-msgid "Photo Cache settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:137
+msgid "Accept unsolicited comments for moderation"
+msgstr "Godta kommentarer fra fremmede for moderering"
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:43
-msgid ""
-"Saves a copy of images from external sites locally to increase your "
-"anonymity in the web."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:137
+msgid "Otherwise they will be silently dropped"
+msgstr "Ellers vil de avvises uten varsel"
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:49
-msgid "Minimal photo size for caching"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:138
+msgid "Enable OCAP access"
+msgstr "Slå på OCAP tilgangskontroll"
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:51
-msgid "In pixels. From 1 up to 1024, 0 will be replaced with system default."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Privacy.php:138
+msgid "Grant limited posts the right to access linked private media"
+msgstr "Gi begrensede innlegg tilgang til private media som er lenket fra dem"
-#: ../../extend/addon/hzaddons/photocache/Mod_Photocache.php:60
-msgid "Photo Cache"
+#: ../../Zotlabs/Module/Settings/Events.php:40
+msgid "Events Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/logrot/logrot.php:36
-msgid "Logfile archive directory"
+#: ../../Zotlabs/Module/Settings/Photos.php:40
+msgid "Photos Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/logrot/logrot.php:36
-msgid "Directory to store rotated logs"
+#: ../../Zotlabs/Module/Settings/Conversation.php:42
+msgid "Conversation Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/logrot/logrot.php:37
-msgid "Logfile size in bytes before rotating"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:106
+#: ../../Zotlabs/Module/Settings/Channel.php:218
+msgid "Please select a channel role"
+msgstr "Velg en kanalrolle"
-#: ../../extend/addon/hzaddons/logrot/logrot.php:38
-msgid "Number of logfiles to retain"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:195
+msgid "Your channel address is"
+msgstr "Din kanaladresse er"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:23
-msgid "XMPP settings updated."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:198
+msgid "Your files/photos are accessible via WebDAV at"
+msgstr "Dine filer og foto er tilgjengelig via WebDAV på"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:35
-msgid "XMPP App"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:229
+msgid "Channel Settings"
+msgstr "Kanalinnstillinger"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:36
-msgid "Embedded XMPP (Jabber) client"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:236
+msgid "Basic Settings"
+msgstr "Grunninnstillinger"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:52
-msgid "Individual credentials"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:237
+msgid "Channel timezone:"
+msgstr "Tidssone for kanalen:"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:58
-msgid "Jabber BOSH server"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:238
+msgid "Default post location:"
+msgstr "Standard plassering for innlegg:"
-#: ../../extend/addon/hzaddons/xmpp/Mod_Xmpp.php:67
-msgid "XMPP Settings"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:238
+msgid "Geographical location to display on your posts"
+msgstr "Geografisk plassering som vises på dine innlegg"
-#: ../../extend/addon/hzaddons/xmpp/xmpp.php:44
-msgid "Jabber BOSH host"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:239
+msgid "Use browser location"
+msgstr "Bruk nettleserplassering"
-#: ../../extend/addon/hzaddons/xmpp/xmpp.php:45
-msgid "Use central userbase"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:240
+msgid "Adult content"
+msgstr "Voksent innhold"
-#: ../../extend/addon/hzaddons/xmpp/xmpp.php:45
-msgid ""
-"If enabled, members will automatically login to an ejabberd server that has "
-"to be installed on this machine with synchronized credentials via the "
-"\"auth_ejabberd.php\" script."
+#: ../../Zotlabs/Module/Settings/Channel.php:240
+msgid "This channel frequently or regularly publishes adult content"
msgstr ""
+"Denne kanalen vil ofte, eller regelmessig poste innlegg med voksent innhold"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:41
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:109
-msgid "(No Title)"
-msgstr "(Ingen tittel)"
-
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:123
-msgid "Wiki page create failed."
-msgstr "Kunne ikke opprette wikiside."
-
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:137
-msgid "Wiki not found."
-msgstr "Fant ikke wikien."
-
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:148
-msgid "Destination name already exists"
-msgstr "Navnet finnes allerede"
-
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:181
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:376
-msgid "Page not found"
-msgstr "Fant ikke siden"
-
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:211
-msgid "Error reading page content"
-msgstr "Det oppstod en feil under lesing av innholder på siden"
+#: ../../Zotlabs/Module/Settings/Channel.php:241
+msgid "Maximum Friend Requests/Day:"
+msgstr "Maksimalt antall venneforespørsler per dag:"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:367
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:425
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:493
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:534
-msgid "Error reading wiki"
-msgstr "Det oppstod en feil ved lesing av wikien"
+#: ../../Zotlabs/Module/Settings/Channel.php:241
+msgid "May reduce spam activity"
+msgstr "Kan redusere søppelpostaktivitet"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:411
-msgid "Page update failed."
-msgstr "Oppdatering av siden mislyktes."
+#: ../../Zotlabs/Module/Settings/Channel.php:243
+msgid "By default post a status message when:"
+msgstr "Legg inn en statusmelding når du:"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:447
-msgid "Nothing deleted"
-msgstr "Ingenting ble slettet"
+#: ../../Zotlabs/Module/Settings/Channel.php:244
+msgid "accepting a friend request"
+msgstr "aksepterer en venneforespørsel"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:514
-msgid "Compare: object not found."
-msgstr "Sammanlign: fant ikke objektet."
+#: ../../Zotlabs/Module/Settings/Channel.php:245
+msgid "joining a forum/community"
+msgstr "blir med i et forum/miljø"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:521
-msgid "Page updated"
-msgstr "Siden ble oppdatert"
+#: ../../Zotlabs/Module/Settings/Channel.php:246
+msgid "making an <em>interesting</em> profile change"
+msgstr "gjør en <em>interessant</em> profilendring"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:529
-msgid "Wiki resource_id required for git commit"
-msgstr "Wiki ressurs_id er nødvendig for å lagre i git"
+#: ../../Zotlabs/Module/Settings/Channel.php:247
+msgid "Send a notification email when:"
+msgstr "Send en varsel-e-post når:"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:589
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_page_history.php:29
-msgctxt "wiki_history"
-msgid "Message"
-msgstr "Melding"
+#: ../../Zotlabs/Module/Settings/Channel.php:248
+msgid "You receive a connection request"
+msgstr "Du har mottatt en forespørsel om forbindelse"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:590
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_page_history.php:30
-msgid "Date"
-msgstr "Dato"
+#: ../../Zotlabs/Module/Settings/Channel.php:249
+msgid "Your connections are confirmed"
+msgstr "Dine forbindelser er bekreftet"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:591
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:364
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_page_history.php:31
-msgid "Revert"
-msgstr "Tilbakestill"
+#: ../../Zotlabs/Module/Settings/Channel.php:250
+msgid "Someone writes on your profile wall"
+msgstr "Noen skriver på din profilvegg"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWikiPage.php:592
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_page_history.php:32
-msgid "Compare"
-msgstr "Sammenlign"
+#: ../../Zotlabs/Module/Settings/Channel.php:251
+msgid "Someone writes a followup comment"
+msgstr "Noen skriver en oppfølgende kommentar"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWiki.php:144
-msgid "Wiki updated successfully"
-msgstr "Wikien ble oppdatert"
+#: ../../Zotlabs/Module/Settings/Channel.php:252
+msgid "You receive a private message"
+msgstr "Du mottar en privat melding"
-#: ../../extend/addon/hzaddons/wiki/Lib/NativeWiki.php:204
-msgid "Wiki files deleted successfully"
-msgstr "Wikifiler ble slettet"
+#: ../../Zotlabs/Module/Settings/Channel.php:253
+msgid "You receive a friend suggestion"
+msgstr "Du mottok et venneforslag"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:136
-msgid "Error retrieving wiki"
-msgstr "Det oppstod en feil ved henting av wikien"
+#: ../../Zotlabs/Module/Settings/Channel.php:254
+msgid "You are tagged in a post"
+msgstr "Du merkes i et innlegg"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:143
-msgid "Error creating zip file export folder"
-msgstr "Det oppstod en feil ved eksport av mappe til zip fil"
+#: ../../Zotlabs/Module/Settings/Channel.php:255
+msgid "You are poked/prodded/etc. in a post"
+msgstr "Du ble prikket/oppildnet/og så vider i et innlegg"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:194
-msgid "Error downloading wiki: "
+#: ../../Zotlabs/Module/Settings/Channel.php:256
+msgid "Someone likes your post/comment"
msgstr ""
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:209
-#: ../../extend/addon/hzaddons/wiki/wiki.php:45
-#: ../../extend/addon/hzaddons/wiki/wiki.php:98
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_list.php:23
-msgid "Wikis"
-msgstr "Wikier"
+#: ../../Zotlabs/Module/Settings/Channel.php:257
+msgid "Show visual notifications including:"
+msgstr "Vis visuelle varslinger om:"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:219
-msgid "Wiki name"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:258
+msgid "Unseen stream activity"
+msgstr "Ny aktivitet i nettverksstrømmen"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:220
-msgid "Content type"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:260
+msgid "Unseen private messages"
+msgstr "Nye private meldinger"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:223
-msgid "Any&nbsp;type"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:260
+#: ../../Zotlabs/Module/Settings/Channel.php:265
+#: ../../Zotlabs/Module/Settings/Channel.php:266
+#: ../../Zotlabs/Module/Settings/Channel.php:267
+msgid "Recommended"
+msgstr "Anbefalt"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:230
-msgid "Lock content type"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:261
+msgid "Upcoming events"
+msgstr "Kommende hendelser"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:231
-msgid "Create a status post for this wiki"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:262
+msgid "Events today"
+msgstr "Hendelser idag"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:232
-msgid "Edit Wiki Name"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:263
+msgid "Upcoming birthdays"
+msgstr "Kommende fødselsdager"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:277
-#, fuzzy
-msgid "Wiki not found"
-msgstr "Fant ikke wikien."
+#: ../../Zotlabs/Module/Settings/Channel.php:263
+msgid "Not available in all themes"
+msgstr "Ikke tilgjengelig i alle temaer"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:303
-msgid "Rename page"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:264
+msgid "System (personal) notifications"
+msgstr "System (personlige) varslinger"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:318
-msgid "Error retrieving page content"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:265
+msgid "System info messages"
+msgstr "System infomeldinger"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:326
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:328
-msgid "New page"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:266
+msgid "System critical alerts"
+msgstr "System kritiske varsel"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:363
-msgid "Revision Comparison"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:268
+msgid "System Registrations"
+msgstr "Systemregistreringer"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:371
-msgid "Short description of your changes (optional)"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:269
+msgid "Unseen shared files"
+msgstr "Nye delte filer"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:391
-msgid "New page name"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:271
+msgid "Unseen likes and dislikes"
+msgstr "Nye liker/ikke liker"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:396
-msgid "Embed image from photo albums"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:272
+msgid "Unseen forum posts"
+msgstr "Ny forumpost"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:407
-msgid "History"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:273
+msgid "Email notification hub (hostname)"
+msgstr "Hub for epostvarsler (tjenernavn)"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:485
-msgid "Error creating wiki. Invalid name."
+#: ../../Zotlabs/Module/Settings/Channel.php:273
+#, php-format
+msgid ""
+"If your channel is mirrored to multiple hubs, set this to your preferred "
+"location. This will prevent duplicate email notifications. Example: %s"
msgstr ""
+"Hvis kanalen din er klonet til flere tjenere kan du sette dette til den du "
+"vil skal sende deg epostvarsler. Dette vil forhindre duplikate epostvarsler. "
+"Eksempel: %s"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:492
-msgid "A wiki with this name already exists."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:274
+msgid "Show new wall posts, private messages and connections under Notices"
+msgstr "Vis nye veggposter, private meldinger og forbindelser under varsler"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:505
-msgid "Wiki created, but error creating Home page."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:275
+msgid "Mark all notices of the thread read if a notice is clicked"
+msgstr "Merk alle varsler for en tråd som lest når du klikker på ett varsel"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:512
-msgid "Error creating wiki"
+#: ../../Zotlabs/Module/Settings/Channel.php:275
+msgid "If no, only the clicked notice will be marked read"
msgstr ""
+"Om du velger \"Nei\", blir kun det varslet du klikker på merket som lest"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:536
-msgid "Error updating wiki. Invalid name."
+#: ../../Zotlabs/Module/Settings/Channel.php:276
+msgid ""
+"Desktop notifications are unavailable because the required browser "
+"permission has not been granted"
msgstr ""
+"Skriverbordsvarsler er ikke tilgjengelig fordi nettstedet ikke har de "
+"nødvendige nettlesertillatelsene"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:555
-msgid "Error updating wiki"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:277
+msgid "Grant permission"
+msgstr "Gi tilgang"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:570
-msgid "Wiki delete permission denied."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:278
+msgid "Notify me of events this many days in advance"
+msgstr "Varsle meg om hendelser dette antall dager på forhånd"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:580
-msgid "Error deleting wiki"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:278
+msgid "Must be greater than 0"
+msgstr "Må være større enn 0"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:613
-msgid "New page created"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:281
+msgid "Default photo upload folder"
+msgstr "Standard mappe for opplasting av bilder"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:739
-msgid "Cannot delete Home"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:281
+#: ../../Zotlabs/Module/Settings/Channel.php:282
+msgid "%Y - current year, %m - current month"
+msgstr "%Y - nåværende år, %m - nåværende måned"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:815
-msgid "Current Revision"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:282
+msgid "Default file upload folder"
+msgstr "Standard mappe for opplasting av filer"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:815
-msgid "Selected Revision"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:284
+msgid "Remove this channel."
+msgstr "Fjern denne kanalen."
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:870
-msgid "You must be authenticated."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:285
+msgid "Expire other channel content after this many days"
+msgstr "Annet kanal innhold utløper etter så mange dager"
-#: ../../extend/addon/hzaddons/wiki/Mod_Wiki.php:899
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:64
-msgid "Add new page"
-msgstr "Legg til ny side"
+#: ../../Zotlabs/Module/Settings/Channel.php:285
+msgid "0 or blank to use the website limit."
+msgstr "0 eller la være tomt for å bruke grensen til nettstedet."
-#: ../../extend/addon/hzaddons/wiki/wiki.php:48
-#: ../../extend/addon/hzaddons/cards/cards.php:51
-msgid "View Cards"
+#: ../../Zotlabs/Module/Settings/Channel.php:285
+#, php-format
+msgid "This website expires after %d days."
msgstr ""
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:58
-msgid "Wiki Pages"
-msgstr "Wikisider"
-
-#: ../../extend/addon/hzaddons/wiki/Widget/Wiki_pages.php:69
-msgid "Page name"
-msgstr "Sidenavn"
+#: ../../Zotlabs/Module/Settings/Channel.php:285
+msgid "This website does not expire imported content."
+msgstr "Dette nettstedet sletter ikke importert innhold."
-#: ../../extend/addon/hzaddons/wholikesme/wholikesme.php:30
-msgid "Who likes me?"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Channel.php:285
+msgid "The website limit takes precedence if lower than your limit."
+msgstr "Nettstedets grense vil gjelde dersom den er lavere enn din grense."
-#: ../../extend/addon/hzaddons/cards/Mod_Card_edit.php:129
-msgid "Edit Card"
+#: ../../Zotlabs/Module/Settings/Channel.php:286
+#: ../../Zotlabs/Module/Settings/Channel.php:287
+msgid ""
+"Words one per line or #tags, $categories, /patterns/, lang=xx, lang!=xx - "
+"leave blank to import all posts"
msgstr ""
+"Ett ord per linje eller #emneknagger, $kategorier, /mønster/, lang=xx, lang!"
+"=xx - la være tomt for å importere alle innlegg"
-#: ../../extend/addon/hzaddons/cards/Mod_Cards.php:115
-msgid "Add Card"
+#: ../../Zotlabs/Module/Settings/Profiles.php:41
+msgid "Default profile for new contacts"
msgstr ""
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:25
-msgid "Libertree Crosspost Connector Settings saved."
+#: ../../Zotlabs/Module/Settings/Profiles.php:49
+msgid "Profiles Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:49
-msgid "Libertree API token"
+#: ../../Zotlabs/Module/Settings/Connections.php:40
+msgid "Connections Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:53
-msgid "Libertree site URL"
+#: ../../Zotlabs/Module/Settings/Calendar.php:40
+msgid "Calendar Settings"
msgstr ""
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:57
-msgid "Post to Libertree by default"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Multifactor.php:23
+#, fuzzy
+#| msgid "Name is required"
+msgid "Password is required"
+msgstr "Navn er påkrevd"
-#: ../../extend/addon/hzaddons/libertree/Mod_Libertree.php:65
-msgid "Libertree Crosspost Connector"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:29
+msgid "The provided password is not correct"
msgstr ""
-#: ../../extend/addon/hzaddons/libertree/libertree.php:43
-msgid "Post to Libertree"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Multifactor.php:68
+msgid "Account Multi-Factor Authentication"
+msgstr "Tofaktorautentisering"
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:19
-msgid "Send email to all members"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:69
+msgid ""
+"This is your generated secret. It may be used in some cases if the QR image "
+"cannot be read. Please store it in a safe place."
msgstr ""
+"Dette er en generert sikkerhetskode. Den kan være nyttig i enkelte tilfeller "
+"hvor QR koden ikke kan leses. Lagre den et sikkert sted."
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:74
-#, php-format
-msgid "%1$d of %2$d messages sent."
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Multifactor.php:70
+msgid "Please enter the code from your authenticator app"
+msgstr "Skriv inn koden fra din autentiseringsapp"
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:82
-msgid "Send email to all hub members."
+#: ../../Zotlabs/Module/Settings/Multifactor.php:71
+msgid "You will only be able to enable MFA if the test passes"
msgstr ""
+"Flerfaktorautentisering vil kun bli slått på dersom denne testen lykkes"
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:94
-msgid "Sender Email address"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:75
+msgid "Congratulations, the provided code was correct"
msgstr ""
-#: ../../extend/addon/hzaddons/hubwall/hubwall.php:95
-msgid "Test mode (only send to hub administrator)"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:76
+msgid "Incorrect code"
msgstr ""
-#: ../../extend/addon/hzaddons/mdpost/mdpost.php:42
-msgid "Use markdown for editing posts"
-msgstr ""
+#: ../../Zotlabs/Module/Settings/Multifactor.php:79
+#, fuzzy
+#| msgid "Failed authentication"
+msgid "Enable Multi-Factor Authentication"
+msgstr "Mislykket autentisering"
-#: ../../extend/addon/hzaddons/openid/openid.php:49
-msgid ""
-"We encountered a problem while logging in with the OpenID you provided. "
-"Please check the correct spelling of the ID."
+#: ../../Zotlabs/Module/Settings/Multifactor.php:81
+msgid "Logging in will require you to be in possession of your smartphone"
msgstr ""
-"Vi støtte på et problem under innloggingen med din OpenID. Vennligst sjekk "
-"at ID-en er stavet riktig."
-
-#: ../../extend/addon/hzaddons/openid/openid.php:49
-msgid "The error message was:"
-msgstr "Feilmeldingen var:"
-
-#: ../../extend/addon/hzaddons/openid/Mod_Openid.php:32
-msgid "OpenID protocol error. No ID returned."
-msgstr "OpenID protokollfeil. Ingen ID ble returnert."
-
-#: ../../extend/addon/hzaddons/openid/Mod_Openid.php:78
-#: ../../extend/addon/hzaddons/openid/Mod_Openid.php:179
-#, php-format
-msgid "Welcome %s. Remote authentication successful."
-msgstr "Velkommen %s. Ekstern autentisering er vellykket."
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:52
-msgid "First Name"
-msgstr "Fornavn"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:53
-msgid "Last Name"
-msgstr "Etternavn"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:55
-msgid "Full Name"
-msgstr "Fullt navn"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:61
-msgid "Profile Photo 16px"
-msgstr "Profilbilde 16px"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:62
-msgid "Profile Photo 32px"
-msgstr "Profilbilde 32px"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:63
-msgid "Profile Photo 48px"
-msgstr "Profilbilde 48px"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:64
-msgid "Profile Photo 64px"
-msgstr "Profilbilde 64px"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:65
-msgid "Profile Photo 80px"
-msgstr "Profilbilde 80px"
-
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:66
-msgid "Profile Photo 128px"
-msgstr "Profilbilde 128px"
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:70
-msgid "Birth Year"
-msgstr "Fødselsår"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:84
+#, fuzzy
+#| msgid "Your new password is"
+msgid "Your account password"
+msgstr "Ditt nye passord er"
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:71
-msgid "Birth Month"
-msgstr "Fødselsmåne"
+#: ../../Zotlabs/Module/Settings/Multifactor.php:86
+msgid "Test"
+msgstr "Sjekk koden"
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:72
-msgid "Birth Day"
-msgstr "Fødselsdag"
+#: ../../Zotlabs/Module/Rmagic.php:46
+msgid "Authentication failed."
+msgstr "Autentisering mislyktes."
-#: ../../extend/addon/hzaddons/openid/MysqlProvider.php:73
-msgid "Birthdate"
-msgstr "Fødselsdato"
+#: ../../Zotlabs/Module/Editblock.php:138
+msgid "Edit Block"
+msgstr "Endre byggekloss"
-#: ../../extend/addon/hzaddons/irc/Mod_Irc.php:23
-#: ../../extend/addon/hzaddons/irc/irc.php:41
-msgid "Popular Channels"
+#: ../../Zotlabs/Module/Email_validation.php:37
+msgid "Email Verification Required"
msgstr ""
-#: ../../extend/addon/hzaddons/irc/irc.php:37
-msgid "Channels to auto connect"
+#: ../../Zotlabs/Module/Email_validation.php:38
+#, php-format
+msgid ""
+"A verification token was sent to your email address [%s]. Enter that token "
+"here to complete the account verification step. Please allow a few minutes "
+"for delivery, and check your spam folder if you do not see the message."
msgstr ""
+"En verifikasjonskode ble sendt til epostadressen din [%s]. Skriv inn koden "
+"du mottok her for å fullføre kontoverifikasjonen. Det kan ta noen minutter "
+"før du mottar koden. Sjekk også at eposten ikke har havnet i mappen for "
+"søppelpost om du ikke ser den etter en stund."
-#: ../../extend/addon/hzaddons/irc/irc.php:37
-#: ../../extend/addon/hzaddons/irc/irc.php:41
-msgid "Comma separated list"
+#: ../../Zotlabs/Module/Email_validation.php:39
+msgid "Resend Email"
msgstr ""
-#: ../../extend/addon/hzaddons/irc/irc.php:45
-msgid "IRC Settings"
+#: ../../Zotlabs/Module/Email_validation.php:42
+msgid "Validation token"
msgstr ""
-#: ../../extend/addon/hzaddons/irc/irc.php:54
-msgid "IRC settings saved."
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:71
+msgid "Nothing to import."
+msgstr "Ingenting å importere."
-#: ../../extend/addon/hzaddons/irc/irc.php:58
-msgid "IRC Chatroom"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:87 ../../Zotlabs/Module/Import.php:101
+msgid "Unable to download data from old server"
+msgstr "Ikke i stand til å laste ned data fra gammel tjener"
-#: ../../extend/addon/hzaddons/opensearch/opensearch.php:26
+#: ../../Zotlabs/Module/Import.php:164
#, php-format
-msgctxt "opensearch"
-msgid "Search %1$s (%2$s)"
-msgstr "Søk %1$s (%2$s)"
-
-#: ../../extend/addon/hzaddons/opensearch/opensearch.php:28
-msgctxt "opensearch"
-msgid "$Projectname"
-msgstr "$Projectname"
+msgid "Your service plan only allows %d channels."
+msgstr "Din tjenesteplan tillater bare %d kanaler."
-#: ../../extend/addon/hzaddons/opensearch/opensearch.php:43
-msgid "Search $Projectname"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:191
+msgid "No channel. Import failed."
+msgstr "Ingen kanal. Import mislyktes."
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:28
-msgid "ActivityPub Protocol Settings updated."
+#: ../../Zotlabs/Module/Import.php:197
+msgid "Channel exists but has been marked removed on this hub. Import failed."
msgstr ""
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:44
+#: ../../Zotlabs/Module/Import.php:569
msgid ""
-"The activitypub protocol does not support location independence. Connections "
-"you make within that network may be unreachable from alternate channel "
-"locations."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:50
-msgid "Send activities of type note instead of article"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:50
-msgid "Microblog services such as Mastodon do not properly support articles"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/pubcrawl/Mod_Pubcrawl.php:58
-msgid "Activitypub Protocol"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/flashcards/Mod_Flashcards.php:225
-msgid "Not allowed."
-msgstr ""
-
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:23
-msgid "Insane Journal Crosspost Connector Settings saved."
+"Automatic content and files import was not possible due to API version "
+"incompatiblity. Please import content and files manually!"
msgstr ""
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:35
-msgid "Insane Journal Crosspost Connector App"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:597
+msgid "You must be logged in to use this feature."
+msgstr "Du må være innlogget for å bruke denne funksjonen."
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:36
-msgid "Relay public postings to Insane Journal"
+#: ../../Zotlabs/Module/Import.php:604
+msgid "Channel Import"
msgstr ""
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:53
-msgid "InsaneJournal username"
+#: ../../Zotlabs/Module/Import.php:605
+msgid ""
+"Use this form to import an existing channel from a different server/hub. You "
+"may retrieve the channel identity from the old server/hub via the network or "
+"provide an export file."
msgstr ""
+"Bruk dette skjemaet for å importere en eksisterende kanal fra en annen "
+"tjener/hub. Du kan hente inn kanalidentiteten fra den gamle tjeneren/huben "
+"via nettverket eller ved å bruke en eksportfil."
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:57
-msgid "InsaneJournal password"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:607
+msgid "Or provide the old server/hub details"
+msgstr "Eller oppgi detaljene fra den gamle tjeneren/hub-en"
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:61
-msgid "Post to InsaneJournal by default"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:609
+msgid "Your old identity address (xyz@example.com)"
+msgstr "Din gamle identitetsadresse (xyz@example.com)"
-#: ../../extend/addon/hzaddons/ijpost/Mod_Ijpost.php:69
-msgid "Insane Journal Crosspost Connector"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:610
+msgid "Your old login email address"
+msgstr "Din gamle innloggings e-postadresse"
-#: ../../extend/addon/hzaddons/ijpost/ijpost.php:44
-msgid "Post to Insane Journal"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:611
+msgid "Your old login password"
+msgstr "Ditt gamle innloggingspassord"
-#: ../../extend/addon/hzaddons/planets/Mod_Planets.php:23
-msgid "Random Planet App"
+#: ../../Zotlabs/Module/Import.php:612
+msgid "Import your items and files (limited by available memory)"
msgstr ""
-#: ../../extend/addon/hzaddons/planets/Mod_Planets.php:25
+#: ../../Zotlabs/Module/Import.php:614
msgid ""
-"Set a random planet from the Star Wars Empire as your location when posting"
-msgstr ""
-
-#: ../../extend/addon/hzaddons/donate/donate.php:21
-msgid "Project Servers and Resources"
+"For either option, please choose whether to make this hub your new primary "
+"address, or whether your old location should continue this role. You will be "
+"able to post from either location, but only one can be marked as the primary "
+"location for files, photos, and media."
msgstr ""
+"Enten du tar det ene eller det andre valget, vennligst angi om du vil at "
+"denne hubben skal være din nye primære adresse, eller om din gamle "
+"plassering skal fortsette å ha denne rollen. Du kan lage innlegg fra den ene "
+"eller den andre plasseringen, men bare en av dem kan markeres som den "
+"primære plasseringen for filer, bilder og media."
-#: ../../extend/addon/hzaddons/donate/donate.php:22
-msgid "Project Creator and Tech Lead"
-msgstr ""
+#: ../../Zotlabs/Module/Import.php:616
+msgid "Make this hub my primary location"
+msgstr "Gjør dette nettstedet til min primære plassering"
-#: ../../extend/addon/hzaddons/donate/donate.php:49
-msgid ""
-"And the hundreds of other people and organisations who helped make the "
-"Hubzilla possible."
+#: ../../Zotlabs/Module/Import.php:617
+msgid "Move this channel (disable all previous locations)"
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:52
-msgid ""
-"The Redmatrix/Hubzilla projects are provided primarily by volunteers giving "
-"their time and expertise - and often paying out of pocket for services they "
-"share with others."
+#: ../../Zotlabs/Module/Import.php:618
+msgid "Use this channel nickname instead of the one provided"
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:53
+#: ../../Zotlabs/Module/Import.php:618
msgid ""
-"There is no corporate funding and no ads, and we do not collect and sell "
-"your personal information. (We don't control your personal information - "
-"<strong>you do</strong>.)"
+"Leave blank to keep your existing channel nickname. You will be randomly "
+"assigned a similar nickname if either name is already allocated on this site."
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:54
+#: ../../Zotlabs/Module/Import.php:620
msgid ""
-"Help support our ground-breaking work in decentralisation, web identity, and "
-"privacy."
+"This process may take several minutes to complete. Please submit the form "
+"only once and leave this page open until finished."
msgstr ""
+"Denne prosessen kan ta flere minutter å fullføre. Vennligst send inn dette "
+"skjemaet bare en gang og la siden være åpen inntil den er ferdig."
-#: ../../extend/addon/hzaddons/donate/donate.php:56
-msgid ""
-"Your donations keep servers and services running and also helps us to "
-"provide innovative new features and continued development."
-msgstr ""
+#: ../../Zotlabs/Module/Acl.php:371
+msgid "network"
+msgstr "nettverk"
-#: ../../extend/addon/hzaddons/donate/donate.php:59
-msgid "Donate"
+#: ../../Zotlabs/Module/Filer.php:64
+msgid "Enter a folder name"
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:61
-msgid ""
-"Choose a project, developer, or public hub to support with a one-time "
-"donation"
+#: ../../Zotlabs/Module/Filer.php:64
+msgid "or select an existing folder (doubleclick)"
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:62
-msgid "Donate Now"
-msgstr ""
+#: ../../Zotlabs/Module/Vote.php:40
+msgid "Poll not found."
+msgstr "Fant ikke spørreskjema."
-#: ../../extend/addon/hzaddons/donate/donate.php:63
-msgid ""
-"<strong><em>Or</em></strong> become a project sponsor (Hubzilla Project only)"
+#: ../../Zotlabs/Module/Vote.php:69
+msgid "Invalid response."
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:64
-msgid ""
-"Please indicate if you would like your first name or full name (or nothing) "
-"to appear in our sponsor listing"
+#: ../../Zotlabs/Module/Vote.php:128
+msgid "Response submitted. Updates may not appear instantly."
msgstr ""
-#: ../../extend/addon/hzaddons/donate/donate.php:65
-msgid "Sponsor"
-msgstr ""
+#~ msgid "Please enter the required information."
+#~ msgstr "Vennligst skriv inn nødvendig informasjon."
-#: ../../extend/addon/hzaddons/donate/donate.php:68
-msgid "Special thanks to: "
-msgstr ""
+#~ msgctxt "title"
+#~ msgid "Likes"
+#~ msgstr "Liker"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:22
-#, fuzzy
-#| msgid "Edit App"
-msgid "Fediquest App"
-msgstr "Endre app"
+#~ msgctxt "title"
+#~ msgid "Dislikes"
+#~ msgstr "Liker ikke"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:23
-msgid "A distributed quest for a given word (game)."
-msgstr ""
+#~ msgctxt "title"
+#~ msgid "Attending"
+#~ msgstr "Deltar"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:24
-msgid ""
-"To start a game, enter [fediquest]your_word[/fediquest] somewhere in a "
-"toplevel post."
-msgstr ""
+#~ msgctxt "title"
+#~ msgid "Might attend"
+#~ msgstr "Deltar kanskje"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:25
-msgid "Your contacts can post their guess in the comments."
-msgstr ""
+#~ msgctxt "title"
+#~ msgid "Repeats"
+#~ msgstr "Videresendinger"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:26
-msgid ""
-"Your channel will evaluate the guess and automatically post the response."
-msgstr ""
+#~ msgid "Approve this item"
+#~ msgstr "Godkjenn dette elementet"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:28
-msgid "Correct letters"
-msgstr ""
+#~ msgid "Delete this item"
+#~ msgstr "Slett dette elementet"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:29
-msgid "Letters contained in the word but at the wrong spot"
-msgstr ""
+#~ msgctxt "noun"
+#~ msgid "Not Attending"
+#~ msgid_plural "Not Attending"
+#~ msgstr[0] "Deltar ikke"
+#~ msgstr[1] "Deltar ikke"
-#: ../../extend/addon/hzaddons/fediquest/Mod_Fediquest.php:30
-msgid "Letters not contained in the word"
-msgstr ""
+#~ msgid "created a new post"
+#~ msgstr "laget et nytt innlegg"
-#: ../../extend/addon/hzaddons/fediquest/fediquest.php:211
-msgid "ERROR: word length is not correct!"
-msgstr ""
+#~ msgid "Stream"
+#~ msgstr "Tidslinje"
-#: ../../extend/addon/hzaddons/wppost/wppost.php:47
-msgid "Post to WordPress"
-msgstr ""
+#~ msgid "Direct message"
+#~ msgstr "Direktemelding"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:30
-msgid "Wordpress Settings saved."
-msgstr ""
+#~ msgid "Image"
+#~ msgstr "Bilde"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:67
-msgid "WordPress username"
-msgstr ""
+#~ msgid "New direct messages notifications"
+#~ msgstr "Varsel om nye direktemeldinger"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:71
-msgid "WordPress password"
-msgstr ""
+#~ msgid "Direct messages stream"
+#~ msgstr "Strøm for direktemeldinger"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:75
-msgid "WordPress API URL"
-msgstr ""
+#~ msgid "Mark all events seen"
+#~ msgstr "Merk alle hendelser som sett"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:76
-msgid "Typically https://your-blog.tld/xmlrpc.php"
-msgstr ""
+#~ msgid "Notices"
+#~ msgstr "Varsel"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:79
-msgid "WordPress blogid"
-msgstr ""
+#~ msgid "Direct messages"
+#~ msgstr "Direktemeldinger"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:80
-msgid "For multi-user sites such as wordpress.com, otherwise leave blank"
-msgstr ""
+#~ msgid "No messages"
+#~ msgstr "Ingen meldinger"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:84
-msgid "Post to WordPress by default"
-msgstr ""
+#~ msgid "Unseen"
+#~ msgstr "Ulest"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:88
-msgid "Forward comments (requires hubzilla_wp plugin)"
-msgstr ""
+#~ msgid "Preload images before rendering the page"
+#~ msgstr "Last inn bildene før gjengivelsen av siden"
-#: ../../extend/addon/hzaddons/wppost/Mod_Wppost.php:104
-msgid "Wordpress Post"
-msgstr ""
+#~ msgid ""
+#~ "The subjective page load time will be longer but the page will be ready "
+#~ "when displayed"
+#~ msgstr ""
+#~ "Den personlige opplevelsen av lastetiden vil være lenger, men siden vil "
+#~ "være klar når den vises"
-#: ../../extend/addon/hzaddons/authchoose/Mod_Authchoose.php:30
-msgid ""
-"Allow magic authentication only to websites of your immediate connections"
-msgstr ""
+#~ msgid "timeago.prefixAgo"
+#~ msgstr "timeago.prefixAgo"
-#: ../../extend/addon/hzaddons/authchoose/Mod_Authchoose.php:36
-msgid "Authchoose"
-msgstr ""
+#~ msgid "timeago.prefixFromNow"
+#~ msgstr "timeago.prefixFromNow"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:48
-msgid "Your channel has been upgraded to $Projectname version"
-msgstr ""
+#~ msgid "timeago.suffixAgo"
+#~ msgstr "siden"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:50
-msgid "Please have a look at the"
-msgstr ""
+#~ msgid "less than a minute"
+#~ msgstr "mindre enn ett minutt"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:52
-msgid "git history"
-msgstr ""
+#~ msgid "about a minute"
+#~ msgstr "omtrent et minutt"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:54
-msgid "change log"
-msgstr ""
+#~ msgid "about an hour"
+#~ msgstr "omtrent en time"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:55
-msgid "for further info."
-msgstr ""
+#~ msgid "a day"
+#~ msgstr "en dag"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:60
-#, fuzzy
-#| msgid "$Projectname"
-msgid "$Projectname Upgrade Info"
-msgstr "$Projectname"
+#~ msgid "about a month"
+#~ msgstr "omtrent en måned"
-#: ../../extend/addon/hzaddons/upgrade_info/upgrade_info.php:64
-msgid "Do not show this again"
-msgstr ""
+#~ msgid "about a year"
+#~ msgstr "omtrent et år"
-#: ../../extend/addon/hzaddons/hideaside/Mod_Hideaside.php:28
-msgid "Hide Aside App"
-msgstr ""
+#~ msgid " "
+#~ msgstr " "
-#: ../../extend/addon/hzaddons/hideaside/Mod_Hideaside.php:29
-msgid "Fade out aside areas after a while when using endless scroll"
-msgstr ""
+#~ msgid "timeago.numbers"
+#~ msgstr "timeago.numbers"
-#: ../../extend/addon/hzaddons/msgfooter/msgfooter.php:47
-msgid "text to include in all outgoing posts from this site"
-msgstr ""
+#, fuzzy
+#~ msgid "(optional)"
+#~ msgstr "Valgfritt"
#~ msgid "Link color"
#~ msgstr "Lenkefarge"
diff --git a/view/nb/hstrings.php b/view/nb/hstrings.php
index e1ab75bef..526f29d09 100644
--- a/view/nb/hstrings.php
+++ b/view/nb/hstrings.php
@@ -6,31 +6,100 @@ function string_plural_select_nb($n){
}}
App::$rtl = 0;
App::$strings["plural_function_code"] = "(n != 1 ? 1 : 0)";
-App::$strings["Image/photo"] = "Bilde/fotografi";
-App::$strings["Encrypted content"] = "Kryptert innhold";
-App::$strings["Install %1\$s element %2\$s"] = "";
-App::$strings["This post contains an installable %s element, however you lack permissions to install it on this site."] = "Dette innlegget inneholder det installerbare elementet %s, men du mangler tillatelse til å installere det på dette nettstedet.";
-App::$strings["webpage"] = "nettside";
-App::$strings["layout"] = "layout";
-App::$strings["block"] = "byggekloss";
-App::$strings["menu"] = "meny";
-App::$strings["card"] = "";
-App::$strings["article"] = "";
-App::$strings["post"] = "innlegg";
-App::$strings["%1\$s wrote the following %2\$s %3\$s"] = "%1\$s skrev følgende %2\$s %3\$s";
-App::$strings["Click to open/close"] = "Klikk for å åpne/lukke";
-App::$strings["spoiler"] = "";
-App::$strings["View article"] = "";
-App::$strings["View summary"] = "";
-App::$strings["Different viewers will see this text differently"] = "Denne teksten vil se forskjellig ut for ulike besøkende";
-App::$strings["$1 wrote:"] = "$1 skrev:";
+App::$strings["Source channel not found."] = "Fant ikke kildekanalen.";
+App::$strings["Default"] = "Standard";
+App::$strings["Focus (Hubzilla default)"] = "Focus (Hubzilla standard)";
+App::$strings["Submit"] = "Lagre";
+App::$strings["Theme settings"] = "Instillinger for utseende";
+App::$strings["Dark style"] = "Mørk drakt";
+App::$strings["Light style"] = "Lys drakt";
+App::$strings["Common settings"] = "Felles innstillinger";
+App::$strings["Primary theme color"] = "Primærfarge for tema";
+App::$strings["Current color, leave empty for default"] = "Gjeldende farge, la feltet stå tomt for å bruke standardfarge";
+App::$strings["Success theme color"] = "Farge for vellykket utførelse";
+App::$strings["Info theme color"] = "Farge for informasjon";
+App::$strings["Warning theme color"] = "Farge for advarsler";
+App::$strings["Danger theme color"] = "Farge for fare";
+App::$strings["Default to dark mode"] = "Bruk mørk drakt som standard";
+App::$strings["No"] = "Nei";
+App::$strings["Yes"] = "Ja";
+App::$strings["Always use light icons for navbar"] = "Alltid bruk lyse ikoner i navigasjonslinjen";
+App::$strings["Enable this option if you use a dark navbar color in light mode"] = "Slå på dette valget om du bruker mørk farge for navigasjonslinjen med lys drakt";
+App::$strings["Narrow navbar"] = "Smal navigasjonslinje";
+App::$strings["Navigation bar background color"] = "Navigasjonslinjens bakgrunnsfarge";
+App::$strings["Dark navigation bar background color"] = "Mørk bakgrunnsfarge for navigasjonslinjen";
+App::$strings["Set the background color"] = "Angi bakgrunnsfargen";
+App::$strings["Set the dark background color"] = "Angi bakgrunnsfargen for mørk drakt";
+App::$strings["Set the background image"] = "Angi bakgrunnsbilde";
+App::$strings["Set the dark background image"] = "Angi bakgrunnsbilde for mørk drakt";
+App::$strings["Set font-size for the entire application"] = "Angi skriftstørrelsen for hele programmet";
+App::$strings["Examples: 1rem, 100%, 16px"] = "For eksempel: 1rem, 100%, 16px";
+App::$strings["Set radius of corners in rem"] = "Angi hjørneradius i rem";
+App::$strings["Leave empty for default radius"] = "La feltet stå tomt for å bruke standard radius";
+App::$strings["Set maximum width of content region in rem"] = "Set maksbredde for hovedregionen i rem";
+App::$strings["Leave empty for default width"] = "La feltet stå tomt for å bruke standard bredde";
+App::$strings["Set size of conversation author photo"] = "Angi størrelsen for samtalens forfatterbilde";
+App::$strings["Leave empty for default size"] = "La feltet stå tomt for å bruke standard størrelse";
+App::$strings["Set size of followup author photos"] = "Angi størrelsen på forfatterbilder ved oppfølging";
+App::$strings["Show advanced settings"] = "Vis avanserte innstillinger";
+App::$strings["%1\$s's bookmarks"] = "%1\$s sine bokmerker";
+App::$strings["This is the home page of %s."] = "Dette er hjemmesiden til %s.";
+App::$strings["Delegation session ended."] = "Delegasjonsøkt avsluttet.";
+App::$strings["Logged out."] = "Logget ut.";
+App::$strings["Email validation is incomplete. Please check your email."] = "Verifisering via epost er ikke fullført. Sjekk eposten din.";
+App::$strings["Failed authentication"] = "Mislykket autentisering";
+App::$strings["Login failed."] = "Innlogging mislyktes.";
+App::$strings["View PDF"] = "Vis PDF";
+App::$strings[" by "] = " av ";
+App::$strings[" on "] = "på";
+App::$strings["Embedded content"] = "Innebygget innhold";
+App::$strings["Embedding disabled"] = "Innbygging avskrudd";
+App::$strings["l F d, Y \\@ g:i A"] = "l F d, Y \\@ g:i A";
+App::$strings["Starts:"] = "Starter:";
+App::$strings["Finishes:"] = "Slutter:";
+App::$strings["Location:"] = "Plassering:";
+App::$strings["l F d, Y"] = "l d. F, Y";
+App::$strings["Start:"] = "Start:";
+App::$strings["End:"] = "Slutt:";
+App::$strings["Timezone"] = "Tidssone";
+App::$strings["This event has been added to your calendar."] = "Denne hendelsen er lagt til i din kalender.";
+App::$strings["event"] = "hendelse";
+App::$strings["Not specified"] = "Ikke spesifisert";
+App::$strings["Needs Action"] = "Trenger handling";
+App::$strings["Completed"] = "Ferdig";
+App::$strings["In Process"] = "Igang";
+App::$strings["Cancelled"] = "Avbrutt";
+App::$strings["Mobile"] = "Mobil";
+App::$strings["Home"] = "Hjem";
+App::$strings["Home, Voice"] = "Hjem, telefon";
+App::$strings["Home, Fax"] = "Hjem, fax";
+App::$strings["Work"] = "Arbeid";
+App::$strings["Work, Voice"] = "Arbeid, telefon";
+App::$strings["Work, Fax"] = "Arbeid, fax";
+App::$strings["Other"] = "Annen";
+App::$strings["unknown"] = "ukjent";
+App::$strings["Permission denied"] = "Tillatelse avvist";
+App::$strings["Visible to anybody on the internet."] = "Synlig for alle.";
+App::$strings["Visible to you only."] = "Synlig bare for deg.";
+App::$strings["Visible to anybody in this network."] = "Synlig for alle i dette nettverket.";
+App::$strings["Visible to anybody authenticated."] = "Synlig for alle som er autentisert.";
+App::$strings["Visible to anybody on %s."] = "Synlig for alle på %s.";
+App::$strings["Visible to all connections."] = "Synlig for alle forbindelser.";
+App::$strings["Visible to approved connections."] = "Synlig for godkjente forbindelser.";
+App::$strings["Visible to specific connections."] = "Synlig for spesifikke forbindelser.";
+App::$strings["&#x1f501; Repeated %1\$s's %2\$s"] = "&#x1f501; videreformidlet %2\$s til %1\$s ";
+App::$strings["Privacy group not found."] = "Personverngruppen ble ikke funnet.";
+App::$strings["Privacy group is empty."] = "Personverngruppen er tom.";
+App::$strings["Privacy group: %s"] = "Personverngruppe: %s";
+App::$strings["Connection: %s"] = "Forbindelse: %s";
+App::$strings["Connection not found."] = "Forbindelsen ble ikke funnet.";
App::$strings["Off"] = "Av";
App::$strings["On"] = "På";
App::$strings["Calendar"] = "Kalender";
-App::$strings["Start calendar week on Monday"] = "";
-App::$strings["Default is Sunday"] = "";
-App::$strings["Event Timezone Selection"] = "";
-App::$strings["Allow event creation in timezones other than your own."] = "";
+App::$strings["Start calendar week on Monday"] = "Mandag er ukestart";
+App::$strings["Default is Sunday"] = "Søndag er standard";
+App::$strings["Event Timezone Selection"] = "Tidssone for arrangementet";
+App::$strings["Allow event creation in timezones other than your own."] = "Tillat arrangementer i andre tidssoner enn din egen.";
App::$strings["Channel Home"] = "Kanalhjem";
App::$strings["Search by Date"] = "Søk etter dato";
App::$strings["Ability to select posts by date ranges"] = "Mulighet for å velge innlegg etter datoområde";
@@ -41,19 +110,21 @@ App::$strings["Comments will be displayed separately"] = "Kommentarer blir vist
App::$strings["Connections"] = "Forbindelser";
App::$strings["Connection Filtering"] = "Filtrer forbindelser";
App::$strings["Filter incoming posts from connections based on keywords/content"] = "Filtrer innkommende innlegg fra forbindelser basert på nøkkelord/innhold";
-App::$strings["Conversation"] = "";
-App::$strings["Emoji Reactions"] = "";
-App::$strings["Add emoji reaction ability to posts"] = "";
+App::$strings["Conversation"] = "Samtale";
+App::$strings["Emoji Reactions"] = "Hurtigsvar med Emojier";
+App::$strings["Add emoji reaction ability to posts"] = "Legger til en knapp for hurtige svar på innlegg med en emoji";
App::$strings["Dislike Posts"] = "Mislik innlegg";
App::$strings["Ability to dislike posts/comments"] = "Mulighet til å mislike innlegg/kommentarer";
-App::$strings["Star Posts"] = "Stjerneinnlegg";
-App::$strings["Ability to mark special posts with a star indicator"] = "Mulighet til å merke spesielle innlegg med en stjerne";
-App::$strings["Reply on comment"] = "";
-App::$strings["Ability to reply on selected comment"] = "";
+App::$strings["Star Posts"] = "Favorittmerk innlegg";
+App::$strings["Ability to mark conversations with a star"] = "Mulighet til å favorittmerke innlegg med en stjerne";
+App::$strings["File Posts"] = "Arkivér innlegg";
+App::$strings["Ability to file posts"] = "Mulighet til å arkivere innlegg i mapper";
+App::$strings["Reply on comment"] = "Svar på kommentar";
+App::$strings["Ability to reply on selected comment"] = "Mulighet for å svare på en valgt kommentar";
App::$strings["Directory"] = "Katalog";
App::$strings["Advanced Directory Search"] = "Avansert katalogsøk";
App::$strings["Allows creation of complex directory search queries"] = "Gjør det mulig å bruke avanserte kriterier ved søk i katalogen";
-App::$strings["Editor"] = "";
+App::$strings["Editor"] = "Innleggsredigering";
App::$strings["Post Categories"] = "Innleggskategorier";
App::$strings["Add categories to your posts"] = "Legg kategorier til dine innlegg";
App::$strings["Large Photos"] = "Store bilder";
@@ -61,7 +132,7 @@ App::$strings["Include large (1024px) photo thumbnails in posts. If not enabled,
App::$strings["Even More Encryption"] = "Enda mer kryptering";
App::$strings["Allow optional encryption of content end-to-end with a shared secret key"] = "Tillat valgfri kryptering av innhold ende-til-ende via en delt hemmelig nøkkel";
App::$strings["Disable Comments"] = "Slå av kommentarer";
-App::$strings["Provide the option to disable comments for a post"] = "Slår på valg for å ikke tillate kommentarer til innlegget";
+App::$strings["Provide the option to disable comments for a post"] = "Gjør det mulig å slå av kommentarer for et innlegg";
App::$strings["Delayed Posting"] = "Tidfest publisering";
App::$strings["Allow posts to be published at a later date"] = "Tillat innlegg å bli publisert på et senere tidspunkt";
App::$strings["Content Expiration"] = "Innholdet utløper";
@@ -70,8 +141,8 @@ App::$strings["Suppress Duplicate Posts/Comments"] = "Forhindre duplikat av innl
App::$strings["Prevent posts with identical content to be published with less than two minutes in between submissions."] = "Forhindre innlegg med identisk innhold fra å bli publisert hvis det er mindre enn to minutter mellom innsendingene.";
App::$strings["Auto-save drafts of posts and comments"] = "Automatisk lagring av kladd for innlegg og kommentarer";
App::$strings["Automatically saves post and comment drafts in local browser storage to help prevent accidental loss of compositions"] = "Lagrer automatisk utkast til innlegg og kommentarer i nettleserens interne lager for å unngå å tape innholet ved et uhell";
-App::$strings["Manage"] = "";
-App::$strings["Navigation Channel Select"] = "Navigasjon kanalvalg";
+App::$strings["Manage"] = "Administrasjon";
+App::$strings["Navigation Channel Select"] = "Enklere kanalvalg";
App::$strings["Change channels directly from within the navigation dropdown menu"] = "Endre kanaler direkte fra navigasjonsmenyen";
App::$strings["Network"] = "Nettverk";
App::$strings["Events Filter"] = "Arrangementsfilter";
@@ -93,251 +164,290 @@ App::$strings["Ability to display only posts that you've interacted on"] = "Muli
App::$strings["Photos"] = "Bilder";
App::$strings["Photo Location"] = "Bildeplassering";
App::$strings["If location data is available on uploaded photos, link this to a map."] = "Hvis plasseringsdata er tilgjengelige i opplastede bilder, plasser dette på et kart.";
-App::$strings["Flag Adult Photos"] = "";
-App::$strings["Provide photo edit option to hide inappropriate photos from default album view"] = "";
-App::$strings["Profiles"] = "";
+App::$strings["Flag Adult Photos"] = "Merk bilder med voksent innhold";
+App::$strings["Provide photo edit option to hide inappropriate photos from default album view"] = "Gir mulighet for å merke bilder med upassende innhold fra standard albumvisning";
+App::$strings["Profiles"] = "Profiler";
App::$strings["Advanced Profiles"] = "Avanserte profiler";
App::$strings["Additional profile sections and selections"] = "Ytterlige seksjoner og utvalg til profilen";
App::$strings["Profile Import/Export"] = "Profil-import/-eksport";
App::$strings["Save and load profile details across sites/channels"] = "Lagre og åpne profildetaljer på tvers av nettsteder/kanaler";
App::$strings["Multiple Profiles"] = "Flere profiler";
App::$strings["Ability to create multiple profiles"] = "Mulig å lage flere profiler";
-App::$strings["%1\$s's bookmarks"] = "%1\$s sine bokmerker";
-App::$strings["unknown"] = "ukjent";
App::$strings["Permission denied."] = "Tillatelse avslått.";
App::$strings["Item was not found."] = "Elementet ble ikke funnet.";
-App::$strings["Unknown error."] = "";
+App::$strings["Unknown error."] = "Ukjent feil.";
App::$strings["No source file."] = "Ingen kildefil.";
App::$strings["Cannot locate file to replace"] = "Kan ikke finne filen som skal byttes ut";
App::$strings["Cannot locate file to revise/update"] = "Finner ikke filen som skal revideres/oppdateres";
+App::$strings["Filename too long"] = "Filnavnet er for langt";
App::$strings["File exceeds size limit of %d"] = "Filens størrelse overgår grensen på %d";
App::$strings["You have reached your limit of %1$.0f Mbytes attachment storage."] = "Du har nådd din lagringsgrense for vedlegg på %1$.0f Mbytes.";
App::$strings["File upload failed. Possible system limit or action terminated."] = "Mislyktes med å laste opp filen. Mulig systemgrense eller handling avbrutt.";
App::$strings["Stored file could not be verified. Upload failed."] = "Lagret fil kunne ikke bekreftes. Opplasting mislyktes.";
App::$strings["Path not available."] = "Stien er ikke tilgjengelig.";
App::$strings["Empty pathname"] = "Tomt sti-navn";
-App::$strings["duplicate filename or path"] = "duplikat av filnavn eller sti";
-App::$strings["Path not found."] = "Stien ble ikke funnet.";
-App::$strings["mkdir failed."] = "mkdir mislyktes.";
+App::$strings["Pathname too long"] = "Filbanen er for lang";
+App::$strings["duplicate filename or path"] = "duplikat av filnavn eller filbane";
+App::$strings["Path not found."] = "Filbanen ble ikke funnet.";
+App::$strings["mkdir failed."] = "mkdir fikk en feil.";
App::$strings["database storage failed."] = "databaselagring mislyktes.";
-App::$strings["Empty path"] = "Tom sti";
-App::$strings["%s shared an %s with you"] = "Filer: delt med meg";
-App::$strings["%s shared a %s with you"] = "";
-App::$strings["image"] = "Bilde";
-App::$strings["file"] = "";
-App::$strings["url: "] = "";
-App::$strings["error_code: "] = "";
-App::$strings["error_string: "] = "";
-App::$strings["content-type: "] = "";
-App::$strings["Friendica"] = "Friendica";
-App::$strings["OStatus"] = "OStatus";
-App::$strings["GNU-Social"] = "";
-App::$strings["RSS/Atom"] = "RSS/Atom";
-App::$strings["ActivityPub"] = "";
-App::$strings["Email"] = "E-post";
-App::$strings["Diaspora"] = "Diaspora";
-App::$strings["Facebook"] = "Facebook";
-App::$strings["Zot"] = "Zot";
-App::$strings["LinkedIn"] = "LinkedIn";
-App::$strings["XMPP/IM"] = "XMPP/IM";
-App::$strings["MySpace"] = "MySpace";
-App::$strings["Delegation session ended."] = "";
-App::$strings["Logged out."] = "Logget ut.";
-App::$strings["Email validation is incomplete. Please check your email."] = "";
-App::$strings["Failed authentication"] = "Mislykket autentisering";
-App::$strings["Login failed."] = "Innlogging mislyktes.";
-App::$strings["This is the home page of %s."] = "";
-App::$strings["Remote authentication"] = "Fjernautentisering";
-App::$strings["Click to authenticate to your home hub"] = "Klikk for å godkjennes mot din hjemme-hub";
-App::$strings["Channels"] = "Kanaler";
-App::$strings["Manage your channels"] = "Behandle kanalene dine";
-App::$strings["Settings"] = "Innstillinger";
-App::$strings["Account/Channel Settings"] = "Konto-/kanalinnstillinger";
-App::$strings["Logout"] = "Logg ut";
-App::$strings["End this session"] = "Avslutt denne økten";
-App::$strings["View Profile"] = "Vis profil";
-App::$strings["Your profile page"] = "Din profilside";
-App::$strings["Edit Profiles"] = "Rediger profiler";
-App::$strings["Manage/Edit profiles"] = "Håndter/endre profiler";
-App::$strings["Edit Profile"] = "Rediger profil";
-App::$strings["Edit your profile"] = "Rediger profil";
-App::$strings["Login"] = "Logg inn";
-App::$strings["Sign in"] = "Logg på";
-App::$strings["Take me home"] = "";
-App::$strings["Log me out of this site"] = "";
-App::$strings["Register"] = "Registrer";
-App::$strings["Create an account"] = "Lag en konto";
-App::$strings["Help"] = "Hjelp";
-App::$strings["Help and documentation"] = "Hjelp og dokumentasjon";
-App::$strings["Search"] = "Søk";
-App::$strings["Search site @name, !forum, #tag, ?docs, content"] = "Søk etter @navn, !forum, #emne, ?dokumentasjon eller innhold";
-App::$strings["Admin"] = "Administrator";
-App::$strings["Site Setup and Configuration"] = "Nettstedsoppsett og -konfigurasjon";
-App::$strings["Loading"] = "Laster...";
-App::$strings["@name, #tag, ?doc, content"] = "@navn, #merkelapp, ?dokumentasjon, innhold";
-App::$strings["Please wait..."] = "Vennligst vent...";
-App::$strings["Apps"] = "Apper";
-App::$strings["Channel Apps"] = "";
-App::$strings["System Apps"] = "";
-App::$strings["Pinned Apps"] = "";
-App::$strings["Featured Apps"] = "Fremhevede apper";
-App::$strings["Channel"] = "Kanal";
-App::$strings["Status Messages and Posts"] = "Statusmeldinger og -innlegg";
-App::$strings["About"] = "Om";
-App::$strings["Profile Details"] = "Profildetaljer";
-App::$strings["Photo Albums"] = "Fotoalbum";
-App::$strings["Files"] = "Filer";
-App::$strings["Files and Storage"] = "Filer og lagring";
-App::$strings["Chatrooms"] = "Chatrom";
-App::$strings["Bookmarks"] = "Bokmerker";
-App::$strings["Saved Bookmarks"] = "Lagrede bokmerker";
-App::$strings["Webpages"] = "Websider";
-App::$strings["View Webpages"] = "";
-App::$strings["l F d, Y \\@ g:i A"] = "l F d, Y \\@ g:i A";
-App::$strings["Starts:"] = "Starter:";
-App::$strings["Finishes:"] = "Slutter:";
-App::$strings["Location:"] = "Plassering:";
-App::$strings["l F d, Y"] = "";
-App::$strings["Start:"] = "";
-App::$strings["End:"] = "";
-App::$strings["Timezone"] = "Tidssone";
-App::$strings["This event has been added to your calendar."] = "Denne hendelsen er lagt til i din kalender.";
-App::$strings["event"] = "hendelse";
-App::$strings["Not specified"] = "Ikke spesifisert";
-App::$strings["Needs Action"] = "Trenger handling";
-App::$strings["Completed"] = "Ferdig";
-App::$strings["In Process"] = "Igang";
-App::$strings["Cancelled"] = "Avbrutt";
-App::$strings["Mobile"] = "mobil";
-App::$strings["Home"] = "Hjem";
-App::$strings["Home, Voice"] = "";
-App::$strings["Home, Fax"] = "";
-App::$strings["Work"] = "";
-App::$strings["Work, Voice"] = "";
-App::$strings["Work, Fax"] = "";
-App::$strings["Other"] = "Annen";
-App::$strings["Image exceeds website size limit of %lu bytes"] = "Bilde overstiger nettstedets størrelsesbegrensning på %lu bytes";
-App::$strings["Image file is empty."] = "Bildefilen er tom.";
-App::$strings["Unable to process image"] = "Kan ikke behandle bildet";
-App::$strings["Photo storage failed."] = "Bildelagring mislyktes.";
-App::$strings["a new photo"] = "et nytt bilde";
-App::$strings["__ctx:photo_upload__ %1\$s posted %2\$s to %3\$s"] = "%1\$s la inn %2\$s til %3\$s";
-App::$strings["Recent Photos"] = "Nye bilder";
-App::$strings["Upload New Photos"] = "Last opp nye bilder";
-App::$strings["Permission denied"] = "Tillatelse avvist";
-App::$strings["Visible to anybody on the internet."] = "Synlig for enhver på Internett.";
-App::$strings["Visible to you only."] = "Synlig bare for deg.";
-App::$strings["Visible to anybody in this network."] = "Synlig for enhver i dette nettverket.";
-App::$strings["Visible to anybody authenticated."] = "Synlig for enhver som er autentisert.";
-App::$strings["Visible to anybody on %s."] = "Synlig for alle på %s.";
-App::$strings["Visible to all connections."] = "Synlig for alle forbindelser.";
-App::$strings["Visible to approved connections."] = "Synlig for godkjente forbindelser.";
-App::$strings["Visible to specific connections."] = "Synlig for spesifikke forbindelser.";
-App::$strings["&#x1f501; Repeated %1\$s's %2\$s"] = "";
-App::$strings["Item not found."] = "Elementet ble ikke funnet.";
-App::$strings["Privacy group not found."] = "Personverngruppen ble ikke funnet.";
-App::$strings["Privacy group is empty."] = "Personverngruppen er tom.";
-App::$strings["Privacy group: %s"] = "Personverngruppe: %s";
-App::$strings["Connection: %s"] = "Forbindelse: %s";
-App::$strings["Connection not found."] = "Forbindelsen ble ikke funnet.";
-App::$strings["The provided email address is not valid"] = "";
-App::$strings["The provided email domain is not among those allowed on this site"] = "";
-App::$strings["The provided email address is already registered at this site"] = "";
-App::$strings["There is a pending registration for this address - click \"Register\" to continue verification"] = "";
-App::$strings["An invitation is required."] = "En invitasjon er påkrevd.";
-App::$strings["Invitation could not be verified."] = "Invitasjon kunne ikke bekreftes.";
-App::$strings["Please enter the required information."] = "Vennligst skriv inn nødvendig informasjon.";
-App::$strings["Failed to store account information."] = "Mislyktes med å lagre kontoinformasjon.";
-App::$strings["Registration confirmation for %s"] = "Registreringsbekreftelse for %s";
-App::$strings["Registration request at %s"] = "Registreringsforespørsel hos %s";
-App::$strings["your registration password"] = "ditt registreringspassord";
-App::$strings["Registration details for %s"] = "Registreringsdetaljer for %s";
-App::$strings["Account approved."] = "Konto godkjent.";
-App::$strings["Registration revoked for %s"] = "Registrering trukket tilbake for %s";
-App::$strings["Could not revoke registration for %s"] = "";
-App::$strings["Click here to upgrade."] = "Klikk her for å oppgradere.";
-App::$strings["This action exceeds the limits set by your subscription plan."] = "Denne handlingen går utenfor grensene satt i din abonnementsplan.";
-App::$strings["This action is not available under your subscription plan."] = "Denne handlingen er ikke tilgjengelig i din abonnementsplan.";
-App::$strings["open"] = "";
-App::$strings["closed"] = "";
-App::$strings["Registration is currently"] = "";
-App::$strings["please come back"] = "";
-App::$strings["Profile Photos"] = "Profilbilder";
+App::$strings["Empty path"] = "Tom filbane";
+App::$strings["%s shared an %s with you"] = "%s delte en %s med deg";
+App::$strings["%s shared a %s with you"] = "%s delte et %s med deg";
+App::$strings["image"] = "bilde";
+App::$strings["file"] = "fil";
+App::$strings["Select a profile to assign to this contact"] = "Velg en profil for denne kontakten";
+App::$strings["Frequently"] = "Ofte";
+App::$strings["Hourly"] = "Hver time";
+App::$strings["Twice daily"] = "To ganger daglig";
+App::$strings["Daily"] = "Daglig";
+App::$strings["Weekly"] = "Ukentlig";
+App::$strings["Monthly"] = "Månedlig";
+App::$strings["Male"] = "Mannlig";
+App::$strings["Female"] = "Kvinnelig";
+App::$strings["Currently Male"] = "For tiden mann";
+App::$strings["Currently Female"] = "For tiden kvinne";
+App::$strings["Mostly Male"] = "For det meste mann";
+App::$strings["Mostly Female"] = "For det meste kvinne";
+App::$strings["Transgender"] = "Transkjønnet";
+App::$strings["Intersex"] = "interkjønnet";
+App::$strings["Transsexual"] = "Transseksuell";
+App::$strings["Hermaphrodite"] = "Hermafroditt";
+App::$strings["Neuter"] = "Intetkjønn";
+App::$strings["Non-specific"] = "Ubestemt";
+App::$strings["Undecided"] = "Ubestemt";
+App::$strings["Males"] = "Menn";
+App::$strings["Females"] = "Kvinner";
+App::$strings["Gay"] = "Homo";
+App::$strings["Lesbian"] = "Lesbisk";
+App::$strings["No Preference"] = "Ingen preferanse";
+App::$strings["Bisexual"] = "Biseksuell";
+App::$strings["Autosexual"] = "Autoseksuell";
+App::$strings["Abstinent"] = "Avholdende";
+App::$strings["Virgin"] = "Jomfru";
+App::$strings["Deviant"] = "Avviker";
+App::$strings["Fetish"] = "Fetisj";
+App::$strings["Oodles"] = "Masse";
+App::$strings["Nonsexual"] = "Ikke-seksuell";
+App::$strings["Single"] = "Enslig";
+App::$strings["Lonely"] = "Ensom";
+App::$strings["Available"] = "Tilgjengelig";
+App::$strings["Unavailable"] = "Ikke tilgjengelig";
+App::$strings["Has crush"] = "Er forelsket";
+App::$strings["Infatuated"] = "Betatt";
+App::$strings["Dating"] = "Sammen med";
+App::$strings["Unfaithful"] = "Utro";
+App::$strings["Sex Addict"] = "Sexavhengig";
+App::$strings["Friends"] = "Venner";
+App::$strings["Friends/Benefits"] = "Venner/Frynsegoder";
+App::$strings["Casual"] = "Tilfeldig";
+App::$strings["Engaged"] = "Forlovet";
+App::$strings["Married"] = "Gift";
+App::$strings["Imaginarily married"] = "Gift i fantasien";
+App::$strings["Partners"] = "Partnere";
+App::$strings["Cohabiting"] = "Samboer";
+App::$strings["Common law"] = "Samboer";
+App::$strings["Happy"] = "Lykkelig";
+App::$strings["Not looking"] = "Ikke på utkikk";
+App::$strings["Swinger"] = "Partnerbytte";
+App::$strings["Betrayed"] = "Bedratt";
+App::$strings["Separated"] = "Separert";
+App::$strings["Unstable"] = "Ustabilt";
+App::$strings["Divorced"] = "Skilt";
+App::$strings["Imaginarily divorced"] = "Skilt i fantasien";
+App::$strings["Widowed"] = "Enke";
+App::$strings["Uncertain"] = "Usikkert";
+App::$strings["It's complicated"] = "Det er komplisert";
+App::$strings["Don't care"] = "Bryr meg ikke";
+App::$strings["Ask me"] = "Spør meg";
App::$strings[" and "] = " og ";
-App::$strings[", "] = " ";
+App::$strings[", "] = ".";
App::$strings["Profile Photo"] = "Profilbilde";
-App::$strings["Cover Photo"] = "Forsidebilder";
+App::$strings["Cover Photo"] = "Forsidebilde";
App::$strings["public profile"] = "offentlig profil";
-App::$strings["%1\$s %2\$s has been updated to %3\$s."] = "%1\$s har oppdatert %2\$s, endret %3\$s.";
+App::$strings["%1\$s %2\$s has been updated to %3\$s."] = "%1\$s %2\$s har blitt oppdatert til %3\$s.";
App::$strings["%1\$s updated the %2\$s. Changed %3\$s."] = "%1\$s har oppdatert %2\$s, endret %3\$s.";
+App::$strings["%d invitation available"] = array(
+ 0 => "%d invitasjon tilgjengelig",
+ 1 => "%d invitasjoner tilgjengelig",
+);
+App::$strings["Advanced"] = "Avansert";
+App::$strings["Find Channels"] = "Finn kanaler";
+App::$strings["Enter name or interest"] = "Skriv navn eller interesse";
+App::$strings["Connect/Follow"] = "Forbindelse/Følg";
+App::$strings["Examples: Robert Morgenstein, Fishing"] = "Eksempler: Ola Nordmann, fisking";
+App::$strings["Find"] = "Finn";
+App::$strings["Channel Suggestions"] = "Kanalforslag";
+App::$strings["Random Profile"] = "Tilfeldig profil";
+App::$strings["Invite Friends"] = "Inviter venner";
+App::$strings["Advanced example: name=fred and country=iceland"] = "Avansert eksempel: navn=fred og land=island";
+App::$strings["Everything"] = "Alt";
+App::$strings["Categories"] = "Kategorier";
+App::$strings["Common Connections"] = "Felles forbindelser";
+App::$strings["View all %d common connections"] = "Vis alle %d felles forbindelser";
+App::$strings["INVALID EVENT DISMISSED!"] = "UGYLDIG HELDELSE AVVIST!";
+App::$strings["Summary: "] = "Sammendrag:";
+App::$strings["Unknown"] = "Ukjent";
+App::$strings["Date: "] = "Dato:";
+App::$strings["Reason: "] = "Grunn:";
+App::$strings["INVALID CARD DISMISSED!"] = "UGYLDIG KORT AVVIST!";
+App::$strings["Name: "] = "Navn:";
App::$strings["A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name."] = "En slettet gruppe med dette navnet ble gjenopprettet. Eksisterende tillatelser for elementet <strong>kan</strong> gjelde for denne gruppen og fremtidige medlemmer. Hvis du ønsket noe annet, vennligst lag en ny gruppe med et annet navn.";
App::$strings["Add new connections to this privacy group"] = "Legg nye forbindelser i denne personverngruppen";
App::$strings["edit"] = "endre";
App::$strings["Privacy Groups"] = "Personverngrupper";
App::$strings["Edit group"] = "Endre gruppe";
-App::$strings["Manage privacy groups"] = "";
+App::$strings["Manage privacy groups"] = "Administrer personverngrupper";
App::$strings["Channels not in any privacy group"] = "Kanaler uten personverngruppe";
App::$strings["add"] = "legg til";
-App::$strings["default"] = "standard";
-App::$strings["Select an alternate language"] = "Velg et annet språk";
-App::$strings["Unable to import a removed channel."] = "";
-App::$strings["Cannot create a duplicate channel identifier on this system. Import failed."] = "Kan ikke lage en kopi av kanal-identifikatoren på dette systemet. Import mislyktes.";
-App::$strings["Unable to create a unique channel address. Import failed."] = "Klarte ikke å lage en unik kanaladresse. Import mislyktes.";
-App::$strings["Cloned channel not found. Import failed."] = "Klonet kanal ble ikke funnet. Import mislyktes.";
-App::$strings["Miscellaneous"] = "Forskjellig";
-App::$strings["Birthday"] = "Fødselsdag:";
-App::$strings["Age: "] = "Alder: ";
-App::$strings["YYYY-MM-DD or MM-DD"] = "YYYY-MM-DD eller MM-DD";
-App::$strings["Required"] = "Påkrevd";
-App::$strings["never"] = "aldri";
-App::$strings["less than a second ago"] = "for mindre enn ett sekund siden";
-App::$strings["__ctx:e.g. 22 hours ago, 1 minute ago__ %1\$d %2\$s ago"] = "%1\$d %2\$s siden";
-App::$strings["__ctx:relative_date__ year"] = array(
- 0 => "år",
- 1 => "år",
-);
-App::$strings["__ctx:relative_date__ month"] = array(
- 0 => "måned",
- 1 => "måneder",
+App::$strings["Trending"] = "Populært";
+App::$strings["Tags"] = "Merkelapper";
+App::$strings["Keywords"] = "Nøkkelord";
+App::$strings["have"] = "har";
+App::$strings["has"] = "har";
+App::$strings["want"] = "ønsker";
+App::$strings["wants"] = "ønsker";
+App::$strings["like"] = "liker";
+App::$strings["likes"] = "liker";
+App::$strings["dislike"] = "misliker";
+App::$strings["dislikes"] = "misliker";
+App::$strings["__ctx:noun__ Like"] = array(
+ 0 => "Liker",
+ 1 => "Liker",
);
-App::$strings["__ctx:relative_date__ week"] = array(
- 0 => "uke",
- 1 => "uker",
+App::$strings["Profile Photos"] = "Profilbilder";
+App::$strings["The provided email address is not valid"] = "Epostadressen som er oppgitt er ikke gyldig";
+App::$strings["The provided email domain is not among those allowed on this site"] = "Epostadressen som er oppgitt har ikke et domene som er tillatt på dette nettstedet";
+App::$strings["The provided email address is already registered at this site"] = "Epostadressen som ble oppgitt er allerede registrert på dette nettstedet";
+App::$strings["There is a pending registration for this address - click \"Register\" to continue verification"] = "Det er en ventende registrering for denne addressen - klikk på \"Registrér\" for å fortsette verifiseringen";
+App::$strings["An invitation is required."] = "En invitasjon er påkrevd.";
+App::$strings["Invitation could not be verified."] = "Invitasjon kunne ikke bekreftes.";
+App::$strings["Failed to store account information."] = "Mislyktes med å lagre kontoinformasjon.";
+App::$strings["Registration confirmation for %s"] = "Registreringsbekreftelse for %s";
+App::$strings["Registration request at %s"] = "Registreringsforespørsel hos %s";
+App::$strings["your registration password"] = "ditt registreringspassord";
+App::$strings["Registration details for %s"] = "Registreringsdetaljer for %s";
+App::$strings["Account approved."] = "Konto godkjent.";
+App::$strings["Registration revoked for %s"] = "Registrering trukket tilbake for %s";
+App::$strings["Could not revoke registration for %s"] = "Registreringen for %s kunne ikke tilbakekalles";
+App::$strings["Click here to upgrade."] = "Klikk her for å oppgradere.";
+App::$strings["This action exceeds the limits set by your subscription plan."] = "Denne handlingen går utenfor grensene satt i din abonnementsplan.";
+App::$strings["This action is not available under your subscription plan."] = "Denne handlingen er ikke tilgjengelig i din abonnementsplan.";
+App::$strings["open"] = "åpen";
+App::$strings["closed"] = "lukket";
+App::$strings["Registration is currently"] = "Registrering er for øyeblikket";
+App::$strings["please come back"] = "kom tilbake";
+App::$strings["Delete this item?"] = "Slett dette elementet?";
+App::$strings["Item deleted"] = "Element slettet";
+App::$strings["Comment"] = "Kommentar";
+App::$strings["expand"] = "utvid";
+App::$strings["collapse"] = "kollaps";
+App::$strings["Password too short"] = "Passordet er for kort";
+App::$strings["Passwords do not match"] = "Passordene er ikke like";
+App::$strings["everybody"] = "alle";
+App::$strings["Secret Passphrase"] = "Hemmelig passordsetning";
+App::$strings["Passphrase hint"] = "Hint om passordsetning";
+App::$strings["Notice: Permissions have changed but have not yet been submitted."] = "Varsel: Tillatelser har blitt endret, men de har ennå ikke blitt sendt inn.";
+App::$strings["close all"] = "lukk alle";
+App::$strings["Nothing new here"] = "Ikke noe nytt her";
+App::$strings["Rate This Channel (this is public)"] = "Vurder denne kanalen (dette er offentlig)";
+App::$strings["Rating"] = "Vurdering";
+App::$strings["Describe (optional)"] = "Beskriv (valgfritt)";
+App::$strings["Please enter a link URL"] = "Vennligst skriv inn en lenke URL";
+App::$strings["Unsaved changes. Are you sure you wish to leave this page?"] = "Endringene er ikke lagret. Er du sikker på at du ønsker å forlate denne siden?";
+App::$strings["Location"] = "Plassering";
+App::$strings["lovely"] = "fint";
+App::$strings["wonderful"] = "nydelig";
+App::$strings["fantastic"] = "fantastisk";
+App::$strings["great"] = "kjempebra";
+App::$strings["Your chosen nickname was either already taken or not valid. Please use our suggestion ("] = "Kallenavnet du har valgt er enten allerede opptatt, eller ikke gyldig. Bruk vårt forslag (";
+App::$strings[") or enter a new one."] = ") eller velg et annet.";
+App::$strings["Thank you, this nickname is valid."] = "Takk, dette kallenavnet er gyldig.";
+App::$strings["A channel name is required."] = "Et kanalnavn er påkrevet.";
+App::$strings["This is a "] = "Dette er et";
+App::$strings[" channel name"] = "kanalnavn";
+App::$strings["Back to reply"] = "Tilbake til svar";
+App::$strings["Pinned"] = "Festet";
+App::$strings["Pin to the top"] = "Fest til toppen";
+App::$strings["Unpin from the top"] = "Ikke fest til toppen lengre";
+App::$strings["Double click to exit zoom"] = "Dobbelklikk for å gå ut av forstørring";
+App::$strings["%d minutes"] = array(
+ 0 => "%d minutter",
+ 1 => "%d minutter",
);
-App::$strings["__ctx:relative_date__ day"] = array(
- 0 => "dag",
- 1 => "dager",
+App::$strings["about %d hours"] = array(
+ 0 => "omtrent %d timer",
+ 1 => "omtrent %d timer",
);
-App::$strings["__ctx:relative_date__ hour"] = array(
- 0 => "time",
- 1 => "timer",
+App::$strings["%d days"] = array(
+ 0 => "%d dager",
+ 1 => "%d dager",
);
-App::$strings["__ctx:relative_date__ minute"] = array(
- 0 => "minutt",
- 1 => "minutter",
+App::$strings["%d months"] = array(
+ 0 => "%d måneder",
+ 1 => "%d måneder",
);
-App::$strings["__ctx:relative_date__ second"] = array(
- 0 => "sekund",
- 1 => "sekunder",
+App::$strings["%d years"] = array(
+ 0 => "%d år",
+ 1 => "%d år",
);
-App::$strings["%1\$s's birthday"] = "%1\$s sin fødselsdag";
-App::$strings["Happy Birthday %1\$s"] = "Gratulerer med dagen, %1\$s";
+App::$strings["January"] = "januar";
+App::$strings["February"] = "februar";
+App::$strings["March"] = "mars";
+App::$strings["April"] = "april";
+App::$strings["__ctx:long__ May"] = "mai";
+App::$strings["June"] = "juni";
+App::$strings["July"] = "juli";
+App::$strings["August"] = "august";
+App::$strings["September"] = "september";
+App::$strings["October"] = "oktober";
+App::$strings["November"] = "november";
+App::$strings["December"] = "desember";
+App::$strings["Jan"] = "Jan";
+App::$strings["Feb"] = "Feb";
+App::$strings["Mar"] = "Mar";
+App::$strings["Apr"] = "Apr";
+App::$strings["__ctx:short__ May"] = "mai";
+App::$strings["Jun"] = "Jun";
+App::$strings["Jul"] = "Jul";
+App::$strings["Aug"] = "Aug";
+App::$strings["Sep"] = "Sep";
+App::$strings["Oct"] = "Okt";
+App::$strings["Nov"] = "Nov";
+App::$strings["Dec"] = "Des";
+App::$strings["Sunday"] = "søndag";
+App::$strings["Monday"] = "mandag";
+App::$strings["Tuesday"] = "tirsdag";
+App::$strings["Wednesday"] = "onsdag";
+App::$strings["Thursday"] = "torsdag";
+App::$strings["Friday"] = "fredag";
+App::$strings["Saturday"] = "lørdag";
+App::$strings["Sun"] = "Søn";
+App::$strings["Mon"] = "Man";
+App::$strings["Tue"] = "Tirs";
+App::$strings["Wed"] = "Ons";
+App::$strings["Thu"] = "Tors";
+App::$strings["Fri"] = "Fre";
+App::$strings["Sat"] = "Lør";
+App::$strings["__ctx:calendar__ today"] = "idag";
+App::$strings["__ctx:calendar__ month"] = "måned";
+App::$strings["__ctx:calendar__ week"] = "uke";
+App::$strings["__ctx:calendar__ day"] = "dag";
+App::$strings["__ctx:calendar__ All day"] = "Hele dagen";
+App::$strings["Please stand by while your download is being prepared."] = "Nedlastingen din blir klargjort.";
+App::$strings["Email address not valid"] = "Epostadressen er ikke gyldig";
+App::$strings["Required"] = "Påkrevd";
+App::$strings["OpenWebAuth: %1\$s welcomes %2\$s"] = "OpenWebAuth: %1\$s ønsker %2\$s velkommen";
App::$strings["photo"] = "foto";
App::$strings["channel"] = "kanal";
-App::$strings["comment"] = "kommentar";
+App::$strings["message"] = "melding";
App::$strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s liker %2\$s sin %3\$s";
-App::$strings["likes %1\$s's %2\$s"] = "";
+App::$strings["likes %1\$s's %2\$s"] = "liker %2\$s til %1\$s";
App::$strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s liker ikke %2\$s sin %3\$s";
-App::$strings["doesn't like %1\$s's %2\$s"] = "";
-App::$strings["%1\$s repeated %2\$s's %3\$s"] = "%1\$s deltar kanskje på %2\$ss %3\$s";
-App::$strings["repeated %1\$s's %2\$s"] = "Besøk %1\$s sitt %2\$s";
-App::$strings["This is an unsaved preview"] = "";
-App::$strings["__ctx:title__ Likes"] = "Liker";
-App::$strings["__ctx:title__ Dislikes"] = "Liker ikke";
-App::$strings["__ctx:title__ Attending"] = "Deltar";
-App::$strings["__ctx:title__ Not attending"] = "Deltar ikke";
-App::$strings["__ctx:title__ Might attend"] = "Deltar kanskje";
-App::$strings["__ctx:title__ Repeats"] = "";
+App::$strings["doesn't like %1\$s's %2\$s"] = "liker ikke %2\$s til %1\$s";
+App::$strings["%1\$s repeated %2\$s's %3\$s"] = "%1\$s videreformidlet %3\$s til %2\$s";
+App::$strings["repeated %1\$s's %2\$s"] = "videreformidlet %2\$s til %1\$s";
+App::$strings["This is an unsaved preview"] = "Dette er en forhåndsvisning som ikke er lagret";
App::$strings["Select"] = "Velg";
App::$strings["Delete"] = "Slett";
App::$strings["Toggle Star Status"] = "Skru av og på stjernestatus";
@@ -360,12 +470,10 @@ App::$strings["Delete Selected Items"] = "Slett valgte elementer";
App::$strings["View Source"] = "Vis kilde";
App::$strings["Follow Thread"] = "Følg tråd";
App::$strings["Unfollow Thread"] = "Ikke følg tråd";
+App::$strings["View Profile"] = "Vis profil";
App::$strings["Recent Activity"] = "Nylig aktivitet";
App::$strings["Connect"] = "Koble";
App::$strings["Edit Connection"] = "Endre forbindelse";
-App::$strings["Unknown"] = "Ukjent";
-App::$strings["Approve this item"] = "Fjern denne filen";
-App::$strings["Delete this item"] = "Slett dette elementet?";
App::$strings["%s likes this."] = "%s liker dette.";
App::$strings["%s doesn't like this."] = "%s liker ikke dette.";
App::$strings["<span %1\$s>%2\$d people</span> like this."] = array(
@@ -393,23 +501,24 @@ App::$strings["Where are you right now?"] = "Hvor er du akkurat nå?";
App::$strings["Choose images to embed"] = "Velg bilder";
App::$strings["Choose an album"] = "Velg et album";
App::$strings["Choose a different album..."] = "Velg et annet album...";
-App::$strings["Error getting album list"] = "";
-App::$strings["Error getting photo link"] = "";
-App::$strings["Error getting album"] = "";
-App::$strings["Comments enabled"] = "";
-App::$strings["Comments disabled"] = "";
-App::$strings["Confirm delete"] = "Profilen er slettet.";
+App::$strings["Error getting album list"] = "Klarte ikke å hente albumlisten";
+App::$strings["Error getting photo link"] = "Klarte ikke å hente fotolenken";
+App::$strings["Error getting album"] = "Klarte ikke å hente album";
+App::$strings["Comments enabled"] = "Kommentarer er tillatt";
+App::$strings["Comments disabled"] = "Komentarer er ikke tillatt";
+App::$strings["Confirm delete"] = "Bekreft sletting";
App::$strings["Preview"] = "Forhåndsvisning";
-App::$strings["Share"] = "Del";
+App::$strings["Start a conversation"] = "Start en samtale";
App::$strings["Page link name"] = "Sidens lenkenavn";
App::$strings["Post as"] = "Lag innlegg som";
App::$strings["Bold"] = "Uthevet";
App::$strings["Italic"] = "Kursiv";
+App::$strings["Highlight selected text"] = "Fremhev valgt tekst";
App::$strings["Underline"] = "Understreket";
App::$strings["Quote"] = "Sitat";
App::$strings["Code"] = "Kode";
App::$strings["Attach/Upload file"] = "Last opp fil/vedlegg";
-App::$strings["Embed an image from your albums"] = "";
+App::$strings["Embed an image from your albums"] = "Sett inn et bilde fra albumene dine";
App::$strings["Cancel"] = "Avbryt";
App::$strings["OK"] = "OK";
App::$strings["Toggle voting"] = "Skru av eller på stemming";
@@ -417,13 +526,11 @@ App::$strings["Toggle poll"] = "Spørreskjema (på/av)";
App::$strings["Option"] = "Valg";
App::$strings["Add option"] = "Legg til valg";
App::$strings["Minutes"] = "Minutter";
-App::$strings["Hours"] = "timer";
-App::$strings["Days"] = "dager";
+App::$strings["Hours"] = "Timer";
+App::$strings["Days"] = "Dager";
App::$strings["Allow multiple answers"] = "Tillat flere svar";
-App::$strings["No"] = "Nei";
-App::$strings["Yes"] = "Ja";
App::$strings["Disable comments"] = "Slå av kommentarer";
-App::$strings["Toggle comments"] = "";
+App::$strings["Toggle comments"] = "Slå av/på kommentarer";
App::$strings["Title (optional)"] = "Tittel (valgfri)";
App::$strings["Summary (optional)"] = "Sammendrag (valgfritt)";
App::$strings["Categories (optional, comma-separated list)"] = "Kategorier (valgfri, kommaseparert liste)";
@@ -432,23 +539,27 @@ App::$strings["Other networks and post services"] = "Andre nettverk og innleggst
App::$strings["Set expiration date"] = "Angi utløpsdato";
App::$strings["Set publish date"] = "Angi publiseringsdato";
App::$strings["Encrypt text"] = "Krypter tekst";
-App::$strings["__ctx:noun__ Like"] = array(
- 0 => "Liker",
- 1 => "Liker",
-);
App::$strings["__ctx:noun__ Repeat"] = array(
- 0 => "",
- 1 => "",
+ 0 => "Videresending",
+ 1 => "Videresendinger",
);
App::$strings["__ctx:noun__ Dislike"] = array(
0 => "Liker ikke",
1 => "Liker ikke",
);
+App::$strings["__ctx:noun__ Comment"] = array(
+ 0 => "Kommentar",
+ 1 => "Kommentarer",
+);
+App::$strings["__ctx:noun__ Reply"] = array(
+ 0 => "Svar",
+ 1 => "Svar",
+);
App::$strings["__ctx:noun__ Attending"] = array(
0 => "Deltar",
1 => "Deltar",
);
-App::$strings["__ctx:noun__ Not Attending"] = array(
+App::$strings["__ctx:noun__ Not attending"] = array(
0 => "Deltar ikke",
1 => "Deltar ikke",
);
@@ -456,190 +567,202 @@ App::$strings["__ctx:noun__ Undecided"] = array(
0 => "Ikke bestemt",
1 => "Ikke bestemt",
);
+App::$strings["Remote authentication"] = "Fjernautentisering";
+App::$strings["Click to authenticate to your home hub"] = "Klikk for å godkjennes mot din hjemme-hub";
+App::$strings["Channels"] = "Kanaler";
+App::$strings["Manage your channels"] = "Behandle kanalene dine";
+App::$strings["Settings"] = "Innstillinger";
+App::$strings["Account/Channel Settings"] = "Konto-/kanalinnstillinger";
+App::$strings["Logout"] = "Logg ut";
+App::$strings["End this session"] = "Avslutt denne økten";
+App::$strings["Your profile page"] = "Din profilside";
+App::$strings["Edit Profiles"] = "Rediger profiler";
+App::$strings["Manage/Edit profiles"] = "Håndter/endre profiler";
+App::$strings["Edit Profile"] = "Rediger profil";
+App::$strings["Edit your profile"] = "Rediger profil";
+App::$strings["Login"] = "Logg inn";
+App::$strings["Sign in"] = "Logg på";
+App::$strings["Take me home"] = "Ta meg hjem";
+App::$strings["Log me out of this site"] = "Logg meg ut fra dette nettstedet";
+App::$strings["Register"] = "Registrer";
+App::$strings["Create an account"] = "Lag en konto";
+App::$strings["Help"] = "Hjelp";
+App::$strings["Help and documentation"] = "Hjelp og dokumentasjon";
+App::$strings["Search"] = "Søk";
+App::$strings["Search site @name, !forum, #tag, ?docs, content"] = "Søk etter @navn, !forum, #emne, ?dokumentasjon eller innhold";
+App::$strings["Admin"] = "Administrator";
+App::$strings["Site Setup and Configuration"] = "Nettstedsoppsett og -konfigurasjon";
+App::$strings["Loading"] = "Laster";
+App::$strings["@name, #tag, ?doc, content"] = "@navn, #merkelapp, ?dokumentasjon, innhold";
+App::$strings["Please wait..."] = "Vennligst vent...";
+App::$strings["Apps"] = "Apper";
+App::$strings["Channel Apps"] = "Kanalapper";
+App::$strings["System Apps"] = "Systemapper";
+App::$strings["Pinned Apps"] = "Festede apper";
+App::$strings["Featured Apps"] = "Fremhevede apper";
+App::$strings["Channel"] = "Kanal";
+App::$strings["Status Messages and Posts"] = "Statusmeldinger og -innlegg";
+App::$strings["About"] = "Om";
+App::$strings["Profile Details"] = "Profildetaljer";
+App::$strings["Photo Albums"] = "Fotoalbum";
+App::$strings["Files"] = "Filer";
+App::$strings["Files and Storage"] = "Filer og lagring";
+App::$strings["Chatrooms"] = "Chatrom";
+App::$strings["Bookmarks"] = "Bokmerker";
+App::$strings["Saved Bookmarks"] = "Lagrede bokmerker";
+App::$strings["Webpages"] = "Websider";
+App::$strings["View Webpages"] = "Vis websider";
+App::$strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = "Skjemaets sikkerhetspollett var ikke gyldig. Dette skjedde antakelig fordi skjemaet har vært åpnet for lenge (>3 timer) før det ble sendt inn.";
+App::$strings["Miscellaneous"] = "Forskjellig";
+App::$strings["Birthday"] = "Fødselsdag";
+App::$strings["Age: "] = "Alder: ";
+App::$strings["YYYY-MM-DD or MM-DD"] = "YYYY-MM-DD eller MM-DD";
+App::$strings["never"] = "aldri";
+App::$strings["less than a second ago"] = "for mindre enn ett sekund siden";
+App::$strings["__ctx:e.g. 22 hours ago, 1 minute ago__ %1\$d %2\$s ago"] = "%1\$d %2\$s siden";
+App::$strings["ago"] = "siden";
+App::$strings["in"] = "i";
+App::$strings["now"] = "nå";
+App::$strings["__ctx:relative_date__ year"] = array(
+ 0 => "år",
+ 1 => "år",
+);
+App::$strings["__ctx:relative_date__ month"] = array(
+ 0 => "måned",
+ 1 => "måneder",
+);
+App::$strings["__ctx:relative_date__ week"] = array(
+ 0 => "uke",
+ 1 => "uker",
+);
+App::$strings["__ctx:relative_date__ day"] = array(
+ 0 => "dag",
+ 1 => "dager",
+);
+App::$strings["__ctx:relative_date__ hour"] = array(
+ 0 => "time",
+ 1 => "timer",
+);
+App::$strings["__ctx:relative_date__ minute"] = array(
+ 0 => "minutt",
+ 1 => "minutter",
+);
+App::$strings["__ctx:relative_date__ second"] = array(
+ 0 => "sekund",
+ 1 => "sekunder",
+);
+App::$strings["%1\$s's birthday"] = "%1\$s sin fødselsdag";
+App::$strings["Happy Birthday %1\$s"] = "Gratulerer med dagen, %1\$s";
+App::$strings["Image exceeds website size limit of %lu bytes"] = "Bilde overstiger nettstedets størrelsesbegrensning på %lu bytes";
+App::$strings["Image file is empty."] = "Bildefilen er tom.";
+App::$strings["Unable to process image"] = "Kan ikke behandle bildet";
+App::$strings["Photo storage failed."] = "Bildelagring mislyktes.";
+App::$strings["a new photo"] = "et nytt bilde";
+App::$strings["__ctx:photo_upload__ %1\$s posted %2\$s to %3\$s"] = "%1\$s la inn %2\$s til %3\$s";
+App::$strings["Recent Photos"] = "Nye bilder";
+App::$strings["Upload New Photos"] = "Last opp nye bilder";
+App::$strings["New window"] = "Nytt vindu";
+App::$strings["Open the selected location in a different window or browser tab"] = "Åpne det valgte stedet i et annet vindu eller nettleser-fane";
+App::$strings["url: "] = "url:";
+App::$strings["error_code: "] = "feilkode:";
+App::$strings["error_string: "] = "feilmelding";
+App::$strings["content-type: "] = "";
+App::$strings["Friendica"] = "Friendica";
+App::$strings["OStatus"] = "OStatus";
+App::$strings["GNU-Social"] = "";
+App::$strings["RSS/Atom"] = "RSS/Atom";
+App::$strings["ActivityPub"] = "";
+App::$strings["Email"] = "E-post";
+App::$strings["Diaspora"] = "Diaspora";
+App::$strings["Facebook"] = "Facebook";
+App::$strings["Zot"] = "Zot";
+App::$strings["LinkedIn"] = "LinkedIn";
+App::$strings["XMPP/IM"] = "XMPP/IM";
+App::$strings["MySpace"] = "MySpace";
+App::$strings["%1\$s wrote the following %2\$s %3\$s"] = "%1\$s skrev følgende %2\$s %3\$s";
+App::$strings["post"] = "innlegg";
+App::$strings["spoiler"] = "røpealarm";
+App::$strings["Edit"] = "Rediger";
+App::$strings["Visible to your default audience"] = "Synlig for ditt standard publikum";
+App::$strings["Profile-Based Privacy Groups"] = "Profilbaserte personverngrupper";
+App::$strings["Private Forum"] = "Privat forum";
+App::$strings["Forums"] = "Forum";
+App::$strings["Only me"] = "Kun meg";
+App::$strings["Share with"] = "Del med";
+App::$strings["Custom selection"] = "Tilpasset utvalg";
+App::$strings["Select \"Allow\" to allow viewing. \"Don't allow\" lets you override and limit the scope of \"Allow\"."] = "Velg \"Tillat\" for å tillate visning. \"Ikke tillat\" lar deg overstyre og begrense gyldigheten av \"Tillat\".";
+App::$strings["Allow"] = "Tillat";
+App::$strings["Don't allow"] = "Ikke tillat";
+App::$strings["Permissions"] = "Tillatelser";
+App::$strings["Close"] = "Lukk";
+App::$strings["Post permissions %s cannot be changed %s after a post is shared.</br />These permissions set who is allowed to view the post."] = "Tillatelsene til innlegget %s kan ikke endres %s etter at innlegget er delt.</br />Disse tillatelsene avgjør hvem som kan se innlegget.";
App::$strings["prev"] = "forrige";
App::$strings["first"] = "første";
App::$strings["last"] = "siste";
App::$strings["next"] = "neste";
App::$strings["older"] = "eldre";
App::$strings["newer"] = "nyere";
-App::$strings["Accepts"] = "";
-App::$strings["Comments"] = "";
-App::$strings["Stream items"] = "";
-App::$strings["Wall posts"] = "";
-App::$strings["Nothing"] = "";
+App::$strings["Accepts"] = "Godtar";
+App::$strings["Comments"] = "Kommentarer";
+App::$strings["Stream items"] = "Elementer fra tidslinjen";
+App::$strings["Wall posts"] = "Veggposter";
+App::$strings["Nothing"] = "Ingenting";
App::$strings["View all %s connections"] = "Vis alle %s forbindelser";
-App::$strings["Network: %s"] = "";
+App::$strings["Network: %s"] = "Nettverk: %s";
App::$strings["Save"] = "Lagre";
-App::$strings["Monday"] = "mandag";
-App::$strings["Tuesday"] = "tirsdag";
-App::$strings["Wednesday"] = "onsdag";
-App::$strings["Thursday"] = "torsdag";
-App::$strings["Friday"] = "fredag";
-App::$strings["Saturday"] = "lørdag";
-App::$strings["Sunday"] = "søndag";
-App::$strings["January"] = "januar";
-App::$strings["February"] = "februar";
-App::$strings["March"] = "mars";
-App::$strings["April"] = "april";
App::$strings["May"] = "mai";
-App::$strings["June"] = "juni";
-App::$strings["July"] = "juli";
-App::$strings["August"] = "august";
-App::$strings["September"] = "september";
-App::$strings["October"] = "oktober";
-App::$strings["November"] = "november";
-App::$strings["December"] = "desember";
App::$strings["Unknown attachment"] = "Ukjent vedlegg";
App::$strings["Size"] = "Størrelse";
App::$strings["remove category"] = "fjern kategori";
App::$strings["remove from file"] = "fjern fra fil";
-App::$strings["Download binary/encrypted content"] = "";
+App::$strings["Download binary/encrypted content"] = "Last ned binært/kryptert innhold";
App::$strings["__ctx:noun__ %d Vote"] = array(
- 0 => "",
- 1 => "",
+ 0 => "%d stemme",
+ 1 => "%d stemmer",
);
App::$strings["__ctx:noun__ %d Vote in total"] = array(
- 0 => "",
- 1 => "",
+ 0 => "%d stemme totalt",
+ 1 => "%d stemmer totalt",
);
App::$strings["Poll has ended"] = "Spørreskjema har utløpt";
-App::$strings["Poll ends in %s"] = "Spørreskjema utløper om %s";
-App::$strings["Vote"] = "";
+App::$strings["Poll ends %s"] = "Spørreskjema utløper %s";
+App::$strings["Vote"] = "Stem";
App::$strings["Link to Source"] = "Lenke til kilde";
+App::$strings["default"] = "standard";
App::$strings["Page layout"] = "Sidens layout";
App::$strings["You can create your own with the layouts tool"] = "Du kan lage din egen med layout-verktøyet";
App::$strings["BBcode"] = "BBcode";
App::$strings["HTML"] = "";
App::$strings["Markdown"] = "Markdown";
App::$strings["Text"] = "Tekst";
-App::$strings["Comanche Layout"] = "";
+App::$strings["Comanche Layout"] = "Comanche Utlegg";
App::$strings["PHP"] = "";
App::$strings["Page content type"] = "Sidens innholdstype";
+App::$strings["conversation"] = "samtale";
App::$strings["activity"] = "aktivitet";
App::$strings["poll"] = "spørreskjema";
-App::$strings["a-z, 0-9, -, and _ only"] = "";
+App::$strings["a-z, 0-9, -, and _ only"] = "kun a-z, 0-9, - og _";
App::$strings["Design Tools"] = "Designverktøy";
App::$strings["Blocks"] = "Byggeklosser";
App::$strings["Menus"] = "Menyer";
App::$strings["Layouts"] = "Layout";
App::$strings["Pages"] = "Sider";
App::$strings["Import"] = "Importer";
-App::$strings["Import website..."] = "";
-App::$strings["Select folder to import"] = "";
-App::$strings["Import from a zipped folder:"] = "";
-App::$strings["Import from cloud files:"] = "";
-App::$strings["/cloud/channel/path/to/folder"] = "";
-App::$strings["Enter path to website files"] = "";
-App::$strings["Select folder"] = "";
-App::$strings["Export website..."] = "";
-App::$strings["Export to a zip file"] = "";
+App::$strings["Import website..."] = "Importer vevside";
+App::$strings["Select folder to import"] = "Velg mappe som skal importeres";
+App::$strings["Import from a zipped folder:"] = "Importer fra en zippet mappe:";
+App::$strings["Import from cloud files:"] = "Importer fra filer i skyen:";
+App::$strings["/cloud/channel/path/to/folder"] = "/cloud/channel/bane/til/mappe";
+App::$strings["Enter path to website files"] = "Oppgi bane til vesidens filer";
+App::$strings["Select folder"] = "Velg mappe";
+App::$strings["Export website..."] = "Eksporter vevside…";
+App::$strings["Export to a zip file"] = "Eksporter til zip-fil";
App::$strings["website.zip"] = "";
-App::$strings["Enter a name for the zip file."] = "";
-App::$strings["Export to cloud files"] = "";
-App::$strings["/path/to/export/folder"] = "";
-App::$strings["Enter a path to a cloud files destination."] = "";
-App::$strings["Specify folder"] = "";
-App::$strings["Default"] = "Standard";
-App::$strings["View PDF"] = "";
-App::$strings[" by "] = " av ";
-App::$strings[" on "] = "På";
-App::$strings["Embedded content"] = "Innebygget innhold";
-App::$strings["Embedding disabled"] = "Innbygging avskrudd";
-App::$strings["Delete this item?"] = "Slett dette elementet?";
-App::$strings["Item deleted"] = "";
-App::$strings["Comment"] = "Kommentar";
-App::$strings["show all"] = "Vis alle";
-App::$strings["show less"] = "Vis eldste først";
-App::$strings["expand"] = "";
-App::$strings["collapse"] = "";
-App::$strings["Password too short"] = "Passordet er for kort";
-App::$strings["Passwords do not match"] = "Passordene er ikke like";
-App::$strings["everybody"] = "alle";
-App::$strings["Secret Passphrase"] = "Hemmelig passordsetning";
-App::$strings["Passphrase hint"] = "Hint om passordsetning";
-App::$strings["Notice: Permissions have changed but have not yet been submitted."] = "Varsel: Tillatelser har blitt endret, men de har ennå ikke blitt sendt inn.";
-App::$strings["close all"] = "lukk alle";
-App::$strings["Nothing new here"] = "Ikke noe nytt her";
-App::$strings["Rate This Channel (this is public)"] = "Vurder denne kanalen (dette er offentlig)";
-App::$strings["Rating"] = "Vurdering";
-App::$strings["Describe (optional)"] = "Beskriv (valgfritt)";
-App::$strings["Submit"] = "Lagre";
-App::$strings["Please enter a link URL"] = "Vennligst skriv inn en lenke URL";
-App::$strings["Unsaved changes. Are you sure you wish to leave this page?"] = "Endringene er ikke lagret. Er du sikker på at du ønsker å forlate denne siden?";
-App::$strings["Location"] = "Plassering";
-App::$strings["lovely"] = "";
-App::$strings["wonderful"] = "";
-App::$strings["fantastic"] = "";
-App::$strings["great"] = "";
-App::$strings["Your chosen nickname was either already taken or not valid. Please use our suggestion ("] = "";
-App::$strings[") or enter a new one."] = "";
-App::$strings["Thank you, this nickname is valid."] = "";
-App::$strings["A channel name is required."] = "";
-App::$strings["This is a "] = "";
-App::$strings[" channel name"] = "Kanalnavn";
-App::$strings["Back to reply"] = "";
-App::$strings["Pinned"] = "";
-App::$strings["Pin to the top"] = "";
-App::$strings["Unpin from the top"] = "";
-App::$strings["%d minutes"] = array(
- 0 => "%d minutter",
- 1 => "%d minutter",
-);
-App::$strings["about %d hours"] = array(
- 0 => "omtrent %d timer",
- 1 => "omtrent %d timer",
-);
-App::$strings["%d days"] = array(
- 0 => "%d dager",
- 1 => "%d dager",
-);
-App::$strings["%d months"] = array(
- 0 => "%d måneder",
- 1 => "%d måneder",
-);
-App::$strings["%d years"] = array(
- 0 => "%d år",
- 1 => "%d år",
-);
-App::$strings["timeago.prefixAgo"] = "timeago.prefixAgo";
-App::$strings["timeago.prefixFromNow"] = "timeago.prefixFromNow";
-App::$strings["timeago.suffixAgo"] = "siden";
-App::$strings["timeago.suffixFromNow"] = "";
-App::$strings["less than a minute"] = "mindre enn ett minutt";
-App::$strings["about a minute"] = "omtrent et minutt";
-App::$strings["about an hour"] = "omtrent en time";
-App::$strings["a day"] = "en dag";
-App::$strings["about a month"] = "omtrent en måned";
-App::$strings["about a year"] = "omtrent et år";
-App::$strings[" "] = " ";
-App::$strings["timeago.numbers"] = "timeago.numbers";
-App::$strings["__ctx:long__ May"] = "mai";
-App::$strings["Jan"] = "Jan";
-App::$strings["Feb"] = "Feb";
-App::$strings["Mar"] = "Mar";
-App::$strings["Apr"] = "Apr";
-App::$strings["__ctx:short__ May"] = "mai";
-App::$strings["Jun"] = "Jun";
-App::$strings["Jul"] = "Jul";
-App::$strings["Aug"] = "Aug";
-App::$strings["Sep"] = "Sep";
-App::$strings["Oct"] = "Okt";
-App::$strings["Nov"] = "Nov";
-App::$strings["Dec"] = "Des";
-App::$strings["Sun"] = "Søn";
-App::$strings["Mon"] = "Man";
-App::$strings["Tue"] = "Tirs";
-App::$strings["Wed"] = "Ons";
-App::$strings["Thu"] = "Tors";
-App::$strings["Fri"] = "Fre";
-App::$strings["Sat"] = "Lør";
-App::$strings["__ctx:calendar__ today"] = "idag";
-App::$strings["__ctx:calendar__ month"] = "måned";
-App::$strings["__ctx:calendar__ week"] = "uke";
-App::$strings["__ctx:calendar__ day"] = "dag";
-App::$strings["__ctx:calendar__ All day"] = "Hele dagen";
-App::$strings["Please stand by while your download is being prepared."] = "";
-App::$strings["Email address not valid"] = "";
+App::$strings["Enter a name for the zip file."] = "Oppgi et navn til zip-filen.";
+App::$strings["Export to cloud files"] = "Eksporter til filen i skyen";
+App::$strings["/path/to/export/folder"] = "/bane/til/eksport/mappe";
+App::$strings["Enter a path to a cloud files destination."] = "Oppgi bane i skyen hvor filene skal lagres";
+App::$strings["Specify folder"] = "Velg mappe";
App::$strings["Unable to obtain identity information from database"] = "Klarer ikke å få tak i identitetsinformasjon fra databasen";
App::$strings["Empty name"] = "Mangler navn";
App::$strings["Name too long"] = "Navnet er for langt";
@@ -649,15 +772,13 @@ App::$strings["Reserved nickname. Please choose another."] = "Reservert kallenav
App::$strings["Nickname has unsupported characters or is already being used on this site."] = "Kallenavnet inneholder tegn som ikke er støttet eller det er allerede i bruk på dette nettstedet.";
App::$strings["Unable to retrieve created identity"] = "Klarer ikke å hente den lagede identiteten";
App::$strings["Default Profile"] = "Standardprofil";
-App::$strings["Friends"] = "Venner";
-App::$strings["Unable to retrieve modified identity"] = "";
-App::$strings["Requested channel is not available"] = "";
+App::$strings["Unable to retrieve modified identity"] = "Ikke i stand til å hente endret identitet";
+App::$strings["Requested channel is not available"] = "Kanalen du prøver å nå er ikke tilgjengelig";
App::$strings["Requested profile is not available."] = "Forespurt profil er ikke tilgjengelig.";
App::$strings["Change profile photo"] = "Endre profilbilde";
-App::$strings["Edit"] = "Endre";
App::$strings["Create New Profile"] = "Lag ny profil";
App::$strings["Profile Image"] = "Profilbilde";
-App::$strings["Visible to everybody"] = "";
+App::$strings["Visible to everybody"] = "Synlig for alle";
App::$strings["Edit visibility"] = "Endre synlighet";
App::$strings["Gender:"] = "Kjønn:";
App::$strings["Status:"] = "Status:";
@@ -665,12 +786,8 @@ App::$strings["Homepage:"] = "Hjemmeside:";
App::$strings["Hometown:"] = "Hjemby:";
App::$strings["Online Now"] = "Online nå";
App::$strings["This channel has not added a profile description yet"] = "Denne kanalen har ikke lagt til en profilbeskrivelse enda";
-App::$strings["Change your profile photo"] = "";
-App::$strings["Female"] = "Kvinnelig";
-App::$strings["Male"] = "Mannlig";
+App::$strings["Change your profile photo"] = "Endre profilbildet ditt";
App::$strings["Trans"] = "";
-App::$strings["Neuter"] = "Intetkjønn";
-App::$strings["Non-specific"] = "Ubestemt";
App::$strings["Full Name:"] = "Fullt navn:";
App::$strings["j F, Y"] = "j F, Y";
App::$strings["j F"] = "j F";
@@ -702,151 +819,655 @@ App::$strings["Remote Authentication"] = "Fjernautentisering";
App::$strings["Enter your channel address (e.g. channel@example.com)"] = "Skriv din kanaladresse (for eksempel channel@exampel.com)";
App::$strings["Authenticate"] = "Autentiser";
App::$strings["Account '%s' deleted"] = "Kontoen '%s' slettet";
-App::$strings["%d invitation available"] = array(
- 0 => "%d invitasjon tilgjengelig",
- 1 => "%d invitasjoner tilgjengelig",
-);
-App::$strings["Advanced"] = "Avansert";
-App::$strings["Find Channels"] = "Finn kanaler";
-App::$strings["Enter name or interest"] = "Skriv navn eller interesse";
-App::$strings["Connect/Follow"] = "Forbindelse/Følg";
-App::$strings["Examples: Robert Morgenstein, Fishing"] = "Eksempler: Ola Nordmann, fisking";
-App::$strings["Find"] = "Finn";
-App::$strings["Channel Suggestions"] = "Kanalforslag";
-App::$strings["Random Profile"] = "Tilfeldig profil";
-App::$strings["Invite Friends"] = "Inviter venner";
-App::$strings["Advanced example: name=fred and country=iceland"] = "Avansert eksempel: navn=fred og land=island";
-App::$strings["Everything"] = "Alt";
-App::$strings["Categories"] = "Kategorier";
-App::$strings["Common Connections"] = "";
-App::$strings["View all %d common connections"] = "";
-App::$strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = "Skjemaets sikkerhetspollett var ikke gyldig. Dette skjedde antakelig fordi skjemaet har vært åpnet for lenge (>3 timer) før det ble sendt inn.";
-App::$strings["Trending"] = "";
-App::$strings["Tags"] = "Merkelapper";
-App::$strings["Keywords"] = "Nøkkelord";
-App::$strings["have"] = "har";
-App::$strings["has"] = "har";
-App::$strings["want"] = "ønsker";
-App::$strings["wants"] = "ønsker";
-App::$strings["like"] = "liker";
-App::$strings["likes"] = "liker";
-App::$strings["dislike"] = "misliker";
-App::$strings["dislikes"] = "misliker";
-App::$strings["New window"] = "Nytt vindu";
-App::$strings["Open the selected location in a different window or browser tab"] = "Åpne det valgte stedet i et annet vindu eller nettleser-fane";
-App::$strings["Visible to your default audience"] = "Synlig for ditt standard publikum";
-App::$strings["Profile-Based Privacy Groups"] = "";
-App::$strings["Private Forum"] = "";
-App::$strings["Forums"] = "Forum";
-App::$strings["Only me"] = "Kun meg";
-App::$strings["Share with"] = "Del med";
-App::$strings["Custom selection"] = "Tilpasset utvalg";
-App::$strings["Select \"Allow\" to allow viewing. \"Don't allow\" lets you override and limit the scope of \"Allow\"."] = "";
-App::$strings["Allow"] = "";
-App::$strings["Don't allow"] = "";
-App::$strings["Permissions"] = "Tillatelser";
-App::$strings["Close"] = "Lukk";
-App::$strings["Post permissions %s cannot be changed %s after a post is shared.</br />These permissions set who is allowed to view the post."] = "Tillatelsene til innlegget %s kan ikke endres %s etter at innlegget er delt.</br />Disse tillatelsene avgjør hvem som kan se innlegget.";
-App::$strings["INVALID EVENT DISMISSED!"] = "";
-App::$strings["Summary: "] = "Sammendrag";
-App::$strings["Date: "] = "Dato";
-App::$strings["Reason: "] = "";
-App::$strings["INVALID CARD DISMISSED!"] = "";
-App::$strings["Name: "] = "Navn";
-App::$strings["Select a profile to assign to this contact"] = "Velg en profil for denne kontakten";
-App::$strings["Frequently"] = "Ofte";
-App::$strings["Hourly"] = "Hver time";
-App::$strings["Twice daily"] = "To ganger daglig";
-App::$strings["Daily"] = "Daglig";
-App::$strings["Weekly"] = "Ukentlig";
-App::$strings["Monthly"] = "Månedlig";
-App::$strings["Currently Male"] = "For tiden mann";
-App::$strings["Currently Female"] = "For tiden kvinne";
-App::$strings["Mostly Male"] = "For det meste mann";
-App::$strings["Mostly Female"] = "For det meste kvinne";
-App::$strings["Transgender"] = "Transkjønnet";
-App::$strings["Intersex"] = "interkjønnet";
-App::$strings["Transsexual"] = "Transseksuell";
-App::$strings["Hermaphrodite"] = "Hermafroditt";
-App::$strings["Undecided"] = "Ubestemt";
-App::$strings["Males"] = "Menn";
-App::$strings["Females"] = "Kvinner";
-App::$strings["Gay"] = "Homo";
-App::$strings["Lesbian"] = "Lesbisk";
-App::$strings["No Preference"] = "Ingen preferanse";
-App::$strings["Bisexual"] = "Biseksuell";
-App::$strings["Autosexual"] = "Autoseksuell";
-App::$strings["Abstinent"] = "Avholdende";
-App::$strings["Virgin"] = "Jomfru";
-App::$strings["Deviant"] = "Avviker";
-App::$strings["Fetish"] = "Fetisj";
-App::$strings["Oodles"] = "Masse";
-App::$strings["Nonsexual"] = "Ikke-seksuell";
-App::$strings["Single"] = "Enslig";
-App::$strings["Lonely"] = "Ensom";
-App::$strings["Available"] = "Tilgjengelig";
-App::$strings["Unavailable"] = "Ikke tilgjengelig";
-App::$strings["Has crush"] = "Er forelsket";
-App::$strings["Infatuated"] = "Betatt";
-App::$strings["Dating"] = "Sammen med";
-App::$strings["Unfaithful"] = "Utro";
-App::$strings["Sex Addict"] = "Sexavhengig";
-App::$strings["Friends/Benefits"] = "Venner/Frynsegoder";
-App::$strings["Casual"] = "Tilfeldig";
-App::$strings["Engaged"] = "Forlovet";
-App::$strings["Married"] = "Gift";
-App::$strings["Imaginarily married"] = "Gift i fantasien";
-App::$strings["Partners"] = "Partnere";
-App::$strings["Cohabiting"] = "Samboer";
-App::$strings["Common law"] = "Samboer";
-App::$strings["Happy"] = "Lykkelig";
-App::$strings["Not looking"] = "Ikke på utkikk";
-App::$strings["Swinger"] = "Partnerbytte";
-App::$strings["Betrayed"] = "Bedratt";
-App::$strings["Separated"] = "Separert";
-App::$strings["Unstable"] = "Ustabilt";
-App::$strings["Divorced"] = "Skilt";
-App::$strings["Imaginarily divorced"] = "Skilt i fantasien";
-App::$strings["Widowed"] = "Enke";
-App::$strings["Uncertain"] = "Usikkert";
-App::$strings["It's complicated"] = "Det er komplisert";
-App::$strings["Don't care"] = "Bryr meg ikke";
-App::$strings["Ask me"] = "Spør meg";
-App::$strings["OpenWebAuth: %1\$s welcomes %2\$s"] = "OpenWebAuth: %1\$s ønsker %2\$s velkommen";
-App::$strings["Focus (Hubzilla default)"] = "Focus (Hubzilla standard)";
-App::$strings["Theme settings"] = "Instillinger for utseende";
-App::$strings["Dark style"] = "";
-App::$strings["Light style"] = "";
-App::$strings["Common settings"] = "";
-App::$strings["Primary theme color"] = "";
-App::$strings["Current color, leave empty for default"] = "La feltet stå tomt for å bruke standard bredde";
-App::$strings["Success theme color"] = "";
-App::$strings["Info theme color"] = "Lenkefarge når musepekeren er over";
-App::$strings["Warning theme color"] = "Lenkefarge når musepekeren er over";
-App::$strings["Danger theme color"] = "Lenkefarge når musepekeren er over";
-App::$strings["Default to dark mode"] = "Standard mappe for opplasting av bilder";
-App::$strings["Always use light icons for navbar"] = "";
-App::$strings["Enable this option if you use a dark navbar color in light mode"] = "";
-App::$strings["Narrow navbar"] = "Smal navigasjonslinje";
-App::$strings["Navigation bar background color"] = "Navigasjonslinjens bakgrunnsfarge";
-App::$strings["Dark navigation bar background color"] = "Navigasjonslinjens bakgrunnsfarge";
-App::$strings["Set the background color"] = "Angi bakgrunnsfargen";
-App::$strings["Set the dark background color"] = "Angi bakgrunnsfargen";
-App::$strings["Set the background image"] = "Angi bakgrunnsbilde";
-App::$strings["Set the dark background image"] = "Angi bakgrunnsbilde";
-App::$strings["Set font-size for the entire application"] = "Angi skriftstørrelsen for hele programmet";
-App::$strings["Examples: 1rem, 100%, 16px"] = "For eksempel: 1rem, 100%, 16px";
-App::$strings["Set radius of corners in rem"] = "Angi hjørneradius";
-App::$strings["Leave empty for default radius"] = "La feltet stå tomt for å bruke standard bredde";
-App::$strings["Set maximum width of content region in rem"] = "Set maksbredde for hovedregionen i rem";
-App::$strings["Leave empty for default width"] = "La feltet stå tomt for å bruke standard bredde";
-App::$strings["Set size of conversation author photo"] = "Angi størrelsen for samtalens forfatterbilde";
-App::$strings["Leave empty for default size"] = "La feltet stå tomt for å bruke standard bredde";
-App::$strings["Set size of followup author photos"] = "Angi størrelsen på forfatterbilder ved oppfølging";
-App::$strings["Show advanced settings"] = "Vis avanserte innstillinger";
+App::$strings["Image/photo"] = "Bilde/fotografi";
+App::$strings["Encrypted content"] = "Kryptert innhold";
+App::$strings["Install %1\$s element %2\$s"] = "Installer %1\$s element %2\$s";
+App::$strings["This post contains an installable %s element, however you lack permissions to install it on this site."] = "Dette innlegget inneholder det installerbare elementet %s, men du mangler tillatelse til å installere det på dette nettstedet.";
+App::$strings["webpage"] = "nettside";
+App::$strings["layout"] = "layout";
+App::$strings["block"] = "byggekloss";
+App::$strings["menu"] = "meny";
+App::$strings["card"] = "kort";
+App::$strings["article"] = "artikkel";
+App::$strings["Click to open/close"] = "Klikk for å åpne/lukke";
+App::$strings["View article"] = "Vis artikkel";
+App::$strings["View summary"] = "Vis sammendrag";
+App::$strings["Different viewers will see this text differently"] = "Denne teksten vil se forskjellig ut for ulike besøkende";
+App::$strings["$1 wrote:"] = "$1 skrev:";
+App::$strings["Unable to import a removed channel."] = "Ikke i stand til å importere en kanal som er slettet.";
+App::$strings["Cannot create a duplicate channel identifier on this system. Import failed."] = "Kan ikke lage en kopi av kanal-identifikatoren på dette systemet. Import mislyktes.";
+App::$strings["Unable to create a unique channel address. Import failed."] = "Klarte ikke å lage en unik kanaladresse. Import mislyktes.";
+App::$strings["Cloned channel not found. Import failed."] = "Klonet kanal ble ikke funnet. Import mislyktes.";
+App::$strings["Popular Channels"] = "Populære kanaler";
+App::$strings["Channels to auto connect"] = "Kanaler som skal kobles automatisk";
+App::$strings["Comma separated list"] = "Kommaseparert liste";
+App::$strings["IRC Settings"] = "Innstillinger for IRC";
+App::$strings["IRC settings saved."] = "Innstillinger for IRC lagret.";
+App::$strings["IRC Chatroom"] = "IRC-kanal";
+App::$strings["ERROR: word length is not correct!"] = "FEIL: lengden på ordet er ikke riktig!";
+App::$strings["Fediquest App"] = "Fediquest";
+App::$strings["A distributed quest for a given word (game)."] = "En distribuert gjettelek om ord (spill)";
+App::$strings["To start a game, enter [fediquest]your_word[/fediquest] somewhere in a toplevel post."] = "For å starte et spill, skriv [fediquest]ditt_ord[/fediquest] et sted i et nytt innlegg.";
+App::$strings["Your contacts can post their guess in the comments."] = "Forbindelsene dine kan poste sine gjetninger i kommentarene til innlegget.";
+App::$strings["Your channel will evaluate the guess and automatically post the response."] = "Kanalen din vil automatisk vurdere svarene og poste resultatet i tråden.";
+App::$strings["Correct letters"] = "Riktige bokstaver";
+App::$strings["Letters contained in the word but at the wrong spot"] = "Bokstaver som er riktige, men på feil plass";
+App::$strings["Letters not contained in the word"] = "Bokstaver som ikke finnes i ordet";
+App::$strings["Network error"] = "Nettverksfeil";
+App::$strings["API error"] = "API-feil";
+App::$strings["Unknown issue"] = "Ukjent problem";
+App::$strings["Unable to retrieve email address from remote identity provider"] = "Fikk ikke til å hente epostadressen din fra identitetstilbyderen";
+App::$strings["Unable to login using email address "] = "Fikk ikke til å logge inn ved bruk av epostadresse";
+App::$strings["Social Authentication using your social media account"] = "Sosial Autentisering ved bruk av din sosiale nettverkskonto";
+App::$strings["This app enables one or more social provider sign-in buttons on the login page."] = "Denne appen legger til en eller flere login-knapper fra sosiale nettverk på innloggingssiden.";
+App::$strings["Add an identity provider"] = "Legg til en identitetstilbyder";
+App::$strings["Enable "] = "Skru på ";
+App::$strings["Key"] = "Nøkkel";
+App::$strings["Word"] = "Ord";
+App::$strings["Secret"] = "Hemmelighet";
+App::$strings["Add a custom provider"] = "Legg til en egendefinert tilbyder";
+App::$strings["Remove an identity provider"] = "Fjern en identitetstilbyder";
+App::$strings["Social authentication"] = "Sosial autentisering";
+App::$strings["Error while saving provider settings"] = "Det oppsto en feil ved lagring av innstillingene for tilbyderen";
+App::$strings["Custom provider already exists"] = "Den egendefinerte tilbyderen finnes allerede";
+App::$strings["Social authentication settings saved."] = "Innstillingene for sosial autentisering ble lagret.";
+App::$strings["Show Upload Limits"] = "Vis opplastingsgrenser";
+App::$strings["Hubzilla configured maximum size: "] = "Maksimum størrelse definert av Hubzilla:";
+App::$strings["PHP upload_max_filesize: "] = "";
+App::$strings["PHP post_max_size (must be larger than upload_max_filesize): "] = "PHP post_max_size (må være større enn upload_max_filesize):";
+App::$strings["Send your identity to all websites"] = "Send din identitet til alle nettsteder";
+App::$strings["Send ZID"] = "";
+App::$strings["Send email to all members"] = "Send epost til alle medlemmer";
+App::$strings["%s Administrator"] = "%s administrator";
+App::$strings["No recipients found."] = "Fant ingen mottakere";
+App::$strings["%1\$d of %2\$d messages sent."] = "%1\$d av %2\$d meldinger sendt.";
+App::$strings["Send email to all hub members."] = "Send epost til alle medlemmer av hub'en";
+App::$strings["Message subject"] = "Emne for meldingen";
+App::$strings["Sender Email address"] = "Avsenderens epostadresse";
+App::$strings["Test mode (only send to hub administrator)"] = "Testmodus (send kun til hubens administrator)";
+App::$strings["Rainbow Tag App"] = "";
+App::$strings["Add some colour to tag clouds"] = "";
+App::$strings["Rainbow Tag"] = "";
+App::$strings["We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."] = "Vi støtte på et problem under innloggingen med din OpenID. Vennligst sjekk at ID-en er stavet riktig.";
+App::$strings["The error message was:"] = "Feilmeldingen var:";
+App::$strings["OpenID protocol error. No ID returned."] = "OpenID protokollfeil. Ingen ID ble returnert.";
+App::$strings["Welcome %s. Remote authentication successful."] = "Velkommen %s. Ekstern autentisering er vellykket.";
+App::$strings["First Name"] = "Fornavn";
+App::$strings["Last Name"] = "Etternavn";
+App::$strings["Nickname"] = "Kallenavn";
+App::$strings["Full Name"] = "Fullt navn";
+App::$strings["Profile Photo 16px"] = "Profilbilde 16px";
+App::$strings["Profile Photo 32px"] = "Profilbilde 32px";
+App::$strings["Profile Photo 48px"] = "Profilbilde 48px";
+App::$strings["Profile Photo 64px"] = "Profilbilde 64px";
+App::$strings["Profile Photo 80px"] = "Profilbilde 80px";
+App::$strings["Profile Photo 128px"] = "Profilbilde 128px";
+App::$strings["Homepage URL"] = "Hjemmeside URL";
+App::$strings["Language"] = "Språk";
+App::$strings["Birth Year"] = "Fødselsår";
+App::$strings["Birth Month"] = "Fødselsmåne";
+App::$strings["Birth Day"] = "Fødselsdag";
+App::$strings["Birthdate"] = "Fødselsdato";
+App::$strings["Gender"] = "Kjønn";
+App::$strings["System defaults:"] = "";
+App::$strings["Preferred Clipart IDs"] = "";
+App::$strings["List of preferred clipart ids. These will be shown first."] = "";
+App::$strings["Default Search Term"] = "Standard søkeord";
+App::$strings["The default search term. These will be shown second."] = "Standard søkeord. Disse vil vises som nummer to.";
+App::$strings["Return After"] = "";
+App::$strings["Page to load after image selection."] = "";
+App::$strings["Profile List"] = "";
+App::$strings["Order of Preferred"] = "";
+App::$strings["Sort order of preferred clipart ids."] = "";
+App::$strings["Newest first"] = "";
+App::$strings["As entered"] = "";
+App::$strings["Order of other"] = "";
+App::$strings["Sort order of other clipart ids."] = "";
+App::$strings["Most downloaded first"] = "";
+App::$strings["Most liked first"] = "";
+App::$strings["Preferred IDs Message"] = "";
+App::$strings["Message to display above preferred results."] = "";
+App::$strings["Uploaded by: "] = "";
+App::$strings["Drawn by: "] = "";
+App::$strings["Use this image"] = "";
+App::$strings["Or select from a free OpenClipart.org image:"] = "";
+App::$strings["Search Term"] = "Søkeord";
+App::$strings["Unknown error. Please try again later."] = "";
+App::$strings["Shift-reload the page or clear browser cache if the new photo does not display immediately."] = "Hold nede Shift-knappen og last siden på nytt eller tøm nettleserens mellomlager hvis det nye bildet ikke vises umiddelbart.";
+App::$strings["Profile photo updated successfully."] = "";
+App::$strings["NSA Bait App"] = "";
+App::$strings["Make yourself a political target."] = "";
+App::$strings["Recent Channel/Profile Viewers"] = "";
+App::$strings["No entries."] = "";
+App::$strings["Hubzilla File Storage Import"] = "";
+App::$strings["This will import all your cloud files from another server."] = "";
+App::$strings["Hubzilla Server base URL"] = "";
+App::$strings["Since modified date yyyy-mm-dd"] = "";
+App::$strings["Until modified date yyyy-mm-dd"] = "";
+App::$strings["Your channel has been upgraded to \$Projectname version"] = "";
+App::$strings["Please have a look at the"] = "";
+App::$strings["git history"] = "";
+App::$strings["change log"] = "";
+App::$strings["for further info."] = "";
+App::$strings["\$Projectname Upgrade Info"] = "\$Projectname";
+App::$strings["Do not show this again"] = "";
+App::$strings["__ctx:opensearch__ Search %1\$s (%2\$s)"] = "Søk %1\$s (%2\$s)";
+App::$strings["__ctx:opensearch__ \$Projectname"] = "\$Projectname";
+App::$strings["\$Projectname"] = "\$Projectname";
+App::$strings["Search \$Projectname"] = "";
+App::$strings["Hubzilla Directory Stats"] = "";
+App::$strings["Total Hubs"] = "";
+App::$strings["Hubzilla Hubs"] = "";
+App::$strings["Friendica Hubs"] = "";
+App::$strings["Diaspora Pods"] = "";
+App::$strings["Hubzilla Channels"] = "";
+App::$strings["Friendica Channels"] = "";
+App::$strings["Diaspora Channels"] = "";
+App::$strings["Aged 35 and above"] = "";
+App::$strings["Aged 34 and under"] = "";
+App::$strings["Average Age"] = "";
+App::$strings["Known Chatrooms"] = "";
+App::$strings["Known Tags"] = "";
+App::$strings["Please note Diaspora and Friendica statistics are merely those **this directory** is aware of, and not all those known in the network. This also applies to chatrooms,"] = "";
+App::$strings["This website is tracked using the <a href='http://www.piwik.org'>Piwik</a> analytics tool."] = "";
+App::$strings["If you do not want that your visits are logged this way you <a href='%s'>can set a cookie to prevent Piwik from tracking further visits of the site</a> (opt-out)."] = "";
+App::$strings["Piwik Base URL"] = "";
+App::$strings["Absolute path to your Piwik installation. (without protocol (http/s), with trailing slash)"] = "";
+App::$strings["Site ID"] = "";
+App::$strings["Show opt-out cookie link?"] = "";
+App::$strings["Asynchronous tracking"] = "";
+App::$strings["Enable frontend JavaScript error tracking"] = "";
+App::$strings["This feature requires Piwik >= 2.2.0"] = "";
+App::$strings["Settings updated."] = "Innstillinger oppdatert.";
+App::$strings["Photos imported"] = "";
+App::$strings["Redmatrix Photo Album Import"] = "";
+App::$strings["This will import all your Redmatrix photo albums to this channel."] = "";
+App::$strings["Redmatrix Server base URL"] = "";
+App::$strings["Redmatrix Login Username"] = "";
+App::$strings["Redmatrix Login Password"] = "";
+App::$strings["Import just this album"] = "";
+App::$strings["Leave blank to import all albums"] = "";
+App::$strings["Maximum count to import"] = "";
+App::$strings["0 or blank to import all available"] = "";
+App::$strings["pageheader Settings saved."] = "";
+App::$strings["Message to display on every page on this server"] = "";
+App::$strings["Page Header"] = "";
+App::$strings["Report Bug"] = "";
+App::$strings["Hide Aside App"] = "";
+App::$strings["Fade out aside areas after a while when using endless scroll"] = "";
+App::$strings["Post to Insane Journal"] = "";
+App::$strings["Insane Journal Crosspost Connector Settings saved."] = "";
+App::$strings["Insane Journal Crosspost Connector App"] = "";
+App::$strings["Not Installed"] = "";
+App::$strings["Relay public postings to Insane Journal"] = "";
+App::$strings["InsaneJournal username"] = "";
+App::$strings["InsaneJournal password"] = "";
+App::$strings["Post to InsaneJournal by default"] = "";
+App::$strings["Insane Journal Crosspost Connector"] = "";
+App::$strings["Who likes me?"] = "";
+App::$strings["Post to Hubzilla"] = "";
+App::$strings["Channel is required."] = "";
+App::$strings["Invalid channel."] = "Ugyldig kanal";
+App::$strings["Hubzilla Crosspost Connector Settings saved."] = "";
+App::$strings["Send public postings to Hubzilla channel by default"] = "";
+App::$strings["Hubzilla API Path"] = "";
+App::$strings["https://{sitename}/api"] = "";
+App::$strings["Hubzilla login name"] = "";
+App::$strings["Hubzilla channel name"] = "";
+App::$strings["Hubzilla password"] = "";
+App::$strings["Hubzilla Crosspost Connector"] = "";
+App::$strings["Cards"] = "";
+App::$strings["View Cards"] = "";
+App::$strings["Page not found."] = "Siden ikke funnet.";
+App::$strings["Add Card"] = "";
+App::$strings["Item not found"] = "Elementet ble ikke funnet.";
+App::$strings["Channel not found."] = "Kanalen ble ikke funnet.";
+App::$strings["Edit Card"] = "";
+App::$strings["Page to load after login"] = "";
+App::$strings["Examples: \"apps\", \"network?f=&gid=37\" (privacy collection), \"channel\" or \"notifications/system\" (leave blank for default.)"] = "";
+App::$strings["Startpage"] = "";
+App::$strings["Post to Libertree"] = "";
+App::$strings["Libertree Crosspost Connector Settings saved."] = "";
+App::$strings["Libertree API token"] = "";
+App::$strings["Libertree site URL"] = "";
+App::$strings["Post to Libertree by default"] = "";
+App::$strings["Libertree Crosspost Connector"] = "";
+App::$strings["Logfile archive directory"] = "";
+App::$strings["Directory to store rotated logs"] = "";
+App::$strings["Logfile size in bytes before rotating"] = "";
+App::$strings["Number of logfiles to retain"] = "";
+App::$strings["Your account on %s will expire in a few days."] = "";
+App::$strings["Your test account is about to expire."] = "";
+App::$strings["The activitypub protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "";
+App::$strings["Activitypub Protocol"] = "";
+App::$strings["Refresh failed"] = "Forny";
+App::$strings["Refresh succeeded"] = "";
+App::$strings["Save Bookmarks"] = "Lagre bokmerker";
+App::$strings["Random Planet App"] = "";
+App::$strings["Set a random planet from the Star Wars Empire as your location when posting"] = "";
+App::$strings["Post to Dreamwidth"] = "";
+App::$strings["Source"] = "";
+App::$strings["Dreamwidth Crosspost Connector Settings saved."] = "";
+App::$strings["Dreamwidth username"] = "";
+App::$strings["Dreamwidth password"] = "";
+App::$strings["Post to Dreamwidth by default"] = "";
+App::$strings["Add link to original post"] = "";
+App::$strings["Link description (default:"] = "";
+App::$strings["Dreamwidth Crosspost Connector"] = "";
+App::$strings["Please install the statistics addon to be able to configure a diaspora relay"] = "";
+App::$strings["Diaspora Relay Handle"] = "";
+App::$strings["Address of a diaspora relay. Example: relay@diasporarelay.tld"] = "";
+App::$strings["Diaspora relay could not be imported"] = "";
+App::$strings["No subject"] = "";
+App::$strings["No account to import to."] = "Ingenting å importere.";
+App::$strings["Incompatible data version - aborting"] = "";
+App::$strings["No username found in import file."] = "Ingen brukernavn ble funnet i importfilen.";
+App::$strings["Import completed."] = "Import ferdig.";
+App::$strings["\$projectname"] = "\$projectname";
+App::$strings["%1\$s dislikes %2\$s's %3\$s"] = "";
+App::$strings["status"] = "status";
+App::$strings["%1\$s is attending %2\$s's %3\$s"] = "%1\$s deltar på %2\$ss %3\$s";
+App::$strings["%1\$s is not attending %2\$s's %3\$s"] = "%1\$s deltar ikke på %2\$ss %3\$s";
+App::$strings["%1\$s may attend %2\$s's %3\$s"] = "%1\$s deltar kanskje på %2\$ss %3\$s";
+App::$strings["Diaspora Protocol Settings updated."] = "";
+App::$strings["The diaspora protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "";
+App::$strings["Prevent your hashtags from being redirected to other sites"] = "";
+App::$strings["Sign and forward posts and comments with no existing Diaspora signature"] = "";
+App::$strings["Followed hashtags (comma separated, do not include the #)"] = "";
+App::$strings["Diaspora Protocol"] = "";
+App::$strings["Fuzzloc Settings updated."] = "";
+App::$strings["Minimum offset in meters"] = "";
+App::$strings["Maximum offset in meters"] = "";
+App::$strings["Fuzzy Location"] = "";
+App::$strings["QR code"] = "";
+App::$strings["QR Generator"] = "";
+App::$strings["Enter some text"] = "";
+App::$strings["superblock settings updated"] = "innstillingene til superblokk ble oppdatert";
+App::$strings["Currently blocked"] = "Blokkert for øyeblikket";
+App::$strings["No channels currently blocked"] = "Ingen kanaler er blokkert i øyeblikket";
+App::$strings["Remove"] = "Fjern";
+App::$strings["Block Completely"] = "Blokker helt";
+App::$strings["Block from site"] = "Byggeklossens tittel";
+App::$strings["You're welcome."] = "";
+App::$strings["Ah shucks..."] = "";
+App::$strings["Don't mention it."] = "";
+App::$strings["&lt;blush&gt;"] = "";
+App::$strings["Save Settings"] = "";
+App::$strings["text to include in all outgoing posts from this site"] = "";
+App::$strings["Send test email"] = "";
+App::$strings["Mail sent."] = "";
+App::$strings["Sending of mail failed."] = "";
+App::$strings["Mail Test"] = "";
+App::$strings["NSFW Settings saved."] = "";
+App::$strings["This app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter."] = "";
+App::$strings["Comma separated list of keywords to hide"] = "";
+App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = "";
+App::$strings["NSFW"] = "";
+App::$strings["Possible adult content"] = "";
+App::$strings["%s - view"] = "";
+App::$strings["Errors encountered deleting database table "] = "";
+App::$strings["Submit Settings"] = "";
+App::$strings["Drop tables when uninstalling?"] = "";
+App::$strings["If checked, the Rendezvous database tables will be deleted when the plugin is uninstalled."] = "";
+App::$strings["Mapbox Access Token"] = "";
+App::$strings["If you enter a Mapbox access token, it will be used to retrieve map tiles from Mapbox instead of the default OpenStreetMap tile server."] = "";
+App::$strings["Rendezvous"] = "";
+App::$strings["This identity has been deleted by another member due to inactivity. Please press the \"New identity\" button or refresh the page to register a new identity. You may use the same name."] = "";
+App::$strings["Welcome to Rendezvous!"] = "";
+App::$strings["Enter your name to join this rendezvous. To begin sharing your location with the other members, tap the GPS control. When your location is discovered, a red dot will appear and others will be able to see you on the map."] = "";
+App::$strings["Let's meet here"] = "";
+App::$strings["Name"] = "Navn";
+App::$strings["Description"] = "Beskrivelse";
+App::$strings["New marker"] = "";
+App::$strings["Edit marker"] = "";
+App::$strings["New identity"] = "";
+App::$strings["Delete marker"] = "";
+App::$strings["Delete member"] = "";
+App::$strings["Edit proximity alert"] = "";
+App::$strings["A proximity alert will be issued when this member is within a certain radius of you.<br><br>Enter a radius in meters (0 to disable):"] = "";
+App::$strings["distance"] = "";
+App::$strings["Proximity alert distance (meters)"] = "";
+App::$strings["A proximity alert will be issued when you are within a certain radius of the marker location.<br><br>Enter a radius in meters (0 to disable):"] = "";
+App::$strings["Marker proximity alert"] = "";
+App::$strings["Reminder note"] = "";
+App::$strings["Enter a note to be displayed when you are within the specified proximity..."] = "";
+App::$strings["Add new rendezvous"] = "";
+App::$strings["Create a new rendezvous and share the access link with those you wish to invite to the group. Those who open the link become members of the rendezvous. They can view other member locations, add markers to the map, or share their own locations with the group."] = "";
+App::$strings["You have no rendezvous. Press the button above to create a rendezvous!"] = "";
+App::$strings["Errors encountered creating database tables."] = "Feil oppstod under opprettelsen av databasetabeller.";
+App::$strings["You are now authenticated to pumpio."] = "";
+App::$strings["return to the featured settings page"] = "tilbake til funksjonsinnstillinger";
+App::$strings["Post to Pump.io"] = "";
+App::$strings["Pump.io Settings saved."] = "";
+App::$strings["Pump.io servername"] = "";
+App::$strings["Without \"http://\" or \"https://\""] = "";
+App::$strings["Pump.io username"] = "";
+App::$strings["Without the servername"] = "";
+App::$strings["You are not authenticated to pumpio"] = "";
+App::$strings["(Re-)Authenticate your pump.io connection"] = "";
+App::$strings["Post to pump.io by default"] = "";
+App::$strings["Should posts be public"] = "";
+App::$strings["Mirror all public posts"] = "";
+App::$strings["Pump.io Crosspost Connector"] = "";
+App::$strings["Post to WordPress"] = "";
+App::$strings["Wordpress Settings saved."] = "";
+App::$strings["WordPress username"] = "";
+App::$strings["WordPress password"] = "";
+App::$strings["WordPress API URL"] = "";
+App::$strings["Typically https://your-blog.tld/xmlrpc.php"] = "";
+App::$strings["WordPress blogid"] = "";
+App::$strings["For multi-user sites such as wordpress.com, otherwise leave blank"] = "";
+App::$strings["Post to WordPress by default"] = "";
+App::$strings["Forward comments (requires hubzilla_wp plugin)"] = "";
+App::$strings["Wordpress Post"] = "";
+App::$strings["Edit Article"] = "";
+App::$strings["Articles"] = "";
+App::$strings["View Articles"] = "";
+App::$strings["Add Article"] = "";
+App::$strings["Profile Unavailable."] = "Profilen er ikke tilgjengelig.";
+App::$strings["Invalid channel"] = "Ugyldig kanal";
+App::$strings["Error retrieving wiki"] = "Det oppstod en feil ved henting av wikien";
+App::$strings["Error creating zip file export folder"] = "Det oppstod en feil ved eksport av mappe til zip fil";
+App::$strings["Error downloading wiki: "] = "";
+App::$strings["Wikis"] = "Wikier";
+App::$strings["Download"] = "";
+App::$strings["View"] = "Vis";
+App::$strings["Create New"] = "Lag ny profil";
+App::$strings["Wiki name"] = "";
+App::$strings["Content type"] = "";
+App::$strings["Type"] = "Type";
+App::$strings["Any&nbsp;type"] = "";
+App::$strings["Lock content type"] = "";
+App::$strings["Create a status post for this wiki"] = "";
+App::$strings["Edit Wiki Name"] = "";
+App::$strings["Wiki not found"] = "Fant ikke wikien.";
+App::$strings["Rename page"] = "";
+App::$strings["Share"] = "Del";
+App::$strings["Error retrieving page content"] = "";
+App::$strings["New page"] = "";
+App::$strings["Revision Comparison"] = "";
+App::$strings["Revert"] = "Tilbakestill";
+App::$strings["Short description of your changes (optional)"] = "";
+App::$strings["New page name"] = "";
+App::$strings["Embed image from photo albums"] = "";
+App::$strings["Choose a different album"] = "Velg et annet album";
+App::$strings["History"] = "";
+App::$strings["Error creating wiki. Invalid name."] = "";
+App::$strings["A wiki with this name already exists."] = "";
+App::$strings["Wiki created, but error creating Home page."] = "";
+App::$strings["Error creating wiki"] = "";
+App::$strings["Error updating wiki. Invalid name."] = "";
+App::$strings["Error updating wiki"] = "";
+App::$strings["Wiki delete permission denied."] = "";
+App::$strings["Error deleting wiki"] = "";
+App::$strings["New page created"] = "";
+App::$strings["Cannot delete Home"] = "";
+App::$strings["Current Revision"] = "";
+App::$strings["Selected Revision"] = "";
+App::$strings["You must be authenticated."] = "";
+App::$strings["Add new page"] = "Legg til ny side";
+App::$strings["Options"] = "Valg";
+App::$strings["Wiki updated successfully"] = "Wikien ble oppdatert";
+App::$strings["Wiki files deleted successfully"] = "Wikifiler ble slettet";
+App::$strings["(No Title)"] = "(Ingen tittel)";
+App::$strings["Wiki page create failed."] = "Kunne ikke opprette wikiside.";
+App::$strings["Wiki not found."] = "Fant ikke wikien.";
+App::$strings["Destination name already exists"] = "Navnet finnes allerede";
+App::$strings["Page not found"] = "Fant ikke siden";
+App::$strings["Error reading page content"] = "Det oppstod en feil under lesing av innholder på siden";
+App::$strings["Error reading wiki"] = "Det oppstod en feil ved lesing av wikien";
+App::$strings["Page update failed."] = "Oppdatering av siden mislyktes.";
+App::$strings["Nothing deleted"] = "Ingenting ble slettet";
+App::$strings["Compare: object not found."] = "Sammanlign: fant ikke objektet.";
+App::$strings["Page updated"] = "Siden ble oppdatert";
+App::$strings["Wiki resource_id required for git commit"] = "Wiki ressurs_id er nødvendig for å lagre i git";
+App::$strings["__ctx:wiki_history__ Message"] = "Melding";
+App::$strings["Date"] = "Dato";
+App::$strings["Compare"] = "Sammenlign";
+App::$strings["Wiki Pages"] = "Wikisider";
+App::$strings["Page name"] = "Sidenavn";
+App::$strings["Project Servers and Resources"] = "";
+App::$strings["Project Creator and Tech Lead"] = "";
+App::$strings["And the hundreds of other people and organisations who helped make the Hubzilla possible."] = "";
+App::$strings["The Redmatrix/Hubzilla projects are provided primarily by volunteers giving their time and expertise - and often paying out of pocket for services they share with others."] = "";
+App::$strings["There is no corporate funding and no ads, and we do not collect and sell your personal information. (We don't control your personal information - <strong>you do</strong>.)"] = "";
+App::$strings["Help support our ground-breaking work in decentralisation, web identity, and privacy."] = "";
+App::$strings["Your donations keep servers and services running and also helps us to provide innovative new features and continued development."] = "";
+App::$strings["Donate"] = "";
+App::$strings["Choose a project, developer, or public hub to support with a one-time donation"] = "";
+App::$strings["Donate Now"] = "";
+App::$strings["<strong><em>Or</em></strong> become a project sponsor (Hubzilla Project only)"] = "";
+App::$strings["Please indicate if you would like your first name or full name (or nothing) to appear in our sponsor listing"] = "";
+App::$strings["Sponsor"] = "";
+App::$strings["Special thanks to: "] = "";
+App::$strings["Jabber BOSH host"] = "";
+App::$strings["Use central userbase"] = "";
+App::$strings["If enabled, members will automatically login to an ejabberd server that has to be installed on this machine with synchronized credentials via the \"auth_ejabberd.php\" script."] = "";
+App::$strings["XMPP settings updated."] = "";
+App::$strings["XMPP App"] = "";
+App::$strings["Embedded XMPP (Jabber) client"] = "";
+App::$strings["Individual credentials"] = "";
+App::$strings["Jabber BOSH server"] = "";
+App::$strings["XMPP Settings"] = "";
+App::$strings["New registration"] = "";
+App::$strings["%s : Message delivery failed."] = "%s : meldingslevering feilet.";
+App::$strings["Message sent to %s. New account registration: %s"] = "";
+App::$strings["Post to GNU social"] = "";
+App::$strings["Site name"] = "Nettstedets navn";
+App::$strings["API URL"] = "";
+App::$strings["Consumer Secret"] = "Consumer Secret";
+App::$strings["Consumer Key"] = "Consumer Key";
+App::$strings["Application name"] = "";
+App::$strings["Please contact your site administrator.<br />The provided API URL is not valid."] = "Den oppgitte URLen for APIet er ikke gyldig. Kontakt serverens administrator.";
+App::$strings["We could not contact the GNU social API with the Path you entered."] = "";
+App::$strings["GNU social settings updated."] = "";
+App::$strings["Globally Available GNU social OAuthKeys"] = "";
+App::$strings["There are preconfigured OAuth key pairs for some GNU social servers available. If you are using one of them, please use these credentials.<br />If not feel free to connect to any other GNU social instance (see below)."] = "";
+App::$strings["Provide your own OAuth Credentials"] = "";
+App::$strings["No consumer key pair for GNU social found. Register your Hubzilla Account as an desktop client on your GNU social account, copy the consumer key pair here and enter the API base root.<br />Before you register your own OAuth key pair ask the administrator if there is already a key pair for this Hubzilla installation at your favourite GNU social installation."] = "";
+App::$strings["OAuth Consumer Key"] = "";
+App::$strings["OAuth Consumer Secret"] = "";
+App::$strings["Base API Path"] = "";
+App::$strings["Remember the trailing /"] = "";
+App::$strings["GNU social application name"] = "";
+App::$strings["To connect to your GNU social account click the button below to get a security code from GNU social which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to GNU social."] = "";
+App::$strings["Log in with GNU social"] = "";
+App::$strings["Copy the security code from GNU social here"] = "";
+App::$strings["Cancel Connection Process"] = "";
+App::$strings["Current GNU social API is"] = "";
+App::$strings["Cancel GNU social Connection"] = "";
+App::$strings["Currently connected to: "] = "";
+App::$strings["<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to GNU social will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "";
+App::$strings["Post to GNU social by default"] = "";
+App::$strings["If enabled your public postings will be posted to the associated GNU-social account by default"] = "";
+App::$strings["Clear OAuth configuration"] = "";
+App::$strings["GNU-Social Crosspost Connector"] = "";
+App::$strings["Affinity"] = "";
+App::$strings["Post to Livejournal"] = "";
+App::$strings["Posted by"] = "";
+App::$strings["Livejournal username"] = "";
+App::$strings["Livejournal password"] = "";
+App::$strings["Post to Livejournal by default"] = "";
+App::$strings["Send wall-to-wall posts to Livejournal"] = "";
+App::$strings["Livejournal Crosspost Connector"] = "";
+App::$strings["Error: order mismatch. Please try again."] = "";
+App::$strings["Manual payments are not enabled."] = "";
+App::$strings["Order not found."] = "";
+App::$strings["Finished"] = "";
+App::$strings["Enable Hubzilla Services Module"] = "";
+App::$strings["New Sku"] = "";
+App::$strings["Cannot save edits to locked item."] = "";
+App::$strings["SKU not found."] = "";
+App::$strings["Invalid Activation Directive."] = "";
+App::$strings["Invalid Deactivation Directive."] = "";
+App::$strings["Add to this privacy group"] = "";
+App::$strings["Set user service class"] = "";
+App::$strings["You must be using a local account to purchase this service."] = "";
+App::$strings["Changes Locked"] = "";
+App::$strings["Item available for purchase."] = "";
+App::$strings["Price"] = "";
+App::$strings["Photo URL"] = "";
+App::$strings["Add buyer to privacy group"] = "";
+App::$strings["Add buyer as connection"] = "";
+App::$strings["Set Service Class"] = "";
+App::$strings["Enable Subscription Management Module"] = "";
+App::$strings["Cannot include subscription items with different terms in the same order."] = "";
+App::$strings["Select Subscription to Edit"] = "";
+App::$strings["Edit Subscriptions"] = "";
+App::$strings["Subscription SKU"] = "";
+App::$strings["Catalog Description"] = "";
+App::$strings["Subscription available for purchase."] = "";
+App::$strings["Maximum active subscriptions to this item per account."] = "";
+App::$strings["Subscription price."] = "";
+App::$strings["Quantity"] = "";
+App::$strings["Term"] = "";
+App::$strings["Enable Paypal Button Module (API-v2)"] = "";
+App::$strings["Use Production Key"] = "";
+App::$strings["Paypal Sandbox Client Key"] = "";
+App::$strings["Paypal Sandbox Secret Key"] = "";
+App::$strings["Paypal Production Client Key"] = "";
+App::$strings["Paypal Production Secret Key"] = "";
+App::$strings["Paypal button payments are not enabled."] = "";
+App::$strings["Paypal button payments are not properly configured. Please choose another payment option."] = "";
+App::$strings["Enable Paypal Button Module"] = "";
+App::$strings["Enable Manual Cart Module"] = "";
+App::$strings["Enable Order/Item Options"] = "";
+App::$strings["Label"] = "";
+App::$strings["Instructions"] = "";
+App::$strings["Access Denied."] = "Ingen tilgang";
+App::$strings["Order Not Found"] = "";
+App::$strings["Access Denied"] = "Ingen tilgang";
+App::$strings["Invalid Item"] = "Ugyldig element.";
+App::$strings["DB Cleanup Failure"] = "";
+App::$strings["[cart] Item Added"] = "";
+App::$strings["Order already checked out."] = "";
+App::$strings["Drop database tables when uninstalling."] = "";
+App::$strings["Cart Settings"] = "";
+App::$strings["Shop"] = "";
+App::$strings["You must be logged into the Grid to shop."] = "";
+App::$strings["Access denied."] = "Ingen tilgang";
+App::$strings["No Order Found"] = "";
+App::$strings["An unknown error has occurred Please start again."] = "";
+App::$strings["Requirements not met."] = "";
+App::$strings["Review your order and complete any needed requirements."] = "";
+App::$strings["Invalid Payment Type. Please start again."] = "";
+App::$strings["Order not found"] = "";
+App::$strings["Enable Test Catalog"] = "";
+App::$strings["Enable Manual Payments"] = "";
+App::$strings["Base Merchant Currency"] = "";
+App::$strings["Post to Twitter"] = "";
+App::$strings["Twitter settings updated."] = "";
+App::$strings["No consumer key pair for Twitter found. Please contact your site administrator."] = "";
+App::$strings["At this Hubzilla instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter."] = "";
+App::$strings["Log in with Twitter"] = "";
+App::$strings["Copy the PIN from Twitter here"] = "";
+App::$strings["<strong>Note:</strong> Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "";
+App::$strings["Twitter post length"] = "";
+App::$strings["Maximum tweet length"] = "";
+App::$strings["Send public postings to Twitter by default"] = "";
+App::$strings["If enabled your public postings will be posted to the associated Twitter account by default"] = "";
+App::$strings["Twitter Crosspost Connector"] = "";
+App::$strings["generic profile image"] = "";
+App::$strings["random geometric pattern"] = "";
+App::$strings["monster face"] = "";
+App::$strings["computer generated face"] = "";
+App::$strings["retro arcade style face"] = "";
+App::$strings["Hub default profile photo"] = "";
+App::$strings["Information"] = "";
+App::$strings["Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar."] = "";
+App::$strings["Default avatar image"] = "";
+App::$strings["Select default avatar image if none was found at Gravatar. See README"] = "";
+App::$strings["Rating of images"] = "";
+App::$strings["Select the appropriate avatar rating for your site. See README"] = "";
+App::$strings["Gravatar settings updated."] = "";
+App::$strings["WYSIWYG status editor"] = "";
+App::$strings["WYSIWYG Status App"] = "";
+App::$strings["WYSIWYG Status"] = "";
+App::$strings["Redmatrix File Storage Import"] = "";
+App::$strings["This will import all your Redmatrix cloud files to this channel."] = "";
+App::$strings["View Larger"] = "";
+App::$strings["Tile Server URL"] = "";
+App::$strings["A list of <a href=\"http://wiki.openstreetmap.org/wiki/TMS\" target=\"_blank\">public tile servers</a>"] = "";
+App::$strings["Nominatim (reverse geocoding) Server URL"] = "";
+App::$strings["A list of <a href=\"http://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\">Nominatim servers</a>"] = "";
+App::$strings["Default zoom"] = "";
+App::$strings["The default zoom level. (1:world, 18:highest, also depends on tile server)"] = "";
+App::$strings["Include marker on map"] = "";
+App::$strings["Include a marker on the map."] = "";
+App::$strings["Workflow user."] = "";
+App::$strings["This channel"] = "";
+App::$strings["Primary"] = "Primær";
+App::$strings["Create New Workflow Item"] = "";
+App::$strings["Workflow"] = "";
+App::$strings["No Workflows Available"] = "";
+App::$strings["Add item to which workflow"] = "";
+App::$strings["Create Workflow Item"] = "";
+App::$strings["Link"] = "";
+App::$strings["Web link."] = "";
+App::$strings["Title"] = "Tittel";
+App::$strings["Brief description or title"] = "";
+App::$strings["Notes"] = "Merknader";
+App::$strings["Notes and Info"] = "";
+App::$strings["Priority"] = "Prioritet";
+App::$strings["Used to order links"] = "";
+App::$strings["Body"] = "";
+App::$strings["Workflow Settings"] = "";
+App::$strings["Workflow settings"] = "Instillinger for redigering";
+App::$strings["Some setting"] = "";
+App::$strings["A setting"] = "";
+App::$strings["Skeleton Settings"] = "";
+App::$strings["Allow magic authentication only to websites of your immediate connections"] = "";
+App::$strings["Authchoose"] = "";
+App::$strings["No server specified"] = "";
+App::$strings["Posts imported"] = "";
+App::$strings["Files imported"] = "";
+App::$strings["Content Import"] = "";
+App::$strings["This will import all your conversations and cloud files from a cloned channel on another server. This may take a while if you have lots of posts and or files."] = "";
+App::$strings["Include posts"] = "";
+App::$strings["Conversations, Articles, Cards, and other posted content"] = "";
+App::$strings["Include files"] = "";
+App::$strings["Files, Photos and other cloud storage"] = "";
+App::$strings["Original Server base URL"] = "";
+App::$strings["Use markdown for editing posts"] = "";
+App::$strings["Friendica Crosspost Connector Settings saved."] = "";
+App::$strings["Send public postings to Friendica by default"] = "";
+App::$strings["Friendica API Path"] = "";
+App::$strings["Friendica login name"] = "";
+App::$strings["Friendica password"] = "";
+App::$strings["Friendica Crosspost Connector"] = "";
+App::$strings["Post to Friendica"] = "";
+App::$strings["An account has been created for you."] = "";
+App::$strings["Authentication successful but rejected: account creation is disabled."] = "";
+App::$strings["Federate"] = "";
+App::$strings["nofed Settings saved."] = "";
+App::$strings["Federate posts by default"] = "";
+App::$strings["No Federation"] = "";
+App::$strings["Photo Cache settings saved."] = "";
+App::$strings["Saves a copy of images from external sites locally to increase your anonymity in the web."] = "";
+App::$strings["Minimal photo size for caching"] = "";
+App::$strings["In pixels. From 1 up to 1024, 0 will be replaced with system default."] = "";
+App::$strings["Photo Cache"] = "";
+App::$strings["Gallery"] = "";
+App::$strings["Photo Gallery"] = "";
+App::$strings["Your Webbie:"] = "";
+App::$strings["Fontsize (px):"] = "";
+App::$strings["Link:"] = "";
+App::$strings["Like us on Hubzilla"] = "";
+App::$strings["Embed:"] = "";
+App::$strings["Three Dimensional Tic-Tac-Toe"] = "";
+App::$strings["3D Tic-Tac-Toe"] = "";
+App::$strings["New game"] = "";
+App::$strings["New game with handicap"] = "";
+App::$strings["Three dimensional tic-tac-toe is just like the traditional game except that it is played on multiple levels simultaneously. "] = "";
+App::$strings["In this case there are three levels. You win by getting three in a row on any level, as well as up, down, and diagonally across the different levels."] = "";
+App::$strings["The handicap game disables the center position on the middle level because the player claiming this square often has an unfair advantage."] = "";
+App::$strings["You go first..."] = "";
+App::$strings["I'm going first this time..."] = "";
+App::$strings["You won!"] = "";
+App::$strings["\"Cat\" game!"] = "";
+App::$strings["I won!"] = "";
App::$strings["Create an account to access services and applications"] = "";
App::$strings["Email or nickname"] = "Epost eller brukernavn";
-App::$strings["Nickname"] = "Kallenavn";
App::$strings["Password"] = "Passord";
App::$strings["Remember me"] = "Husk meg";
App::$strings["Forgot your password?"] = "Glemt passordet ditt?";
@@ -855,23 +1476,79 @@ App::$strings["[\$Projectname] Website SSL error for %s"] = "";
App::$strings["Website SSL certificate is not valid. Please correct."] = "Nettstedets SSL-sertifikat er ikke gyldig. Vennligst fiks dette.";
App::$strings["[\$Projectname] Cron tasks not running on %s"] = "";
App::$strings["Cron/Scheduled tasks not running."] = "Cron/planlagte oppgaver kjører ikke.";
-App::$strings["0. Beginner/Basic"] = "";
-App::$strings["1. Novice - not skilled but willing to learn"] = "";
-App::$strings["2. Intermediate - somewhat comfortable"] = "";
-App::$strings["3. Advanced - very comfortable"] = "";
-App::$strings["4. Expert - I can write computer code"] = "";
-App::$strings["5. Wizard - I probably know more than you do"] = "";
+App::$strings["\$Projectname Notification"] = "\$Projectname varsling";
+App::$strings["Thank You,"] = "Tusen takk,";
+App::$strings["This email was sent by %1\$s at %2\$s."] = "";
+App::$strings["To stop receiving these messages, please adjust your Notification Settings at %s"] = "";
+App::$strings["To stop receiving these messages, please adjust your %s."] = "";
+App::$strings["Notification Settings"] = "Varslingsinnstillinger";
+App::$strings["%s <!item_type!>"] = "%s <!item_type!>";
+App::$strings["[\$Projectname:Notify] New direct message received at %s"] = "[\$Projectname:Notify] Ny direktemelding mottatt kl. %s";
+App::$strings["%1\$s sent you a new private message at %2\$s"] = "%1\$s sendte deg en ny direktemelding kl. %2\$s";
+App::$strings["%1\$s sent you %2\$s."] = "%1\$s sendte deg %2\$s.";
+App::$strings["a direct message"] = "en direktemelding";
+App::$strings["Please visit %s to view and/or reply to your private messages."] = "Gå til %s for å vise og/eller svare på dine direktemeldinger.";
+App::$strings["requested to post in"] = "vil like";
+App::$strings["posted in"] = "lagt inn";
+App::$strings["requested to like"] = "vil like";
+App::$strings["liked"] = "";
+App::$strings["requested to dislike"] = "";
+App::$strings["disliked"] = "";
+App::$strings["requested to repeat"] = "vil like";
+App::$strings["repeated"] = "videresendte";
+App::$strings["voted on"] = "";
+App::$strings["%1\$s %2\$s [zrl=%3\$s]a %4\$s[/zrl]"] = "";
+App::$strings["%1\$s %2\$s [zrl=%3\$s]%4\$s's %5\$s[/zrl]"] = "";
+App::$strings["%1\$s %2\$s [zrl=%3\$s]your %4\$s[/zrl]"] = "";
+App::$strings["[\$Projectname:Notify] Moderated Comment to conversation #%1\$d by %2\$s"] = "";
+App::$strings["[\$Projectname:Notify] Comment to conversation #%1\$d by %2\$s"] = "";
+App::$strings["%1\$s commented on an item/conversation you have been following"] = "";
+App::$strings["Please visit %s to view and/or reply to the conversation."] = "Vennligst besøk %s for å se og/eller svare i samtalen.";
+App::$strings["Please visit %s to approve or reject this comment."] = "";
+App::$strings["[\$Projectname:Notify] Like received to conversation #%1\$d by %2\$s"] = "";
+App::$strings["%1\$s liked an item/conversation you created"] = "";
+App::$strings["[\$Projectname:Notify] %s posted to your profile wall"] = "";
+App::$strings["%1\$s posted to your profile wall at %2\$s"] = "";
+App::$strings["%1\$s posted to [zrl=%2\$s]your wall[/zrl]"] = "";
+App::$strings["[\$Projectname:Notify] %s tagged you"] = "";
+App::$strings["%1\$s tagged you at %2\$s"] = "";
+App::$strings["%1\$s [zrl=%2\$s]tagged you[/zrl]."] = "";
+App::$strings["[\$Projectname:Notify] %1\$s poked you"] = "";
+App::$strings["%1\$s poked you at %2\$s"] = "";
+App::$strings["%1\$s [zrl=%2\$s]poked you[/zrl]."] = "";
+App::$strings["[\$Projectname:Notify] %s tagged your post"] = "";
+App::$strings["%1\$s tagged your post at %2\$s"] = "";
+App::$strings["%1\$s tagged [zrl=%2\$s]your post[/zrl]"] = "";
+App::$strings["[\$Projectname:Notify] Introduction received"] = "";
+App::$strings["You've received an new connection request from '%1\$s' at %2\$s"] = "";
+App::$strings["You've received [zrl=%1\$s]a new connection request[/zrl] from %2\$s."] = "";
+App::$strings["You may visit their profile at %s"] = "Du kan besøke profilen deres på %s";
+App::$strings["Please visit %s to approve or reject the connection request."] = "Vennligst besøk %s for å godkjenne eller avslå forespørselen om forbindelse.";
+App::$strings["[\$Projectname:Notify] Friend suggestion received"] = "";
+App::$strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = "";
+App::$strings["You've received [zrl=%1\$s]a friend suggestion[/zrl] for %2\$s from %3\$s."] = "";
+App::$strings["Name:"] = "Navn:";
+App::$strings["Photo:"] = "Bilde:";
+App::$strings["Please visit %s to approve or reject the suggestion."] = "Vennligst besøk %s for å godkjenne eller avslå dette forslaget.";
+App::$strings["[\$Projectname:Notify]"] = "";
+App::$strings["started a poll"] = "opprettet spørreskjema";
+App::$strings["started a conversation"] = "started en samtale";
+App::$strings["voted on %s's poll"] = "stemte på %s sitt spørreskjema";
+App::$strings["posted in %s's conversation"] = "postet i %s sin samtale";
+App::$strings["shared a file with you"] = "delte en fil med deg";
+App::$strings["edited a message dated %s"] = "redigerte en melding datert %s";
+App::$strings["added your channel"] = "la til din kanal";
+App::$strings["sent you a direct message"] = "sendte deg en direktemelding";
+App::$strings["g A l F d"] = "g A l F d";
+App::$strings["[today]"] = "[idag]";
+App::$strings["created an event"] = "opprettet et arrangement";
+App::$strings["status verified"] = "status verifisert";
App::$strings["Affinity Tool"] = "Nærhetsverktøy";
-App::$strings["Articles"] = "";
-App::$strings["Cards"] = "";
App::$strings["Site Admin"] = "Nettstedsadministrator";
-App::$strings["Report Bug"] = "";
-App::$strings["Content Filter"] = "";
-App::$strings["Content Import"] = "";
-App::$strings["Remote Diagnostics"] = "";
+App::$strings["Content Filter"] = "Innholdsfilter";
+App::$strings["Remote Diagnostics"] = "Fjerndiagnostikk";
App::$strings["Suggest Channels"] = "Foreslå kanaler";
App::$strings["Channel Manager"] = "Kanalstyring";
-App::$strings["Stream"] = "Tidslinje";
App::$strings["Wiki"] = "";
App::$strings["Mail"] = "Melding";
App::$strings["Chat"] = "Chat";
@@ -880,37 +1557,52 @@ App::$strings["Suggest"] = "Forreslå";
App::$strings["Random Channel"] = "Tilfeldig kanal";
App::$strings["Invite"] = "Inviter";
App::$strings["Features"] = "Funksjoner";
-App::$strings["Language"] = "Språk";
App::$strings["Post"] = "Innlegg";
App::$strings["Notifications"] = "Varsler";
-App::$strings["Order Apps"] = "";
+App::$strings["Order Apps"] = "Ordne apper";
App::$strings["CardDAV"] = "";
App::$strings["Channel Sources"] = "Kanalkilder";
-App::$strings["Guest Access"] = "";
-App::$strings["Notes"] = "Merknader";
+App::$strings["Guest Access"] = "Gjestetilgang";
App::$strings["OAuth Apps Manager"] = "";
App::$strings["OAuth2 Apps Manager"] = "";
-App::$strings["PDL Editor"] = "";
-App::$strings["Contact Roles"] = "";
-App::$strings["Public Stream"] = "";
-App::$strings["My Chatrooms"] = "";
-App::$strings["Channel Export"] = "";
+App::$strings["PDL Editor"] = "PDL Redigering";
+App::$strings["Contact Roles"] = "Kontaktroller";
+App::$strings["Public Stream"] = "Offentlig strøm";
+App::$strings["My Chatrooms"] = "Mine chattekanaler";
+App::$strings["Channel Export"] = "Kanaleksport";
App::$strings["Update"] = "Oppdater";
App::$strings["Install"] = "Installer";
App::$strings["Purchase"] = "Kjøp";
-App::$strings["Undelete"] = "";
+App::$strings["Undelete"] = "Hent tilbake";
App::$strings["Add to app-tray"] = "Legg til i meny";
App::$strings["Remove from app-tray"] = "Fjern fra meny";
App::$strings["Pin to navbar"] = "Fest til navigasjonslinjen";
App::$strings["Unpin from navbar"] = "Fjern fra navigasjonslinjen";
-App::$strings["Directory Options"] = "Kataloginnstillinger";
-App::$strings["Safe Mode"] = "Trygg modus";
-App::$strings["Public Forums Only"] = "Bare offentlige forum";
-App::$strings["This Website Only"] = "Kun dette nettstedet";
-App::$strings["A deleted privacy group with this name was revived. Existing item permissions <strong>may</strong> apply to this privacy group and any future members. If this is not what you intended, please create another privacy group with a different name."] = "";
-App::$strings["Select a privacy group"] = "";
+App::$strings["Public"] = "Offentlig";
+App::$strings["Anybody in the \$Projectname network"] = "Alle i \$Projectname-nettverket";
+App::$strings["Any account on %s"] = "Brukerkontoer på %s";
+App::$strings["Any of my connections"] = "Alle mine forbindelser";
+App::$strings["Only connections I specifically allow"] = "Kun forbindelser jeg gir tilgang";
+App::$strings["Anybody authenticated (could include visitors from other networks)"] = "Alle som er autentisert (det kan inkludere besøkende fra andre nettverk)";
+App::$strings["Any connections including those who haven't yet been approved"] = "Alle forbindelser inkludert forbindelser som ikke enda har blitt godkjent";
+App::$strings["This is your default setting for the audience of your normal stream, and posts."] = "Dette er standardinnstillingene for hvem som vil motta din vanlige strøm og innlegg.";
+App::$strings["This is your default setting for who can view your default channel profile"] = "Dette er standardinnstillingene for hvem som kan se kanalens standardprofil";
+App::$strings["This is your default setting for who can view your connections"] = "Dette er standardinnstillingene for hvem som kan se dine forbindelser";
+App::$strings["This is your default setting for who can view your file storage and photos"] = "Dette er standardinnstillingene for hvem som kan se dine filer og bilder";
+App::$strings["This is your default setting for the audience of your webpages"] = "Dette er standardinnstillingene for hvem som kan se dine websider";
+App::$strings["__ctx:permcat__ Default"] = "Standard";
+App::$strings["Source code of failed update: "] = "Kildekoden til oppdateringen som mislyktes:";
+App::$strings["Update Error at %s"] = "Oppdateringsfeil ved %s";
+App::$strings["Update %s failed. See error logs."] = "Oppdatering %s mislyktes. Se feilloggen.";
+App::$strings["Channel is blocked on this site."] = "Kanalen er blokkert på dette nettstedet.";
+App::$strings["Channel location missing."] = "Kanalplassering mangler.";
+App::$strings["Remote channel or protocol unavailable."] = "Kanalen eller protokollen er ikke tilgjengelig på den andre serveren.";
+App::$strings["Channel discovery failed."] = "Kanaloppdagelse mislyktes.";
+App::$strings["Protocol disabled."] = "Protokollen er avskrudd.";
+App::$strings["Cannot connect to yourself."] = "Kan ikke lage forbindelse med deg selv.";
+App::$strings["error saving data"] = "feil ved lagring av data";
App::$strings["Restricted message"] = "Begrenset melding";
-App::$strings["Direct message"] = "Direktemelding";
+App::$strings["Private message"] = "Privat melding";
App::$strings["Public Policy"] = "";
App::$strings["Privacy conflict. Discretion advised."] = "";
App::$strings["Admin Delete"] = "";
@@ -918,9 +1610,7 @@ App::$strings["Save to Folder"] = "Lagre i mappe";
App::$strings["I will attend"] = "Jeg vil delta";
App::$strings["I will not attend"] = "Jeg deltar ikke";
App::$strings["I might attend"] = "Jeg vil kanskje delta";
-App::$strings["I like this (toggle)"] = "Jeg liker dette (skru av og på)";
-App::$strings["I don't like this (toggle)"] = "Jeg liker ikke dette (skru av og på)";
-App::$strings["Reply to this comment"] = "Slett denne menyen";
+App::$strings["Reply to this message"] = "Svar på denne meldingen";
App::$strings["reply"] = "";
App::$strings["Reply to"] = "";
App::$strings["share"] = "del";
@@ -932,136 +1622,61 @@ App::$strings["%d comment"] = array(
1 => "%d kommentarer",
);
App::$strings["%d unseen"] = "%d uleste";
+App::$strings["Load the next few of total %d comments"] = "Last inn noen flere av ialt %d kommentarer";
+App::$strings["Expand Replies"] = "";
App::$strings["Forum"] = "";
App::$strings["to"] = "til";
App::$strings["via"] = "via";
App::$strings["Wall-to-Wall"] = "Vegg-til-vegg";
App::$strings["via Wall-To-Wall:"] = "via vegg-til-vegg:";
-App::$strings["Attend"] = "";
-App::$strings["Attendance Options"] = "";
-App::$strings["Voting Options"] = "";
-App::$strings["Go to previous comment"] = "";
-App::$strings["Pinned post"] = "";
-App::$strings["Save Bookmarks"] = "Lagre bokmerker";
+App::$strings["Last edited %s"] = "Sist endret: %s";
+App::$strings["Expires %s"] = "Utløper %s";
+App::$strings["Published %s"] = "Publisert %s";
+App::$strings["Attend"] = "Delta";
+App::$strings["Attendance Options"] = "Valg for deltagelse";
+App::$strings["Voting Options"] = "Valg for avstemning";
+App::$strings["Go to previous comment"] = "Gå til forrige kommentar";
+App::$strings["Pinned post"] = "Festet innlegg";
App::$strings["Add to Calendar"] = "Legg til i kalender";
-App::$strings["Mark all comments seen"] = "Merk alle hendelser som sett";
-App::$strings["Add yours"] = "Legg til bilder";
-App::$strings["Remove yours"] = "Fjern begrep";
+App::$strings["Mark all comments seen"] = "Merk alle kommentarer som sett";
+App::$strings["Add yours"] = "Legg til din";
+App::$strings["Remove yours"] = "Fjern din";
+App::$strings["show less"] = "vis færre";
+App::$strings["show more"] = "vis flere";
+App::$strings["show all"] = "vis alle";
App::$strings["This is you"] = "Dette er deg";
-App::$strings["Image"] = "Bilde";
App::$strings["Insert Link"] = "Sett inn lenke";
App::$strings["Video"] = "Video";
-App::$strings["Your full name (required)"] = "";
-App::$strings["Your email address (required)"] = "";
-App::$strings["Your website URL (optional)"] = "";
+App::$strings["Your full name (required)"] = "Fullt navn (påkrevd)";
+App::$strings["Your email address (required)"] = "Epostaddresse (påkrevd)";
+App::$strings["Your website URL (optional)"] = "Webside (valgfritt)";
+App::$strings["Unable to verify channel signature"] = "Ikke i stand til å sjekke kanalsignaturen";
App::$strings["Likes %1\$s's %2\$s"] = "";
App::$strings["Doesn't like %1\$s's %2\$s"] = "";
App::$strings["Will attend %s's event"] = "";
App::$strings["Will not attend %s's event"] = "";
App::$strings["May attend %s's event"] = "";
App::$strings["May not attend %s's event"] = "";
+App::$strings["Unable to verify site signature for %s"] = "Ikke i stand til å bekrefte signaturen til %s";
+App::$strings["Directory Options"] = "Kataloginnstillinger";
+App::$strings["Safe Mode"] = "Trygg modus";
+App::$strings["Public Forums Only"] = "Bare offentlige forum";
+App::$strings["This Website Only"] = "Kun dette nettstedet";
App::$strings["Missing room name"] = "Mangler romnavn";
App::$strings["Duplicate room name"] = "Duplikat romnavn";
App::$strings["Invalid room specifier."] = "Ugyldig rom-spesifisering.";
App::$strings["Room not found."] = "Rommet ble ikke funnet.";
App::$strings["Room is full"] = "Rommet er fullt";
-App::$strings["\$Projectname Notification"] = "\$Projectname varsling";
-App::$strings["\$projectname"] = "\$projectname";
-App::$strings["Thank You,"] = "Tusen takk,";
-App::$strings["%s Administrator"] = "%s administrator";
-App::$strings["This email was sent by %1\$s at %2\$s."] = "";
-App::$strings["\$Projectname"] = "\$Projectname";
-App::$strings["To stop receiving these messages, please adjust your Notification Settings at %s"] = "";
-App::$strings["To stop receiving these messages, please adjust your %s."] = "";
-App::$strings["Notification Settings"] = "Varslingsinnstillinger";
-App::$strings["%s <!item_type!>"] = "%s <!item_type!>";
-App::$strings["[\$Projectname:Notify] New direct message received at %s"] = "[\$Projectname:Notify] Ny direktemelding mottatt kl. %s";
-App::$strings["%1\$s sent you a new direct message at %2\$s"] = "%1\$s sendte deg en ny direktemelding kl. %2\$s";
-App::$strings["%1\$s sent you %2\$s."] = "%1\$s sendte deg %2\$s.";
-App::$strings["a direct message"] = "en direktemelding";
-App::$strings["Please visit %s to view and/or reply to your direct messages."] = "Gå til %s for å vise og/eller svare på dine direktemeldinger.";
-App::$strings["requested to comment on"] = "";
-App::$strings["commented on"] = "";
-App::$strings["requested to like"] = "Dato for forespørsel";
-App::$strings["liked"] = "";
-App::$strings["requested to dislike"] = "";
-App::$strings["disliked"] = "";
-App::$strings["repeated"] = "Laget";
-App::$strings["voted on"] = "";
-App::$strings["%1\$s %2\$s [zrl=%3\$s]a %4\$s[/zrl]"] = "";
-App::$strings["%1\$s %2\$s [zrl=%3\$s]%4\$s's %5\$s[/zrl]"] = "";
-App::$strings["%1\$s %2\$s [zrl=%3\$s]your %4\$s[/zrl]"] = "";
-App::$strings["[\$Projectname:Notify] Moderated Comment to conversation #%1\$d by %2\$s"] = "";
-App::$strings["[\$Projectname:Notify] Comment to conversation #%1\$d by %2\$s"] = "";
-App::$strings["%1\$s commented on an item/conversation you have been following"] = "";
-App::$strings["Please visit %s to view and/or reply to the conversation."] = "Vennligst besøk %s for å se og/eller svare i samtalen.";
-App::$strings["Please visit %s to approve or reject this comment."] = "";
-App::$strings["[\$Projectname:Notify] Like received to conversation #%1\$d by %2\$s"] = "";
-App::$strings["%1\$s liked an item/conversation you created"] = "";
-App::$strings["[\$Projectname:Notify] %s posted to your profile wall"] = "";
-App::$strings["%1\$s posted to your profile wall at %2\$s"] = "";
-App::$strings["%1\$s posted to [zrl=%2\$s]your wall[/zrl]"] = "";
-App::$strings["[\$Projectname:Notify] %s tagged you"] = "";
-App::$strings["%1\$s tagged you at %2\$s"] = "";
-App::$strings["%1\$s [zrl=%2\$s]tagged you[/zrl]."] = "";
-App::$strings["[\$Projectname:Notify] %1\$s poked you"] = "";
-App::$strings["%1\$s poked you at %2\$s"] = "";
-App::$strings["%1\$s [zrl=%2\$s]poked you[/zrl]."] = "";
-App::$strings["[\$Projectname:Notify] %s tagged your post"] = "";
-App::$strings["%1\$s tagged your post at %2\$s"] = "";
-App::$strings["%1\$s tagged [zrl=%2\$s]your post[/zrl]"] = "";
-App::$strings["[\$Projectname:Notify] Introduction received"] = "";
-App::$strings["You've received an new connection request from '%1\$s' at %2\$s"] = "";
-App::$strings["You've received [zrl=%1\$s]a new connection request[/zrl] from %2\$s."] = "";
-App::$strings["You may visit their profile at %s"] = "Du kan besøke profilen deres på %s";
-App::$strings["Please visit %s to approve or reject the connection request."] = "Vennligst besøk %s for å godkjenne eller avslå forespørselen om forbindelse.";
-App::$strings["[\$Projectname:Notify] Friend suggestion received"] = "";
-App::$strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = "";
-App::$strings["You've received [zrl=%1\$s]a friend suggestion[/zrl] for %2\$s from %3\$s."] = "";
-App::$strings["Name:"] = "Navn:";
-App::$strings["Photo:"] = "Bilde:";
-App::$strings["Please visit %s to approve or reject the suggestion."] = "Vennligst besøk %s for å godkjenne eller avslå dette forslaget.";
-App::$strings["[\$Projectname:Notify]"] = "";
-App::$strings["created a new poll"] = "opprettet spørreskjema";
-App::$strings["created a new post"] = "laget et nytt innlegg";
-App::$strings["voted on %s's poll"] = "stemte på %s sitt spørreskjema";
-App::$strings["commented on %s's post"] = "kommenterte på %s sitt innlegg";
-App::$strings["shared a file with you"] = "";
-App::$strings["edited a post dated %s"] = "";
-App::$strings["edited a comment dated %s"] = "";
-App::$strings["added your channel"] = "la til din kanal";
-App::$strings["sent you a direct message"] = "sendte deg en direktemelding";
-App::$strings["g A l F d"] = "g A l F d";
-App::$strings["[today]"] = "[idag]";
-App::$strings["created an event"] = "";
-App::$strings["status verified"] = "";
-App::$strings["Source code of failed update: "] = "";
-App::$strings["Update Error at %s"] = "Oppdateringsfeil ved %s";
-App::$strings["Update %s failed. See error logs."] = "Oppdatering %s mislyktes. Se feilloggen.";
-App::$strings["Channel is blocked on this site."] = "Kanalen er blokkert på dette nettstedet.";
-App::$strings["Channel location missing."] = "Kanalplassering mangler.";
-App::$strings["Remote channel or protocol unavailable."] = "";
-App::$strings["Channel discovery failed."] = "Kanaloppdagelse mislyktes.";
-App::$strings["Protocol disabled."] = "Protokollen er avskrudd.";
-App::$strings["Cannot connect to yourself."] = "Kan ikke lage forbindelse med deg selv.";
-App::$strings["error saving data"] = "";
-App::$strings["Unable to verify channel signature"] = "Ikke i stand til å sjekke kanalsignaturen";
-App::$strings["Public"] = "Offentlig";
-App::$strings["Anybody in the \$Projectname network"] = "";
-App::$strings["Any account on %s"] = "";
-App::$strings["Any of my connections"] = "";
-App::$strings["Only connections I specifically allow"] = "";
-App::$strings["Anybody authenticated (could include visitors from other networks)"] = "";
-App::$strings["Any connections including those who haven't yet been approved"] = "";
-App::$strings["This is your default setting for the audience of your normal stream, and posts."] = "";
-App::$strings["This is your default setting for who can view your default channel profile"] = "";
-App::$strings["This is your default setting for who can view your connections"] = "";
-App::$strings["This is your default setting for who can view your file storage and photos"] = "";
-App::$strings["This is your default setting for the audience of your webpages"] = "";
-App::$strings["Unable to verify site signature for %s"] = "Ikke i stand til å bekrefte signaturen til %s";
-App::$strings["__ctx:permcat__ Default"] = "Standard";
+App::$strings["0. Beginner/Basic"] = "";
+App::$strings["1. Novice - not skilled but willing to learn"] = "";
+App::$strings["2. Intermediate - somewhat comfortable"] = "";
+App::$strings["3. Advanced - very comfortable"] = "";
+App::$strings["4. Expert - I can write computer code"] = "";
+App::$strings["5. Wizard - I probably know more than you do"] = "";
+App::$strings["A deleted privacy group with this name was revived. Existing item permissions <strong>may</strong> apply to this privacy group and any future members. If this is not what you intended, please create another privacy group with a different name."] = "";
+App::$strings["Select a privacy group"] = "";
App::$strings["Social Networking"] = "Sosialt nettverk";
-App::$strings["Social - Federation"] = "";
+App::$strings["Social - Federation"] = "Sosial - føderert";
App::$strings["Social - Mostly Public"] = "Sosial - ganske offentlig";
App::$strings["Social - Restricted"] = "Sosial - begrenset";
App::$strings["Social - Private"] = "Sosial - privat";
@@ -1077,26 +1692,198 @@ App::$strings["Special - Celebrity/Soapbox"] = "Spesiell - kjendis/talerstol";
App::$strings["Special - Group Repository"] = "Spesiell - gruppelager";
App::$strings["Custom/Expert Mode"] = "Tilpasset/Ekspertmodus";
App::$strings["Personal"] = "Personlig";
-App::$strings["Community forum"] = "Forum for fellesskap";
+App::$strings["Community forum"] = "Forum";
App::$strings["Custom"] = "";
-App::$strings["Can view my channel stream and posts"] = "";
+App::$strings["Can view my channel stream and posts"] = "Kan se min kanalstrøm og innlegg";
App::$strings["Can send me their channel stream and posts"] = "Kan sende meg deres kanalstrøm og innlegg";
App::$strings["Can view my default channel profile"] = "Kan se min standard kanalprofil";
App::$strings["Can view my connections"] = "Kan se mine forbindelser";
App::$strings["Can view my file storage and photos"] = "Kan se mine filer og bilder";
-App::$strings["Can upload/modify my file storage and photos"] = "";
-App::$strings["Can view my channel webpages"] = "";
-App::$strings["Can view my wiki pages"] = "";
-App::$strings["Can create/edit my channel webpages"] = "";
-App::$strings["Can write to my wiki pages"] = "";
-App::$strings["Can post on my channel (wall) page"] = "";
+App::$strings["Can upload/modify my file storage and photos"] = "Kan laste opp/redigere mine filer og bilder";
+App::$strings["Can view my channel webpages"] = "Kan se min kanal sine websider";
+App::$strings["Can view my wiki pages"] = "Kan se mine wiki-sider";
+App::$strings["Can create/edit my channel webpages"] = "Kan opprette/redigere websider på min kanal";
+App::$strings["Can write to my wiki pages"] = "Kan skrive på mine wiki-sider";
+App::$strings["Can post on my channel (wall) page"] = "Kan poste på min kanal (vegg)";
App::$strings["Can comment on or like my posts"] = "Kan kommentere på eller like mine innlegg";
App::$strings["Can send me direct messages"] = "Kan sende meg direktemeldinger";
-App::$strings["Can like/dislike profiles and profile things"] = "";
-App::$strings["Can chat with me"] = "";
-App::$strings["Can source/mirror my public posts in derived channels"] = "";
-App::$strings["Can administer my channel"] = "";
-App::$strings["Page not found."] = "Siden ikke funnet.";
+App::$strings["Can like/dislike profiles and profile things"] = "Kan like/ikke like profiler og profilting";
+App::$strings["Can chat with me"] = "Kan chatte med meg";
+App::$strings["Can source/mirror my public posts in derived channels"] = "Kan speile eller bruke mine offentlige innlegg i avledede kanaler";
+App::$strings["Can administer my channel"] = "Kan administrere min kanal";
+App::$strings["Tasks"] = "Oppgaver";
+App::$strings["Click to show more"] = "";
+App::$strings["App Collections"] = "Appsamlinger";
+App::$strings["Installed apps"] = "Installerte apper";
+App::$strings["Available Apps"] = "Tilgjengelige apper";
+App::$strings["Rating Tools"] = "Vurderingsverktøy";
+App::$strings["Rate Me"] = "Vurder meg";
+App::$strings["View Ratings"] = "Vis vurderinger";
+App::$strings["Unseen network activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["Network stream"] = "";
+App::$strings["Mark all read"] = "Merk alle som sett";
+App::$strings["Conversation starters"] = "Samtaleinnstillinger";
+App::$strings["Filter by name or address"] = "Filtrer etter navn eller adresse";
+App::$strings["Unseen channel activity"] = "Ny kanalaktivitet";
+App::$strings["Channel stream"] = "Kanalnavn";
+App::$strings["Mark all seen"] = "Merk alle som sett";
+App::$strings["Private"] = "Privat forum";
+App::$strings["Unseen private activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["Private stream"] = "Privat forum";
+App::$strings["Events"] = "Hendelser";
+App::$strings["Unseen events activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["View events"] = "";
+App::$strings["New Connections"] = "Nye forbindelser";
+App::$strings["New connections"] = "Nye forbindelser";
+App::$strings["View all"] = "Vis alle";
+App::$strings["Useen files activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["Unseen notifications"] = "Systemvarsler";
+App::$strings["Unseen forums activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["Registrations"] = "";
+App::$strings["Unseen registration activity"] = "Ny aktivitet i nettverksstrømmen";
+App::$strings["Unseen public stream activity"] = "Ny aktivitet i den offentlige strømmen";
+App::$strings["Public stream"] = "";
+App::$strings["Sorry, you have got no notifications at the moment"] = "";
+App::$strings["Bookmarked Chatrooms"] = "Bokmerkede chatrom";
+App::$strings["Overview"] = "";
+App::$strings["Add new guest"] = "";
+App::$strings["Guest access"] = "";
+App::$strings["Profile Creation"] = "Oppretting av profil";
+App::$strings["Upload profile photo"] = "Last opp profilbilde";
+App::$strings["Upload cover photo"] = "Last opp omslagsbilde";
+App::$strings["Find and Connect with others"] = "Finn andre";
+App::$strings["View the directory"] = "Se i katalogen";
+App::$strings["View friend suggestions"] = "Vis venneforslag";
+App::$strings["Manage your connections"] = "Behandle forbindelser";
+App::$strings["Communicate"] = "Kommuniser";
+App::$strings["View your channel homepage"] = "Vis kanalens hjemmeside";
+App::$strings["View your network stream"] = "Vis nettverksstrømmen";
+App::$strings["Documentation"] = "Dokumentasjon";
+App::$strings["Missing Features?"] = "Noe som mangler?";
+App::$strings["Pin apps to navigation bar"] = "Fest apper til navigasjonslinjen";
+App::$strings["Install more apps"] = "Legg til flere apper";
+App::$strings["View public stream"] = "";
+App::$strings["New Member Links"] = "Lenker for nye medlemmer";
+App::$strings["Ignore/Hide"] = "Ignorer/Skjul";
+App::$strings["Suggestions"] = "Forslag";
+App::$strings["See more..."] = "Se mer...";
+App::$strings["Public and restricted conversations"] = "Offentlige og begrensede samtaler";
+App::$strings["Private conversations"] = "Private samtaler";
+App::$strings["Starred conversations"] = "Stjernemerkede samtaler";
+App::$strings["Filed messages"] = "Lagrede meldinger";
+App::$strings["No conversations"] = "Ingen samtaler";
+App::$strings["Unseen reactions"] = "Usette reaksjoner";
+App::$strings["Filter by file name"] = "Filtrer etter navn";
+App::$strings["Toggle post editor"] = "Vis redigering av innlegg";
+App::$strings["Toggle personal notes"] = "";
+App::$strings["Channel activities"] = "";
+App::$strings["View Photo"] = "Vis foto";
+App::$strings["Edit Album"] = "Endre album";
+App::$strings["Upload"] = "Last opp";
+App::$strings["Share This"] = "Del dette";
+App::$strings["View %s's profile - %s"] = "Vis %s sin profil - %s";
+App::$strings["Don't show"] = "Ikke vis";
+App::$strings["You have %1$.0f of %2$.0f allowed connections."] = "Du har %1$.0f av %2$.0f tillate forbindelser.";
+App::$strings["Add New Connection"] = "Legg til ny forbindelse";
+App::$strings["Enter channel address"] = "Skriv kanaladressen";
+App::$strings["Examples: bob@example.com, https://example.com/barbara"] = "Eksempel: ola@eksempel.no, https://eksempel.no/kari";
+App::$strings["Read mode"] = "";
+App::$strings["Edit mode"] = "";
+App::$strings["Editing"] = "";
+App::$strings["Saving"] = "";
+App::$strings["Saved"] = "";
+App::$strings["Posted Date"] = "Innleggsdato";
+App::$strings["Order by last posted date"] = "Sorter etter dato innlegg ble postet";
+App::$strings["Commented Date"] = "Sist kommentert";
+App::$strings["Order by last commented date"] = "Sorter etter dato for siste kommentar";
+App::$strings["Date Unthreaded"] = "Utrådet";
+App::$strings["Order unthreaded by date"] = "Sorter innlegg og kommentarer uavhengig av hverandre etter dato";
+App::$strings["Stream Order"] = "Sortering av innlegg";
+App::$strings["Direct Messages"] = "Direktemeldinger";
+App::$strings["Show direct (private) messages"] = "Vis direktemeldinger (private meldinger)";
+App::$strings["Show posts that include events"] = "";
+App::$strings["Polls"] = "Spørreskjema";
+App::$strings["Show posts that include polls"] = "Vis innlegg som inneholder spørreskjema";
+App::$strings["Show posts related to the %s privacy group"] = "";
+App::$strings["Show my privacy groups"] = "";
+App::$strings["Show posts to this forum"] = "";
+App::$strings["Show forums"] = "";
+App::$strings["Starred Posts"] = "";
+App::$strings["Show posts that I have starred"] = "";
+App::$strings["Personal Posts"] = "";
+App::$strings["Show posts that mention or involve me"] = "";
+App::$strings["Show posts that I have filed to %s"] = "";
+App::$strings["Show filed post categories"] = "";
+App::$strings["Panel search"] = "";
+App::$strings["Filter by name"] = "Filtrer etter navn";
+App::$strings["Remove active filter"] = "";
+App::$strings["Stream Filters"] = "Filtere for tidslinjen";
+App::$strings["Welcome"] = "Velkommen";
+App::$strings["No recent activities"] = "Nylig aktivitet";
+App::$strings["__ctx:noun__ new connection"] = array(
+ 0 => "ny forbindelse",
+ 1 => "nye forbindelser",
+);
+App::$strings["__ctx:noun__ notice"] = array(
+ 0 => "varsel",
+ 1 => "varsel",
+);
+App::$strings["Me"] = "Meg";
+App::$strings["Family"] = "Familie";
+App::$strings["Acquaintances"] = "Bekjente";
+App::$strings["All"] = "Alle";
+App::$strings["Refresh"] = "Forny";
+App::$strings["Site"] = "Nettsted";
+App::$strings["Accounts"] = "Kontoer";
+App::$strings["Member registrations waiting for confirmation"] = "";
+App::$strings["Security"] = "Sikkerhet";
+App::$strings["Addons"] = "Tillegg";
+App::$strings["Themes"] = "Utseende";
+App::$strings["Inspect queue"] = "Inspiser kø";
+App::$strings["Queueworker"] = "";
+App::$strings["Profile Fields"] = "Profilfelter";
+App::$strings["DB updates"] = "Databaseoppdateringer";
+App::$strings["Logs"] = "Logger";
+App::$strings["Addon Features"] = "";
+App::$strings["Add new role"] = "Ny rolle";
+App::$strings["Contact roles"] = "Kontaktroller";
+App::$strings["Role members"] = "Medlemmer";
+App::$strings["__ctx:widget__ Activity"] = "Aktivitet";
+App::$strings["photo/image"] = "foto/bilde";
+App::$strings["Remove term"] = "Fjern begrep";
+App::$strings["Chat Members"] = "Medlemmer";
+App::$strings["Suggested Chatrooms"] = "Foreslåtte chatrom";
+App::$strings["App Categories"] = "Appkategorier";
+App::$strings["Account settings"] = "Kontoinnstillinger";
+App::$strings["Channel settings"] = "Kanalinnstillinger";
+App::$strings["Privacy settings"] = "Personverninnstillinger";
+App::$strings["Display settings"] = "Visningsinnstillinger";
+App::$strings["Manage locations"] = "";
+App::$strings["Archives"] = "Arkiv";
+App::$strings["Select Channel"] = "";
+App::$strings["Read-write"] = "";
+App::$strings["Read-only"] = "";
+App::$strings["Channel Calendar"] = "";
+App::$strings["CalDAV Calendars"] = "";
+App::$strings["Shared CalDAV Calendars"] = "";
+App::$strings["Share this calendar"] = "";
+App::$strings["Calendar name and color"] = "";
+App::$strings["Create new CalDAV calendar"] = "";
+App::$strings["Create"] = "Lag";
+App::$strings["Calendar Name"] = "";
+App::$strings["Calendar Tools"] = "";
+App::$strings["Channel Calendars"] = "";
+App::$strings["Import calendar"] = "";
+App::$strings["Select a calendar to import to"] = "";
+App::$strings["Addressbooks"] = "";
+App::$strings["Addressbook name"] = "";
+App::$strings["Create new addressbook"] = "";
+App::$strings["Addressbook Name"] = "";
+App::$strings["Addressbook Tools"] = "";
+App::$strings["Import addressbook"] = "";
+App::$strings["Select an addressbook to import to"] = "";
+App::$strings["Add new group"] = "";
+App::$strings["Privacy groups"] = "Personverngrupper";
App::$strings["Change filename to"] = "";
App::$strings["Select a target location"] = "";
App::$strings["Copy to target location"] = "";
@@ -1105,19 +1892,15 @@ App::$strings["Notify your contacts about this file"] = "Varsle dine kontakter o
App::$strings["File category"] = "";
App::$strings["Total"] = "Totalt";
App::$strings["Shared"] = "Delt";
-App::$strings["Create"] = "Lag";
App::$strings["Add Files"] = "";
-App::$strings["Name"] = "Navn";
-App::$strings["Type"] = "Type";
App::$strings["Last Modified"] = "Sist endret";
App::$strings["parent"] = "opp et nivå";
App::$strings["Copy/paste this code to attach file to a post"] = "Kopier og lim inn denne koden for å legge til filen i et innlegg";
App::$strings["Copy/paste this URL to link file from a web page"] = "Kopier og lim inn denne URL-en for å lenke til filen fra en webside";
-App::$strings["Select All"] = "velg alle";
+App::$strings["Select All"] = "Velg alle";
App::$strings["Bulk Actions"] = "";
App::$strings["Adjust Permissions"] = "";
App::$strings["Move or Copy"] = "";
-App::$strings["Download"] = "";
App::$strings["Info"] = "";
App::$strings["Rename"] = "";
App::$strings["Attachment BBcode"] = "";
@@ -1128,298 +1911,69 @@ App::$strings["You are using %1\$s of %2\$s available file storage. (%3\$s&#37;)
App::$strings["WARNING:"] = "ADVARSEL:";
App::$strings["Create new folder"] = "Lag ny mappe";
App::$strings["Upload file"] = "Last opp fil";
-App::$strings["Upload"] = "Last opp";
App::$strings["Drop files here to immediately upload"] = "";
App::$strings["Show in your contacts shared folder"] = "";
App::$strings["You can select files via the upload button or drop them right here or into an existing folder."] = "";
-App::$strings["Unable to update menu."] = "Ikke i stand til å oppdatere meny.";
-App::$strings["Unable to create menu."] = "Ikke i stand til å lage meny.";
-App::$strings["Menu Name"] = "Menynavn";
-App::$strings["Unique name (not visible on webpage) - required"] = "Unikt navn (ikke synlig på websiden) - påkrevet";
-App::$strings["Menu Title"] = "Menytittel";
-App::$strings["Visible on webpage - leave empty for no title"] = "Synlig på websiden - la stå tomt for ingen tittel";
-App::$strings["Allow Bookmarks"] = "Tillat bokmerker";
-App::$strings["Menu may be used to store saved bookmarks"] = "Menyen kan brukes til å lagre lagrede bokmerker";
-App::$strings["Submit and proceed"] = "Send inn og fortsett";
-App::$strings["Drop"] = "Slett";
-App::$strings["Created"] = "Laget";
-App::$strings["Edited"] = "Endret";
-App::$strings["New"] = "Nye";
-App::$strings["Bookmarks allowed"] = "Bokmerker tillatt";
-App::$strings["Delete this menu"] = "Slett denne menyen";
-App::$strings["Edit menu contents"] = "Endre menyinnholdet";
-App::$strings["Edit this menu"] = "Endre denne menyen";
-App::$strings["Menu could not be deleted."] = "Menyen kunne ikke bli slettet.";
-App::$strings["Menu not found."] = "Menyen ble ikke funnet.";
-App::$strings["Edit Menu"] = "Endre meny";
-App::$strings["Add or remove entries to this menu"] = "Legg til eller fjern punkter i denne menyen";
-App::$strings["Menu name"] = "Menynavn";
-App::$strings["Must be unique, only seen by you"] = "Må være unik, ses bare av deg";
-App::$strings["Menu title"] = "Menytittel";
-App::$strings["Menu title as seen by others"] = "Menytittelen andre ser";
-App::$strings["Allow bookmarks"] = "Tillat bokmerker";
-App::$strings["Not found."] = "Ikke funnet.";
-App::$strings["Documentation Search"] = "Søk i dokumentasjon";
-App::$strings["Not Found"] = "Ikke funnet";
-App::$strings["\$Projectname Documentation"] = "\$Projectname dokumentasjon";
-App::$strings["Contents"] = "";
-App::$strings["Members"] = "Medlemmer";
-App::$strings["Administrators"] = "";
-App::$strings["Developers"] = "";
-App::$strings["Tutorials"] = "";
-App::$strings["Help:"] = "Hjelp:";
-App::$strings["Item not found"] = "Elementet ble ikke funnet.";
-App::$strings["Block Name"] = "Byggeklossens navn";
-App::$strings["Edit Block"] = "Endre byggekloss";
-App::$strings["Posts and comments"] = "Innlegg og kommentarer";
-App::$strings["Only posts"] = "Kun innlegg";
+App::$strings["Invalid item."] = "Ugyldig element.";
+App::$strings["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+App::$strings["Item sync completed!"] = "";
+App::$strings["Import host does not seem to be online or compatible"] = "";
+App::$strings["Item sync completed but no items were found!"] = "";
+App::$strings["File sync completed!"] = "";
+App::$strings["File sync completed but no files were found!"] = "";
+App::$strings["Channel clone status"] = "";
+App::$strings["Item sync status"] = "";
+App::$strings["File sync status"] = "";
+App::$strings["Channel cloning completed!"] = "";
+App::$strings["Resume"] = "";
+App::$strings["Only resume if sync stalled!"] = "";
+App::$strings["Public access denied."] = "Offentlig tilgang avvist.";
App::$strings["Malformed message id."] = "";
-App::$strings["Insufficient permissions. Request redirected to profile page."] = "Utilstrekkelig tillatelse. Forespørsel omdirigert til profilsiden.";
-App::$strings["Search Results For:"] = "Søkeresultat for:";
+App::$strings["Item not found."] = "Elementet ble ikke funnet.";
App::$strings["Reset form"] = "Nullstill skjema";
App::$strings["You must enable javascript for your browser to be able to view this content."] = "";
-App::$strings["You have created %1$.0f of %2$.0f allowed channels."] = "Du har laget %1$.0f av %2$.0f tillatte kanaler.";
-App::$strings["Your real name is recommended."] = "";
-App::$strings["Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation Group\""] = "Eksempel: \"Ola Nordmann\", \"Lisa og hestene hennes\", \"Fotball\", \"Sykkelgruppa\"";
-App::$strings["This will be used to create a unique network address (like an email address)."] = "";
-App::$strings["Allowed characters are a-z 0-9, - and _"] = "";
-App::$strings["Channel name"] = "Kanalnavn";
-App::$strings["Choose a short nickname"] = "Velg et kort kallenavn";
-App::$strings["Channel role"] = "Kanalrolle";
-App::$strings["Create a Channel"] = "";
-App::$strings["A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things."] = "";
-App::$strings["or <a href=\"import\">import an existing channel</a> from another location."] = "eller <a href=\"import\">importer en eksisterende kanal</a> fra et annet sted.";
-App::$strings["Validate"] = "";
-App::$strings["Unable to find your hub."] = "Ikke i stand til å finne hubben din.";
-App::$strings["Post successful."] = "Innlegg vellykket.";
-App::$strings["Not a zip file or zip file corrupted."] = "";
-App::$strings["Import Items"] = "Importer elementer";
-App::$strings["Use this form to import existing posts and content from an export file."] = "Bruk dette skjemaet for å importere eksisterende innlegg og innhold fra en eksportfil.";
-App::$strings["File to Upload"] = "Fil som skal lastes opp";
-App::$strings["Imported file is empty."] = "Importert fil er tom.";
-App::$strings["Content import completed"] = "";
-App::$strings["Chatroom import completed"] = "";
-App::$strings["Channel calendar import 1/2 completed"] = "";
-App::$strings["Channel calendar import 2/2 completed"] = "";
-App::$strings["Menu import completed"] = "";
-App::$strings["Wiki import completed"] = "";
-App::$strings["Webpages import completed"] = "";
-App::$strings["Edit post"] = "Endre innlegg";
-App::$strings["Invalid item."] = "Ugyldig element.";
-App::$strings["Channel not found."] = "Kanalen ble ikke funnet.";
-App::$strings["Language App"] = "";
-App::$strings["Not Installed"] = "";
-App::$strings["Token verification failed."] = "";
-App::$strings["Email verification resent"] = "";
-App::$strings["Unable to resend email verification message."] = "";
-App::$strings["\$Projectname Server - Setup"] = "\$Projectname-tjener - oppsett";
-App::$strings["Could not connect to database."] = "Fikk ikke kontakt med databasen.";
-App::$strings["Could not connect to specified site URL. Possible SSL certificate or DNS issue."] = "Fikk ikke kontakt med det angitte nettstedets URL. Problemet kan muligens skyldes SSL-sertifikatet eller DNS.";
-App::$strings["Could not create table."] = "Kunne ikke lage tabellen.";
-App::$strings["Your site database has been installed."] = "Databasen til ditt nettsted har blitt installert.";
-App::$strings["You may need to import the file \"install/schema_xxx.sql\" manually using a database client."] = "Du må kanskje importere filen \"install/schmea_xxx.sql\" manuelt ved å bruke en databaseklient.";
-App::$strings["Please see the file \"install/INSTALL.txt\"."] = "Vennligst les filen \"install/INSTALL.txt\".";
-App::$strings["System check"] = "Systemsjekk";
-App::$strings["Next"] = "Neste";
-App::$strings["Check again"] = "Sjekk igjen";
-App::$strings["Database connection"] = "Databaseforbindelse";
-App::$strings["In order to install \$Projectname we need to know how to connect to your database."] = "For å installere \$Projectname må du oppgi hvordan din database kan kontaktes.";
-App::$strings["Please contact your hosting provider or site administrator if you have questions about these settings."] = "Vennligst kontakt din nettstedstilbyder eller nettstedsadministrator hvis du har spørsmål om disse innstillingene.";
-App::$strings["The database you specify below should already exist. If it does not, please create it before continuing."] = "Databasen du oppgir nedenfor må finnes på forhånd. Hvis den ikke finnes, vennligst lag den før du fortsetter.";
-App::$strings["Database Server Name"] = "Navn på databasetjener";
-App::$strings["Default is 127.0.0.1"] = "Standard er 127.0.0.1";
-App::$strings["Database Port"] = "Databaseport";
-App::$strings["Communication port number - use 0 for default"] = "Kommunikasjonsportnummer - bruk 0 for standard";
-App::$strings["Database Login Name"] = "Database innloggingsnavn";
-App::$strings["Database Login Password"] = "Database innloggingspassord";
-App::$strings["Database Name"] = "Databasenavn";
-App::$strings["Database Type"] = "Databasetype";
-App::$strings["Site administrator email address"] = "E-postadressen til administrator ved nettstedet";
-App::$strings["Your account email address must match this in order to use the web admin panel."] = "Din konto sin e-postadresse må være lik denne for å kunne bruke web-administrasjonspanelet.";
-App::$strings["Website URL"] = "Nettstedets URL";
-App::$strings["Please use SSL (https) URL if available."] = "Vennligst bruk SSL (https) URL hvis tilgjengelig.";
-App::$strings["Please select a default timezone for your website"] = "Vennligst velg en standard tidssone for ditt nettsted";
-App::$strings["Site settings"] = "Nettstedets innstillinger";
-App::$strings["PHP version 8.0 or greater is required."] = "";
-App::$strings["PHP version"] = "";
-App::$strings["Could not find a command line version of PHP in the web server PATH."] = "Fant ikke en kommandolinjeversjon av PHP i webtjenerens sti (PATH).";
-App::$strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron."] = "Hvis du ikke har en kommandolinjeversjon av PHP installert på tjeneren, så vil du ikke kunne kjøre bakgrunnshenting via cron.";
-App::$strings["PHP executable path"] = "PHP-kjørefilens sti";
-App::$strings["Enter full path to php executable. You can leave this blank to continue the installation."] = "Skriv full sti til kjørefilen for PHP. Du kan la denne stå blank for å fortsette installasjonen.";
-App::$strings["Command line PHP"] = "Kommandolinje PHP";
-App::$strings["Unable to check command line PHP, as shell_exec() is disabled. This is required."] = "";
-App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = "Kommandolinjeversjonen av PHP på ditt system har ikke \"register_argc_argv\" påskrudd.";
-App::$strings["This is required for message delivery to work."] = "Dette er påkrevd for at meldingslevering skal virke.";
-App::$strings["PHP register_argc_argv"] = "PHP register_argc_argv";
-App::$strings["This is not sufficient to upload larger images or files. You should be able to upload at least 4 MB at once."] = "";
-App::$strings["Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once."] = "Den største totale opplastingsstørrelsen du er tillatt er satt til %s. Filstørrelsen på en enkelt fil er satt til å maksimalt være %s. Du har lov til å laste opp inntil %d filer samtidig.";
-App::$strings["You can adjust these settings in the server php.ini file."] = "";
-App::$strings["PHP upload limits"] = "PHP opplastingsgrenser";
-App::$strings["Error: the \"openssl_pkey_new\" function on this system is not able to generate encryption keys"] = "Feil: \"openssl_pkey_new\"-funksjonen på dette systemet er ikke i stand til å lage krypteringsnøkler";
-App::$strings["If running under Windows, please see \"http://www.php.net/manual/en/openssl.installation.php\"."] = "Ved kjøring på Windows, vennligst se \"http://www.php.net/manual/en/openssl.installation.php\".";
-App::$strings["Generate encryption keys"] = "Lag krypteringsnøkler";
-App::$strings["Error: the sodium encryption library is not installed."] = "";
-App::$strings["Generate ed25519 encryption keys"] = "Lag krypteringsnøkler";
-App::$strings["Error: one of \"bcmath\" or \"gmp\" (bigmath library) extensions are required."] = "";
-App::$strings["Bigmath library (either bcmath or gmp)"] = "";
-App::$strings["libCurl PHP module"] = "libCurl PHP-modul";
-App::$strings["GD graphics PHP module"] = "GD graphics PHP-modul";
-App::$strings["OpenSSL PHP module"] = "OpenSSL PHP-modul";
-App::$strings["PDO database PHP module"] = "";
-App::$strings["mb_string PHP module"] = "mb_string PHP-modul";
-App::$strings["xml PHP module"] = "xml PHP modul";
-App::$strings["zip PHP module"] = "";
-App::$strings["intl PHP module"] = "xml PHP modul";
-App::$strings["Apache mod_rewrite module"] = "Apache mod_rewrite-modul";
-App::$strings["Error: Apache webserver mod-rewrite module is required but not installed."] = "Feil: Apache web-tjenerens mod-rewrite-modul er påkrevd, men ikke installert.";
-App::$strings["exec"] = "";
-App::$strings["Error: exec is required but is either not installed or has been disabled in php.ini"] = "";
-App::$strings["shell_exec"] = "";
-App::$strings["Error: shell_exec is required but is either not installed or has been disabled in php.ini"] = "";
-App::$strings["Error: libCURL PHP module required but not installed."] = "Feil: libCURL PHP-modul er påkrevd, men er ikke installert.";
-App::$strings["Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed."] = "";
-App::$strings["Error: openssl PHP module required but not installed."] = "Feil: openssl PHP-modul er påkrevd, men er ikke installert.";
-App::$strings["Error: PDO database PHP module missing a driver for either mysql or pgsql."] = "";
-App::$strings["Error: PDO database PHP module required but not installed."] = "";
-App::$strings["Error: mb_string PHP module required but not installed."] = "Feil: mb_string PHP-modul er påkrevd, men er ikke installert.";
-App::$strings["Error: xml PHP module required for DAV but not installed."] = "Feil: XML PHP modul er påkrevet for DAV, men den er ikke installert.";
-App::$strings["Error: zip PHP module required but not installed."] = "";
-App::$strings["Error: intl PHP module required but not installed."] = "Feil: openssl PHP-modul er påkrevd, men er ikke installert.";
-App::$strings[".htconfig.php is writable"] = ".htconfig.php kan skrives til";
-App::$strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = "Web-installasjonen må kunne lage en fil kalt \".htconfig.php\" i toppkatalogen til web-tjeneren din, men dette får den ikke til.";
-App::$strings["This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can."] = "Dette er oftest tillatelsesinnstilling, ettersom webtjeneren kanskje kan skrive til filer i din mappe - selv om du kan.";
-App::$strings["Please see install/INSTALL.txt for additional information."] = "";
-App::$strings["This software uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering."] = "";
-App::$strings["In order to store these compiled templates, the web server needs to have write access to the directory %s under the top level web folder."] = "";
-App::$strings["Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder."] = "Vennligst sikre at brukeren som din web-tjeneste kjører som (for eksempel www-data) har skrivetilgang til denne katalogen.";
-App::$strings["Note: as a security measure, you should give the web server write access to %s only--not the template files (.tpl) that it contains."] = "Merknad: som et sikkerhetstiltak bør du bare gi webtjerenn skrivetilgang til %s - ikke til malfilene (.tpl) som den inneholder.";
-App::$strings["%s is writable"] = "%s kan skrives til";
-App::$strings["This software uses the store directory to save uploaded files. The web server needs to have write access to the store directory under the top level web folder"] = "";
-App::$strings["store is writable"] = "lageret kan skrives til";
-App::$strings["SSL certificate cannot be validated. Fix certificate or disable https access to this site."] = "SSL-sertifikatet kan ikke kontrolleres. Fiks sertifikatet eller skru av https tilgang til dette nettstedet.";
-App::$strings["If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!"] = "Hvis du har HTTPS-tilgang til ditt nettsted eller tillater forbindelser til TCP port 443 (HTTPS-porten), så MÅ du bruke nettlesergodkjent sertifkater. Du MÅ IKKE bruke egensignert sertifikater!";
-App::$strings["This restriction is incorporated because public posts from you may for example contain references to images on your own hub."] = "Denne begrensningen er tatt inn fordi offentlige innlegg fra deg kan for eksempel inneholde referanser til bilder på din egen hub.";
-App::$strings["If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues."] = "Hvis sertifikatet ditt ikke gjenkjennes, så vil medlemmer på andre nettsteder (som selv kan ha godkjente sertifikater) få en beskjed med en advarsel på deres eget nettsted som klager over sikkerhetsproblemer.";
-App::$strings["This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement."] = "Dette kan gi problemer med brukervennlighet (ikke bare på ditt eget nettsted), så vi må insistere på dette kravet.";
-App::$strings["Providers are available that issue free certificates which are browser-valid."] = "Det finnes tilbydere som utsteder gratis sertifikater som er gyldige i nettlesere.";
-App::$strings["If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications."] = "";
-App::$strings["SSL certificate validation"] = "SSL sertifikat-kontroll";
-App::$strings["Url rewrite in .htaccess is not working. Check your server configuration.Test: "] = "URL omskriving (rewrite) i .htaccess virker ikke. Sjekk konfigurasjonen til tjeneren din. Test: ";
-App::$strings["Url rewrite is working"] = "URL rewrite virker";
-App::$strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = "Databasekonfigurasjonsfilen \".htconfig.php\" kunne ikke skrives. Vennligst bruk den medfølgende teksten for å lage en konfigurasjonsfil i toppkatalogen av din web-tjener.";
-App::$strings["Errors encountered creating database tables."] = "Feil oppstod under opprettelsen av databasetabeller.";
-App::$strings["<h1>What next?</h1>"] = "";
-App::$strings["IMPORTANT: You will need to [manually] setup a scheduled task for the poller."] = "VIKTIG: Du må [manuelt] sette opp en automatisert tidfestet oppgave til bakgrunnshenteren.";
-App::$strings["Bookmark added"] = "Bokmerke lagt til";
-App::$strings["My Connections Bookmarks"] = "Mine forbindelsers bokmerker";
-App::$strings["Update to Hubzilla 5.0 step 2"] = "";
-App::$strings["To complete the update please run"] = "";
-App::$strings["php util/z6convert.php"] = "";
-App::$strings["from the terminal."] = "";
-App::$strings["Email resent"] = "";
-App::$strings["Email resend failed"] = "";
-App::$strings["Verification successful"] = "";
-App::$strings["Account successfull created"] = "";
-App::$strings["Channel successfull created"] = "";
-App::$strings["Automatic channel creation failed. Please create a channel."] = "";
-App::$strings["Account creation error"] = "";
-App::$strings["Verify failed"] = "";
-App::$strings["Token verification failed"] = "";
-App::$strings["Request not inside time frame"] = "";
-App::$strings["Identity unknown"] = "";
-App::$strings["dId2 mistaken"] = "";
-App::$strings["Your Registration ID"] = "";
-App::$strings["Registration verification"] = "";
-App::$strings["Hold on, you can start verification in"] = "";
-App::$strings["Please remember your verification token for ID"] = "";
-App::$strings["Token validity"] = "";
-App::$strings["Resend email"] = "";
-App::$strings["Registration status"] = "";
-App::$strings["Verification successful!"] = "";
-App::$strings["Your login ID is"] = "";
-App::$strings["After your account has been approved by our administrator you will be able to login with your login ID and your provided password."] = "";
-App::$strings["Registration request revoked"] = "";
-App::$strings["Sorry for any inconvience. Thank you for your response."] = "";
-App::$strings["Please enter your verification token for ID"] = "";
-App::$strings["Please check your email!"] = "";
-App::$strings["Verification token"] = "";
-App::$strings["ID expired"] = "";
-App::$strings["You will require the verification token for ID"] = "";
-App::$strings["Unknown or expired ID"] = "";
-App::$strings["dId2 malformed"] = "";
-App::$strings["Authorize application connection"] = "Tillat programforbindelse";
-App::$strings["Return to your app and insert this Security Code:"] = "";
-App::$strings["Please login to continue."] = "Vennligst logg inn for å fortsette.";
-App::$strings["Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?"] = "Vil du tillate dette programmet å få tilgang til dine innlegg og kontakter, og/eller lage nye innlegg for deg?";
-App::$strings["Event can not end before it has started."] = "Hendelsen kan ikke slutte før den starter.";
-App::$strings["Unable to generate preview."] = "Klarer ikke å lage forhåndsvisning.";
-App::$strings["Event title and start time are required."] = "Hendelsestittel og starttidspunkt er påkrevd.";
-App::$strings["Event not found."] = "Hendelsen ble ikke funnet.";
-App::$strings["Edit event"] = "Endre hendelse";
-App::$strings["Delete event"] = "Slett hendelse";
-App::$strings["Link to source"] = "Lenke til kilde";
-App::$strings["calendar"] = "kalender";
-App::$strings["Failed to remove event"] = "Mislyktes med å slette hendelse";
-App::$strings["Remote privacy information not available"] = "";
-App::$strings["__ctx:acl__ Profile"] = "Profil";
-App::$strings["Privacy group"] = "Personverngruppe:";
-App::$strings["Item"] = "";
-App::$strings["Click to copy link to this ressource for guest %s to clipboard"] = "";
-App::$strings["Link copied"] = "";
-App::$strings["Access"] = "Tilgang";
-App::$strings["Guest access"] = "";
-App::$strings["OCAP access"] = "";
-App::$strings["Layout Name"] = "Layout-navn";
-App::$strings["Layout Description (Optional)"] = "Layoutens beskrivelse (valgfritt)";
-App::$strings["Edit Layout"] = "Endre layout";
-App::$strings["Album not found."] = "Albumet ble ikke funnet.";
-App::$strings["Delete Album"] = "Slett album";
-App::$strings["Delete Photo"] = "Slett bilde";
-App::$strings["Public access denied."] = "Offentlig tilgang avvist.";
-App::$strings["No photos selected"] = "Ingen bilder valgt";
-App::$strings["Access to this item is restricted."] = "Tilgang til dette elementet er begrenset.";
-App::$strings["%1$.2f MB photo storage used."] = "%1$.2f MB lagringsplass til bilder er brukt.";
-App::$strings["%1$.2f MB of %2$.2f MB photo storage used."] = "%1$.2f MB av %2$.2f MB lagringsplass til bilder er brukt.";
-App::$strings["Upload Photos"] = "Last opp bilder";
-App::$strings["Enter an album name"] = "Skriv et albumnavn";
-App::$strings["or select an existing album (doubleclick)"] = "eller velg et eksisterende album (dobbeltklikk)";
-App::$strings["Create a status post for this upload"] = "Lag et statusinnlegg for denne opplastingen";
-App::$strings["Description (optional)"] = "Beskrivelse (valgritt)";
-App::$strings["Show Newest First"] = "Vis nyeste først";
-App::$strings["Show Oldest First"] = "Vis eldste først";
-App::$strings["View Photo"] = "Vis foto";
-App::$strings["Edit Album"] = "Endre album";
-App::$strings["Add Photos"] = "Legg til bilder";
-App::$strings["Permission denied. Access to this item may be restricted."] = "Tillatelse avvist. Tilgang til dette elementet kan være begrenset.";
-App::$strings["Photo not available"] = "Bilde er utilgjengelig";
-App::$strings["Use as profile photo"] = "Bruk som profilbilde";
-App::$strings["Use as cover photo"] = "Bruk som omslagsbilde";
-App::$strings["Private Photo"] = "Privat bilde";
-App::$strings["Previous"] = "Forrige";
-App::$strings["View Full Size"] = "Vis i full størrelse";
-App::$strings["Remove"] = "Fjern";
-App::$strings["Edit photo"] = "Endre bilde";
-App::$strings["Rotate CW (right)"] = "Roter med klokka (mot høyre)";
-App::$strings["Rotate CCW (left)"] = "Roter mot klokka (venstre)";
-App::$strings["Move photo to album"] = "";
-App::$strings["Enter a new album name"] = "Skriv et nytt albumnavn";
-App::$strings["or select an existing one (doubleclick)"] = "eller velg et eksisterende album (dobbeltklikk)";
-App::$strings["Add a Tag"] = "Legg til merkelapp";
-App::$strings["Example: @bob, @Barbara_Jensen, @jim@example.com"] = "Eksempel: @bob, @Barbara_Jensen, @jim@example.com";
-App::$strings["Flag as adult in album view"] = "Flag som voksent i albumvisning";
-App::$strings["View all"] = "Vis alle";
-App::$strings["Photo Tools"] = "Fotoverktøy";
-App::$strings["In This Photo:"] = "I dette bildet:";
-App::$strings["Map"] = "Kart";
-App::$strings["__ctx:noun__ Likes"] = "Liker";
-App::$strings["__ctx:noun__ Dislikes"] = "Liker ikke";
+App::$strings["Article"] = "";
+App::$strings["Item has been removed."] = "";
+App::$strings["Image uploaded but image cropping failed."] = "Bildet ble lastet opp, men beskjæring av bildet mislyktes.";
+App::$strings["Cover Photos"] = "Forsidebilder";
+App::$strings["Image resize failed."] = "Endring av bildestørrelse mislyktes.";
+App::$strings["Image upload failed."] = "Opplasting av bildet mislyktes.";
+App::$strings["Unable to process image."] = "Kan ikke behandle bildet.";
+App::$strings["Photo not available."] = "Bildet er ikke tilgjengelig.";
+App::$strings["Your cover photo may be visible to anybody on the internet"] = "Omslagsbildet ditt vil være synlig for alle på internett";
+App::$strings["Upload File:"] = "Last opp fil:";
+App::$strings["Select a profile:"] = "Velg en profil:";
+App::$strings["Change Cover Photo"] = "Endre omslagsbilde";
+App::$strings["Use a photo from your albums"] = "";
+App::$strings["Select existing photo"] = "Velg eksisterende bilde";
+App::$strings["Crop Image"] = "Beskjær bildet";
+App::$strings["Please adjust the image cropping for optimum viewing."] = "Vennligst juster bildebeskjæringen for optimal visning.";
+App::$strings["Done Editing"] = "Lagre endringer";
+App::$strings["Page link"] = "Sidelenke";
+App::$strings["Edit Webpage"] = "Endre webside";
+App::$strings["Private forum"] = "";
+App::$strings["Public forum"] = "Offentlig forum:";
+App::$strings["Import Webpage Elements"] = "";
+App::$strings["Import selected"] = "";
+App::$strings["Export Webpage Elements"] = "";
+App::$strings["Export selected"] = "";
+App::$strings["Actions"] = "Handlinger";
+App::$strings["Page Link"] = "Sidelenke";
+App::$strings["Page Title"] = "Sidetittel";
+App::$strings["Created"] = "Laget";
+App::$strings["Edited"] = "Endret";
+App::$strings["Invalid file type."] = "";
+App::$strings["Error opening zip file"] = "";
+App::$strings["Invalid folder path."] = "";
+App::$strings["No webpage elements detected."] = "";
+App::$strings["Import complete."] = "";
+App::$strings["%s element installed"] = "%s element installert";
+App::$strings["%s element installation failed"] = "Installasjon av %s-element mislyktes";
App::$strings["Name is required"] = "Navn er påkrevd";
App::$strings["Key and Secret are required"] = "Nøkkel og hemmelighet er påkrevd";
App::$strings["Add application"] = "Legg til program";
App::$strings["Name of application"] = "Navn på program";
-App::$strings["Consumer Key"] = "Consumer Key";
App::$strings["Automatically generated - change if desired. Max length 20"] = "Automatisk laget - kan endres om du vil. Største lengde 20";
-App::$strings["Consumer Secret"] = "Consumer Secret";
App::$strings["Redirect"] = "Omdirigering";
App::$strings["Redirect URI - leave blank unless your application specifically requires this"] = "Omdirigerings-URI - la stå tomt hvis ikke ditt program spesifikt krever dette";
App::$strings["Icon url"] = "Ikon-URL";
@@ -1429,228 +1983,210 @@ App::$strings["Connected OAuth Apps"] = "";
App::$strings["Client key starts with"] = "Klientnøkkel starter med";
App::$strings["No name"] = "Ikke noe navn";
App::$strings["Remove authorization"] = "Fjern tillatelse";
-App::$strings["Available Apps"] = "Tilgjengelige apper";
-App::$strings["Installed Apps"] = "Installerte apper";
-App::$strings["Manage Apps"] = "Behandle apper";
-App::$strings["Create Custom App"] = "";
-App::$strings["No connections."] = "Ingen forbindelser.";
-App::$strings["Visit %s's profile [%s]"] = "Besøk %s sin profil [%s]";
-App::$strings["View Connections"] = "Vis forbindelser";
-App::$strings["toggle full screen mode"] = "";
-App::$strings["Item is not editable"] = "Elementet kan ikke endres";
-App::$strings["Post not found."] = "";
-App::$strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s merket %3\$s til %2\$s med %4\$s";
-App::$strings["No channel."] = "Ingen kanal.";
-App::$strings["No connections in common."] = "Ingen forbindelser felles.";
-App::$strings["View Common Connections"] = "";
-App::$strings["Profile not found."] = "Profilen ble ikke funnet.";
-App::$strings["Profile deleted."] = "Profilen er slettet.";
-App::$strings["Profile-"] = "Profil-";
-App::$strings["New profile created."] = "Ny profil opprettet.";
-App::$strings["Profile unavailable to clone."] = "Profilen er utilgjengelig for klonen.";
-App::$strings["Profile unavailable to export."] = "Profilen er utilgjengelig for eksport.";
-App::$strings["Profile Name is required."] = "Profilnavn er påkrevd.";
-App::$strings["Marital Status"] = "Sivilstand";
-App::$strings["Romantic Partner"] = "Romantisk partner";
-App::$strings["Likes"] = "Liker";
-App::$strings["Dislikes"] = "Liker ikke";
-App::$strings["Work/Employment"] = "Arbeid/sysselsetting";
-App::$strings["Religion"] = "Religion";
-App::$strings["Political Views"] = "Politiske synspunkter";
-App::$strings["Gender"] = "Kjønn";
-App::$strings["Sexual Preference"] = "Seksuelle preferanser";
-App::$strings["Homepage"] = "Hjemmeside";
-App::$strings["Interests"] = "Interesser";
-App::$strings["Address"] = "Adresse";
-App::$strings["Profile updated."] = "Profilen er oppdatert.";
-App::$strings["Hide my connections from viewers of this profile"] = "Skjul mine forbindelser fra besøkende som ser denne profilen";
-App::$strings["Publish my default profile in the network directory"] = "La standardprofilen min vises i nettverkskatalonen";
-App::$strings["Suggest me as a potential contact to new members"] = "Foreslå meg som mulig kontakt til nye medlemmer";
-App::$strings["Reveal my online status"] = "La andre se om jeg er pålogget eller ikke";
-App::$strings["Edit Profile Details"] = "Endre profildetaljer";
-App::$strings["View this profile"] = "Vis denne profilen";
-App::$strings["Profile Tools"] = "Profilverktøy";
-App::$strings["Change cover photo"] = "Endre omslagsbilde";
-App::$strings["Create a new profile using these settings"] = "Lag en ny profil ved å bruke disse innstillingene";
-App::$strings["Clone this profile"] = "Klon denne profilen";
-App::$strings["Delete this profile"] = "Slett denne profilen";
-App::$strings["Add profile things"] = "Legg til profilting";
-App::$strings["Basic"] = "Grunnleggende";
-App::$strings["Relationship"] = "Forhold";
-App::$strings["Import profile from file"] = "Importer profil fra fil";
-App::$strings["Export profile to file"] = "Eksporter profil til fil";
-App::$strings["Your gender"] = "Kjønn";
-App::$strings["Marital status"] = "Sivilstatus";
-App::$strings["Sexual preference"] = "Legning";
-App::$strings["Profile name"] = "Profilnavn";
-App::$strings["This is your default profile."] = "Dette er din standardprofil.";
-App::$strings["Your full name"] = "Fullt navn";
-App::$strings["Short title/description"] = "Kort tittel/beskrivelse";
-App::$strings["Maximal 190 characters"] = "Maksimum 190 tegn";
-App::$strings["Street address"] = "Gateadresse";
-App::$strings["Locality/City"] = "Sted/by";
-App::$strings["Region/State"] = "Region";
-App::$strings["Postal/Zip code"] = "Postnummer";
-App::$strings["Country"] = "Land";
-App::$strings["Who (if applicable)"] = "Hvem (om relevant)";
-App::$strings["Examples: cathy123, Cathy Williams, cathy@example.com"] = "Eksempler: kari123, Kari Villiamsen, kari@example.com";
-App::$strings["Since (date)"] = "Fra (dato)";
-App::$strings["Tell us about yourself"] = "Fortell oss om deg selv";
-App::$strings["Homepage URL"] = "Hjemmeside URL";
-App::$strings["Hometown"] = "Hjemsted";
-App::$strings["Political views"] = "Politiske holdninger";
-App::$strings["Religious views"] = "Religiøse holdninger";
-App::$strings["Keywords used in directory listings"] = "Nøkkelord for bruk i katalogoppføringen";
-App::$strings["Example: fishing photography software"] = "Eksempel: fisking fotografering programvare";
-App::$strings["Musical interests"] = "Musikkinteresser";
-App::$strings["Books, literature"] = "Bøker, litteratur";
-App::$strings["Television"] = "TV/fjernsyn";
-App::$strings["Film/Dance/Culture/Entertainment"] = "Film/dans/kultur/underholdning";
-App::$strings["Hobbies/Interests"] = "Hobbier/Interesser";
-App::$strings["Love/Romance"] = "Kjærlighet/romantikk";
-App::$strings["School/Education"] = "Skolle/utdanning";
-App::$strings["Contact information and social networks"] = "Kontaktinformasjon og andre sosiale nettverk";
-App::$strings["My other channels"] = "Mine andre kanaler";
-App::$strings["Create New"] = "Lag ny profil";
App::$strings["No such group"] = "Gruppen finnes ikke";
App::$strings["No such channel"] = "Ingen slik kanal";
+App::$strings["Search Results For:"] = "Søkeresultat for:";
App::$strings["Privacy group is empty"] = "Personverngruppen er tom";
App::$strings["Privacy group: "] = "Personverngruppe: ";
-App::$strings["Invalid channel."] = "Ugyldig kanal";
-App::$strings["Invite App"] = "";
-App::$strings["Register is closed"] = "";
-App::$strings["Note, the invitation code is valid up to"] = "";
-App::$strings["Too many recipients for one invitation (max %d)"] = "";
-App::$strings["No recipients for this invitation"] = "";
-App::$strings["(%s) : Not a real email address"] = "(%s): Ikke en virkelig epostadresse";
-App::$strings["(%s) : Not allowed email address"] = "(%s): Ikke en tillatt epostadresse";
-App::$strings["(%s) : email address already in use"] = "(%s): epostadressen er allerede i bruk";
-App::$strings["(%s) : Accepted email address"] = "(%s): Godkjent epostadresse";
-App::$strings["%s : Message delivery failed."] = "%s : meldingslevering feilet.";
-App::$strings["To %s : Message delivery success."] = "";
-App::$strings["%1\$d mail(s) sent, %2\$d mail error(s)"] = "";
-App::$strings["Invites not proposed by configuration"] = "";
-App::$strings["Contact the site admin"] = "";
-App::$strings["Invites by users not enabled"] = "";
-App::$strings["You have no more invitations available"] = "Du har ikke flere invitasjoner tilgjengelig";
-App::$strings["Not on xchan"] = "";
-App::$strings["All users invitation limit exceeded."] = "";
-App::$strings["Minute(s)"] = "";
-App::$strings["Hour(s)"] = "";
-App::$strings["Day(s)"] = "";
-App::$strings["Invitation expires after"] = "";
-App::$strings["duration up from now"] = "";
-App::$strings["Invitation"] = "";
-App::$strings["Send invitations"] = "Send invitasjoner";
-App::$strings["Invitations I am using"] = "Invitasjoner jeg bruker";
-App::$strings["Invitations we are using"] = "";
-App::$strings["§ Note, the email(s) sent will be recorded in the system logs"] = "";
-App::$strings["Enter email addresses, one per line:"] = "Skriv e-postadresser, en per linje:";
-App::$strings["Your message:"] = "Din melding:";
-App::$strings["Invite template"] = "";
-App::$strings["Subject:"] = "Emne:";
-App::$strings["Here you may enter personal notes to the recipient(s)"] = "";
-App::$strings["Failed to create source. No channel selected."] = "Mislyktes med å lage kilde. Ingen kanal er valgt.";
-App::$strings["Source created."] = "Kilden er laget.";
-App::$strings["Source updated."] = "Kilden er oppdatert.";
-App::$strings["*"] = "*";
-App::$strings["Manage remote sources of content for your channel."] = "Håndtere eksterne innholdskilder til din kanal.";
-App::$strings["New Source"] = "Ny kilde";
-App::$strings["Import all or selected content from the following channel into this channel and distribute it according to your channel settings."] = "Importer alt eller et utvalgt av innhold fra følgende kanal inn i denne kanalen og distribuer det i henhold til dine egne kanalinnstillinger.";
-App::$strings["Only import content with these words (one per line)"] = "Bare importer innhold med disse ordene (ett ord per linje)";
-App::$strings["Leave blank to import all public content"] = "La stå tomt for å importere alt offentlig innhold";
-App::$strings["Channel Name"] = "Kanalnavn";
-App::$strings["Add the following categories to posts imported from this source (comma separated)"] = "";
-App::$strings["Resend posts with this channel as author"] = "";
-App::$strings["Copyrights may apply"] = "";
-App::$strings["Source not found."] = "Kilden ble ikke funnet.";
-App::$strings["Edit Source"] = "Endre kilde";
-App::$strings["Delete Source"] = "Slett kilde";
-App::$strings["Source removed"] = "Kilden er fjernet";
-App::$strings["Unable to remove source."] = "Ikke i stand til å fjerne kilde.";
-App::$strings["No suggestions available. If this is a new site, please try again in 24 hours."] = "Ingen forslag tilgjengelige. Hvis dette er et nytt nettsted, vennligst prøv igjen om 24 timer.";
-App::$strings["Ignore/Hide"] = "Ignorer/Skjul";
-App::$strings["%s element installed"] = "%s element installert";
-App::$strings["%s element installation failed"] = "Installasjon av %s-element mislyktes";
-App::$strings["Privacy group created."] = "Personverngruppen er opprettet.";
-App::$strings["Could not create privacy group."] = "Kunne ikke opprette personverngruppen.";
-App::$strings["Privacy group updated."] = "Personverngruppen er oppdatert.";
-App::$strings["Post to this group by default"] = "";
-App::$strings["Add new contacts to this group by default"] = "";
-App::$strings["Privacy group name"] = "Personverngruppens navn:";
-App::$strings["Members are visible to other channels"] = "Medlemmer er synlig for andre kanaler";
-App::$strings["Privacy group removed."] = "Personverngruppen er fjernet.";
-App::$strings["Unable to remove privacy group."] = "Ikke i stand til å fjerne personverngruppen.";
-App::$strings["Privacy Group: %s"] = "Personverngruppe: %s";
-App::$strings["Privacy group name: "] = "Personverngruppens navn: ";
-App::$strings["Group members"] = "";
-App::$strings["Not in this group"] = "";
-App::$strings["Click a channel to toggle membership"] = "";
-App::$strings["Comanche page description language help"] = "Hjelp med Comanche sidebeskrivelsesspråk";
-App::$strings["Layout Description"] = "Layout-beskrivelse";
-App::$strings["Download PDL file"] = "Last ned PDL-fil";
-App::$strings["View"] = "Vis";
-App::$strings["Name and Secret are required"] = "";
-App::$strings["Add OAuth2 application"] = "";
-App::$strings["Grant Types"] = "";
-App::$strings["leave blank unless your application sepcifically requires this"] = "";
-App::$strings["Authorization scope"] = "";
-App::$strings["OAuth2 Application not found."] = "";
-App::$strings["leave blank unless your application specifically requires this"] = "";
-App::$strings["Connected OAuth2 Apps"] = "";
-App::$strings["item"] = "";
-App::$strings["Image uploaded but image cropping failed."] = "Bildet ble lastet opp, men beskjæring av bildet mislyktes.";
-App::$strings["Cover Photos"] = "Forsidebilder";
-App::$strings["Image resize failed."] = "Endring av bildestørrelse mislyktes.";
-App::$strings["Image upload failed."] = "Opplasting av bildet mislyktes.";
-App::$strings["Unable to process image."] = "Kan ikke behandle bildet.";
-App::$strings["Photo not available."] = "Bildet er ikke tilgjengelig.";
-App::$strings["Your cover photo may be visible to anybody on the internet"] = "Omslagsbildet ditt vil være synlig for enhver på internett";
-App::$strings["Upload File:"] = "Last opp fil:";
-App::$strings["Select a profile:"] = "Velg en profil:";
-App::$strings["Change Cover Photo"] = "Endre omslagsbilde";
-App::$strings["Use a photo from your albums"] = "";
-App::$strings["Choose a different album"] = "Velg et annet album";
-App::$strings["Select existing photo"] = "Velg eksisterende bilde";
-App::$strings["Crop Image"] = "Beskjær bildet";
-App::$strings["Please adjust the image cropping for optimum viewing."] = "Vennligst juster bildebeskjæringen for optimal visning.";
-App::$strings["Done Editing"] = "Lagre endringer";
-App::$strings["You must be logged in to see this page."] = "Du må være innloegget for å se denne siden.";
-App::$strings["Room not found"] = "Rommet ble ikke funnet";
-App::$strings["Leave Room"] = "Forlat rom";
-App::$strings["Delete Room"] = "Slett rom";
-App::$strings["I am away right now"] = "Jeg er borte akkurat nå";
-App::$strings["I am online"] = "Jeg er pålogget";
-App::$strings["Bookmark this room"] = "Bokmerk dette rommet";
-App::$strings["New Chatroom"] = "Nytt chatrom";
-App::$strings["Chatroom name"] = "Romnavn";
-App::$strings["Expiration of chats (minutes)"] = "Chat utgår (antall minutter)";
-App::$strings["%1\$s's Chatrooms"] = "%1\$s sine chatrom";
-App::$strings["No chatrooms available"] = "Ingen rom tilgjengelige";
-App::$strings["Add Room"] = "";
-App::$strings["Expiration"] = "Utløp";
-App::$strings["min"] = "min";
-App::$strings["Invalid message"] = "Ugyldig melding";
-App::$strings["no results"] = "ingen resultater";
-App::$strings["channel sync processed"] = "kanalsynkronisering er behandlet";
-App::$strings["queued"] = "lagt i kø";
-App::$strings["posted"] = "lagt inn";
-App::$strings["accepted for delivery"] = "akseptert for levering";
-App::$strings["updated"] = "oppdatert";
-App::$strings["update ignored"] = "oppdatering ignorert";
-App::$strings["permission denied"] = "tillatelse avvist";
-App::$strings["recipient not found"] = "mottaker ble ikke funnet";
-App::$strings["Delivery report for %1\$s"] = "Leveringsrapport for %1\$s";
-App::$strings["Options"] = "Valg";
-App::$strings["Redeliver"] = "";
-App::$strings["Select a bookmark folder"] = "Velg en bokmerkemappe";
-App::$strings["Save Bookmark"] = "Lagre bokmerke";
-App::$strings["URL of bookmark"] = "URL-en til bokmerket";
-App::$strings["Description"] = "Beskrivelse";
-App::$strings["Or enter new bookmark folder name"] = "Eller skriv nytt navn på bokmerkemappe";
-App::$strings["Some blurb about what to do when you're new here"] = "En standardtekst om hva du bør gjøre som ny her";
-App::$strings["Private forum"] = "";
-App::$strings["Public forum"] = "Offentlig forum:";
+App::$strings["Installed Apps"] = "Installerte apper";
+App::$strings["Manage Apps"] = "Behandle apper";
+App::$strings["Create Custom App"] = "";
+App::$strings["No service class restrictions found."] = "Ingen restriksjoner er funnet i tjenesteklasse.";
+App::$strings["No valid account found."] = "Ingen gyldig konto funnet.";
+App::$strings["Password reset request issued. Check your email."] = "Forespørsel om å tilbakestille passord er mottatt. Sjekk e-posten din.";
+App::$strings["Site Member (%s)"] = "Nettstedsmedlem (%s)";
+App::$strings["Password reset requested at %s"] = "Forespurt om å tilbakestille passord hos %s";
+App::$strings["Request could not be verified. (You may have previously submitted it.) Password reset failed."] = "Forespørsel kunne ikke bekreftes. (Du kan ha sendt den inn tidligere.) Tilbakestilling av passord mislyktes.";
+App::$strings["Your password has been reset as requested."] = "Ditt passord har blitt tilbakestilt som forespurt.";
+App::$strings["Your new password is"] = "Ditt nye passord er";
+App::$strings["Save or copy your new password - and then"] = "Lagre eller kopier ditt nye passord, og deretter kan du";
+App::$strings["click here to login"] = "klikke her for å logge inn";
+App::$strings["Your password may be changed from the <em>Settings</em> page after successful login."] = "Ditt passord kan endres på siden <em>Innstillinger</em> etter vellykket innlogging.";
+App::$strings["Your password has changed at %s"] = "Ditt passord er endret hos %s";
+App::$strings["Forgot your Password?"] = "Glemt passord ditt?";
+App::$strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = "Skriv e-postadressen din og send inn for å tilbakestille passordet ditt. Sjekk deretter din e-post for videre instruksjoner.";
+App::$strings["Email Address"] = "E-postadresse";
+App::$strings["Reset"] = "Tilbakestill";
+App::$strings["Channel removals are not allowed within 48 hours of changing the account password."] = "Fjerning av kanaler er ikke tillatt innen 48 timer etter endring av kontopassordet.";
+App::$strings["Remove Channel"] = "Fjern kanal";
+App::$strings["WARNING: "] = "ADVARSEL: ";
+App::$strings["This channel will be permanently removed. "] = "";
+App::$strings["This action can not be undone!"] = "";
+App::$strings["Please enter your password for verification:"] = "Vennligst skriv ditt passord for å få bekreftelse:";
+App::$strings["Not a zip file or zip file corrupted."] = "";
+App::$strings["Import Items"] = "Importer elementer";
+App::$strings["Use this form to import existing posts and content from an export file."] = "Bruk dette skjemaet for å importere eksisterende innlegg og innhold fra en eksportfil.";
+App::$strings["File to Upload"] = "Fil som skal lastes opp";
+App::$strings["Imported file is empty."] = "Importert fil er tom.";
+App::$strings["Content import completed"] = "";
+App::$strings["Chatroom import completed"] = "";
+App::$strings["Channel calendar import 1/2 completed"] = "";
+App::$strings["Channel calendar import 2/2 completed"] = "";
+App::$strings["Menu import completed"] = "";
+App::$strings["Wiki import completed"] = "";
+App::$strings["Webpages import completed"] = "";
+App::$strings["This site is not a directory server"] = "Dette nettstedet er ikke en katalogtjener";
+App::$strings["New"] = "Nye";
+App::$strings["No more system notifications."] = "Ingen flere systemvarsler.";
+App::$strings["System Notifications"] = "Systemvarsler";
+App::$strings["Update to Hubzilla 5.0 step 2"] = "";
+App::$strings["To complete the update please run"] = "";
+App::$strings["php util/z6convert.php"] = "";
+App::$strings["from the terminal."] = "";
+App::$strings["+ Repeat again"] = "";
+App::$strings["- Remove yours"] = "Fjern begrep";
+App::$strings["+ Add yours"] = "+ Legg til";
+App::$strings["Thing updated"] = "Tingen er oppdatert";
+App::$strings["Object store: failed"] = "Objektlagring: mislyktes";
+App::$strings["Thing added"] = "Ting lagt til";
+App::$strings["OBJ: %1\$s %2\$s %3\$s"] = "OBJ: %1\$s %2\$s %3\$s";
+App::$strings["item not found."] = "element ble ikke funnet.";
+App::$strings["Edit Thing"] = "Endre ting";
+App::$strings["Select a profile"] = "Velg en profil";
+App::$strings["Post an activity"] = "Legg inn en aktivitet";
+App::$strings["Only sends to viewers of the applicable profile"] = "Sender bare til seere av den aktuelle profilen";
+App::$strings["Name of thing e.g. something"] = "Navn på ting for eksempel noe";
+App::$strings["URL of thing (optional)"] = "URL til ting (valgfritt)";
+App::$strings["URL for photo of thing (optional)"] = "URL til bilde av ting (valgfritt)";
+App::$strings["Add Thing to your Profile"] = "Legg til ting i din profil";
+App::$strings["Account removals are not allowed within 48 hours of changing the account password."] = "Sletting av kontoer er ikke tillatt innen 48 timer etter endring av kontopassordet.";
+App::$strings["Remove This Account"] = "Slett denne kontoen";
+App::$strings["This account and all its channels will be completely removed from the network. "] = "Denne kontoen og alle dens kanaler vil bli fullstendig fjernet fra nettverket. ";
+App::$strings["This action is permanent and can not be undone!"] = "Denne handlingen er permanent og kan ikke angres!";
+App::$strings["Remove Account"] = "Slett konto";
+App::$strings["This channel is limited to %d tokens"] = "";
+App::$strings["Name and Password are required."] = "";
+App::$strings["Token saved."] = "";
+App::$strings["Use this form to create temporary access identifiers to share things with non-members. These identities may be used in privacy groups and visitors may login using these credentials to access private content."] = "";
+App::$strings["Please select a role for this guest!"] = "";
+App::$strings["Select a role for this guest"] = "";
+App::$strings["Login Name"] = "";
+App::$strings["Login Password"] = "";
+App::$strings["Expires (yyyy-mm-dd)"] = "";
+App::$strings["Layout updated."] = "Layout er oppdatert.";
+App::$strings["Edit System Page Description"] = "Endre beskrivelsen av systemsiden";
+App::$strings["(modified)"] = "(endret)";
+App::$strings["Layout not found."] = "Layouten ble ikke funnet.";
+App::$strings["Module Name:"] = "Modulnavn:";
+App::$strings["Layout Help"] = "Layout-hjelp";
+App::$strings["Edit another layout"] = "";
+App::$strings["System layout"] = "";
+App::$strings["Continue"] = "Fortsett";
+App::$strings["Premium Channel Setup"] = "Premiumkanal-oppsett";
+App::$strings["Enable premium channel connection restrictions"] = "Slå på restriksjoner for forbindelse med premiumkanal";
+App::$strings["Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc."] = "Vennligst skriv dine restriksjoner og betingelser, slik som PayPal-kvittering, retningslinjer for bruk, og så videre.";
+App::$strings["This channel may require additional steps or acknowledgement of the following conditions prior to connecting:"] = "Denne kanalen kan kreve ytterligere steg og bekreftelse av følgende betingelser før tilkobling:";
+App::$strings["Potential connections will then see the following text before proceeding:"] = "Potensielle forbindelser vil da se følgende tekst før de går videre:";
+App::$strings["By continuing, I certify that I have complied with any instructions provided on this page."] = "Ved å fortsette bekrefter jeg at jeg har oppfylt alle instruksjoner gitt på denne siden.";
+App::$strings["(No specific instructions have been provided by the channel owner.)"] = "(Ingen spesifikke instruksjoner er gitt av kanaleieren.)";
+App::$strings["Restricted or Premium Channel"] = "Begrenset kanal eller premiumkanal";
+App::$strings["Deprecated!"] = "";
+App::$strings["File not found."] = "Filen ble ikke funnet.";
+App::$strings["Permission Denied."] = "Tillatelse avvist.";
+App::$strings["Edit file permissions"] = "Endre filtillatelser";
+App::$strings["Set/edit permissions"] = "Angi/endre tillatelser";
+App::$strings["Include all files and sub folders"] = "Inkluder alle filer og undermapper";
+App::$strings["Return to file list"] = "Gå tilbake til filoversikten";
+App::$strings["Share this file"] = "Del denne filen";
+App::$strings["Show URL to this file"] = "Vis URLen til denne filen";
+App::$strings["Account not found."] = "Kontoen ble ikke funnet.";
+App::$strings["Multifactor Verification"] = "";
+App::$strings["Please enter the verification key from your authenticator app"] = "";
+App::$strings["Verify"] = "";
+App::$strings["Invalid abook_id"] = "";
+App::$strings["Could not access contact record."] = "Fikk ikke tilgang til kontaktinformasjonen.";
+App::$strings["Could not locate selected profile."] = "Fant ikke valgt profil.";
+App::$strings["is now connected to"] = "er nå forbundet til";
+App::$strings["Contact Tools"] = "Kontaktverktøy";
+App::$strings["Approve this contact"] = "";
+App::$strings["Accept contact to allow communication"] = "";
+App::$strings["Please select a role for this contact!"] = "";
+App::$strings["This contact is unreachable from this location."] = "";
+App::$strings["This contact may be unreachable from other channel locations."] = "";
+App::$strings["Location independence is not supported by their network."] = "";
+App::$strings["View profile"] = "Vis profil";
+App::$strings["This is a group/forum channel"] = "";
+App::$strings["Select a role for this contact"] = "Velg en rolle for denne forbindelsen";
+App::$strings["Slide to adjust your degree of friendship"] = "Flytt for å justere din grad av vennskap";
+App::$strings["Custom Filter"] = "Tilpasset filter";
+App::$strings["Only import posts with this text"] = "Bare importer innlegg med disse ordene";
+App::$strings["words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts"] = "ord per linje eller #merkelapper eller /mønster/ eller språk lang=xx, la stå blankt for å importere alle innlegg";
+App::$strings["Do not import posts with this text"] = "Ikke importer innlegg med denne teksten";
+App::$strings["Approve contact"] = "";
+App::$strings["Their"] = "Deres";
+App::$strings["My"] = "Mine";
+App::$strings["Roles"] = "Roller";
+App::$strings["Compare permissions"] = "Sammenlign tillatelser";
+App::$strings["Permission"] = "Tillatelse";
+App::$strings["Content filter"] = "";
+App::$strings["Contact updated"] = "";
+App::$strings["Contact update failed"] = "";
+App::$strings["Approve connection"] = "Godkjenn forbindelse";
+App::$strings["Block status updated"] = "";
+App::$strings["Block failed"] = "";
+App::$strings["Ignore status updated"] = "";
+App::$strings["Ignore failed"] = "";
+App::$strings["Archive status updated"] = "";
+App::$strings["Archive failed"] = "";
+App::$strings["Hide status updated"] = "";
+App::$strings["Hide failed"] = "";
+App::$strings["Contact removed"] = "";
+App::$strings["Delete failed"] = "";
+App::$strings["Refetch contact info"] = "";
+App::$strings["Unblock"] = "Ikke blokker lenger";
+App::$strings["Block"] = "Blokker";
+App::$strings["Block (or Unblock) all communications with this connection"] = "Blokker eller fjern blokkering av all kommunikasjon med denne forbindelsen";
+App::$strings["This connection is blocked!"] = "Denne forbindelsen er blokkert!";
+App::$strings["Unignore"] = "Ikke ignorer lenger";
+App::$strings["Ignore"] = "Ignorer";
+App::$strings["Ignore (or Unignore) all inbound communications from this connection"] = "Ignorer eller fjern ignorering av all inngående kommunikasjon fra denne forbindelsen";
+App::$strings["This connection is ignored!"] = "Denne forbindelsen er ignorert!";
+App::$strings["Unarchive"] = "Ikke arkiver lenger";
+App::$strings["Archive"] = "Arkiver";
+App::$strings["Archive (or Unarchive) this connection - mark channel dead but keep content"] = "Arkiver eller fjern arkivering av denne forbindelsen - marker kanal som død, men behold innhold";
+App::$strings["This connection is archived!"] = "Denne forbindelsen er arkivert!";
+App::$strings["Unhide"] = "Ikke skjul lenger";
+App::$strings["Hide"] = "Skjul";
+App::$strings["Hide or Unhide this connection from your other connections"] = "Skjul eller fjern skjuling av denne forbindelsen fra dine andre forbindelser";
+App::$strings["This connection is hidden!"] = "Denne forbindelsen er skjult!";
+App::$strings["Delete this connection"] = "Slett denne forbindelsen";
+App::$strings["Items tagged with: %s"] = "Elementer merket med: %s";
+App::$strings["Search results for: %s"] = "Søkeresultater for: %s";
+App::$strings["Email address required"] = "";
+App::$strings["No password provided"] = "";
+App::$strings["Terms of Service not accepted"] = "";
+App::$strings["Invitation code succesfully applied"] = "";
+App::$strings["Invitation not in time or too late"] = "";
+App::$strings["Invitation email failed"] = "";
+App::$strings["Invitation code failed"] = "";
+App::$strings["Invitations are not available"] = "";
+App::$strings["Registration on this hub is by invitation only"] = "";
+App::$strings["New register request"] = "";
+App::$strings["Error creating dId A"] = "";
+App::$strings["Registration on this hub is disabled."] = "Registrering ved dette nettstedet er skrudd av.";
+App::$strings["Why do you want to join this hub?"] = "";
+App::$strings["This will help to review your registration"] = "";
+App::$strings["Registration on this hub is by approval only."] = "Registrering ved dette nettstedet skjer på godkjenning.";
+App::$strings["Register at another affiliated hub in case when prefered"] = "";
+App::$strings["Registration on this hub is by invitation only."] = "";
+App::$strings["Register at another affiliated hub"] = "";
+App::$strings["Terms of Service"] = "Tjenesteavtale";
+App::$strings["I accept the %s for this website"] = "Jeg godtar %s for dette nettstedet";
+App::$strings["I am over %s years of age and accept the %s for this website"] = "Jeg er mer enn %s år gammel, og godtar %s for dette nettstedet";
+App::$strings["Your email address"] = "Din e-postadresse";
+App::$strings["Choose a password"] = "Velg et passord";
+App::$strings["Please re-enter your password"] = "Vennligst skriv ditt passord en gang til";
+App::$strings["Please enter your invitation code"] = "Vennligst skriv din invitasjonskode";
+App::$strings["Your name"] = "Navn";
+App::$strings["Real name is preferred"] = "";
+App::$strings["Choose a short nickname"] = "Velg et kort kallenavn";
+App::$strings["Your nickname will be used to create an easy to remember channel address"] = "";
+App::$strings["Registration"] = "Registrering";
+App::$strings["I have an invite code"] = "";
+App::$strings["This site has exceeded the number of allowed daily account registrations."] = "";
+App::$strings["This setting requires special processing and editing has been blocked."] = "Denne innstillingen krever spesiell behandling og redigering har blitt blokkert.";
+App::$strings["Configuration Editor"] = "Konfigurasjonsbehandler";
+App::$strings["Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature."] = "Advarsel: kanalen din kan slutte å virke ved endring av enkelte innstillinger. Vennligst forlat denne siden med mindre du er komfortabel med dette og vet hvordan du bruker denne funksjonen riktig.";
App::$strings["App installed."] = "App installert.";
App::$strings["Malformed app."] = "Feil oppsett for app-en.";
App::$strings["Embed code"] = "Innbyggingskode";
@@ -1664,37 +2200,20 @@ App::$strings["Categories (optional, comma separated list)"] = "Kategorier (valg
App::$strings["Version ID"] = "Versjons-ID";
App::$strings["Price of app"] = "Pris på app";
App::$strings["Location (URL) to purchase app"] = "Plassering (URL) for å kjøpe app";
-App::$strings["No default suggestions were found."] = "";
-App::$strings["Gender: "] = "Kjønn: ";
-App::$strings["Status: "] = "Status: ";
-App::$strings["Homepage: "] = "Hjemmeside: ";
-App::$strings["Description:"] = "Beskrivelse:";
-App::$strings["Unsafe"] = "";
-App::$strings["Spam"] = "";
-App::$strings["Public Forum:"] = "Offentlig forum:";
-App::$strings["Keywords: "] = "Nøkkelord: ";
-App::$strings["Don't suggest"] = "Ikke foreslå";
-App::$strings["Common connections (estimated):"] = "";
-App::$strings["Global Directory"] = "Global katalog";
-App::$strings["Local Directory"] = "Lokal katalog";
-App::$strings["Finding:"] = "Finner:";
-App::$strings["next page"] = "neste side";
-App::$strings["previous page"] = "forrige side";
-App::$strings["Sort options"] = "Sorteringsvalg";
-App::$strings["Alphabetic"] = "Alfabetisk";
-App::$strings["Reverse Alphabetic"] = "Omvendt alfabetisk";
-App::$strings["Newest to Oldest"] = "Nyest til eldst";
-App::$strings["Oldest to Newest"] = "Eldst til nyest";
-App::$strings["No entries (some entries may be hidden)."] = "Ingen oppføringer (noen oppføringer kan være skjult).";
-App::$strings["No service class restrictions found."] = "Ingen restriksjoner er funnet i tjenesteklasse.";
-App::$strings["Tag removed"] = "Merkelapp fjernet";
-App::$strings["Remove Item Tag"] = "Fjern merkelapp fra element";
-App::$strings["Select a tag to remove: "] = "Velg merkelapp å fjerne: ";
-App::$strings["Could not access contact record."] = "Fikk ikke tilgang til kontaktinformasjonen.";
-App::$strings["Could not locate selected profile."] = "Fant ikke valgt profil.";
+App::$strings["item"] = "";
+App::$strings["Unable to locate original post."] = "Ikke i stand til å finne opprinnelig innlegg.";
+App::$strings["Empty post discarded."] = "Tomt innlegg forkastet.";
+App::$strings["Duplicate post suppressed."] = "Duplikat av innlegg forhindret.";
+App::$strings["System error. Post not saved."] = "Systemfeil. Innlegg ble ikke lagret.";
+App::$strings["Your comment is awaiting approval."] = "";
+App::$strings["Unable to obtain post information from database."] = "Ikke i stand til å få tak i informasjon om innlegg fra databasen.";
+App::$strings["You have reached your limit of %1$.0f top level posts."] = "Du har nådd din grense på %1$.0f startinnlegg.";
+App::$strings["You have reached your limit of %1$.0f webpages."] = "Du har nådd din grense på %1$.0f websider.";
+App::$strings["Some blurb about what to do when you're new here"] = "En standardtekst om hva du bør gjøre som ny her";
+App::$strings["Unable to find your hub."] = "Ikke i stand til å finne hubben din.";
+App::$strings["Post successful."] = "Innlegg vellykket.";
App::$strings["Connection updated."] = "Forbindelsen er oppdatert.";
App::$strings["Failed to update connection record."] = "Mislyktes med å oppdatere forbindelsesinformasjonen.";
-App::$strings["is now connected to"] = "er nå forbundet til";
App::$strings["Could not access address book record."] = "Fikk ikke tilgang til informasjonen i adresseboken.";
App::$strings["Refresh failed - channel is currently unavailable."] = "Oppfrisking mislyktes - kanalen er for øyeblikket utilgjengelig.";
App::$strings["Unable to set address book parameters."] = "Ikke i stand til å angi parametre for adresseboken.";
@@ -1705,70 +2224,33 @@ App::$strings["Fetch updated permissions"] = "Hent oppdaterte tillatelser";
App::$strings["Refresh Photo"] = "";
App::$strings["Fetch updated photo"] = "";
App::$strings["View recent posts and comments"] = "Vis nylige innlegg og kommentarer";
-App::$strings["Unblock"] = "Ikke blokker lenger";
-App::$strings["Block"] = "Blokker";
-App::$strings["Block (or Unblock) all communications with this connection"] = "Blokker eller fjern blokkering av all kommunikasjon med denne forbindelsen";
-App::$strings["This connection is blocked!"] = "Denne forbindelsen er blokkert!";
-App::$strings["Unignore"] = "Ikke ignorer lenger";
-App::$strings["Ignore"] = "Ignorer";
-App::$strings["Ignore (or Unignore) all inbound communications from this connection"] = "Ignorer eller fjern ignorering av all inngående kommunikasjon fra denne forbindelsen";
-App::$strings["This connection is ignored!"] = "Denne forbindelsen er ignorert!";
-App::$strings["Unarchive"] = "Ikke arkiver lenger";
-App::$strings["Archive"] = "Arkiver";
-App::$strings["Archive (or Unarchive) this connection - mark channel dead but keep content"] = "Arkiver eller fjern arkivering av denne forbindelsen - marker kanal som død, men behold innhold";
-App::$strings["This connection is archived!"] = "Denne forbindelsen er arkivert!";
-App::$strings["Unhide"] = "Ikke skjul lenger";
-App::$strings["Hide"] = "Skjul";
-App::$strings["Hide or Unhide this connection from your other connections"] = "Skjul eller fjern skjuling av denne forbindelsen fra dine andre forbindelser";
-App::$strings["This connection is hidden!"] = "Denne forbindelsen er skjult!";
-App::$strings["Delete this connection"] = "Slett denne forbindelsen";
App::$strings["Fetch Vcard"] = "";
App::$strings["Fetch electronic calling card for this connection"] = "";
-App::$strings["Affinity"] = "";
App::$strings["Open Set Affinity section by default"] = "";
-App::$strings["Me"] = "Meg";
-App::$strings["Family"] = "Familie";
-App::$strings["Acquaintances"] = "Bekjente";
-App::$strings["All"] = "Alle";
App::$strings["Filter"] = "";
App::$strings["Open Custom Filter section by default"] = "";
-App::$strings["Approve this contact"] = "";
-App::$strings["Accept contact to allow communication"] = "";
App::$strings["Set Affinity"] = "Angi nærhet";
App::$strings["Set Profile"] = "Angi profil";
App::$strings["Set Affinity & Profile"] = "Angi nærhet og profil";
-App::$strings["Please select a role for this contact!"] = "";
-App::$strings["This contact is unreachable from this location."] = "";
-App::$strings["This contact may be unreachable from other channel locations."] = "";
-App::$strings["Location independence is not supported by their network."] = "";
App::$strings["Contact: %s"] = "";
App::$strings["Contact role"] = "";
App::$strings["Manage contact roles"] = "";
App::$strings["This contacts's primary address is"] = "";
App::$strings["Available locations:"] = "Tilgjengelige plasseringer:";
App::$strings["The permissions indicated on this page will be applied to all new connections."] = "Tillatelsene angitt på denne siden gjøres gjeldende for alle nye forbindelser.";
-App::$strings["Contact Tools"] = "";
-App::$strings["Slide to adjust your degree of friendship"] = "Flytt for å justere din grad av vennskap";
-App::$strings["Custom Filter"] = "Tilpasset filter";
-App::$strings["Only import posts with this text"] = "Bare importer innlegg med disse ordene";
-App::$strings["words one per line or #tags or /patterns/ or lang=xx, leave blank to import all posts"] = "ord per linje eller #merkelapper eller /mønster/ eller språk lang=xx, la stå blankt for å importere alle innlegg";
-App::$strings["Do not import posts with this text"] = "Ikke importer innlegg med denne teksten";
App::$strings["Contact Pending Approval"] = "";
App::$strings["inherited"] = "arvet";
-App::$strings["Approve contact"] = "";
App::$strings["Please choose the profile you would like to display to %s when viewing your profile securely."] = "Vennligst velg profilen du ønsker å vise %s når profilen din ses på en sikret måte.";
-App::$strings["Their"] = "";
-App::$strings["My"] = "";
App::$strings["Individual Permissions"] = "Individuelle tillatelser";
App::$strings["Some permissions may be inherited from your channel's <a href=\"settings\"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can <strong>not</strong> change those settings here."] = "Noen tillatelser kan være arvet fra din kanals <a ref=\"settings\"><strong>personverninnstillinger</strong></a>, som har høyere prioritet enn individuelle innstillinger. Du kan <strong>ikke</strong> endre arvede innstillingene her.";
App::$strings["Some permissions may be inherited from your channel's <a href=\"settings\"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes."] = "Noen tillatelser kan være arvet fra din kanals <a ref=\"settings\"><strong>personverninnstillinger</strong></a>, som har høyere prioritet enn individuelle innstillinger. Du kan endre disse innstillingene her, men de vil ikke få noen effekt før de arvede innstillingene endres.";
App::$strings["Last update:"] = "Siste oppdatering:";
App::$strings["Details"] = "";
App::$strings["Organisation"] = "";
-App::$strings["Title"] = "Tittel";
App::$strings["Phone"] = "";
App::$strings["Instant messenger"] = "";
App::$strings["Website"] = "";
+App::$strings["Address"] = "Adresse";
App::$strings["Note"] = "";
App::$strings["Add Contact"] = "";
App::$strings["Add Field"] = "";
@@ -1778,101 +2260,7 @@ App::$strings["Street"] = "";
App::$strings["Locality"] = "";
App::$strings["Region"] = "";
App::$strings["ZIP Code"] = "";
-App::$strings["Nothing to import."] = "Ingenting å importere.";
-App::$strings["Unable to download data from old server"] = "Ikke i stand til å laste ned data fra gammel tjener";
-App::$strings["Your service plan only allows %d channels."] = "Din tjenesteplan tillater bare %d kanaler.";
-App::$strings["No channel. Import failed."] = "Ingen kanal. Import mislyktes.";
-App::$strings["Channel exists but has been marked removed on this hub. Import failed."] = "";
-App::$strings["Automatic content and files import was not possible due to API version incompatiblity. Please import content and files manually!"] = "";
-App::$strings["You must be logged in to use this feature."] = "Du må være innlogget for å bruke denne funksjonen.";
-App::$strings["Channel Import"] = "";
-App::$strings["Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file."] = "Bruk dette skjemaet for å importere en eksisterende kanal fra en annen tjener/hub. Du kan hente inn kanalidentiteten fra den gamle tjeneren/huben via nettverket eller ved å bruke en eksportfil.";
-App::$strings["Or provide the old server/hub details"] = "Eller oppgi detaljene fra den gamle tjeneren/hub-en";
-App::$strings["Your old identity address (xyz@example.com)"] = "Din gamle identitetsadresse (xyz@example.com)";
-App::$strings["Your old login email address"] = "Din gamle innloggings e-postadresse";
-App::$strings["Your old login password"] = "Ditt gamle innloggingspassord";
-App::$strings["Import your items and files (limited by available memory)"] = "";
-App::$strings["For either option, please choose whether to make this hub your new primary address, or whether your old location should continue this role. You will be able to post from either location, but only one can be marked as the primary location for files, photos, and media."] = "Enten du tar det ene eller det andre valget, vennligst angi om du vil at denne hubben skal være din nye primære adresse, eller om din gamle plassering skal fortsette å ha denne rollen. Du kan lage innlegg fra den ene eller den andre plasseringen, men bare en av dem kan markeres som den primære plasseringen for filer, bilder og media.";
-App::$strings["Make this hub my primary location"] = "Gjør dette nettstedet til min primære plassering";
-App::$strings["Move this channel (disable all previous locations)"] = "";
-App::$strings["Use this channel nickname instead of the one provided"] = "";
-App::$strings["Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site."] = "";
-App::$strings["This process may take several minutes to complete. Please submit the form only once and leave this page open until finished."] = "Denne prosessen kan ta flere minutter å fullføre. Vennligst send inn dette skjemaet bare en gang og la siden være åpen inntil den er ferdig.";
-App::$strings["Page link"] = "Sidelenke";
-App::$strings["Edit Webpage"] = "Endre webside";
-App::$strings["Enter a folder name"] = "";
-App::$strings["or select an existing folder (doubleclick)"] = "";
-App::$strings["Invalid profile identifier."] = "Ugyldig profil-identifikator.";
-App::$strings["Profile Visibility Editor"] = "Endre profilsynlighet";
-App::$strings["Click on a contact to add or remove."] = "Klikk på en kontakt for å legge til eller fjerne.";
-App::$strings["Visible To"] = "Synlig for";
-App::$strings["All Connections"] = "Alle forbindelser";
-App::$strings["Permissions denied."] = "Tillatelse avvist.";
-App::$strings["Today"] = "Idag";
-App::$strings["Entry censored"] = "";
-App::$strings["Entry OK"] = "";
-App::$strings["network"] = "nettverk";
-App::$strings["This setting requires special processing and editing has been blocked."] = "Denne innstillingen krever spesiell behandling og redigering har blitt blokkert.";
-App::$strings["Configuration Editor"] = "Konfigurasjonsbehandler";
-App::$strings["Warning: Changing some settings could render your channel inoperable. Please leave this page unless you are comfortable with and knowledgeable about how to correctly use this feature."] = "Advarsel: kanalen din kan slutte å virke ved endring av enkelte innstillinger. Vennligst forlat denne siden med mindre du er komfortabel med dette og vet hvordan du bruker denne funksjonen riktig.";
-App::$strings["Xchan Lookup"] = "Xchan oppslag";
-App::$strings["Lookup xchan beginning with (or webbie): "] = "Slå opp xchan som begynner med (eller webbie): ";
-App::$strings["Email address required"] = "";
-App::$strings["No password provided"] = "";
-App::$strings["Terms of Service not accepted"] = "";
-App::$strings["Invitation code succesfully applied"] = "";
-App::$strings["Invitation not in time or too late"] = "";
-App::$strings["Invitation email failed"] = "";
-App::$strings["Invitation code failed"] = "";
-App::$strings["Invitations are not available"] = "";
-App::$strings["Registration on this hub is by invitation only"] = "";
-App::$strings["New register request"] = "";
-App::$strings["Error creating dId A"] = "";
-App::$strings["Registration on this hub is disabled."] = "Registrering ved dette nettstedet er skrudd av.";
-App::$strings["Why do you want to join this hub?"] = "";
-App::$strings["This will help to review your registration"] = "";
-App::$strings["Registration on this hub is by approval only."] = "Registrering ved dette nettstedet skjer på godkjenning.";
-App::$strings["Register at another affiliated hub in case when prefered"] = "";
-App::$strings["Registration on this hub is by invitation only."] = "";
-App::$strings["Register at another affiliated hub"] = "";
-App::$strings["Terms of Service"] = "Tjenesteavtale";
-App::$strings["I accept the %s for this website"] = "Jeg godtar %s for dette nettstedet";
-App::$strings["I am over %s years of age and accept the %s for this website"] = "Jeg er mer enn %s år gammel, og godtar %s for dette nettstedet";
-App::$strings["Your email address"] = "Din e-postadresse";
-App::$strings["Choose a password"] = "Velg et passord";
-App::$strings["Please re-enter your password"] = "Vennligst skriv ditt passord en gang til";
-App::$strings["Please enter your invitation code"] = "Vennligst skriv din invitasjonskode";
-App::$strings["Your name"] = "Navn";
-App::$strings["Real name is preferred"] = "";
-App::$strings["Your nickname will be used to create an easy to remember channel address"] = "";
-App::$strings["Registration"] = "Registrering";
-App::$strings["I have an invite code"] = "";
-App::$strings["This site has exceeded the number of allowed daily account registrations."] = "";
-App::$strings["Public Hubs"] = "Offentlige huber";
-App::$strings["The listed hubs allow public registration for the \$Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself <strong>may</strong> provide additional details."] = "Nettstedene på listen tillater offentlig registrering i \$Projectname-nettverket. Alle nettsteder i nettverket er forbundet så medlemskap på enhver av dem formidler medlemskap i nettverket som helhet. Noen nettsteder kan kreve abonnement eller tilby lagdelte tjenesteavtaler. Nettstedene selv <strong>kan</strong> gi tilleggsopplysninger.";
-App::$strings["Hub URL"] = "Nettstedets URL";
-App::$strings["Access Type"] = "Tilgangstype";
-App::$strings["Registration Policy"] = "Retningslinjer for registrering";
-App::$strings["Stats"] = "Statistikk";
-App::$strings["Software"] = "Programvare";
-App::$strings["Ratings"] = "Vurderinger";
-App::$strings["Rate"] = "Vurder";
-App::$strings["Thing updated"] = "Tingen er oppdatert";
-App::$strings["Object store: failed"] = "Objektlagring: mislyktes";
-App::$strings["Thing added"] = "Ting lagt til";
-App::$strings["OBJ: %1\$s %2\$s %3\$s"] = "OBJ: %1\$s %2\$s %3\$s";
-App::$strings["item not found."] = "element ble ikke funnet.";
-App::$strings["Edit Thing"] = "Endre ting";
-App::$strings["Select a profile"] = "Velg en profil";
-App::$strings["Post an activity"] = "Legg inn en aktivitet";
-App::$strings["Only sends to viewers of the applicable profile"] = "Sender bare til seere av den aktuelle profilen";
-App::$strings["Name of thing e.g. something"] = "Navn på ting for eksempel noe";
-App::$strings["URL of thing (optional)"] = "URL til ting (valgfritt)";
-App::$strings["URL for photo of thing (optional)"] = "URL til bilde av ting (valgfritt)";
-App::$strings["Add Thing to your Profile"] = "Legg til ting i din profil";
-App::$strings["Welcome to %s"] = "Velkommen til %s";
-App::$strings["Block Title"] = "Byggeklossens tittel";
-App::$strings["Settings updated."] = "Innstillinger oppdatert.";
+App::$strings["Country"] = "Land";
App::$strings["Connection Default Permissions"] = "Forbindelsens standard tillatelser";
App::$strings["Apply these permissions automatically"] = "Bruk disse tillatelsene automatisk";
App::$strings["If enabled, connection requests will be approved without your interaction"] = "";
@@ -1881,166 +2269,251 @@ App::$strings["Add permission role"] = "";
App::$strings["Automatic approval settings"] = "";
App::$strings["My Settings"] = "Mine innstillinger";
App::$strings["Some individual permissions may have been preset or locked based on your channel type and privacy settings."] = "";
-App::$strings["No valid account found."] = "Ingen gyldig konto funnet.";
-App::$strings["Password reset request issued. Check your email."] = "Forespørsel om å tilbakestille passord er mottatt. Sjekk e-posten din.";
-App::$strings["Site Member (%s)"] = "Nettstedsmedlem (%s)";
-App::$strings["Password reset requested at %s"] = "Forespurt om å tilbakestille passord hos %s";
-App::$strings["Request could not be verified. (You may have previously submitted it.) Password reset failed."] = "Forespørsel kunne ikke bekreftes. (Du kan ha sendt den inn tidligere.) Tilbakestilling av passord mislyktes.";
-App::$strings["Your password has been reset as requested."] = "Ditt passord har blitt tilbakestilt som forespurt.";
-App::$strings["Your new password is"] = "Ditt nye passord er";
-App::$strings["Save or copy your new password - and then"] = "Lagre eller kopier ditt nye passord, og deretter kan du";
-App::$strings["click here to login"] = "klikke her for å logge inn";
-App::$strings["Your password may be changed from the <em>Settings</em> page after successful login."] = "Ditt passord kan endres på siden <em>Innstillinger</em> etter vellykket innlogging.";
-App::$strings["Your password has changed at %s"] = "Ditt passord er endret hos %s";
-App::$strings["Forgot your Password?"] = "Glemt passord ditt?";
-App::$strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = "Skriv e-postadressen din og send inn for å tilbakestille passordet ditt. Sjekk deretter din e-post for videre instruksjoner.";
-App::$strings["Email Address"] = "E-postadresse";
-App::$strings["Reset"] = "Tilbakestill";
+App::$strings["No suggestions available. If this is a new site, please try again in 24 hours."] = "Ingen forslag tilgjengelige. Hvis dette er et nytt nettsted, vennligst prøv igjen om 24 timer.";
+App::$strings["Connection added."] = "";
+App::$strings["Please login."] = "Vennligst logg inn.";
+App::$strings["No default suggestions were found."] = "";
+App::$strings["Gender: "] = "Kjønn: ";
+App::$strings["Status: "] = "Status: ";
+App::$strings["Homepage: "] = "Hjemmeside: ";
+App::$strings["Description:"] = "Beskrivelse:";
+App::$strings["Unsafe"] = "";
+App::$strings["Spam"] = "";
+App::$strings["Public Forum:"] = "Offentlig forum:";
+App::$strings["Keywords: "] = "Nøkkelord: ";
+App::$strings["Don't suggest"] = "Ikke foreslå";
+App::$strings["Common connections (estimated):"] = "";
+App::$strings["Global Directory"] = "Global katalog";
+App::$strings["Local Directory"] = "Lokal katalog";
+App::$strings["Finding:"] = "Finner:";
+App::$strings["next page"] = "neste side";
+App::$strings["previous page"] = "forrige side";
+App::$strings["Sort options"] = "Sorteringsvalg";
+App::$strings["Alphabetic"] = "Alfabetisk";
+App::$strings["Reverse Alphabetic"] = "Omvendt alfabetisk";
+App::$strings["Newest to Oldest"] = "Nyest til eldst";
+App::$strings["Oldest to Newest"] = "Eldst til nyest";
+App::$strings["No entries (some entries may be hidden)."] = "Ingen oppføringer (noen oppføringer kan være skjult).";
+App::$strings["This profile photo will be visible to anybody on the internet and may be distributed to other websites."] = "Dette profilbildet vil være synlig for alle besøkende, og kan bli gjort tilgjengelig på andre nettsteder.";
+App::$strings["This profile photo will be visible only to channels with permission to view this profile."] = "";
+App::$strings["Use Photo for Profile"] = "";
+App::$strings["Change Profile Photo"] = "Endre profilbilde";
+App::$strings["Reset to default"] = "Tilbakestill til standard";
+App::$strings["Select existing"] = "Velg eksisterende bilde";
+App::$strings["Done editing"] = "Lagre endringer";
+App::$strings["Unknown App"] = "";
+App::$strings["Authorize"] = "";
+App::$strings["Do you authorize the app %s to access your channel data?"] = "";
+App::$strings["Deny"] = "Avslå";
+App::$strings["No connections."] = "Ingen forbindelser.";
+App::$strings["Visit %s's profile [%s]"] = "Besøk %s sin profil [%s]";
+App::$strings["View Connections"] = "Vis forbindelser";
+App::$strings["Authorize application connection"] = "Tillat programforbindelse";
+App::$strings["Return to your app and insert this Security Code:"] = "";
+App::$strings["Please login to continue."] = "Vennligst logg inn for å fortsette.";
+App::$strings["Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?"] = "Vil du tillate dette programmet å få tilgang til dine innlegg og kontakter, og/eller lage nye innlegg for deg?";
+App::$strings["Layout not found"] = "Layouten ble ikke funnet.";
+App::$strings["This template does not support pdledi_gui (no content regions defined)"] = "";
+App::$strings["Main page content"] = "";
+App::$strings["The main page content can not be edited!"] = "";
+App::$strings["Welcome to %s"] = "Velkommen til %s";
+App::$strings["Tag removed"] = "Merkelapp fjernet";
+App::$strings["Remove Item Tag"] = "Fjern merkelapp fra element";
+App::$strings["Select a tag to remove: "] = "Velg merkelapp å fjerne: ";
+App::$strings["Layout Name"] = "Layout-navn";
+App::$strings["Layout Description (Optional)"] = "Layoutens beskrivelse (valgfritt)";
+App::$strings["Comanche page description language help"] = "Hjelp med Comanche sidebeskrivelsesspråk";
+App::$strings["Layout Description"] = "Layout-beskrivelse";
+App::$strings["Download PDL file"] = "Last ned PDL-fil";
+App::$strings["No channel."] = "Ingen kanal.";
+App::$strings["No connections in common."] = "Ingen forbindelser felles.";
+App::$strings["View Common Connections"] = "";
App::$strings["Location not found."] = "Plassering er ikke funnet.";
App::$strings["Location lookup failed."] = "Oppslag på plassering mislyktes.";
App::$strings["Please select another location to become primary before removing the primary location."] = "Vennligst velg en annen plassering som primær før du sletter gjeldende primære plassering.";
App::$strings["Syncing locations"] = "Synkroniserer plasseringer";
App::$strings["No locations found."] = "Ingen plasseringer ble funnet.";
App::$strings["Manage Channel Locations"] = "Håndter kanalplasseringer";
-App::$strings["Primary"] = "Primær";
+App::$strings["Drop"] = "Slett";
App::$strings["Sync Now"] = "Synkroniser nå";
App::$strings["Please wait several minutes between consecutive operations."] = "Vennligst vent flere minutter mellom hver etterfølgende operasjon.";
App::$strings["When possible, drop a location by logging into that website/hub and removing your channel."] = "Når mulig, fjern en plassering ved å logge inn på det nettstedet eller den hub-en og fjern din kanal.";
App::$strings["Use this form to drop the location if the hub is no longer operating."] = "Bruk dette skjemaet for å fjerne plasseringen hvis huben ikke er i drift lenger.";
-App::$strings["Account not found."] = "Kontoen ble ikke funnet";
-App::$strings["Multifactor Verification"] = "";
-App::$strings["Please enter the verification key from your authenticator app"] = "";
-App::$strings["Verify"] = "";
-App::$strings["Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
-App::$strings["Poll not found."] = "Fant ikke spørreskjema.";
-App::$strings["Invalid response."] = "";
-App::$strings["Response submitted. Updates may not appear instantly."] = "";
-App::$strings["Shift-reload the page or clear browser cache if the new photo does not display immediately."] = "Hold nede Shift-knappen og last siden på nytt eller tøm nettleserens mellomlager hvis det nye bildet ikke vises umiddelbart.";
-App::$strings["This profile photo will be visible to anybody on the internet and may be distributed to other websites."] = "Dette profilbildet vil være synlig for enhver besøkende, og kan bli gjort tilgjengelig på andre nettsteder.";
-App::$strings["This profile photo will be visible only to channels with permission to view this profile."] = "";
-App::$strings["Use Photo for Profile"] = "";
-App::$strings["Change Profile Photo"] = "Endre profilbilde";
-App::$strings["Reset to default"] = "Sett tilbake til standard";
-App::$strings["Select existing"] = "Velg eksisterende bilde";
-App::$strings["Done editing"] = "Lagre endringer";
-App::$strings["Import Webpage Elements"] = "";
-App::$strings["Import selected"] = "";
-App::$strings["Export Webpage Elements"] = "";
-App::$strings["Export selected"] = "";
-App::$strings["Actions"] = "Handlinger";
-App::$strings["Page Link"] = "Sidelenke";
-App::$strings["Page Title"] = "Sidetittel";
-App::$strings["Invalid file type."] = "";
-App::$strings["Error opening zip file"] = "";
-App::$strings["Invalid folder path."] = "";
-App::$strings["No webpage elements detected."] = "";
-App::$strings["Import complete."] = "";
-App::$strings["Active"] = "Aktiv";
-App::$strings["Blocked"] = "Blokkert";
-App::$strings["Ignored"] = "Ignorert";
-App::$strings["Hidden"] = "Skjult";
-App::$strings["Archived/Unreachable"] = "Arkivert/utilgjengelig";
-App::$strings["Active Connections"] = "Aktive forbindelser";
-App::$strings["Show active connections"] = "";
-App::$strings["New Connections"] = "Nye forbindelser";
-App::$strings["Show pending (new) connections"] = "Vis ventende (nye) forbindelser";
-App::$strings["Only show blocked connections"] = "Vis bare forbindelser som er blokkert";
-App::$strings["Only show ignored connections"] = "Vis bare ignorerte forbindelser";
-App::$strings["Only show archived/unreachable connections"] = "";
-App::$strings["Only show hidden connections"] = "Vis bare skjulte forbindelser";
-App::$strings["Show all connections"] = "Vis alle forbindelser";
-App::$strings["Pending approval"] = "Venter på godkjenning";
-App::$strings["Archived"] = "Arkivert";
-App::$strings["Not connected at this location"] = "";
-App::$strings["%1\$s [%2\$s]"] = "%1\$s [%2\$s]";
-App::$strings["Edit connection"] = "Endre forbindelse";
-App::$strings["Delete connection"] = "Slett forbindelse";
-App::$strings["Channel address"] = "Kanaladresse";
-App::$strings["Call"] = "";
-App::$strings["Status"] = "Status";
-App::$strings["Connected"] = "Forbundet";
-App::$strings["Approve connection"] = "Godkjenn forbindelse";
-App::$strings["Ignore connection"] = "Ignorer forbindelse";
-App::$strings["Recent activity"] = "Nylig aktivitet";
-App::$strings["Connect at this location"] = "";
-App::$strings["You have %1$.0f of %2$.0f allowed connections."] = "Du har %1$.0f av %2$.0f tillate forbindelser.";
-App::$strings["Search your connections"] = "Søk blant dine forbindelser";
-App::$strings["Contact search"] = "";
-App::$strings["This is a group/forum channel"] = "";
-App::$strings["Continue"] = "Fortsett";
-App::$strings["Premium Channel Setup"] = "Premiumkanal-oppsett";
-App::$strings["Enable premium channel connection restrictions"] = "Slå på restriksjoner for forbindelse med premiumkanal";
-App::$strings["Please enter your restrictions or conditions, such as paypal receipt, usage guidelines, etc."] = "Vennligst skriv dine restriksjoner og betingelser, slik som PayPal-kvittering, retningslinjer for bruk, og så videre.";
-App::$strings["This channel may require additional steps or acknowledgement of the following conditions prior to connecting:"] = "Denne kanalen kan kreve ytterligere steg og bekreftelse av følgende betingelser før tilkobling:";
-App::$strings["Potential connections will then see the following text before proceeding:"] = "Potensielle forbindelser vil da se følgende tekst før de går videre:";
-App::$strings["By continuing, I certify that I have complied with any instructions provided on this page."] = "Ved å fortsette bekrefter jeg at jeg har oppfylt alle instruksjoner gitt på denne siden.";
-App::$strings["(No specific instructions have been provided by the channel owner.)"] = "(Ingen spesifikke instruksjoner er gitt av kanaleieren.)";
-App::$strings["Restricted or Premium Channel"] = "Begrenset kanal eller premiumkanal";
-App::$strings["Affinity Tool settings updated."] = "";
-App::$strings["The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage."] = "";
-App::$strings["Default maximum affinity level"] = "";
-App::$strings["0-99 default 99"] = "Standard";
-App::$strings["Default minimum affinity level"] = "";
-App::$strings["0-99 - default 0"] = "Standard";
-App::$strings["Persistent affinity levels"] = "";
-App::$strings["If disabled the max and min levels will be reset to default after page reload"] = "";
-App::$strings["Affinity Tool Settings"] = "";
-App::$strings["About this site"] = "Om dette nettstedet ";
-App::$strings["Site Name"] = "Nettstedets navn";
-App::$strings["Site Information"] = "Nettstedsinformasjon";
-App::$strings["Administrator"] = "Administrator";
-App::$strings["Software and Project information"] = "Program- og prosjektinformasjon";
-App::$strings["This site is powered by \$Projectname"] = "Dette nettstedet drives av \$Projectname";
-App::$strings["Federated and decentralised networking and identity services provided by"] = "Fødererte og desentraliserte nettverks- og identitetstjenester via Zot";
-App::$strings["Additional federated transport protocols:"] = "Øvrige fødererte transportprotokoller:";
-App::$strings["Version %s"] = "Versjon %s";
-App::$strings["Project homepage"] = "Prosjektets hjemmeside";
-App::$strings["Developer homepage"] = "Utviklers hjemmeside";
-App::$strings["Active addons"] = "Aktive tillegg";
-App::$strings["Blocked sites"] = "Byggeklossens tittel";
-App::$strings["Item sync completed!"] = "";
-App::$strings["Import host does not seem to be online or compatible"] = "";
-App::$strings["Item sync completed but no items were found!"] = "";
-App::$strings["File sync completed!"] = "";
-App::$strings["File sync completed but no files were found!"] = "";
-App::$strings["Channel clone status"] = "";
-App::$strings["Item sync status"] = "";
-App::$strings["File sync status"] = "";
-App::$strings["Channel cloning completed!"] = "";
-App::$strings["Resume"] = "";
-App::$strings["Only resume if sync stalled!"] = "";
-App::$strings["This site is not a directory server"] = "Dette nettstedet er ikke en katalogtjener";
-App::$strings["This directory server requires an access token"] = "Denne katalogtjeneren krever en tilgangsnøkkel (access token)";
-App::$strings["Deprecated!"] = "";
-App::$strings["File not found."] = "Filen ble ikke funnet.";
-App::$strings["Permission Denied."] = "Tillatelse avvist.";
-App::$strings["Edit file permissions"] = "Endre filtillatelser";
-App::$strings["Set/edit permissions"] = "Angi/endre tillatelser";
-App::$strings["Include all files and sub folders"] = "Inkluder alle filer og undermapper";
-App::$strings["Return to file list"] = "Gå tilbake til filoversikten";
-App::$strings["Share this file"] = "Del denne filen";
-App::$strings["Show URL to this file"] = "Vis URLen til denne filen";
-App::$strings["Layout not found"] = "Layouten ble ikke funnet.";
-App::$strings["This template does not support pdledi_gui (no content regions defined)"] = "";
-App::$strings["Main page content"] = "";
-App::$strings["The main page content can not be edited!"] = "";
-App::$strings["Layout updated."] = "Layout er oppdatert.";
-App::$strings["Edit System Page Description"] = "Endre beskrivelsen av systemsiden";
-App::$strings["(modified)"] = "(endret)";
-App::$strings["Layout not found."] = "Layouten ble ikke funnet.";
-App::$strings["Module Name:"] = "Modulnavn:";
-App::$strings["Layout Help"] = "Layout-hjelp";
-App::$strings["Edit another layout"] = "";
-App::$strings["System layout"] = "";
-App::$strings["Item approved"] = "Konto godkjent.";
-App::$strings["Account removals are not allowed within 48 hours of changing the account password."] = "Sletting av kontoer er ikke tillatt innen 48 timer etter endring av kontopassordet.";
-App::$strings["Remove This Account"] = "Slett denne kontoen";
-App::$strings["WARNING: "] = "ADVARSEL: ";
-App::$strings["This account and all its channels will be completely removed from the network. "] = "Denne kontoen og alle dens kanaler vil bli fullstendig fjernet fra nettverket. ";
-App::$strings["This action is permanent and can not be undone!"] = "Denne handlingen er permanent og kan ikke angres!";
-App::$strings["Please enter your password for verification:"] = "Vennligst skriv ditt passord for å få bekreftelse:";
-App::$strings["Remove Account"] = "Slett konto";
-App::$strings["Please login."] = "Vennligst logg inn.";
+App::$strings["Public Hubs"] = "Offentlige huber";
+App::$strings["The listed hubs allow public registration for the \$Projectname network. All hubs in the network are interlinked so membership on any of them conveys membership in the network as a whole. Some hubs may require subscription or provide tiered service plans. The hub itself <strong>may</strong> provide additional details."] = "Nettstedene på listen tillater offentlig registrering i \$Projectname-nettverket. Alle nettsteder i nettverket er forbundet så medlemskap på enhver av dem formidler medlemskap i nettverket som helhet. Noen nettsteder kan kreve abonnement eller tilby lagdelte tjenesteavtaler. Nettstedene selv <strong>kan</strong> gi tilleggsopplysninger.";
+App::$strings["Hub URL"] = "Nettstedets URL";
+App::$strings["Access Type"] = "Tilgangstype";
+App::$strings["Registration Policy"] = "Retningslinjer for registrering";
+App::$strings["Stats"] = "Statistikk";
+App::$strings["Software"] = "Programvare";
+App::$strings["Ratings"] = "Vurderinger";
+App::$strings["Rate"] = "Vurder";
+App::$strings["Channel name changes are not allowed within 48 hours of changing the account password."] = "";
+App::$strings["Change channel nickname/address"] = "";
+App::$strings["Any/all connections on other networks will be lost!"] = "";
+App::$strings["New channel address"] = "";
+App::$strings["Rename Channel"] = "";
+App::$strings["Unable to update menu."] = "Ikke i stand til å oppdatere meny.";
+App::$strings["Unable to create menu."] = "Ikke i stand til å lage meny.";
+App::$strings["Menu Name"] = "Menynavn";
+App::$strings["Unique name (not visible on webpage) - required"] = "Unikt navn (ikke synlig på websiden) - påkrevet";
+App::$strings["Menu Title"] = "Menytittel";
+App::$strings["Visible on webpage - leave empty for no title"] = "Synlig på websiden - la stå tomt for ingen tittel";
+App::$strings["Allow Bookmarks"] = "Tillat bokmerker";
+App::$strings["Menu may be used to store saved bookmarks"] = "Menyen kan brukes til å lagre lagrede bokmerker";
+App::$strings["Submit and proceed"] = "Send inn og fortsett";
+App::$strings["Bookmarks allowed"] = "Bokmerker tillatt";
+App::$strings["Delete this menu"] = "Slett denne menyen";
+App::$strings["Edit menu contents"] = "Endre menyinnholdet";
+App::$strings["Edit this menu"] = "Endre denne menyen";
+App::$strings["Menu could not be deleted."] = "Menyen kunne ikke bli slettet.";
+App::$strings["Menu not found."] = "Menyen ble ikke funnet.";
+App::$strings["Edit Menu"] = "Endre meny";
+App::$strings["Add or remove entries to this menu"] = "Legg til eller fjern punkter i denne menyen";
+App::$strings["Menu name"] = "Menynavn";
+App::$strings["Must be unique, only seen by you"] = "Må være unik, ses bare av deg";
+App::$strings["Menu title"] = "Menytittel";
+App::$strings["Menu title as seen by others"] = "Menytittelen andre ser";
+App::$strings["Allow bookmarks"] = "Tillat bokmerker";
+App::$strings["Not found."] = "Ikke funnet.";
+App::$strings["Account not found"] = "Kontoen ble ikke funnet";
+App::$strings["Account '%s' blocked"] = "Kontoen '%s' blokkert";
+App::$strings["Account '%s' unblocked"] = "Kontoen '%s' er ikke blokkert lenger";
+App::$strings["Unverified"] = "";
+App::$strings["Expired"] = "";
+App::$strings["Administration"] = "Administrasjon";
+App::$strings["Show verified registrations"] = "";
+App::$strings["Show all registrations"] = "";
+App::$strings["Select toggle"] = "";
+App::$strings["Deny selected"] = "";
+App::$strings["Approve selected"] = "";
+App::$strings["All registrations"] = "";
+App::$strings["Verified registrations waiting for approval"] = "";
+App::$strings["Request date"] = "Dato for forespørsel";
+App::$strings["Requests"] = "";
+App::$strings["No registrations available"] = "";
+App::$strings["No verified registrations available"] = "";
+App::$strings["Verified"] = "";
+App::$strings["Not yet verified"] = "";
+App::$strings["ID"] = "ID";
+App::$strings["All channels"] = "";
+App::$strings["Register date"] = "Registreringsdato";
+App::$strings["Last login"] = "Siste innlogging";
+App::$strings["Expires"] = "Utløper";
+App::$strings["Service class"] = "";
+App::$strings["Selected accounts will be deleted!\\n\\nEverything these accounts had posted on this site will be permanently deleted!\\n\\nAre you sure?"] = "Valgte kontoer vil bli slettet!\\n\\nAlt disse kontoene har lagt inn på dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette disse valgte kontoene?";
+App::$strings["The account {0} will be deleted!\\n\\nEverything this account has posted on this site will be permanently deleted!\\n\\nAre you sure?"] = "Kontoen {0} vl bli slettet!\\n\\nAlt denne kontoen har lagt inn på dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette denne kontoen?";
+App::$strings["Message"] = "Melding";
+App::$strings["%s account blocked/unblocked"] = array(
+ 0 => "%s konto blokkert/ikke blokkert lenger",
+ 1 => "%s kontoer blokkert/ikke blokkert lenger",
+);
+App::$strings["%s account deleted"] = array(
+ 0 => "%s konto slettet",
+ 1 => "%s kontoer slettet",
+);
+App::$strings["Max queueworker threads"] = "";
+App::$strings["Minimum 4, default 4"] = "";
+App::$strings["Assume workers dead after"] = "";
+App::$strings["Minimum 120, default 300 seconds"] = "";
+App::$strings["Pause before starting next task"] = "";
+App::$strings["Minimum 100, default 100 microseconds"] = "";
+App::$strings["Automatically adjust pause before starting next task"] = "";
+App::$strings["Queueworker Settings"] = "";
+App::$strings["Theme settings updated."] = "Temainnstillinger er oppdatert.";
+App::$strings["No themes found."] = "Ingen temaer er funnet.";
+App::$strings["Disable"] = "Skru av";
+App::$strings["Enable"] = "Skru på";
+App::$strings["Screenshot"] = "Skjermbilde";
+App::$strings["Toggle"] = "Skru av og på";
+App::$strings["Author: "] = "Forfatter: ";
+App::$strings["Maintainer: "] = "Vedlikeholder: ";
+App::$strings["[Experimental]"] = "[Eksperimentelt]";
+App::$strings["[Unsupported]"] = "[Ingen støtte]";
+App::$strings["Plugin %s disabled."] = "Tilleggsfunksjonen %s er avskrudd.";
+App::$strings["Plugin %s enabled."] = "Tilleggsfunksjonen %s er påskrudd.";
+App::$strings["Minimum project version: "] = "Minimum prosjektversjon: ";
+App::$strings["Maximum project version: "] = "Maksimum prosjektversjon: ";
+App::$strings["Minimum PHP version: "] = "Minimum PHP-versjon: ";
+App::$strings["Compatible Server Roles: "] = "";
+App::$strings["Requires: "] = "Krever: ";
+App::$strings["Disabled - version incompatibility"] = "Skrudd av - versjonsinkompatibilitet";
+App::$strings["Lock feature %s"] = "Lås funksjon %s";
+App::$strings["Manage Additional Features"] = "Håndter tilleggsfunksjoner";
+App::$strings["New Profile Field"] = "Nytt profilfelt";
+App::$strings["Field nickname"] = "Feltets kallenavn";
+App::$strings["System name of field"] = "Systemnavnet til feltet";
+App::$strings["Input type"] = "Inndata-type";
+App::$strings["Field Name"] = "Feltnavn";
+App::$strings["Label on profile pages"] = "Merkelapp på profilsider";
+App::$strings["Help text"] = "Hjelpetekst";
+App::$strings["Additional info (optional)"] = "Tilleggsinformasjon (valgfritt)";
+App::$strings["Field definition not found"] = "Feltdefinisjonen ble ikke funnet";
+App::$strings["Edit Profile Field"] = "Endre profilfelt";
+App::$strings["Basic Profile Fields"] = "Grunnleggende profilfelter";
+App::$strings["Advanced Profile Fields"] = "Utvidede profilfelter";
+App::$strings["(In addition to basic fields)"] = "(I tillegg til grunnleggende felt)";
+App::$strings["All available fields"] = "Alle tilgjengelige felt";
+App::$strings["Custom Fields"] = "";
+App::$strings["Create Custom Field"] = "Legg til egendefinert felt";
+App::$strings["Channel not found"] = "Kanalen ble ikke funnet";
+App::$strings["%s channel censored/uncensored"] = array(
+ 0 => "%s kanal er sensurert/ikke sensurert lenger",
+ 1 => "%s kanaler er sensurert/ikke sensurert lenger",
+);
+App::$strings["%s channel code allowed/disallowed"] = array(
+ 0 => "%s kanal med kode tillatt/ikke tillatt",
+ 1 => "%s kanaler med kode tillatt/ikke tillatt",
+);
+App::$strings["%s channel deleted"] = array(
+ 0 => "%s kanal slettet",
+ 1 => "%s kanaler slettet",
+);
+App::$strings["Channel '%s' deleted"] = "Kanalen '%s' er slettet";
+App::$strings["Channel '%s' censored"] = "Kanalen '%s' er sensurert";
+App::$strings["Channel '%s' uncensored"] = "Kanalen '%s' er ikke sensurert lenger";
+App::$strings["Channel '%s' code allowed"] = "Kanal '%s' kode tillatt";
+App::$strings["Channel '%s' code disallowed"] = "Kanal '%s' kode ikke tillatt";
+App::$strings["select all"] = "velg alle";
+App::$strings["Censor"] = "Sensurer";
+App::$strings["Uncensor"] = "Ikke sensurer lenger";
+App::$strings["Allow Code"] = "Tillat kode";
+App::$strings["Disallow Code"] = "Ikke tillat kode";
+App::$strings["UID"] = "UID";
+App::$strings["Selected channels will be deleted!\\n\\nEverything that was posted in these channels on this site will be permanently deleted!\\n\\nAre you sure?"] = "Valgte kanaler vil bli slettet!\\n\\nAlt innhold som er lagt inn i disse kanalene på dette nettstedet vil bli slettet for alltid!\\n\\nEr du sikker på at du vil slette disse kanalene med alt innhold?";
+App::$strings["The channel {0} will be deleted!\\n\\nEverything that was posted in this channel on this site will be permanently deleted!\\n\\nAre you sure?"] = "Kanalen {0} vil bli slettet!\\n\\nAlt innhold som er lagt inn i denne kanalen på dettet nettstedet vil bli slettet for alltid!\\n\\nEr du sikker på at du vil slette denne kanalen med alt innhold?";
+App::$strings["Password changed for account %d."] = "";
+App::$strings["Account settings updated."] = "";
+App::$strings["Account Edit"] = "";
+App::$strings["New Password"] = "";
+App::$strings["New Password again"] = "";
+App::$strings["Account language (for emails)"] = "";
+App::$strings["Update has been marked successful"] = "Oppdateringen har blitt merket som en suksess";
+App::$strings["Verification of update %s failed. Check system logs."] = "";
+App::$strings["Update %s was successfully applied."] = "Oppdatering %s ble gjennomført med suksess.";
+App::$strings["Verifying update %s did not return a status. Unknown if it succeeded."] = "";
+App::$strings["Update %s does not contain a verification function."] = "";
+App::$strings["Update function %s could not be found."] = "Oppdatering av funksjon %s kunne ikke finnes.";
+App::$strings["Executing update procedure %s failed. Check system logs."] = "";
+App::$strings["Update %s did not return a status. It cannot be determined if it was successful."] = "";
+App::$strings["Failed Updates"] = "Mislykkede oppdateringer";
+App::$strings["Mark success (if update was manually applied)"] = "Marker suksess (hvis oppdateringen ble gjennomført manuelt)";
+App::$strings["Attempt to verify this update if a verification procedure exists"] = "";
+App::$strings["Attempt to execute this update step automatically"] = "Prøv å gjennomføre dette oppdateringstrinnet automatisk";
+App::$strings["No failed updates."] = "Ingen mislykkede oppdateringer.";
+App::$strings["Log settings updated."] = "Logginnstillinger er oppdatert.";
+App::$strings["Clear"] = "Tøm";
+App::$strings["Debugging"] = "Feilsøking";
+App::$strings["Log file"] = "Loggfil";
+App::$strings["Must be writable by web server. Relative to your top-level webserver directory."] = "";
+App::$strings["Log level"] = "Loggnivå";
+App::$strings["Queue Statistics"] = "Køstatistikk";
+App::$strings["Total Entries"] = "Totalt antall oppføringer";
+App::$strings["Destination URL"] = "Mål-URL";
+App::$strings["Mark hub permanently offline"] = "Merk hub som permanent offline";
+App::$strings["Retry delivery to this hub"] = "";
+App::$strings["Empty queue for this hub"] = "Tøm køen for denne hubben";
+App::$strings["Last known contact"] = "Siste kjente kontakt";
App::$strings["Invalid input"] = "";
App::$strings["Errors"] = "";
App::$strings["Site settings updated."] = "Nettstedsinnstillinger er oppdatert.";
@@ -2055,22 +2528,24 @@ App::$strings["My site has free access only"] = "Mitt nettsted har kun gratis ti
App::$strings["My site offers free accounts with optional paid upgrades"] = "Mitt nettsted tilbyr gratis konto med valgfri oppgradering til betalt tjeneste";
App::$strings["Default permission role for new accounts"] = "";
App::$strings["This role will be used for the first channel created after registration."] = "";
+App::$strings["Minute(s)"] = "";
+App::$strings["Hour(s)"] = "";
+App::$strings["Day(s)"] = "";
App::$strings["Week(s)"] = "";
App::$strings["Month(s)"] = "";
App::$strings["Year(s)"] = "";
App::$strings["Register verification delay"] = "";
App::$strings["Time to wait before a registration can be verified"] = "";
+App::$strings["duration up from now"] = "";
App::$strings["Register verification expiration time"] = "";
App::$strings["Time before an unverified registration will expire"] = "";
-App::$strings["Administration"] = "Administrasjon";
-App::$strings["Site"] = "Nettsted";
App::$strings["File upload"] = "Last opp fil";
App::$strings["Policies"] = "Retningslinjer";
-App::$strings["Site name"] = "Nettstedets navn";
App::$strings["Banner/Logo"] = "Banner/Logo";
App::$strings["Unfiltered HTML/CSS/JS is allowed"] = "";
App::$strings["Administrator Information"] = "Administratorinformasjon";
App::$strings["Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here"] = "Kontaktinformasjon til nettstedsadministratorer. Vises på siteinfo-siden. BBCode kan brukes her";
+App::$strings["Site Information"] = "Nettstedsinformasjon";
App::$strings["Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here"] = "";
App::$strings["System theme"] = "Systemtema";
App::$strings["Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"] = "Standard systemtema - kan overstyres av brukerprofiler - <a href='#' id='cnftheme'>endre temainnstillinger</a>";
@@ -2112,10 +2587,10 @@ App::$strings["Present the site homepage in a frame at the original location ins
App::$strings["Allowed friend domains"] = "Tillatte vennedomener";
App::$strings["Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains"] = "Kommaseparert liste over domener som har lov til å etablere vennskap med dette nettstedet. Jokertegn er akseptert. Tøm for å tillate alle domener";
App::$strings["Force publish"] = "Tving publisering";
-App::$strings["Check to force all profiles on this site to be listed in the site directory"] = "Kryss av for å tvinge alle profiler på dette nettstedet til å bli oppført i nettstedet sin katalog.";
+App::$strings["Check to force all profiles on this site to be listed in the site directory"] = "Kryss av for å tvinge alle profiler på dette nettstedet til å bli oppført i nettstedet sin katalog";
App::$strings["Enable public stream"] = "";
-App::$strings["Enable the public stream. Warning: this content is unmoderated"] = "Importer og gi tilgang til offentlig innhold trukket inn fra andre nettsteder. Advarsel: dette innholdet er ikke moderert.";
-App::$strings["Site only public stream"] = "Mitt nettsted er ikke en offentlig tjeneste";
+App::$strings["Enable the public stream. Warning: this content is unmoderated"] = "Importer og gi tilgang til offentlig innhold trukket inn fra andre nettsteder. Advarsel: dette innholdet er ikke moderert";
+App::$strings["Site only public stream"] = "Offentlig strøm kun fra nettstedet";
App::$strings["Restrict the public stream to content originating at this site"] = "";
App::$strings["Allow anybody on the internet to access the public streams"] = "";
App::$strings["Disable to require authentication before viewing"] = "";
@@ -2156,75 +2631,10 @@ App::$strings["Default: profiles"] = "";
App::$strings["Optional: site location"] = "";
App::$strings["Region or country"] = "";
App::$strings["Invalid 24h time value (hhmm/hmm)"] = "";
-App::$strings["Update has been marked successful"] = "Oppdateringen har blitt merket som en suksess";
-App::$strings["Verification of update %s failed. Check system logs."] = "";
-App::$strings["Update %s was successfully applied."] = "Oppdatering %s ble gjennomført med suksess.";
-App::$strings["Verifying update %s did not return a status. Unknown if it succeeded."] = "";
-App::$strings["Update %s does not contain a verification function."] = "";
-App::$strings["Update function %s could not be found."] = "Oppdatering av funksjon %s kunne ikke finnes.";
-App::$strings["Executing update procedure %s failed. Check system logs."] = "";
-App::$strings["Update %s did not return a status. It cannot be determined if it was successful."] = "";
-App::$strings["Failed Updates"] = "Mislykkede oppdateringer";
-App::$strings["Mark success (if update was manually applied)"] = "Marker suksess (hvis oppdateringen ble gjennomført manuelt)";
-App::$strings["Attempt to verify this update if a verification procedure exists"] = "";
-App::$strings["Attempt to execute this update step automatically"] = "Prøv å gjennomføre dette oppdateringstrinnet automatisk";
-App::$strings["No failed updates."] = "Ingen mislykkede oppdateringer.";
-App::$strings["%s account blocked/unblocked"] = array(
- 0 => "%s konto blokkert/ikke blokkert lenger",
- 1 => "%s kontoer blokkert/ikke blokkert lenger",
-);
-App::$strings["%s account deleted"] = array(
- 0 => "%s konto slettet",
- 1 => "%s kontoer slettet",
-);
-App::$strings["Account not found"] = "Kontoen ble ikke funnet";
-App::$strings["Account '%s' blocked"] = "Kontoen '%s' blokkert";
-App::$strings["Account '%s' unblocked"] = "Kontoen '%s' er ikke blokkert lenger";
-App::$strings["Unverified"] = "";
-App::$strings["Expired"] = "";
-App::$strings["Accounts"] = "Kontoer";
-App::$strings["Show verified registrations"] = "";
-App::$strings["Show all registrations"] = "";
-App::$strings["Select toggle"] = "";
-App::$strings["Deny selected"] = "";
-App::$strings["Approve selected"] = "";
-App::$strings["All registrations"] = "";
-App::$strings["Verified registrations waiting for approval"] = "";
-App::$strings["Request date"] = "Dato for forespørsel";
-App::$strings["Requests"] = "";
-App::$strings["No registrations available"] = "";
-App::$strings["No verified registrations available"] = "";
-App::$strings["Deny"] = "Avslå";
-App::$strings["Verified"] = "";
-App::$strings["Not yet verified"] = "";
-App::$strings["ID"] = "ID";
-App::$strings["All channels"] = "";
-App::$strings["Register date"] = "Registreringsdato";
-App::$strings["Last login"] = "Siste innlogging";
-App::$strings["Expires"] = "Utløper";
-App::$strings["Service class"] = "";
-App::$strings["Selected accounts will be deleted!\\n\\nEverything these accounts had posted on this site will be permanently deleted!\\n\\nAre you sure?"] = "Valgte kontoer vil bli slettet!\\n\\nAlt disse kontoene har lagt inn på dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette disse valgte kontoene?";
-App::$strings["The account {0} will be deleted!\\n\\nEverything this account has posted on this site will be permanently deleted!\\n\\nAre you sure?"] = "Kontoen {0} vl bli slettet!\\n\\nAlt denne kontoen har lagt inn på dette nettstedet vil bli slettet permanent!\\n\\nEr du sikker på at du vil slette denne kontoen?";
-App::$strings["Message"] = "Melding";
-App::$strings["Max queueworker threads"] = "";
-App::$strings["Minimum 4, default 4"] = "";
-App::$strings["Assume workers dead after"] = "";
-App::$strings["Minimum 120, default 300 seconds"] = "";
-App::$strings["Pause before starting next task"] = "";
-App::$strings["Minimum 100, default 100 microseconds"] = "";
-App::$strings["Automatically adjust pause before starting next task"] = "";
-App::$strings["Queueworker Settings"] = "";
-App::$strings["Password changed for account %d."] = "";
-App::$strings["Account settings updated."] = "";
-App::$strings["Account Edit"] = "";
-App::$strings["New Password"] = "";
-App::$strings["New Password again"] = "";
-App::$strings["Account language (for emails)"] = "";
App::$strings["By default, unfiltered HTML is allowed in embedded media. This is inherently insecure."] = "Ufiltrert HTML er i utgangspunktet tillatt i innebygde media. Dette er en sikkerhetsrisiko.";
App::$strings["The recommended setting is to only allow unfiltered HTML from the following sites:"] = "Det anbefales at man kun tillater ufiltrert HTML fra følgende nettsteder:";
App::$strings["https://youtube.com/<br />https://www.youtube.com/<br />https://youtu.be/<br />https://vimeo.com/<br />https://soundcloud.com/<br />"] = "https://youtube.com/<br />https://www.youtube.com/<br />https://youtu.be/<br />https://vimeo.com/<br />https://soundcloud.com/<br />";
App::$strings["All other embedded content will be filtered, <strong>unless</strong> embedded content from that site is explicitly blocked."] = "Alt annet innebygget innhold vil bli filtrert, <strong>med mindre</strong> innebygget innhold fra den aktuelle siden eksplisitt er blokkert.";
-App::$strings["Security"] = "Sikkerhet";
App::$strings["Block public"] = "Blokker offentlig tilgang";
App::$strings["Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated."] = "Kryss av for å blokkere tilgang til alle personlige sider som ellers ville vært offentlig tilgjengelige på dette nettstedet med mindre du er logget inn.";
App::$strings["Provide a cloud root directory"] = "";
@@ -2251,117 +2661,171 @@ App::$strings["WARNING: SVG images may contain malicious code."] = "";
App::$strings["Allow embedded (inline) PDF files"] = "";
App::$strings["Additional trusted directory server URLs"] = "Standard katalogtjener";
App::$strings["Accept directory flags (spam, nsfw) from those servers. One per line like https://example.tld"] = "";
-App::$strings["Channel not found"] = "Kanalen ble ikke funnet";
-App::$strings["%s channel censored/uncensored"] = array(
- 0 => "%s kanal er sensurert/ikke sensurert lenger",
- 1 => "%s kanaler er sensurert/ikke sensurert lenger",
-);
-App::$strings["%s channel code allowed/disallowed"] = array(
- 0 => "%s kanal med kode tillatt/ikke tillatt",
- 1 => "%s kanaler med kode tillatt/ikke tillatt",
-);
-App::$strings["%s channel deleted"] = array(
- 0 => "%s kanal slettet",
- 1 => "%s kanaler slettet",
-);
-App::$strings["Channel '%s' deleted"] = "Kanalen '%s' er slettet";
-App::$strings["Channel '%s' censored"] = "Kanalen '%s' er sensurert";
-App::$strings["Channel '%s' uncensored"] = "Kanalen '%s' er ikke sensurert lenger";
-App::$strings["Channel '%s' code allowed"] = "Kanal '%s' kode tillatt";
-App::$strings["Channel '%s' code disallowed"] = "Kanal '%s' kode ikke tillatt";
-App::$strings["select all"] = "velg alle";
-App::$strings["Censor"] = "Sensurer";
-App::$strings["Uncensor"] = "Ikke sensurer lenger";
-App::$strings["Allow Code"] = "Tillat kode";
-App::$strings["Disallow Code"] = "Ikke tillat kode";
-App::$strings["UID"] = "UID";
-App::$strings["Selected channels will be deleted!\\n\\nEverything that was posted in these channels on this site will be permanently deleted!\\n\\nAre you sure?"] = "Valgte kanaler vil bli slettet!\\n\\nAlt innhold som er lagt inn i disse kanalene på dette nettstedet vil bli slettet for alltid!\\n\\nEr du sikker på at du vil slette disse kanalene med alt innhold?";
-App::$strings["The channel {0} will be deleted!\\n\\nEverything that was posted in this channel on this site will be permanently deleted!\\n\\nAre you sure?"] = "Kanalen {0} vil bli slettet!\\n\\nAlt innhold som er lagt inn i denne kanalen på dettet nettstedet vil bli slettet for alltid!\\n\\nEr du sikker på at du vil slette denne kanalen med alt innhold?";
-App::$strings["Lock feature %s"] = "Lås funksjon %s";
-App::$strings["Manage Additional Features"] = "Håndter tilleggsfunksjoner";
-App::$strings["Plugin %s disabled."] = "Tilleggsfunksjonen %s er avskrudd.";
-App::$strings["Plugin %s enabled."] = "Tilleggsfunksjonen %s er påskrudd.";
-App::$strings["Disable"] = "Skru av";
-App::$strings["Enable"] = "Skru på";
-App::$strings["Addons"] = "Tillegg";
-App::$strings["Toggle"] = "Skru av og på";
-App::$strings["Author: "] = "Forfatter: ";
-App::$strings["Maintainer: "] = "Vedlikeholder: ";
-App::$strings["Minimum project version: "] = "Minimum prosjektversjon: ";
-App::$strings["Maximum project version: "] = "Maksimum prosjektversjon: ";
-App::$strings["Minimum PHP version: "] = "Minimum PHP-versjon: ";
-App::$strings["Compatible Server Roles: "] = "";
-App::$strings["Requires: "] = "Krever: ";
-App::$strings["Disabled - version incompatibility"] = "Skrudd av - versjonsinkompatibilitet";
-App::$strings["Enter the public git repository URL of the addon repo."] = "";
-App::$strings["Addon repo git URL"] = "";
-App::$strings["Custom repo name"] = "";
-App::$strings["(optional)"] = "Valgfritt";
-App::$strings["Download Addon Repo"] = "";
-App::$strings["Install new repo"] = "";
-App::$strings["Manage Repos"] = "";
-App::$strings["Installed Addon Repositories"] = "";
-App::$strings["Install a New Addon Repository"] = "";
-App::$strings["Switch branch"] = "";
-App::$strings["Queue Statistics"] = "Køstatistikk";
-App::$strings["Total Entries"] = "Totalt antall oppføringer";
-App::$strings["Priority"] = "Prioritet";
-App::$strings["Destination URL"] = "Mål-URL";
-App::$strings["Mark hub permanently offline"] = "Merk hub som permanent offline";
-App::$strings["Retry delivery to this hub"] = "";
-App::$strings["Empty queue for this hub"] = "Tøm køen for denne hubben";
-App::$strings["Last known contact"] = "Siste kjente kontakt";
-App::$strings["New Profile Field"] = "Nytt profilfelt";
-App::$strings["Field nickname"] = "Feltets kallenavn";
-App::$strings["System name of field"] = "Systemnavnet til feltet";
-App::$strings["Input type"] = "Inndata-type";
-App::$strings["Field Name"] = "Feltnavn";
-App::$strings["Label on profile pages"] = "Merkelapp på profilsider";
-App::$strings["Help text"] = "Hjelpetekst";
-App::$strings["Additional info (optional)"] = "Tilleggsinformasjon (valgfritt)";
-App::$strings["Field definition not found"] = "Feltdefinisjonen ble ikke funnet";
-App::$strings["Edit Profile Field"] = "Endre profilfelt";
-App::$strings["Profile Fields"] = "Profilfelter";
-App::$strings["Basic Profile Fields"] = "Grunnleggende profilfelter";
-App::$strings["Advanced Profile Fields"] = "Utvidede profilfelter";
-App::$strings["(In addition to basic fields)"] = "(I tillegg til grunnleggende felt)";
-App::$strings["All available fields"] = "Alle tilgjengelige felt";
-App::$strings["Custom Fields"] = "";
-App::$strings["Create Custom Field"] = "Legg til egendefinert felt";
-App::$strings["Theme settings updated."] = "Temainnstillinger er oppdatert.";
-App::$strings["No themes found."] = "Ingen temaer er funnet.";
-App::$strings["Screenshot"] = "Skjermbilde";
-App::$strings["Themes"] = "Utseende";
-App::$strings["[Experimental]"] = "[Eksperimentelt]";
-App::$strings["[Unsupported]"] = "[Ingen støtte]";
-App::$strings["Log settings updated."] = "Logginnstillinger er oppdatert.";
-App::$strings["Logs"] = "Logger";
-App::$strings["Clear"] = "Tøm";
-App::$strings["Debugging"] = "Feilsøking";
-App::$strings["Log file"] = "Loggfil";
-App::$strings["Must be writable by web server. Relative to your top-level webserver directory."] = "";
-App::$strings["Log level"] = "Loggnivå";
-App::$strings["Can not copy folder into itself."] = "";
-App::$strings["Can not move folder \"%s\" into itself."] = "";
-App::$strings["Unable to locate original post."] = "Ikke i stand til å finne opprinnelig innlegg.";
-App::$strings["status"] = "status";
+App::$strings["You have created %1$.0f of %2$.0f allowed channels."] = "Du har laget %1$.0f av %2$.0f tillatte kanaler.";
+App::$strings["Create a new channel"] = "Lag en ny kanal";
+App::$strings["Current Channel"] = "Gjeldende kanal";
+App::$strings["Switch to one of your channels by selecting it."] = "Bytt til en av dine kanaler ved å velge den.";
+App::$strings["Default Channel"] = "Standardkanal";
+App::$strings["Make Default"] = "Gjør til standard";
+App::$strings["%d new introductions"] = "%d nye introduksjoner";
+App::$strings["Delegated Channel"] = "Delegert kanal";
+App::$strings["Token verification failed."] = "";
+App::$strings["Email verification resent"] = "";
+App::$strings["Unable to resend email verification message."] = "";
+App::$strings["Invalid message"] = "Ugyldig melding";
+App::$strings["no results"] = "ingen resultater";
+App::$strings["channel sync processed"] = "kanalsynkronisering er behandlet";
+App::$strings["queued"] = "lagt i kø";
+App::$strings["posted"] = "lagt inn";
+App::$strings["accepted for delivery"] = "akseptert for levering";
+App::$strings["updated"] = "oppdatert";
+App::$strings["update ignored"] = "oppdatering ignorert";
+App::$strings["permission denied"] = "tillatelse avvist";
+App::$strings["recipient not found"] = "mottaker ble ikke funnet";
+App::$strings["Delivery report for %1\$s"] = "Leveringsrapport for %1\$s";
+App::$strings["Redeliver"] = "";
+App::$strings["Away"] = "Borte";
+App::$strings["Online"] = "Online";
App::$strings["%1\$s is following %2\$s's %3\$s"] = "%1\$s følger %2\$s sin %3\$s";
App::$strings["%1\$s stopped following %2\$s's %3\$s"] = "%1\$s stopped å følge %2\$s sin %3\$s";
+App::$strings["Permissions denied."] = "Tillatelse avvist.";
+App::$strings["Link to source"] = "Lenke til kilde";
+App::$strings["Previous"] = "Forrige";
+App::$strings["Next"] = "Neste";
+App::$strings["Today"] = "Idag";
+App::$strings["Bookmark added"] = "Bokmerke lagt til";
+App::$strings["My Connections Bookmarks"] = "Mine forbindelsers bokmerker";
+App::$strings["Affinity Tool settings updated."] = "";
+App::$strings["The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage."] = "";
+App::$strings["Default maximum affinity level"] = "";
+App::$strings["0-99 default 99"] = "Standard";
+App::$strings["Default minimum affinity level"] = "";
+App::$strings["0-99 - default 0"] = "Standard";
+App::$strings["Persistent affinity levels"] = "";
+App::$strings["If disabled the max and min levels will be reset to default after page reload"] = "";
+App::$strings["Affinity Tool Settings"] = "";
+App::$strings["Blocked accounts"] = "";
+App::$strings["Expired accounts"] = "";
+App::$strings["Expiring accounts"] = "";
+App::$strings["Message queues"] = "Meldingskøer";
+App::$strings["Your software should be updated"] = "Programvaren bør oppdateres";
+App::$strings["Summary"] = "Sammendrag";
+App::$strings["Registered accounts"] = "Registrerte kontoer";
+App::$strings["Pending registrations"] = "Ventende registreringer";
+App::$strings["Registered channels"] = "Registrerte kanaler";
+App::$strings["Active addons"] = "Aktive tillegg";
+App::$strings["Version"] = "Versjon";
+App::$strings["Repository version (master)"] = "";
+App::$strings["Repository version (dev)"] = "";
+App::$strings["Contact role deleted."] = "Kontaktrollen ble slettet.";
+App::$strings["Permission category name is required."] = "";
+App::$strings["Contact role saved."] = "Kontaktrollen ble lagret.";
+App::$strings["Role to assign affected contacts and default role to"] = "Ny rolle for berørte forbindelser og standardrolle";
+App::$strings["Role to assign affected contacts to"] = "Ny rolle for berørte forbindelser";
+App::$strings["Assign this role to"] = "Bruk denne rollen for";
+App::$strings["All my contacts"] = "Alle mine forbindelser";
+App::$strings["Automatically assign this role to new contacts"] = "Bruk denne rollen for nye kontakter automatisk";
+App::$strings["Role name"] = "Navn";
+App::$strings["System role - not editable"] = "Systemrolle - kan ikke redigeres";
+App::$strings["Deleting"] = "Sletter kanalrolle";
+App::$strings["Role Permissions"] = "Tillatelser";
+App::$strings["Some permissions may be inherited from your <a href=\"settings\">channel role</a>, which have higher priority than contact role settings."] = "Noen tillatelser kan være arvet fra din <a href=\"settings\">kanalrole</a>, som vil ha høyere prioritet enn innstillingene for kontaktrollen.";
+App::$strings["Can not copy folder into itself."] = "Kan ikke kopiere mappe inn i seg selv.";
+App::$strings["Can not move folder \"%s\" into itself."] = "";
+App::$strings["Xchan Lookup"] = "Xchan oppslag";
+App::$strings["Lookup xchan beginning with (or webbie): "] = "Slå opp xchan som begynner med (eller webbie): ";
+App::$strings["Privacy group created."] = "Personverngruppen er opprettet.";
+App::$strings["Could not create privacy group."] = "Kunne ikke opprette personverngruppen.";
+App::$strings["Privacy group updated."] = "Personverngruppen er oppdatert.";
+App::$strings["Post to this group by default"] = "";
+App::$strings["Add new contacts to this group by default"] = "";
+App::$strings["Privacy group name"] = "Personverngruppens navn";
+App::$strings["Members are visible to other channels"] = "Medlemmer er synlig for andre kanaler";
+App::$strings["Privacy group removed."] = "Personverngruppen er fjernet.";
+App::$strings["Unable to remove privacy group."] = "Ikke i stand til å fjerne personverngruppen.";
+App::$strings["Privacy Group: %s"] = "Personverngruppe: %s";
+App::$strings["Privacy group name: "] = "Personverngruppens navn: ";
+App::$strings["Group members"] = "";
+App::$strings["Not in this group"] = "";
+App::$strings["Click a channel to toggle membership"] = "";
+App::$strings["Documentation Search"] = "Søk i dokumentasjon";
+App::$strings["Not Found"] = "Ikke funnet";
+App::$strings["\$Projectname Documentation"] = "\$Projectname dokumentasjon";
+App::$strings["Contents"] = "";
+App::$strings["Members"] = "Medlemmer";
+App::$strings["Administrators"] = "";
+App::$strings["Developers"] = "";
+App::$strings["Tutorials"] = "";
+App::$strings["Help:"] = "Hjelp:";
+App::$strings["Post not found."] = "";
+App::$strings["comment"] = "kommentar";
+App::$strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s merket %3\$s til %2\$s med %4\$s";
+App::$strings["Invite App"] = "";
+App::$strings["Register is closed"] = "";
+App::$strings["Note, the invitation code is valid up to"] = "";
+App::$strings["Too many recipients for one invitation (max %d)"] = "";
+App::$strings["No recipients for this invitation"] = "";
+App::$strings["(%s) : Not a real email address"] = "(%s): Ikke en virkelig epostadresse";
+App::$strings["(%s) : Not allowed email address"] = "(%s): Ikke en tillatt epostadresse";
+App::$strings["(%s) : email address already in use"] = "(%s): epostadressen er allerede i bruk";
+App::$strings["(%s) : Accepted email address"] = "(%s): Godkjent epostadresse";
+App::$strings["To %s : Message delivery success."] = "";
+App::$strings["%1\$d mail(s) sent, %2\$d mail error(s)"] = "";
+App::$strings["Invites not proposed by configuration"] = "";
+App::$strings["Contact the site admin"] = "";
+App::$strings["Invites by users not enabled"] = "";
+App::$strings["You have no more invitations available"] = "Du har ikke flere invitasjoner tilgjengelig";
+App::$strings["Not on xchan"] = "";
+App::$strings["Invitation expires after"] = "";
+App::$strings["Invitation"] = "";
+App::$strings["Send invitations"] = "Send invitasjoner";
+App::$strings["Invitations I am using"] = "Invitasjoner jeg bruker";
+App::$strings["Invitations we are using"] = "";
+App::$strings["§ Note, the email(s) sent will be recorded in the system logs"] = "";
+App::$strings["Enter email addresses, one per line:"] = "Skriv e-postadresser, en per linje:";
+App::$strings["Your message:"] = "Din melding:";
+App::$strings["Invite template"] = "";
+App::$strings["Subject:"] = "Emne:";
+App::$strings["Here you may enter personal notes to the recipient(s)"] = "";
+App::$strings["About this site"] = "Om dette nettstedet ";
+App::$strings["Site Name"] = "Nettstedets navn";
+App::$strings["Administrator"] = "Administrator";
+App::$strings["Software and Project information"] = "Program- og prosjektinformasjon";
+App::$strings["This site is powered by \$Projectname"] = "Dette nettstedet drives av \$Projectname";
+App::$strings["Federated and decentralised networking and identity services provided by"] = "Fødererte og desentraliserte nettverks- og identitetstjenester via Zot";
+App::$strings["Additional federated transport protocols:"] = "Øvrige fødererte transportprotokoller:";
+App::$strings["Version %s"] = "Versjon %s";
+App::$strings["Project homepage"] = "Prosjektets hjemmeside";
+App::$strings["Developer homepage"] = "Utviklers hjemmeside";
+App::$strings["Blocked sites"] = "Byggeklossens tittel";
App::$strings["Item not available."] = "Elementet er ikke tilgjengelig.";
-App::$strings["Channel removals are not allowed within 48 hours of changing the account password."] = "Fjerning av kanaler er ikke tillatt innen 48 timer etter endring av kontopassordet.";
-App::$strings["Remove Channel"] = "Fjern kanal";
-App::$strings["This channel will be permanently removed. "] = "";
-App::$strings["This action can not be undone!"] = "";
-App::$strings["Authentication failed."] = "Autentisering mislyktes.";
-App::$strings["Empty post discarded."] = "Tomt innlegg forkastet.";
-App::$strings["Duplicate post suppressed."] = "Duplikat av innlegg forhindret.";
-App::$strings["System error. Post not saved."] = "Systemfeil. Innlegg ble ikke lagret.";
-App::$strings["Your comment is awaiting approval."] = "";
-App::$strings["Unable to obtain post information from database."] = "Ikke i stand til å få tak i informasjon om innlegg fra databasen.";
-App::$strings["You have reached your limit of %1$.0f top level posts."] = "Du har nådd din grense på %1$.0f startinnlegg.";
-App::$strings["You have reached your limit of %1$.0f webpages."] = "Du har nådd din grense på %1$.0f websider.";
-App::$strings["Article"] = "";
-App::$strings["Item has been removed."] = "";
+App::$strings["No content available for year"] = "";
+App::$strings["Export Channel"] = "Eksporter kanal";
+App::$strings["Export channel"] = "Eksporter kanal";
+App::$strings["This will export your identity and social graph into a file which can be used to import your channel to a new hub."] = "";
+App::$strings["Export content"] = "";
+App::$strings["This will export your posts, direct messages, articles and cards per month stored into a zip file per year. Months with no posts will be dismissed."] = "Dette vil eksportere dine innlegg, direktemeldinger, artikler og kort, en fil for hver måned, pakket i en zip fil for hvert år. Måneder uten innhold blir ignorert.";
+App::$strings["Export wikis"] = "";
+App::$strings["This will export your wikis and wiki pages."] = "";
+App::$strings["Export webpages"] = "";
+App::$strings["This will export your webpages and menus."] = "";
+App::$strings["Export channel calendar"] = "";
+App::$strings["This will export your channel calendar events and associated items. CalDAV calendars are not included."] = "";
+App::$strings["Export chatrooms"] = "";
+App::$strings["This will export your chatrooms. Chat history is dismissed."] = "";
+App::$strings["This export can be imported or restored by visiting <a href=\"%1\$s\">%2\$s</a> on any site containing your channel."] = "";
+App::$strings["Like/Dislike"] = "Liker/Liker ikke";
+App::$strings["This action is restricted to members."] = "Denne handlingen er begrenset til medlemmer.";
+App::$strings["Please <a href=\"rmagic\">login with your \$Projectname ID</a> or <a href=\"register\">register as a new \$Projectname member</a> to continue."] = "Vennligst <a href=\"rmagic\">logg inn med din \$Projectname ID</a> eller <a href=\"register\">registrer deg som et nytt \$Projectname-medlem</a> for å fortsette.";
+App::$strings["Invalid request."] = "Ugyldig forespørsel.";
+App::$strings["thing"] = "ting";
+App::$strings["Channel unavailable."] = "Kanalen er utilgjengelig.";
+App::$strings["Previous action reversed."] = "Forrige handling er omgjort.";
+App::$strings["profile"] = "Profil";
+App::$strings["Action completed."] = "Handling ferdig.";
+App::$strings["Thank you."] = "Tusen takk.";
+App::$strings["Block Name"] = "Byggeklossens navn";
+App::$strings["Block Title"] = "Byggeklossens tittel";
App::$strings["Unable to create element."] = "Klarer ikke å lage element.";
App::$strings["Unable to update menu element."] = "Ikke i stand til å oppdatere menyelement.";
App::$strings["Unable to add menu element."] = "Ikke i stand til å legge til menyelement.";
@@ -2391,54 +2855,465 @@ App::$strings["Menu item deleted."] = "Menyelement slettet.";
App::$strings["Menu item could not be deleted."] = "Menyelement kunne ikke bli slettet.";
App::$strings["Edit Menu Element"] = "Endre menyelement";
App::$strings["Link text"] = "Lenketekst";
-App::$strings["Email Verification Required"] = "";
-App::$strings["A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message."] = "En verifikasjonskode ble sendt til epostadressen din [%s]. Skriv inn koden du mottok her for å fullføre kontoverifikasjonen. Det kan ta noen minutter før du mottar koden. Sjekk også at eposten ikke har havnet i mappen for søppelpost om du ikke ser den etter en stund.";
-App::$strings["Resend Email"] = "";
-App::$strings["Validation token"] = "";
-App::$strings["No content available for year"] = "";
-App::$strings["Export Channel"] = "Eksporter kanal";
-App::$strings["Export channel"] = "Eksporter kanal";
-App::$strings["This will export your identity and social graph into a file which can be used to import your channel to a new hub."] = "";
-App::$strings["Export content"] = "";
-App::$strings["This will export your posts, direct messages, articles and cards per month stored into a zip file per year. Months with no posts will be dismissed."] = "Dette vil eksportere dine innlegg, direktemeldinger, artikler og kort, en fil for hver måned, pakket i en zip fil for hvert år. Måneder uten innhold blir ignorert.";
-App::$strings["Export wikis"] = "";
-App::$strings["This will export your wikis and wiki pages."] = "";
-App::$strings["Export webpages"] = "";
-App::$strings["This will export your webpages and menus."] = "";
-App::$strings["Export channel calendar"] = "";
-App::$strings["This will export your channel calendar events and associated items. CalDAV calendars are not included."] = "";
-App::$strings["Export chatrooms"] = "";
-App::$strings["This will export your chatrooms. Chat history is dismissed."] = "";
-App::$strings["This export can be imported or restored by visiting <a href=\"%1\$s\">%2\$s</a> on any site containing your channel."] = "";
-App::$strings["No more system notifications."] = "Ingen flere systemvarsler.";
-App::$strings["System Notifications"] = "Systemvarsler";
-App::$strings["Mark all seen"] = "Merk alle som sett";
+App::$strings["Entry censored"] = "";
+App::$strings["Entry OK"] = "";
+App::$strings["Change Order of Pinned Navbar Apps"] = "";
+App::$strings["Change Order of App Tray Apps"] = "";
+App::$strings["Use arrows to move the corresponding app left (top) or right (bottom) in the navbar"] = "";
+App::$strings["Use arrows to move the corresponding app up or down in the app tray"] = "";
+App::$strings["Your real name is recommended."] = "";
+App::$strings["Examples: \"Bob Jameson\", \"Lisa and her Horses\", \"Soccer\", \"Aviation Group\""] = "Eksempel: \"Ola Nordmann\", \"Lisa og hestene hennes\", \"Fotball\", \"Sykkelgruppa\"";
+App::$strings["This will be used to create a unique network address (like an email address)."] = "";
+App::$strings["Allowed characters are a-z 0-9, - and _"] = "";
+App::$strings["Channel name"] = "Kanalnavn";
+App::$strings["Channel role"] = "Kanalrolle";
+App::$strings["Create a Channel"] = "";
+App::$strings["A channel is a unique network identity. It can represent a person (social network profile), a forum (group), a business or celebrity page, a newsfeed, and many other things."] = "";
+App::$strings["or <a href=\"import\">import an existing channel</a> from another location."] = "eller <a href=\"import\">importer en eksisterende kanal</a> fra et annet sted.";
+App::$strings["Validate"] = "";
+App::$strings["Album not found."] = "Albumet ble ikke funnet.";
+App::$strings["Delete Album"] = "Slett album";
+App::$strings["Delete Photo"] = "Slett bilde";
+App::$strings["No photos selected"] = "Ingen bilder valgt";
+App::$strings["Access to this item is restricted."] = "Tilgang til dette elementet er begrenset.";
+App::$strings["%1$.2f MB photo storage used."] = "%1$.2f MB lagringsplass til bilder er brukt.";
+App::$strings["%1$.2f MB of %2$.2f MB photo storage used."] = "%1$.2f MB av %2$.2f MB lagringsplass til bilder er brukt.";
+App::$strings["Upload Photos"] = "Last opp bilder";
+App::$strings["Enter an album name"] = "Skriv et albumnavn";
+App::$strings["or select an existing album (doubleclick)"] = "eller velg et eksisterende album (dobbeltklikk)";
+App::$strings["Create a status post for this upload"] = "Lag et statusinnlegg for denne opplastingen";
+App::$strings["Description (optional)"] = "Beskrivelse (valgritt)";
+App::$strings["Show Newest First"] = "Vis nyeste først";
+App::$strings["Show Oldest First"] = "Vis eldste først";
+App::$strings["Add Photos"] = "Legg til bilder";
+App::$strings["Permission denied. Access to this item may be restricted."] = "Tillatelse avvist. Tilgang til dette elementet kan være begrenset.";
+App::$strings["Photo not available"] = "Bilde er utilgjengelig";
+App::$strings["Use as profile photo"] = "Bruk som profilbilde";
+App::$strings["Use as cover photo"] = "Bruk som omslagsbilde";
+App::$strings["Private Photo"] = "Privat bilde";
+App::$strings["View Full Size"] = "Vis i full størrelse";
+App::$strings["Edit photo"] = "Endre bilde";
+App::$strings["Rotate CW (right)"] = "Roter med klokka (mot høyre)";
+App::$strings["Rotate CCW (left)"] = "Roter mot klokka (venstre)";
+App::$strings["Move photo to album"] = "";
+App::$strings["Enter a new album name"] = "Skriv et nytt albumnavn";
+App::$strings["or select an existing one (doubleclick)"] = "eller velg et eksisterende album (dobbeltklikk)";
+App::$strings["Add a Tag"] = "Legg til merkelapp";
+App::$strings["Example: @bob, @Barbara_Jensen, @jim@example.com"] = "Eksempel: @bob, @Barbara_Jensen, @jim@example.com";
+App::$strings["Flag as adult in album view"] = "Flag som voksent i albumvisning";
+App::$strings["I like this (toggle)"] = "Jeg liker dette (skru av og på)";
+App::$strings["I don't like this (toggle)"] = "Jeg liker ikke dette (skru av og på)";
+App::$strings["Photo Tools"] = "Fotoverktøy";
+App::$strings["In This Photo:"] = "I dette bildet:";
+App::$strings["Map"] = "Kart";
+App::$strings["__ctx:noun__ Likes"] = "Liker";
+App::$strings["__ctx:noun__ Dislikes"] = "Liker ikke";
+App::$strings["You must be logged in to see this page."] = "Du må være innloegget for å se denne siden.";
+App::$strings["Posts and comments"] = "Innlegg og kommentarer";
+App::$strings["Only posts"] = "Kun innlegg";
+App::$strings["Insufficient permissions. Request redirected to profile page."] = "Utilstrekkelig tillatelse. Forespørsel omdirigert til profilsiden.";
+App::$strings["vcard"] = "";
+App::$strings["Item approved"] = "Konto godkjent.";
+App::$strings["Failed to create source. No channel selected."] = "Mislyktes med å lage kilde. Ingen kanal er valgt.";
+App::$strings["Source created."] = "Kilden er laget.";
+App::$strings["Source updated."] = "Kilden er oppdatert.";
+App::$strings["*"] = "*";
+App::$strings["Manage remote sources of content for your channel."] = "Håndtere eksterne innholdskilder til din kanal.";
+App::$strings["New Source"] = "Ny kilde";
+App::$strings["Import all or selected content from the following channel into this channel and distribute it according to your channel settings."] = "Importer alt eller et utvalgt av innhold fra følgende kanal inn i denne kanalen og distribuer det i henhold til dine egne kanalinnstillinger.";
+App::$strings["Only import content with these words (one per line)"] = "Bare importer innhold med disse ordene (ett ord per linje)";
+App::$strings["Leave blank to import all public content"] = "La stå tomt for å importere alt offentlig innhold";
+App::$strings["Channel Name"] = "Kanalnavn";
+App::$strings["Add the following categories to posts imported from this source (comma separated)"] = "";
+App::$strings["Resend posts with this channel as author"] = "";
+App::$strings["Copyrights may apply"] = "";
+App::$strings["Source not found."] = "Kilden ble ikke funnet.";
+App::$strings["Edit Source"] = "Endre kilde";
+App::$strings["Delete Source"] = "Slett kilde";
+App::$strings["Source removed"] = "Kilden er fjernet";
+App::$strings["Unable to remove source."] = "Ikke i stand til å fjerne kilde.";
+App::$strings["Profile not found."] = "Profilen ble ikke funnet.";
+App::$strings["Profile deleted."] = "Profilen er slettet.";
+App::$strings["Profile-"] = "Profil-";
+App::$strings["New profile created."] = "Ny profil opprettet.";
+App::$strings["Profile unavailable to clone."] = "Profilen er utilgjengelig for klonen.";
+App::$strings["Profile unavailable to export."] = "Profilen er utilgjengelig for eksport.";
+App::$strings["Profile Name is required."] = "Profilnavn er påkrevd.";
+App::$strings["Marital Status"] = "Sivilstand";
+App::$strings["Romantic Partner"] = "Romantisk partner";
+App::$strings["Likes"] = "Liker";
+App::$strings["Dislikes"] = "Liker ikke";
+App::$strings["Work/Employment"] = "Arbeid/sysselsetting";
+App::$strings["Religion"] = "Religion";
+App::$strings["Political Views"] = "Politiske synspunkter";
+App::$strings["Sexual Preference"] = "Seksuelle preferanser";
+App::$strings["Homepage"] = "Hjemmeside";
+App::$strings["Interests"] = "Interesser";
+App::$strings["Profile updated."] = "Profilen er oppdatert.";
+App::$strings["Hide my connections from viewers of this profile"] = "Skjul mine forbindelser fra besøkende som ser denne profilen";
+App::$strings["Publish my default profile in the network directory"] = "La standardprofilen min vises i nettverkskatalogen";
+App::$strings["Suggest me as a potential contact to new members"] = "Foreslå meg som mulig kontakt til nye medlemmer";
+App::$strings["Reveal my online status"] = "La andre se om jeg er pålogget eller ikke";
+App::$strings["Edit Profile Details"] = "Endre profildetaljer";
+App::$strings["View this profile"] = "Vis denne profilen";
+App::$strings["Profile Tools"] = "Profilverktøy";
+App::$strings["Change cover photo"] = "Endre omslagsbilde";
+App::$strings["Create a new profile using these settings"] = "Lag en ny profil ved å bruke disse innstillingene";
+App::$strings["Clone this profile"] = "Klon denne profilen";
+App::$strings["Delete this profile"] = "Slett denne profilen";
+App::$strings["Add profile things"] = "Legg til profilting";
+App::$strings["Basic"] = "Grunnleggende";
+App::$strings["Relationship"] = "Forhold";
+App::$strings["Import profile from file"] = "Importer profil fra fil";
+App::$strings["Export profile to file"] = "Eksporter profil til fil";
+App::$strings["Your gender"] = "Kjønn";
+App::$strings["Marital status"] = "Sivilstatus";
+App::$strings["Sexual preference"] = "Legning";
+App::$strings["Profile name"] = "Profilnavn";
+App::$strings["This is your default profile."] = "Dette er din standardprofil.";
+App::$strings["Your full name"] = "Fullt navn";
+App::$strings["Short title/description"] = "Kort tittel/beskrivelse";
+App::$strings["Maximal 190 characters"] = "Maksimum 190 tegn";
+App::$strings["Street address"] = "Gateadresse";
+App::$strings["Locality/City"] = "Sted/by";
+App::$strings["Region/State"] = "Region";
+App::$strings["Postal/Zip code"] = "Postnummer";
+App::$strings["Who (if applicable)"] = "Hvem (om relevant)";
+App::$strings["Examples: cathy123, Cathy Williams, cathy@example.com"] = "Eksempler: kari123, Kari Villiamsen, kari@example.com";
+App::$strings["Since (date)"] = "Fra (dato)";
+App::$strings["Tell us about yourself"] = "Fortell oss om deg selv";
+App::$strings["Hometown"] = "Hjemsted";
+App::$strings["Political views"] = "Politiske holdninger";
+App::$strings["Religious views"] = "Religiøse holdninger";
+App::$strings["Keywords used in directory listings"] = "Nøkkelord for bruk i katalogoppføringen";
+App::$strings["Example: fishing photography software"] = "Eksempel: fisking fotografering programvare";
+App::$strings["Musical interests"] = "Musikkinteresser";
+App::$strings["Books, literature"] = "Bøker, litteratur";
+App::$strings["Television"] = "TV/fjernsyn";
+App::$strings["Film/Dance/Culture/Entertainment"] = "Film/dans/kultur/underholdning";
+App::$strings["Hobbies/Interests"] = "Hobbier/Interesser";
+App::$strings["Love/Romance"] = "Kjærlighet/romantikk";
+App::$strings["School/Education"] = "Skolle/utdanning";
+App::$strings["Contact information and social networks"] = "Kontaktinformasjon og andre sosiale nettverk";
+App::$strings["My other channels"] = "Mine andre kanaler";
App::$strings["Files: shared with me"] = "Filer: delt med meg";
App::$strings["NEW"] = "NY";
App::$strings["Remove all files"] = "Fjern alle filer";
App::$strings["Remove this file"] = "Fjern denne filen";
-App::$strings["This channel is limited to %d tokens"] = "";
-App::$strings["Name and Password are required."] = "";
-App::$strings["Token saved."] = "";
-App::$strings["Use this form to create temporary access identifiers to share things with non-members. These identities may be used in privacy groups and visitors may login using these credentials to access private content."] = "";
-App::$strings["Please select a role for this guest!"] = "";
-App::$strings["Select a role for this guest"] = "";
-App::$strings["Login Name"] = "";
-App::$strings["Login Password"] = "";
-App::$strings["Expires (yyyy-mm-dd)"] = "";
-App::$strings["Contact role deleted."] = "";
-App::$strings["Permission category name is required."] = "";
-App::$strings["Contact role saved."] = "";
-App::$strings["Role to assign affected contacts and default role to"] = "";
-App::$strings["Role to assign affected contacts to"] = "";
-App::$strings["Assign this role to"] = "";
-App::$strings["All my contacts"] = "";
-App::$strings["Automatically assign this role to new contacts"] = "";
-App::$strings["Role name"] = "";
-App::$strings["System role - not editable"] = "";
-App::$strings["Deleting"] = "";
-App::$strings["Role Permissions"] = "";
-App::$strings["Some permissions may be inherited from your <a href=\"settings\">channel role</a>, which have higher priority than contact role settings."] = "";
+App::$strings["Select a bookmark folder"] = "Velg en bokmerkemappe";
+App::$strings["Save Bookmark"] = "Lagre bokmerke";
+App::$strings["URL of bookmark"] = "URL-en til bokmerket";
+App::$strings["Or enter new bookmark folder name"] = "Eller skriv nytt navn på bokmerkemappe";
+App::$strings["Remote privacy information not available"] = "";
+App::$strings["__ctx:acl__ Profile"] = "Profil";
+App::$strings["Privacy group"] = "Personverngruppe:";
+App::$strings["Item"] = "";
+App::$strings["Click to copy link to this ressource for guest %s to clipboard"] = "";
+App::$strings["Link copied"] = "";
+App::$strings["Access"] = "Tilgang";
+App::$strings["OCAP access"] = "";
+App::$strings["Event can not end before it has started."] = "Hendelsen kan ikke slutte før den starter.";
+App::$strings["Unable to generate preview."] = "Klarer ikke å lage forhåndsvisning.";
+App::$strings["Event title and start time are required."] = "Hendelsestittel og starttidspunkt er påkrevd.";
+App::$strings["Event not found."] = "Hendelsen ble ikke funnet.";
+App::$strings["Edit event"] = "Endre hendelse";
+App::$strings["Delete event"] = "Slett hendelse";
+App::$strings["calendar"] = "kalender";
+App::$strings["Failed to remove event"] = "Mislyktes med å slette hendelse";
+App::$strings["This page is available only to site members"] = "";
+App::$strings["What would you like to do?"] = "";
+App::$strings["Please bookmark this page if you would like to return to it in the future"] = "";
+App::$strings["Upload a profile photo"] = "";
+App::$strings["Upload a cover photo"] = "";
+App::$strings["Edit your default profile"] = "";
+App::$strings["View the channel directory"] = "";
+App::$strings["View/edit your channel settings"] = "";
+App::$strings["View the site or project documentation"] = "";
+App::$strings["Visit your channel homepage"] = "";
+App::$strings["View your connections and/or add somebody whose address you already know"] = "";
+App::$strings["View your personal stream (this may be empty until you add some connections)"] = "";
+App::$strings["View the public stream. Warning: this content is not moderated"] = "";
+App::$strings["Room not found"] = "Rommet ble ikke funnet";
+App::$strings["Leave Room"] = "Forlat rom";
+App::$strings["Delete Room"] = "Slett rom";
+App::$strings["I am away right now"] = "Jeg er borte akkurat nå";
+App::$strings["I am online"] = "Jeg er pålogget";
+App::$strings["Bookmark this room"] = "Bokmerk dette rommet";
+App::$strings["New Chatroom"] = "Nytt chatrom";
+App::$strings["Chatroom name"] = "Romnavn";
+App::$strings["Expiration of chats (minutes)"] = "Chat utgår (antall minutter)";
+App::$strings["%1\$s's Chatrooms"] = "%1\$s sine chatrom";
+App::$strings["No chatrooms available"] = "Ingen rom tilgjengelige";
+App::$strings["Add Room"] = "";
+App::$strings["Expiration"] = "Utløp";
+App::$strings["min"] = "min";
+App::$strings["Email resent"] = "";
+App::$strings["Email resend failed"] = "";
+App::$strings["Verification successful"] = "";
+App::$strings["Account successfull created"] = "";
+App::$strings["Channel successfull created"] = "";
+App::$strings["Automatic channel creation failed. Please create a channel."] = "";
+App::$strings["Account creation error"] = "";
+App::$strings["Verify failed"] = "";
+App::$strings["Token verification failed"] = "";
+App::$strings["Request not inside time frame"] = "";
+App::$strings["Identity unknown"] = "";
+App::$strings["dId2 mistaken"] = "";
+App::$strings["Your Registration ID"] = "";
+App::$strings["Registration verification"] = "";
+App::$strings["Hold on, you can start verification in"] = "";
+App::$strings["Please remember your verification token for ID"] = "";
+App::$strings["Token validity"] = "";
+App::$strings["Resend email"] = "";
+App::$strings["Registration status"] = "";
+App::$strings["Verification successful!"] = "";
+App::$strings["Your login ID is"] = "";
+App::$strings["After your account has been approved by our administrator you will be able to login with your login ID and your provided password."] = "";
+App::$strings["Registration request revoked"] = "";
+App::$strings["Sorry for any inconvience. Thank you for your response."] = "";
+App::$strings["Please enter your verification token for ID"] = "";
+App::$strings["Please check your email!"] = "";
+App::$strings["Verification token"] = "";
+App::$strings["ID expired"] = "";
+App::$strings["You will require the verification token for ID"] = "";
+App::$strings["Unknown or expired ID"] = "";
+App::$strings["dId2 malformed"] = "";
+App::$strings["Edit Layout"] = "Endre layout";
+App::$strings["\$Projectname Server - Setup"] = "\$Projectname-tjener - oppsett";
+App::$strings["Could not connect to database."] = "Fikk ikke kontakt med databasen.";
+App::$strings["Could not connect to specified site URL. Possible SSL certificate or DNS issue."] = "Fikk ikke kontakt med det angitte nettstedets URL. Problemet kan muligens skyldes SSL-sertifikatet eller DNS.";
+App::$strings["Could not create table."] = "Kunne ikke lage tabellen.";
+App::$strings["Your site database has been installed."] = "Databasen til ditt nettsted har blitt installert.";
+App::$strings["You may need to import the file \"install/schema_xxx.sql\" manually using a database client."] = "Du må kanskje importere filen \"install/schmea_xxx.sql\" manuelt ved å bruke en databaseklient.";
+App::$strings["Please see the file \"install/INSTALL.txt\"."] = "Vennligst les filen \"install/INSTALL.txt\".";
+App::$strings["System check"] = "Systemsjekk";
+App::$strings["Check again"] = "Sjekk igjen";
+App::$strings["Database connection"] = "Databaseforbindelse";
+App::$strings["In order to install \$Projectname we need to know how to connect to your database."] = "For å installere \$Projectname må du oppgi hvordan din database kan kontaktes.";
+App::$strings["Please contact your hosting provider or site administrator if you have questions about these settings."] = "Vennligst kontakt din nettstedstilbyder eller nettstedsadministrator hvis du har spørsmål om disse innstillingene.";
+App::$strings["The database you specify below should already exist. If it does not, please create it before continuing."] = "Databasen du oppgir nedenfor må finnes på forhånd. Hvis den ikke finnes, vennligst lag den før du fortsetter.";
+App::$strings["Database Server Name"] = "Navn på databasetjener";
+App::$strings["Default is 127.0.0.1"] = "Standard er 127.0.0.1";
+App::$strings["Database Port"] = "Databaseport";
+App::$strings["Communication port number - use 0 for default"] = "Kommunikasjonsportnummer - bruk 0 for standard";
+App::$strings["Database Login Name"] = "Database innloggingsnavn";
+App::$strings["Database Login Password"] = "Database innloggingspassord";
+App::$strings["Database Name"] = "Databasenavn";
+App::$strings["Database Type"] = "Databasetype";
+App::$strings["Site administrator email address"] = "E-postadressen til administrator ved nettstedet";
+App::$strings["Your account email address must match this in order to use the web admin panel."] = "Din konto sin e-postadresse må være lik denne for å kunne bruke web-administrasjonspanelet.";
+App::$strings["Website URL"] = "Nettstedets URL";
+App::$strings["Please use SSL (https) URL if available."] = "Vennligst bruk SSL (https) URL hvis tilgjengelig.";
+App::$strings["Please select a default timezone for your website"] = "Vennligst velg en standard tidssone for ditt nettsted";
+App::$strings["Site settings"] = "Nettstedets innstillinger";
+App::$strings["PHP version 8.0 or greater is required."] = "";
+App::$strings["PHP version"] = "";
+App::$strings["Could not find a command line version of PHP in the web server PATH."] = "Fant ikke en kommandolinjeversjon av PHP i webtjenerens sti (PATH).";
+App::$strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron."] = "Hvis du ikke har en kommandolinjeversjon av PHP installert på tjeneren, så vil du ikke kunne kjøre bakgrunnshenting via cron.";
+App::$strings["PHP executable path"] = "PHP-kjørefilens sti";
+App::$strings["Enter full path to php executable. You can leave this blank to continue the installation."] = "Skriv full sti til kjørefilen for PHP. Du kan la denne stå blank for å fortsette installasjonen.";
+App::$strings["Command line PHP"] = "Kommandolinje PHP";
+App::$strings["Unable to check command line PHP, as shell_exec() is disabled. This is required."] = "";
+App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = "Kommandolinjeversjonen av PHP på ditt system har ikke \"register_argc_argv\" påskrudd.";
+App::$strings["This is required for message delivery to work."] = "Dette er påkrevd for at meldingslevering skal virke.";
+App::$strings["PHP register_argc_argv"] = "PHP register_argc_argv";
+App::$strings["This is not sufficient to upload larger images or files. You should be able to upload at least 4 MB at once."] = "";
+App::$strings["Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once."] = "Den største totale opplastingsstørrelsen du er tillatt er satt til %s. Filstørrelsen på en enkelt fil er satt til å maksimalt være %s. Du har lov til å laste opp inntil %d filer samtidig.";
+App::$strings["You can adjust these settings in the server php.ini file."] = "";
+App::$strings["PHP upload limits"] = "PHP opplastingsgrenser";
+App::$strings["Error: the \"openssl_pkey_new\" function on this system is not able to generate encryption keys"] = "Feil: \"openssl_pkey_new\"-funksjonen på dette systemet er ikke i stand til å lage krypteringsnøkler";
+App::$strings["If running under Windows, please see \"http://www.php.net/manual/en/openssl.installation.php\"."] = "Ved kjøring på Windows, vennligst se \"http://www.php.net/manual/en/openssl.installation.php\".";
+App::$strings["Generate encryption keys"] = "Lag krypteringsnøkler";
+App::$strings["Error: the sodium encryption library is not installed."] = "";
+App::$strings["Generate ed25519 encryption keys"] = "Lag krypteringsnøkler";
+App::$strings["Error: one of \"bcmath\" or \"gmp\" (bigmath library) extensions are required."] = "";
+App::$strings["Bigmath library (either bcmath or gmp)"] = "";
+App::$strings["libCurl PHP module"] = "libCurl PHP-modul";
+App::$strings["GD graphics PHP module"] = "GD graphics PHP-modul";
+App::$strings["OpenSSL PHP module"] = "OpenSSL PHP-modul";
+App::$strings["PDO database PHP module"] = "";
+App::$strings["mb_string PHP module"] = "mb_string PHP-modul";
+App::$strings["xml PHP module"] = "xml PHP modul";
+App::$strings["zip PHP module"] = "";
+App::$strings["intl PHP module"] = "xml PHP modul";
+App::$strings["Apache mod_rewrite module"] = "Apache mod_rewrite-modul";
+App::$strings["Error: Apache webserver mod-rewrite module is required but not installed."] = "Feil: Apache web-tjenerens mod-rewrite-modul er påkrevd, men ikke installert.";
+App::$strings["exec"] = "";
+App::$strings["Error: exec is required but is either not installed or has been disabled in php.ini"] = "";
+App::$strings["shell_exec"] = "";
+App::$strings["Error: shell_exec is required but is either not installed or has been disabled in php.ini"] = "";
+App::$strings["Error: libCURL PHP module required but not installed."] = "Feil: libCURL PHP-modul er påkrevd, men er ikke installert.";
+App::$strings["Error: GD PHP module with JPEG support or ImageMagick graphics library required but not installed."] = "";
+App::$strings["Error: openssl PHP module required but not installed."] = "Feil: openssl PHP-modul er påkrevd, men er ikke installert.";
+App::$strings["Error: PDO database PHP module missing a driver for either mysql or pgsql."] = "";
+App::$strings["Error: PDO database PHP module required but not installed."] = "";
+App::$strings["Error: mb_string PHP module required but not installed."] = "Feil: mb_string PHP-modul er påkrevd, men er ikke installert.";
+App::$strings["Error: xml PHP module required for DAV but not installed."] = "Feil: XML PHP modul er påkrevet for DAV, men den er ikke installert.";
+App::$strings["Error: zip PHP module required but not installed."] = "";
+App::$strings["Error: intl PHP module required but not installed."] = "Feil: openssl PHP-modul er påkrevd, men er ikke installert.";
+App::$strings[".htconfig.php is writable"] = ".htconfig.php kan skrives til";
+App::$strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = "Web-installasjonen må kunne lage en fil kalt \".htconfig.php\" i toppkatalogen til web-tjeneren din, men dette får den ikke til.";
+App::$strings["This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can."] = "Dette er oftest tillatelsesinnstilling, ettersom webtjeneren kanskje kan skrive til filer i din mappe - selv om du kan.";
+App::$strings["Please see install/INSTALL.txt for additional information."] = "";
+App::$strings["This software uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering."] = "";
+App::$strings["In order to store these compiled templates, the web server needs to have write access to the directory %s under the top level web folder."] = "";
+App::$strings["Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder."] = "Vennligst sikre at brukeren som din web-tjeneste kjører som (for eksempel www-data) har skrivetilgang til denne katalogen.";
+App::$strings["Note: as a security measure, you should give the web server write access to %s only--not the template files (.tpl) that it contains."] = "Merknad: som et sikkerhetstiltak bør du bare gi webtjerenn skrivetilgang til %s - ikke til malfilene (.tpl) som den inneholder.";
+App::$strings["%s is writable"] = "%s kan skrives til";
+App::$strings["This software uses the store directory to save uploaded files. The web server needs to have write access to the store directory under the top level web folder"] = "";
+App::$strings["store is writable"] = "lageret kan skrives til";
+App::$strings["SSL certificate cannot be validated. Fix certificate or disable https access to this site."] = "SSL-sertifikatet kan ikke kontrolleres. Fiks sertifikatet eller skru av https tilgang til dette nettstedet.";
+App::$strings["If you have https access to your website or allow connections to TCP port 443 (the https: port), you MUST use a browser-valid certificate. You MUST NOT use self-signed certificates!"] = "Hvis du har HTTPS-tilgang til ditt nettsted eller tillater forbindelser til TCP port 443 (HTTPS-porten), så MÅ du bruke nettlesergodkjent sertifkater. Du MÅ IKKE bruke egensignert sertifikater!";
+App::$strings["This restriction is incorporated because public posts from you may for example contain references to images on your own hub."] = "Denne begrensningen er tatt inn fordi offentlige innlegg fra deg kan for eksempel inneholde referanser til bilder på din egen hub.";
+App::$strings["If your certificate is not recognized, members of other sites (who may themselves have valid certificates) will get a warning message on their own site complaining about security issues."] = "Hvis sertifikatet ditt ikke gjenkjennes, så vil medlemmer på andre nettsteder (som selv kan ha godkjente sertifikater) få en beskjed med en advarsel på deres eget nettsted som klager over sikkerhetsproblemer.";
+App::$strings["This can cause usability issues elsewhere (not just on your own site) so we must insist on this requirement."] = "Dette kan gi problemer med brukervennlighet (ikke bare på ditt eget nettsted), så vi må insistere på dette kravet.";
+App::$strings["Providers are available that issue free certificates which are browser-valid."] = "Det finnes tilbydere som utsteder gratis sertifikater som er gyldige i nettlesere.";
+App::$strings["If you are confident that the certificate is valid and signed by a trusted authority, check to see if you have failed to install an intermediate cert. These are not normally required by browsers, but are required for server-to-server communications."] = "";
+App::$strings["SSL certificate validation"] = "SSL sertifikat-kontroll";
+App::$strings["Url rewrite in .htaccess is not working. Check your server configuration.Test: "] = "URL omskriving (rewrite) i .htaccess virker ikke. Sjekk konfigurasjonen til tjeneren din. Test: ";
+App::$strings["Url rewrite is working"] = "URL rewrite virker";
+App::$strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = "Databasekonfigurasjonsfilen \".htconfig.php\" kunne ikke skrives. Vennligst bruk den medfølgende teksten for å lage en konfigurasjonsfil i toppkatalogen av din web-tjener.";
+App::$strings["<h1>What next?</h1>"] = "";
+App::$strings["IMPORTANT: You will need to [manually] setup a scheduled task for the poller."] = "VIKTIG: Du må [manuelt] sette opp en automatisert tidfestet oppgave til bakgrunnshenteren.";
+App::$strings["toggle full screen mode"] = "";
+App::$strings["Invalid profile identifier."] = "Ugyldig profil-identifikator.";
+App::$strings["Profile Visibility Editor"] = "Endre profilsynlighet";
+App::$strings["Click on a contact to add or remove."] = "Klikk på en kontakt for å legge til eller fjerne.";
+App::$strings["Visible To"] = "Synlig for";
+App::$strings["All Connections"] = "Alle forbindelser";
+App::$strings["Language App"] = "";
+App::$strings["Select an alternate language"] = "Velg et annet språk";
+App::$strings["Edit post"] = "Endre innlegg";
+App::$strings["Item is not editable"] = "Elementet kan ikke endres";
+App::$strings["Active"] = "Aktiv";
+App::$strings["Blocked"] = "Blokkert";
+App::$strings["Ignored"] = "Ignorert";
+App::$strings["Hidden"] = "Skjult";
+App::$strings["Archived/Unreachable"] = "Arkivert/utilgjengelig";
+App::$strings["Active Connections"] = "Aktive forbindelser";
+App::$strings["Show active connections"] = "";
+App::$strings["Show pending (new) connections"] = "Vis ventende (nye) forbindelser";
+App::$strings["Only show blocked connections"] = "Vis bare forbindelser som er blokkert";
+App::$strings["Only show ignored connections"] = "Vis bare ignorerte forbindelser";
+App::$strings["Only show archived/unreachable connections"] = "";
+App::$strings["Only show hidden connections"] = "Vis bare skjulte forbindelser";
+App::$strings["Show all connections"] = "Vis alle forbindelser";
+App::$strings["Pending approval"] = "Venter på godkjenning";
+App::$strings["Archived"] = "Arkivert";
+App::$strings["Not connected at this location"] = "";
+App::$strings["%1\$s [%2\$s]"] = "%1\$s [%2\$s]";
+App::$strings["Edit connection"] = "Endre forbindelse";
+App::$strings["Delete connection"] = "Slett forbindelse";
+App::$strings["Channel address"] = "Kanaladresse";
+App::$strings["Call"] = "";
+App::$strings["Status"] = "Status";
+App::$strings["Connected"] = "Forbundet";
+App::$strings["Ignore connection"] = "Ignorer forbindelse";
+App::$strings["Recent activity"] = "Nylig aktivitet";
+App::$strings["Connect at this location"] = "";
+App::$strings["Search your connections"] = "Søk blant dine forbindelser";
+App::$strings["Contact search"] = "";
+App::$strings["Calendar entries imported."] = "Kalenderhendelsene er importert.";
+App::$strings["No calendar entries found."] = "Ingen kalenderhendelser funnet.";
+App::$strings["Event title"] = "Tittel på hendelse";
+App::$strings["Start date and time"] = "Startdato og tidspunkt";
+App::$strings["End date and time"] = "";
+App::$strings["Timezone:"] = "Tidssone";
+App::$strings["Month"] = "måned";
+App::$strings["Week"] = "uke";
+App::$strings["Day"] = "dag";
+App::$strings["List month"] = "";
+App::$strings["List week"] = "";
+App::$strings["List day"] = "";
+App::$strings["More"] = "";
+App::$strings["Less"] = "";
+App::$strings["Select calendar"] = "";
+App::$strings["Delete all"] = "";
+App::$strings["Sorry! Editing of recurrent events is not yet implemented."] = "";
+App::$strings["Could not fetch calendar resource. The selected calendar might be disabled."] = "";
+App::$strings["Default Calendar"] = "";
+App::$strings["Default Addressbook"] = "";
+App::$strings["This directory server requires an access token"] = "Denne katalogtjeneren krever en tilgangsnøkkel (access token)";
+App::$strings["Name and Secret are required"] = "";
+App::$strings["Add OAuth2 application"] = "";
+App::$strings["Grant Types"] = "";
+App::$strings["leave blank unless your application sepcifically requires this"] = "";
+App::$strings["Authorization scope"] = "";
+App::$strings["OAuth2 Application not found."] = "";
+App::$strings["leave blank unless your application specifically requires this"] = "";
+App::$strings["Connected OAuth2 Apps"] = "";
+App::$strings["%s - (Experimental)"] = "%s - (Eksperimentelt)";
+App::$strings["Display Settings"] = "Visningsinnstillinger";
+App::$strings["Theme Settings"] = "Temainnstillinger";
+App::$strings["Custom Theme Settings"] = "Tilpassede temainnstillinger";
+App::$strings["Content Settings"] = "Innholdsinnstillinger";
+App::$strings["Display Theme:"] = "Visningstema:";
+App::$strings["Select scheme"] = "Velg skjema";
+App::$strings["Threaded conversation view"] = "";
+App::$strings["Display replies below their parent message (default yes)"] = "";
+App::$strings["Enable user zoom on mobile devices"] = "Skru på brukerstyrt zoom på mobile enheter";
+App::$strings["Update browser every xx seconds"] = "Oppdater nettleser hvert xx sekunder";
+App::$strings["Minimum of 10 seconds, no maximum"] = "Minimum 10 sekunder, ikke noe maksimum";
+App::$strings["Maximum number of conversations to load at any time:"] = "Maksimalt antall samtaler å laste samtidig:";
+App::$strings["Maximum of 30 items"] = "";
+App::$strings["Show emoticons (smilies) as images"] = "Vis emoticons (smilefjes) som bilder";
+App::$strings["Link post titles to source"] = "Lenk innleggets tittel til kilden";
+App::$strings["Display new member quick links menu"] = "Vis hurtiglenker for nye medlemmer";
+App::$strings["Max height of content (in pixels)"] = "Maks høyde for innhold (i piksler)";
+App::$strings["Click to expand content exceeding this height"] = "Klikk for å vise hele innlegg som overskrider denne grensen";
+App::$strings["Stream Settings"] = "Instillinger for tidslinjen";
+App::$strings["Personal menu to display in your channel pages"] = "Personlig meny som kan vises på dine kanalsider";
+App::$strings["Channel Home Settings"] = "";
+App::$strings["Not valid email."] = "Ikke gyldig e-post.";
+App::$strings["Protected email address. Cannot change to that email."] = "Beskyttet e-postadresse. Kan ikke endre til den e-postadressen.";
+App::$strings["System failure storing new email. Please try again."] = "Systemfeil ved lagring av ny e-post. Vennligst prøv igjen.";
+App::$strings["Password verification failed."] = "Passordbekreftelsen mislyktes.";
+App::$strings["Passwords do not match. Password unchanged."] = "Passordene stemmer ikke overens. Passord uforandret.";
+App::$strings["Empty passwords are not allowed. Password unchanged."] = "Tomme passord er ikke tillatt. Passord uforandret.";
+App::$strings["Password changed."] = "Passord endret.";
+App::$strings["Password update failed. Please try again."] = "Passord oppdatering mislyktes. Vennligst prøv igjen.";
+App::$strings["Account Settings"] = "Kontoinnstillinger";
+App::$strings["Current Password"] = "Nåværende passord";
+App::$strings["Enter New Password"] = "Skriv nytt passord";
+App::$strings["Confirm New Password"] = "Bekreft nytt passord";
+App::$strings["Leave password fields blank unless changing"] = "La passordfeltene stå blanke om det ikke skal endres";
+App::$strings["Multi-Factor Authentication"] = "Flerfaktorautentisering";
+App::$strings["DId2 or Email Address:"] = "DId2 eller epostadresse:";
+App::$strings["Remove this account including all its channels"] = "Slett denne kontoen inkludert alle dens kanaler";
+App::$strings["Editor Settings"] = "Instillinger for redigering";
+App::$strings["Additional Features"] = "Ekstra funksjoner";
+App::$strings["Directory Settings"] = "";
+App::$strings["No feature settings configured"] = "Ingen funksjonsinnstillinger er konfigurert";
+App::$strings["Addon Settings"] = "Instillinger for tillegg";
+App::$strings["Please save/submit changes to any panel before opening another."] = "Husk å lagre endringene i ett panel før du åpner andre.";
+App::$strings["Channel Manager Settings"] = "";
+App::$strings["Privacy settings updated."] = "Personverninnstillingene ble oppdatert.";
+App::$strings["Only those you specifically allow"] = "Bare de du spesifikt tillater";
+App::$strings["Approved connections"] = "Godkjente forbindelser";
+App::$strings["Any connections"] = "Enhver forbindelse";
+App::$strings["Anybody on this website"] = "Enhver ved dette nettstedet";
+App::$strings["Anybody in this network"] = "Enhver i dette nettverket";
+App::$strings["Anybody authenticated"] = "Enhver som er autentisert";
+App::$strings["Anybody on the internet"] = "Enhver på Internett";
+App::$strings["Advise: set to \"Anybody on the internet\" and use privacy groups to restrict access"] = "";
+App::$strings["Privacy Settings"] = "Personverninnstillinger";
+App::$strings["Advanced configuration"] = "";
+App::$strings["Proceed with caution"] = "";
+App::$strings["Changing advanced configuration settings can impact your, and your contacts channels functionality and security."] = "";
+App::$strings["Accept the risk and continue"] = "";
+App::$strings["Automatically approve new contacts"] = "Automatisk godkjenn nye kontakter";
+App::$strings["Opt-out of search engine indexing"] = "Ikke la søkemotorer indeksere denne kanalen";
+App::$strings["Group actor"] = "";
+App::$strings["Allow this channel to act as a forum"] = "";
+App::$strings["Accept all messages which mention you"] = "Godta alle meldinger som nevner deg";
+App::$strings["This setting bypasses normal permissions"] = "Denne instillingen overstyrer vanlig tilgangskontroll";
+App::$strings["Accept unsolicited comments for moderation"] = "Godta kommentarer fra fremmede for moderering";
+App::$strings["Otherwise they will be silently dropped"] = "Ellers vil de avvises uten varsel";
+App::$strings["Enable OCAP access"] = "Slå på OCAP tilgangskontroll";
+App::$strings["Grant limited posts the right to access linked private media"] = "Gi begrensede innlegg tilgang til private media som er lenket fra dem";
+App::$strings["Events Settings"] = "";
+App::$strings["Photos Settings"] = "";
+App::$strings["Conversation Settings"] = "";
App::$strings["Please select a channel role"] = "Velg en kanalrolle";
App::$strings["Your channel address is"] = "Din kanaladresse er";
App::$strings["Your files/photos are accessible via WebDAV at"] = "Dine filer og foto er tilgjengelig via WebDAV på";
@@ -2468,7 +3343,6 @@ App::$strings["You are poked/prodded/etc. in a post"] = "Du ble prikket/oppildne
App::$strings["Someone likes your post/comment"] = "";
App::$strings["Show visual notifications including:"] = "Vis visuelle varslinger om:";
App::$strings["Unseen stream activity"] = "Ny aktivitet i nettverksstrømmen";
-App::$strings["Unseen channel activity"] = "Ny kanalaktivitet";
App::$strings["Unseen private messages"] = "Nye private meldinger";
App::$strings["Recommended"] = "Anbefalt";
App::$strings["Upcoming events"] = "Kommende hendelser";
@@ -2478,10 +3352,8 @@ App::$strings["Not available in all themes"] = "Ikke tilgjengelig i alle temaer"
App::$strings["System (personal) notifications"] = "System (personlige) varslinger";
App::$strings["System info messages"] = "System infomeldinger";
App::$strings["System critical alerts"] = "System kritiske varsel";
-App::$strings["New connections"] = "Nye forbindelser";
App::$strings["System Registrations"] = "Systemregistreringer";
App::$strings["Unseen shared files"] = "Nye delte filer";
-App::$strings["Unseen public stream activity"] = "Ny aktivitet i den offentlige strømmen";
App::$strings["Unseen likes and dislikes"] = "Nye liker/ikke liker";
App::$strings["Unseen forum posts"] = "Ny forumpost";
App::$strings["Email notification hub (hostname)"] = "Hub for epostvarsler (tjenernavn)";
@@ -2503,23 +3375,10 @@ App::$strings["This website expires after %d days."] = "";
App::$strings["This website does not expire imported content."] = "Dette nettstedet sletter ikke importert innhold.";
App::$strings["The website limit takes precedence if lower than your limit."] = "Nettstedets grense vil gjelde dersom den er lavere enn din grense.";
App::$strings["Words one per line or #tags, \$categories, /patterns/, lang=xx, lang!=xx - leave blank to import all posts"] = "Ett ord per linje eller #emneknagger, \$kategorier, /mønster/, lang=xx, lang!=xx - la være tomt for å importere alle innlegg";
-App::$strings["No feature settings configured"] = "Ingen funksjonsinnstillinger er konfigurert";
-App::$strings["Addon Settings"] = "Instillinger for tillegg";
-App::$strings["Please save/submit changes to any panel before opening another."] = "Husk å lagre endringene i ett panel før du åpner andre.";
-App::$strings["Editor Settings"] = "Instillinger for redigering";
-App::$strings["Photos Settings"] = "";
App::$strings["Default profile for new contacts"] = "";
App::$strings["Profiles Settings"] = "";
-App::$strings["Max height of content (in pixels)"] = "Maks høyde for innhold (i piksler)";
-App::$strings["Click to expand content exceeding this height"] = "Klikk for å vise hele innlegg som overskrider denne grensen";
-App::$strings["Stream Settings"] = "Instillinger for tidslinjen";
-App::$strings["Directory Settings"] = "";
-App::$strings["Personal menu to display in your channel pages"] = "Personlig meny som kan vises på dine kanalsider";
-App::$strings["Channel Home Settings"] = "";
-App::$strings["Calendar Settings"] = "";
-App::$strings["Conversation Settings"] = "";
-App::$strings["Additional Features"] = "Ekstra funksjoner";
App::$strings["Connections Settings"] = "";
+App::$strings["Calendar Settings"] = "";
App::$strings["Password is required"] = "Navn er påkrevd";
App::$strings["The provided password is not correct"] = "";
App::$strings["Account Multi-Factor Authentication"] = "Tofaktorautentisering";
@@ -2532,904 +3391,35 @@ App::$strings["Enable Multi-Factor Authentication"] = "Mislykket autentisering";
App::$strings["Logging in will require you to be in possession of your smartphone"] = "";
App::$strings["Your account password"] = "Ditt nye passord er";
App::$strings["Test"] = "Sjekk koden";
-App::$strings["Events Settings"] = "";
-App::$strings["Not valid email."] = "Ikke gyldig e-post.";
-App::$strings["Protected email address. Cannot change to that email."] = "Beskyttet e-postadresse. Kan ikke endre til den e-postadressen.";
-App::$strings["System failure storing new email. Please try again."] = "Systemfeil ved lagring av ny e-post. Vennligst prøv igjen.";
-App::$strings["Password verification failed."] = "Passordbekreftelsen mislyktes.";
-App::$strings["Passwords do not match. Password unchanged."] = "Passordene stemmer ikke overens. Passord uforandret.";
-App::$strings["Empty passwords are not allowed. Password unchanged."] = "Tomme passord er ikke tillatt. Passord uforandret.";
-App::$strings["Password changed."] = "Passord endret.";
-App::$strings["Password update failed. Please try again."] = "Passord oppdatering mislyktes. Vennligst prøv igjen.";
-App::$strings["Account Settings"] = "Kontoinnstillinger";
-App::$strings["Current Password"] = "Nåværende passord";
-App::$strings["Enter New Password"] = "Skriv nytt passord";
-App::$strings["Confirm New Password"] = "Bekreft nytt passord";
-App::$strings["Leave password fields blank unless changing"] = "La passordfeltene stå blanke om det ikke skal endres";
-App::$strings["Multi-Factor Authentication"] = "Flerfaktorautentisering";
-App::$strings["DId2 or Email Address:"] = "DId2 eller epostadresse:";
-App::$strings["Remove this account including all its channels"] = "Slett denne kontoen inkludert alle dens kanaler";
-App::$strings["Privacy settings updated."] = "Personverninnstillingene ble oppdatert.";
-App::$strings["Only those you specifically allow"] = "Bare de du spesifikt tillater";
-App::$strings["Approved connections"] = "Godkjente forbindelser";
-App::$strings["Any connections"] = "Enhver forbindelse";
-App::$strings["Anybody on this website"] = "Enhver ved dette nettstedet";
-App::$strings["Anybody in this network"] = "Enhver i dette nettverket";
-App::$strings["Anybody authenticated"] = "Enhver som er autentisert";
-App::$strings["Anybody on the internet"] = "Enhver på Internett";
-App::$strings["Advise: set to \"Anybody on the internet\" and use privacy groups to restrict access"] = "";
-App::$strings["Privacy Settings"] = "Personverninnstillinger";
-App::$strings["Advanced configuration"] = "";
-App::$strings["Proceed with caution"] = "";
-App::$strings["Changing advanced configuration settings can impact your, and your contacts channels functionality and security."] = "";
-App::$strings["Accept the risk and continue"] = "";
-App::$strings["Automatically approve new contacts"] = "Automatisk godkjenn nye kontakter";
-App::$strings["Opt-out of search engine indexing"] = "Ikke la søkemotorer indeksere denne kanalen";
-App::$strings["Group actor"] = "";
-App::$strings["Allow this channel to act as a forum"] = "";
-App::$strings["Accept all messages which mention you"] = "Godta alle meldinger som nevner deg";
-App::$strings["This setting bypasses normal permissions"] = "Denne instillingen overstyrer vanlig tilgangskontroll";
-App::$strings["Accept unsolicited comments for moderation"] = "Godta kommentarer fra fremmede for moderering";
-App::$strings["Otherwise they will be silently dropped"] = "Ellers vil de avvises uten varsel";
-App::$strings["Enable OCAP access"] = "Slå på OCAP tilgangskontroll";
-App::$strings["Grant limited posts the right to access linked private media"] = "Gi begrensede innlegg tilgang til private media som er lenket fra dem";
-App::$strings["%s - (Experimental)"] = "%s - (Eksperimentelt)";
-App::$strings["Display Settings"] = "Visningsinnstillinger";
-App::$strings["Theme Settings"] = "Temainnstillinger";
-App::$strings["Custom Theme Settings"] = "Tilpassede temainnstillinger";
-App::$strings["Content Settings"] = "Innholdsinnstillinger";
-App::$strings["Display Theme:"] = "Visningstema:";
-App::$strings["Select scheme"] = "Velg skjema";
-App::$strings["Preload images before rendering the page"] = "Last inn bildene før gjengivelsen av siden";
-App::$strings["The subjective page load time will be longer but the page will be ready when displayed"] = "Den personlige opplevelsen av lastetiden vil være lenger, men siden vil være klar når den vises";
-App::$strings["Enable user zoom on mobile devices"] = "Skru på brukerstyrt zoom på mobile enheter";
-App::$strings["Update browser every xx seconds"] = "Oppdater nettleser hvert xx sekunder";
-App::$strings["Minimum of 10 seconds, no maximum"] = "Minimum 10 sekunder, ikke noe maksimum";
-App::$strings["Maximum number of conversations to load at any time:"] = "Maksimalt antall samtaler å laste samtidig:";
-App::$strings["Maximum of 30 items"] = "";
-App::$strings["Show emoticons (smilies) as images"] = "Vis emoticons (smilefjes) som bilder";
-App::$strings["Link post titles to source"] = "Lenk innleggets tittel til kilden";
-App::$strings["New Member Links"] = "Lenker for nye medlemmer";
-App::$strings["Display new member quick links menu"] = "Vis hurtiglenker for nye medlemmer";
-App::$strings["Channel Manager Settings"] = "";
-App::$strings["Change Order of Pinned Navbar Apps"] = "";
-App::$strings["Change Order of App Tray Apps"] = "";
-App::$strings["Use arrows to move the corresponding app left (top) or right (bottom) in the navbar"] = "";
-App::$strings["Use arrows to move the corresponding app up or down in the app tray"] = "";
-App::$strings["Like/Dislike"] = "Liker/Liker ikke";
-App::$strings["This action is restricted to members."] = "Denne handlingen er begrenset til medlemmer.";
-App::$strings["Please <a href=\"rmagic\">login with your \$Projectname ID</a> or <a href=\"register\">register as a new \$Projectname member</a> to continue."] = "Vennligst <a href=\"rmagic\">logg inn med din \$Projectname ID</a> eller <a href=\"register\">registrer deg som et nytt \$Projectname-medlem</a> for å fortsette.";
-App::$strings["Invalid request."] = "Ugyldig forespørsel.";
-App::$strings["thing"] = "ting";
-App::$strings["Channel unavailable."] = "Kanalen er utilgjengelig.";
-App::$strings["Previous action reversed."] = "Forrige handling er omgjort.";
-App::$strings["profile"] = "Profil";
-App::$strings["%1\$s is attending %2\$s's %3\$s"] = "%1\$s deltar på %2\$ss %3\$s";
-App::$strings["%1\$s is not attending %2\$s's %3\$s"] = "%1\$s deltar ikke på %2\$ss %3\$s";
-App::$strings["%1\$s may attend %2\$s's %3\$s"] = "%1\$s deltar kanskje på %2\$ss %3\$s";
-App::$strings["Action completed."] = "Handling ferdig.";
-App::$strings["Thank you."] = "Tusen takk.";
-App::$strings["Connection added."] = "";
-App::$strings["Away"] = "Borte";
-App::$strings["Online"] = "Online";
-App::$strings["Blocked accounts"] = "";
-App::$strings["Expired accounts"] = "";
-App::$strings["Expiring accounts"] = "";
-App::$strings["Message queues"] = "Meldingskøer";
-App::$strings["Your software should be updated"] = "Programvaren bør oppdateres";
-App::$strings["Summary"] = "Sammendrag";
-App::$strings["Registered accounts"] = "Registrerte kontoer";
-App::$strings["Pending registrations"] = "Ventende registreringer";
-App::$strings["Registered channels"] = "Registrerte kanaler";
-App::$strings["Version"] = "Versjon";
-App::$strings["Repository version (master)"] = "";
-App::$strings["Repository version (dev)"] = "";
-App::$strings["Unknown App"] = "";
-App::$strings["Authorize"] = "";
-App::$strings["Do you authorize the app %s to access your channel data?"] = "";
-App::$strings["This page is available only to site members"] = "";
-App::$strings["Welcome"] = "Velkommen";
-App::$strings["What would you like to do?"] = "";
-App::$strings["Please bookmark this page if you would like to return to it in the future"] = "";
-App::$strings["Upload a profile photo"] = "";
-App::$strings["Upload a cover photo"] = "";
-App::$strings["Edit your default profile"] = "";
-App::$strings["View friend suggestions"] = "Vis venneforslag";
-App::$strings["View the channel directory"] = "";
-App::$strings["View/edit your channel settings"] = "";
-App::$strings["View the site or project documentation"] = "";
-App::$strings["Visit your channel homepage"] = "";
-App::$strings["View your connections and/or add somebody whose address you already know"] = "";
-App::$strings["View your personal stream (this may be empty until you add some connections)"] = "";
-App::$strings["View the public stream. Warning: this content is not moderated"] = "";
-App::$strings["Create a new channel"] = "Lag en ny kanal";
-App::$strings["Current Channel"] = "Gjeldende kanal";
-App::$strings["Switch to one of your channels by selecting it."] = "Bytt til en av dine kanaler ved å velge den.";
-App::$strings["Default Channel"] = "Standardkanal";
-App::$strings["Make Default"] = "Gjør til standard";
-App::$strings["%d new introductions"] = "%d nye introduksjoner";
-App::$strings["Delegated Channel"] = "Delegert kanal";
-App::$strings["Items tagged with: %s"] = "Elementer merket med: %s";
-App::$strings["Search results for: %s"] = "Søkeresultater for: %s";
-App::$strings["Channel name changes are not allowed within 48 hours of changing the account password."] = "";
-App::$strings["Change channel nickname/address"] = "";
-App::$strings["Any/all connections on other networks will be lost!"] = "";
-App::$strings["New channel address"] = "";
-App::$strings["Rename Channel"] = "";
-App::$strings["Calendar entries imported."] = "Kalenderhendelsene er importert.";
-App::$strings["No calendar entries found."] = "Ingen kalenderhendelser funnet.";
-App::$strings["Event title"] = "Tittel på hendelse";
-App::$strings["Start date and time"] = "Startdato og tidspunkt";
-App::$strings["End date and time"] = "";
-App::$strings["Timezone:"] = "Tidssone";
-App::$strings["Month"] = "måned";
-App::$strings["Week"] = "uke";
-App::$strings["Day"] = "dag";
-App::$strings["List month"] = "";
-App::$strings["List week"] = "";
-App::$strings["List day"] = "";
-App::$strings["More"] = "";
-App::$strings["Less"] = "";
-App::$strings["Select calendar"] = "";
-App::$strings["Channel Calendars"] = "";
-App::$strings["CalDAV Calendars"] = "";
-App::$strings["Delete all"] = "";
-App::$strings["Sorry! Editing of recurrent events is not yet implemented."] = "";
-App::$strings["Could not fetch calendar resource. The selected calendar might be disabled."] = "";
-App::$strings["Default Calendar"] = "";
-App::$strings["Default Addressbook"] = "";
-App::$strings["Invalid abook_id"] = "";
-App::$strings["View profile"] = "Vis profil";
-App::$strings["Select a role for this contact"] = "";
-App::$strings["Contact roles"] = "";
-App::$strings["Roles"] = "";
-App::$strings["Compare permissions"] = "";
-App::$strings["Permission"] = "";
-App::$strings["Privacy groups"] = "Personverngrupper";
-App::$strings["Content filter"] = "";
-App::$strings["Contact updated"] = "";
-App::$strings["Contact update failed"] = "";
-App::$strings["Refresh succeeded"] = "";
-App::$strings["Refresh failed"] = "Forny";
-App::$strings["Block status updated"] = "";
-App::$strings["Block failed"] = "";
-App::$strings["Ignore status updated"] = "";
-App::$strings["Ignore failed"] = "";
-App::$strings["Archive status updated"] = "";
-App::$strings["Archive failed"] = "";
-App::$strings["Hide status updated"] = "";
-App::$strings["Hide failed"] = "";
-App::$strings["Contact removed"] = "";
-App::$strings["Delete failed"] = "";
-App::$strings["Refresh"] = "Forny";
-App::$strings["Refetch contact info"] = "";
-App::$strings["vcard"] = "";
-App::$strings["Suggestions"] = "Forslag";
-App::$strings["See more..."] = "Se mer...";
-App::$strings["Share This"] = "Del dette";
-App::$strings["View %s's profile - %s"] = "Vis %s sin profil - %s";
-App::$strings["Don't show"] = "Ikke vis";
-App::$strings["Add new group"] = "";
-App::$strings["Remove term"] = "Fjern begrep";
-App::$strings["photo/image"] = "foto/bilde";
-App::$strings["Read mode"] = "";
-App::$strings["Edit mode"] = "";
-App::$strings["Editing"] = "";
-App::$strings["Saving"] = "";
-App::$strings["Saved"] = "";
-App::$strings["Suggested Chatrooms"] = "Foreslåtte chatrom";
-App::$strings["Bookmarked Chatrooms"] = "Bokmerkede chatrom";
-App::$strings["Tasks"] = "Oppgaver";
-App::$strings["Public and restricted messages"] = "";
-App::$strings["Direct messages"] = "Direktemeldinger";
-App::$strings["Starred messages"] = "";
-App::$strings["Notices"] = "Varsel";
-App::$strings["No messages"] = "Ingen meldinger";
-App::$strings["Unseen"] = "Ulest";
-App::$strings["Filter by name or address"] = "Filtrer etter navn eller adresse";
-App::$strings["Click to show more"] = "";
-App::$strings["__ctx:widget__ Activity"] = "aktivitet";
-App::$strings["App Collections"] = "Appsamlinger";
-App::$strings["Installed apps"] = "Installerte apper";
-App::$strings["Toggle post editor"] = "Vis redigering av innlegg";
-App::$strings["Toggle personal notes"] = "";
-App::$strings["Channel activities"] = "";
-App::$strings["Archives"] = "Arkiv";
-App::$strings["No recent activities"] = "Nylig aktivitet";
-App::$strings["__ctx:noun__ new connection"] = array(
- 0 => "ny forbindelse",
- 1 => "nye forbindelser",
-);
-App::$strings["__ctx:noun__ notice"] = array(
- 0 => "varsel",
- 1 => "varsel",
-);
-App::$strings["Account settings"] = "Kontoinnstillinger";
-App::$strings["Channel settings"] = "Kanalinnstillinger";
-App::$strings["Privacy settings"] = "Personverninnstillinger";
-App::$strings["Display settings"] = "Visningsinnstillinger";
-App::$strings["Manage locations"] = "";
-App::$strings["Rating Tools"] = "Vurderingsverktøy";
-App::$strings["Rate Me"] = "Vurder meg";
-App::$strings["View Ratings"] = "Vis vurderinger";
-App::$strings["App Categories"] = "Appkategorier";
-App::$strings["Direct Messages"] = "Direktemeldinger";
-App::$strings["Show direct (private) messages"] = "Vis direktemeldinger (private meldinger)";
-App::$strings["Events"] = "Hendelser";
-App::$strings["Show posts that include events"] = "";
-App::$strings["Polls"] = "Spørreskjema";
-App::$strings["Show posts that include polls"] = "Vis innlegg som inneholder spørreskjema";
-App::$strings["Show posts related to the %s privacy group"] = "";
-App::$strings["Show my privacy groups"] = "";
-App::$strings["Show posts to this forum"] = "";
-App::$strings["Show forums"] = "";
-App::$strings["Starred Posts"] = "";
-App::$strings["Show posts that I have starred"] = "";
-App::$strings["Personal Posts"] = "";
-App::$strings["Show posts that mention or involve me"] = "";
-App::$strings["Show posts that I have filed to %s"] = "";
-App::$strings["Show filed post categories"] = "";
-App::$strings["Panel search"] = "";
-App::$strings["Filter by name"] = "Filtrer etter navn";
-App::$strings["Remove active filter"] = "";
-App::$strings["Stream Filters"] = "Filtere for tidslinjen";
-App::$strings["New network activity notifications"] = "";
-App::$strings["Network stream"] = "";
-App::$strings["Mark all notifications read"] = "";
-App::$strings["Show new posts only"] = "";
-App::$strings["New home activity notifications"] = "";
-App::$strings["Home stream"] = "";
-App::$strings["Mark all notifications seen"] = "";
-App::$strings["New direct messages notifications"] = "Varsel om nye direktemeldinger";
-App::$strings["Direct messages stream"] = "Strøm for direktemeldinger";
-App::$strings["New events notifications"] = "";
-App::$strings["View events"] = "";
-App::$strings["Mark all events seen"] = "Merk alle hendelser som sett";
-App::$strings["New connections notifications"] = "";
-App::$strings["View all connections"] = "";
-App::$strings["New files notifications"] = "";
-App::$strings["View all notices"] = "";
-App::$strings["Mark all notices seen"] = "";
-App::$strings["Registrations"] = "";
-App::$strings["New registrations notifications"] = "";
-App::$strings["New public stream notifications"] = "";
-App::$strings["Public stream"] = "";
-App::$strings["Sorry, you have got no notifications at the moment"] = "";
-App::$strings["Add new guest"] = "";
-App::$strings["Add new role"] = "";
-App::$strings["Role members"] = "";
-App::$strings["Profile Creation"] = "Oppretting av profil";
-App::$strings["Upload profile photo"] = "Last opp profilbilde";
-App::$strings["Upload cover photo"] = "Last opp omslagsbilde";
-App::$strings["Find and Connect with others"] = "Finn andre";
-App::$strings["View the directory"] = "Se i katalogen";
-App::$strings["Manage your connections"] = "Behandle forbindelser";
-App::$strings["Communicate"] = "Kommuniser";
-App::$strings["View your channel homepage"] = "Vis kanalens hjemmeside";
-App::$strings["View your network stream"] = "Vis nettverksstrømmen";
-App::$strings["Documentation"] = "Dokumentasjon";
-App::$strings["Missing Features?"] = "Noe som mangler?";
-App::$strings["Pin apps to navigation bar"] = "Fest apper til navigasjonslinjen";
-App::$strings["Install more apps"] = "Legg til flere apper";
-App::$strings["View public stream"] = "";
-App::$strings["Add New Connection"] = "Legg til ny forbindelse";
-App::$strings["Enter channel address"] = "Skriv kanaladressen";
-App::$strings["Examples: bob@example.com, https://example.com/barbara"] = "Eksempel: ola@eksempel.no, https://eksempel.no/kari";
-App::$strings["Member registrations waiting for confirmation"] = "";
-App::$strings["Inspect queue"] = "Inspiser kø";
-App::$strings["Queueworker"] = "";
-App::$strings["DB updates"] = "Databaseoppdateringer";
-App::$strings["Addon Features"] = "";
-App::$strings["Overview"] = "";
-App::$strings["Select Channel"] = "";
-App::$strings["Read-write"] = "";
-App::$strings["Read-only"] = "";
-App::$strings["Channel Calendar"] = "";
-App::$strings["Shared CalDAV Calendars"] = "";
-App::$strings["Share this calendar"] = "";
-App::$strings["Calendar name and color"] = "";
-App::$strings["Create new CalDAV calendar"] = "";
-App::$strings["Calendar Name"] = "";
-App::$strings["Calendar Tools"] = "";
-App::$strings["Import calendar"] = "";
-App::$strings["Select a calendar to import to"] = "";
-App::$strings["Addressbooks"] = "";
-App::$strings["Addressbook name"] = "";
-App::$strings["Create new addressbook"] = "";
-App::$strings["Addressbook Name"] = "";
-App::$strings["Addressbook Tools"] = "";
-App::$strings["Import addressbook"] = "";
-App::$strings["Select an addressbook to import to"] = "";
-App::$strings["Commented Date"] = "Sist kommentert";
-App::$strings["Order by last commented date"] = "Sorter etter dato for siste kommentar";
-App::$strings["Posted Date"] = "Innleggsdato";
-App::$strings["Order by last posted date"] = "Sorter etter dato innlegg ble postet";
-App::$strings["Date Unthreaded"] = "Utrådet";
-App::$strings["Order unthreaded by date"] = "Sorter innlegg og kommentarer uavhengig av hverandre etter dato";
-App::$strings["Stream Order"] = "Sortering av innlegg";
-App::$strings["Chat Members"] = "";
-App::$strings["Source channel not found."] = "Fant ikke kildekanalen.";
-App::$strings["generic profile image"] = "";
-App::$strings["random geometric pattern"] = "";
-App::$strings["monster face"] = "";
-App::$strings["computer generated face"] = "";
-App::$strings["retro arcade style face"] = "";
-App::$strings["Hub default profile photo"] = "";
-App::$strings["Information"] = "";
-App::$strings["Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar."] = "";
-App::$strings["Save Settings"] = "";
-App::$strings["Default avatar image"] = "";
-App::$strings["Select default avatar image if none was found at Gravatar. See README"] = "";
-App::$strings["Rating of images"] = "";
-App::$strings["Select the appropriate avatar rating for your site. See README"] = "";
-App::$strings["Gravatar settings updated."] = "";
-App::$strings["Send your identity to all websites"] = "";
-App::$strings["Send ZID"] = "";
-App::$strings["An account has been created for you."] = "";
-App::$strings["Authentication successful but rejected: account creation is disabled."] = "";
-App::$strings["DB Cleanup Failure"] = "";
-App::$strings["[cart] Item Added"] = "";
-App::$strings["Order already checked out."] = "";
-App::$strings["Drop database tables when uninstalling."] = "";
-App::$strings["Cart Settings"] = "";
-App::$strings["Shop"] = "";
-App::$strings["Profile Unavailable."] = "Profilen er ikke tilgjengelig.";
-App::$strings["Order Not Found"] = "";
-App::$strings["You must be logged into the Grid to shop."] = "";
-App::$strings["Invalid channel"] = "Ugyldig kanal";
-App::$strings["Order not found."] = "";
-App::$strings["Access denied."] = "Ingen tilgang";
-App::$strings["No Order Found"] = "";
-App::$strings["An unknown error has occurred Please start again."] = "";
-App::$strings["Requirements not met."] = "";
-App::$strings["Review your order and complete any needed requirements."] = "";
-App::$strings["Invalid Payment Type. Please start again."] = "";
-App::$strings["Order not found"] = "";
-App::$strings["Enable Order/Item Options"] = "";
-App::$strings["Label"] = "";
-App::$strings["Instructions"] = "";
-App::$strings["Enable Subscription Management Module"] = "";
-App::$strings["Cannot include subscription items with different terms in the same order."] = "";
-App::$strings["Select Subscription to Edit"] = "";
-App::$strings["Edit Subscriptions"] = "";
-App::$strings["Subscription SKU"] = "";
-App::$strings["Catalog Description"] = "";
-App::$strings["Subscription available for purchase."] = "";
-App::$strings["Maximum active subscriptions to this item per account."] = "";
-App::$strings["Subscription price."] = "";
-App::$strings["Quantity"] = "";
-App::$strings["Term"] = "";
-App::$strings["Enable Manual Cart Module"] = "";
-App::$strings["New Sku"] = "";
-App::$strings["Cannot save edits to locked item."] = "";
-App::$strings["Changes Locked"] = "";
-App::$strings["Item available for purchase."] = "";
-App::$strings["Price"] = "";
-App::$strings["Photo URL"] = "";
-App::$strings["Enable Paypal Button Module (API-v2)"] = "";
-App::$strings["Use Production Key"] = "";
-App::$strings["Paypal Sandbox Client Key"] = "";
-App::$strings["Paypal Sandbox Secret Key"] = "";
-App::$strings["Paypal Production Client Key"] = "";
-App::$strings["Paypal Production Secret Key"] = "";
-App::$strings["Paypal button payments are not enabled."] = "";
-App::$strings["Paypal button payments are not properly configured. Please choose another payment option."] = "";
-App::$strings["Enable Paypal Button Module"] = "";
-App::$strings["Enable Hubzilla Services Module"] = "";
-App::$strings["SKU not found."] = "";
-App::$strings["Invalid Activation Directive."] = "";
-App::$strings["Invalid Deactivation Directive."] = "";
-App::$strings["Add to this privacy group"] = "";
-App::$strings["Set user service class"] = "";
-App::$strings["You must be using a local account to purchase this service."] = "";
-App::$strings["Add buyer to privacy group"] = "";
-App::$strings["Add buyer as connection"] = "";
-App::$strings["Set Service Class"] = "";
-App::$strings["Access Denied."] = "Ingen tilgang";
-App::$strings["Access Denied"] = "Ingen tilgang";
-App::$strings["Invalid Item"] = "Ugyldig element.";
-App::$strings["Error: order mismatch. Please try again."] = "";
-App::$strings["Manual payments are not enabled."] = "";
-App::$strings["Finished"] = "";
-App::$strings["Enable Test Catalog"] = "";
-App::$strings["Enable Manual Payments"] = "";
-App::$strings["Base Merchant Currency"] = "";
-App::$strings["Edit Article"] = "";
-App::$strings["View Articles"] = "";
-App::$strings["Add Article"] = "";
-App::$strings["Hubzilla File Storage Import"] = "";
-App::$strings["This will import all your cloud files from another server."] = "";
-App::$strings["Hubzilla Server base URL"] = "";
-App::$strings["Since modified date yyyy-mm-dd"] = "";
-App::$strings["Until modified date yyyy-mm-dd"] = "";
-App::$strings["View Larger"] = "";
-App::$strings["Tile Server URL"] = "";
-App::$strings["A list of <a href=\"http://wiki.openstreetmap.org/wiki/TMS\" target=\"_blank\">public tile servers</a>"] = "";
-App::$strings["Nominatim (reverse geocoding) Server URL"] = "";
-App::$strings["A list of <a href=\"http://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\">Nominatim servers</a>"] = "";
-App::$strings["Default zoom"] = "";
-App::$strings["The default zoom level. (1:world, 18:highest, also depends on tile server)"] = "";
-App::$strings["Include marker on map"] = "";
-App::$strings["Include a marker on the map."] = "";
-App::$strings["WYSIWYG status editor"] = "";
-App::$strings["WYSIWYG Status App"] = "";
-App::$strings["WYSIWYG Status"] = "";
-App::$strings["Network error"] = "";
-App::$strings["API error"] = "";
-App::$strings["Unknown issue"] = "";
-App::$strings["Unable to retrieve email address from remote identity provider"] = "";
-App::$strings["Unable to login using email address "] = "";
-App::$strings["Social Authentication using your social media account"] = "";
-App::$strings["This app enables one or more social provider sign-in buttons on the login page."] = "";
-App::$strings["Add an identity provider"] = "";
-App::$strings["Enable "] = "Skru på";
-App::$strings["Key"] = "";
-App::$strings["Word"] = "";
-App::$strings["Secret"] = "";
-App::$strings["Add a custom provider"] = "";
-App::$strings["Remove an identity provider"] = "";
-App::$strings["Social authentication"] = "";
-App::$strings["Error while saving provider settings"] = "";
-App::$strings["Custom provider already exists"] = "";
-App::$strings["Social authentication settings saved."] = "";
-App::$strings["You are now authenticated to pumpio."] = "";
-App::$strings["return to the featured settings page"] = "tilbake til funksjonsinnstillinger";
-App::$strings["Post to Pump.io"] = "";
-App::$strings["Pump.io Settings saved."] = "";
-App::$strings["Pump.io servername"] = "";
-App::$strings["Without \"http://\" or \"https://\""] = "";
-App::$strings["Pump.io username"] = "";
-App::$strings["Without the servername"] = "";
-App::$strings["You are not authenticated to pumpio"] = "";
-App::$strings["(Re-)Authenticate your pump.io connection"] = "";
-App::$strings["Post to pump.io by default"] = "";
-App::$strings["Should posts be public"] = "";
-App::$strings["Mirror all public posts"] = "";
-App::$strings["Pump.io Crosspost Connector"] = "";
-App::$strings["Block Completely"] = "Blokker helt";
-App::$strings["superblock settings updated"] = "innstillingene til superblokk ble oppdatert";
-App::$strings["Currently blocked"] = "Blokkert for øyeblikket";
-App::$strings["No channels currently blocked"] = "Ingen kanaler er blokkert i øyeblikket";
-App::$strings["Gallery"] = "";
-App::$strings["Photo Gallery"] = "";
-App::$strings["You're welcome."] = "";
-App::$strings["Ah shucks..."] = "";
-App::$strings["Don't mention it."] = "";
-App::$strings["&lt;blush&gt;"] = "";
-App::$strings["System defaults:"] = "";
-App::$strings["Preferred Clipart IDs"] = "";
-App::$strings["List of preferred clipart ids. These will be shown first."] = "";
-App::$strings["Default Search Term"] = "Standard søkeord";
-App::$strings["The default search term. These will be shown second."] = "Standard søkeord. Disse vil vises som nummer to.";
-App::$strings["Return After"] = "";
-App::$strings["Page to load after image selection."] = "";
-App::$strings["Profile List"] = "";
-App::$strings["Order of Preferred"] = "";
-App::$strings["Sort order of preferred clipart ids."] = "";
-App::$strings["Newest first"] = "";
-App::$strings["As entered"] = "";
-App::$strings["Order of other"] = "";
-App::$strings["Sort order of other clipart ids."] = "";
-App::$strings["Most downloaded first"] = "";
-App::$strings["Most liked first"] = "";
-App::$strings["Preferred IDs Message"] = "";
-App::$strings["Message to display above preferred results."] = "";
-App::$strings["Uploaded by: "] = "";
-App::$strings["Drawn by: "] = "";
-App::$strings["Use this image"] = "";
-App::$strings["Or select from a free OpenClipart.org image:"] = "";
-App::$strings["Search Term"] = "Søkeord";
-App::$strings["Unknown error. Please try again later."] = "";
-App::$strings["Profile photo updated successfully."] = "";
-App::$strings["Hubzilla Directory Stats"] = "";
-App::$strings["Total Hubs"] = "";
-App::$strings["Hubzilla Hubs"] = "";
-App::$strings["Friendica Hubs"] = "";
-App::$strings["Diaspora Pods"] = "";
-App::$strings["Hubzilla Channels"] = "";
-App::$strings["Friendica Channels"] = "";
-App::$strings["Diaspora Channels"] = "";
-App::$strings["Aged 35 and above"] = "";
-App::$strings["Aged 34 and under"] = "";
-App::$strings["Average Age"] = "";
-App::$strings["Known Chatrooms"] = "";
-App::$strings["Known Tags"] = "";
-App::$strings["Please note Diaspora and Friendica statistics are merely those **this directory** is aware of, and not all those known in the network. This also applies to chatrooms,"] = "";
-App::$strings["Please contact your site administrator.<br />The provided API URL is not valid."] = "Den oppgitte URLen for APIet er ikke gyldig. Kontakt serverens administrator.";
-App::$strings["We could not contact the GNU social API with the Path you entered."] = "";
-App::$strings["GNU social settings updated."] = "";
-App::$strings["Globally Available GNU social OAuthKeys"] = "";
-App::$strings["There are preconfigured OAuth key pairs for some GNU social servers available. If you are using one of them, please use these credentials.<br />If not feel free to connect to any other GNU social instance (see below)."] = "";
-App::$strings["Provide your own OAuth Credentials"] = "";
-App::$strings["No consumer key pair for GNU social found. Register your Hubzilla Account as an desktop client on your GNU social account, copy the consumer key pair here and enter the API base root.<br />Before you register your own OAuth key pair ask the administrator if there is already a key pair for this Hubzilla installation at your favourite GNU social installation."] = "";
-App::$strings["OAuth Consumer Key"] = "";
-App::$strings["OAuth Consumer Secret"] = "";
-App::$strings["Base API Path"] = "";
-App::$strings["Remember the trailing /"] = "";
-App::$strings["GNU social application name"] = "";
-App::$strings["To connect to your GNU social account click the button below to get a security code from GNU social which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to GNU social."] = "";
-App::$strings["Log in with GNU social"] = "";
-App::$strings["Copy the security code from GNU social here"] = "";
-App::$strings["Cancel Connection Process"] = "";
-App::$strings["Current GNU social API is"] = "";
-App::$strings["Cancel GNU social Connection"] = "";
-App::$strings["Currently connected to: "] = "";
-App::$strings["<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to GNU social will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "";
-App::$strings["Post to GNU social by default"] = "";
-App::$strings["If enabled your public postings will be posted to the associated GNU-social account by default"] = "";
-App::$strings["Clear OAuth configuration"] = "";
-App::$strings["GNU-Social Crosspost Connector"] = "";
-App::$strings["Post to GNU social"] = "";
-App::$strings["API URL"] = "";
-App::$strings["Application name"] = "";
-App::$strings["Post to Livejournal"] = "";
-App::$strings["Posted by"] = "";
-App::$strings["Source"] = "";
-App::$strings["Livejournal username"] = "";
-App::$strings["Livejournal password"] = "";
-App::$strings["Post to Livejournal by default"] = "";
-App::$strings["Send wall-to-wall posts to Livejournal"] = "";
-App::$strings["Add link to original post"] = "";
-App::$strings["Livejournal Crosspost Connector"] = "";
-App::$strings["pageheader Settings saved."] = "";
-App::$strings["Message to display on every page on this server"] = "";
-App::$strings["Page Header"] = "";
-App::$strings["Rainbow Tag App"] = "";
-App::$strings["Add some colour to tag clouds"] = "";
-App::$strings["Rainbow Tag"] = "";
-App::$strings["Recent Channel/Profile Viewers"] = "";
-App::$strings["No entries."] = "";
-App::$strings["Your account on %s will expire in a few days."] = "";
-App::$strings["Your test account is about to expire."] = "";
-App::$strings["nofed Settings saved."] = "";
-App::$strings["Federate posts by default"] = "";
-App::$strings["No Federation"] = "";
-App::$strings["Federate"] = "";
-App::$strings["Your Webbie:"] = "";
-App::$strings["Fontsize (px):"] = "";
-App::$strings["Link:"] = "";
-App::$strings["Like us on Hubzilla"] = "";
-App::$strings["Embed:"] = "";
-App::$strings["This website is tracked using the <a href='http://www.piwik.org'>Piwik</a> analytics tool."] = "";
-App::$strings["If you do not want that your visits are logged this way you <a href='%s'>can set a cookie to prevent Piwik from tracking further visits of the site</a> (opt-out)."] = "";
-App::$strings["Piwik Base URL"] = "";
-App::$strings["Absolute path to your Piwik installation. (without protocol (http/s), with trailing slash)"] = "";
-App::$strings["Site ID"] = "";
-App::$strings["Show opt-out cookie link?"] = "";
-App::$strings["Asynchronous tracking"] = "";
-App::$strings["Enable frontend JavaScript error tracking"] = "";
-App::$strings["This feature requires Piwik >= 2.2.0"] = "";
-App::$strings["Page to load after login"] = "";
-App::$strings["Examples: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy collection), &quot;channel&quot; or &quot;notifications/system&quot; (leave blank for default network page (grid)."] = "";
-App::$strings["Startpage"] = "";
-App::$strings["Twitter settings updated."] = "";
-App::$strings["No consumer key pair for Twitter found. Please contact your site administrator."] = "";
-App::$strings["At this Hubzilla instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter."] = "";
-App::$strings["Log in with Twitter"] = "";
-App::$strings["Copy the PIN from Twitter here"] = "";
-App::$strings["<strong>Note:</strong> Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "";
-App::$strings["Twitter post length"] = "";
-App::$strings["Maximum tweet length"] = "";
-App::$strings["Send public postings to Twitter by default"] = "";
-App::$strings["If enabled your public postings will be posted to the associated Twitter account by default"] = "";
-App::$strings["Twitter Crosspost Connector"] = "";
-App::$strings["Post to Twitter"] = "";
-App::$strings["Submit Settings"] = "";
-App::$strings["Fuzzloc Settings updated."] = "";
-App::$strings["Minimum offset in meters"] = "";
-App::$strings["Maximum offset in meters"] = "";
-App::$strings["Fuzzy Location"] = "";
-App::$strings["No username found in import file."] = "Ingen brukernavn ble funnet i importfilen.";
-App::$strings["Import completed."] = "Import ferdig.";
-App::$strings["%1\$s dislikes %2\$s's %3\$s"] = "";
-App::$strings["Diaspora Protocol Settings updated."] = "";
-App::$strings["The diaspora protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "";
-App::$strings["Prevent your hashtags from being redirected to other sites"] = "";
-App::$strings["Sign and forward posts and comments with no existing Diaspora signature"] = "";
-App::$strings["Followed hashtags (comma separated, do not include the #)"] = "";
-App::$strings["Diaspora Protocol"] = "";
-App::$strings["Please install the statistics addon to be able to configure a diaspora relay"] = "";
-App::$strings["Diaspora Relay Handle"] = "";
-App::$strings["Address of a diaspora relay. Example: relay@diasporarelay.tld"] = "";
-App::$strings["Diaspora relay could not be imported"] = "";
-App::$strings["No subject"] = "";
-App::$strings["Photos imported"] = "";
-App::$strings["Redmatrix Photo Album Import"] = "";
-App::$strings["This will import all your Redmatrix photo albums to this channel."] = "";
-App::$strings["Redmatrix Server base URL"] = "";
-App::$strings["Redmatrix Login Username"] = "";
-App::$strings["Redmatrix Login Password"] = "";
-App::$strings["Import just this album"] = "";
-App::$strings["Leave blank to import all albums"] = "";
-App::$strings["Maximum count to import"] = "";
-App::$strings["0 or blank to import all available"] = "";
-App::$strings["Some setting"] = "";
-App::$strings["A setting"] = "";
-App::$strings["Skeleton Settings"] = "";
-App::$strings["QR code"] = "";
-App::$strings["QR Generator"] = "";
-App::$strings["Enter some text"] = "";
-App::$strings["Dreamwidth Crosspost Connector Settings saved."] = "";
-App::$strings["Dreamwidth username"] = "";
-App::$strings["Dreamwidth password"] = "";
-App::$strings["Post to Dreamwidth by default"] = "";
-App::$strings["Link description (default:"] = "";
-App::$strings["Dreamwidth Crosspost Connector"] = "";
-App::$strings["Post to Dreamwidth"] = "";
-App::$strings["Show Upload Limits"] = "";
-App::$strings["Hubzilla configured maximum size: "] = "";
-App::$strings["PHP upload_max_filesize: "] = "";
-App::$strings["PHP post_max_size (must be larger than upload_max_filesize): "] = "";
-App::$strings["NSA Bait App"] = "";
-App::$strings["Make yourself a political target."] = "";
-App::$strings["No server specified"] = "";
-App::$strings["Posts imported"] = "";
-App::$strings["Files imported"] = "";
-App::$strings["This will import all your conversations and cloud files from a cloned channel on another server. This may take a while if you have lots of posts and or files."] = "";
-App::$strings["Include posts"] = "";
-App::$strings["Conversations, Articles, Cards, and other posted content"] = "";
-App::$strings["Include files"] = "";
-App::$strings["Files, Photos and other cloud storage"] = "";
-App::$strings["Original Server base URL"] = "";
-App::$strings["Friendica Crosspost Connector Settings saved."] = "";
-App::$strings["Send public postings to Friendica by default"] = "";
-App::$strings["Friendica API Path"] = "";
-App::$strings["https://{sitename}/api"] = "";
-App::$strings["Friendica login name"] = "";
-App::$strings["Friendica password"] = "";
-App::$strings["Friendica Crosspost Connector"] = "";
-App::$strings["Post to Friendica"] = "";
-App::$strings["New registration"] = "";
-App::$strings["Message sent to %s. New account registration: %s"] = "";
-App::$strings["Redmatrix File Storage Import"] = "";
-App::$strings["This will import all your Redmatrix cloud files to this channel."] = "";
-App::$strings["Send test email"] = "";
-App::$strings["No recipients found."] = "";
-App::$strings["Mail sent."] = "";
-App::$strings["Sending of mail failed."] = "";
-App::$strings["Mail Test"] = "";
-App::$strings["Message subject"] = "";
-App::$strings["Errors encountered deleting database table "] = "";
-App::$strings["Drop tables when uninstalling?"] = "";
-App::$strings["If checked, the Rendezvous database tables will be deleted when the plugin is uninstalled."] = "";
-App::$strings["Mapbox Access Token"] = "";
-App::$strings["If you enter a Mapbox access token, it will be used to retrieve map tiles from Mapbox instead of the default OpenStreetMap tile server."] = "";
-App::$strings["Rendezvous"] = "";
-App::$strings["This identity has been deleted by another member due to inactivity. Please press the \"New identity\" button or refresh the page to register a new identity. You may use the same name."] = "";
-App::$strings["Welcome to Rendezvous!"] = "";
-App::$strings["Enter your name to join this rendezvous. To begin sharing your location with the other members, tap the GPS control. When your location is discovered, a red dot will appear and others will be able to see you on the map."] = "";
-App::$strings["Let's meet here"] = "";
-App::$strings["New marker"] = "";
-App::$strings["Edit marker"] = "";
-App::$strings["New identity"] = "";
-App::$strings["Delete marker"] = "";
-App::$strings["Delete member"] = "";
-App::$strings["Edit proximity alert"] = "";
-App::$strings["A proximity alert will be issued when this member is within a certain radius of you.<br><br>Enter a radius in meters (0 to disable):"] = "";
-App::$strings["distance"] = "";
-App::$strings["Proximity alert distance (meters)"] = "";
-App::$strings["A proximity alert will be issued when you are within a certain radius of the marker location.<br><br>Enter a radius in meters (0 to disable):"] = "";
-App::$strings["Marker proximity alert"] = "";
-App::$strings["Reminder note"] = "";
-App::$strings["Enter a note to be displayed when you are within the specified proximity..."] = "";
-App::$strings["Add new rendezvous"] = "";
-App::$strings["Create a new rendezvous and share the access link with those you wish to invite to the group. Those who open the link become members of the rendezvous. They can view other member locations, add markers to the map, or share their own locations with the group."] = "";
-App::$strings["You have no rendezvous. Press the button above to create a rendezvous!"] = "";
-App::$strings["Channel is required."] = "";
-App::$strings["Hubzilla Crosspost Connector Settings saved."] = "";
-App::$strings["Send public postings to Hubzilla channel by default"] = "";
-App::$strings["Hubzilla API Path"] = "";
-App::$strings["Hubzilla login name"] = "";
-App::$strings["Hubzilla channel name"] = "";
-App::$strings["Hubzilla password"] = "";
-App::$strings["Hubzilla Crosspost Connector"] = "";
-App::$strings["Post to Hubzilla"] = "";
-App::$strings["Workflow user."] = "";
-App::$strings["This channel"] = "";
-App::$strings["Create New Workflow Item"] = "";
-App::$strings["Workflow"] = "";
-App::$strings["No Workflows Available"] = "";
-App::$strings["Add item to which workflow"] = "";
-App::$strings["Create Workflow Item"] = "";
-App::$strings["Link"] = "";
-App::$strings["Web link."] = "";
-App::$strings["Brief description or title"] = "";
-App::$strings["Notes and Info"] = "";
-App::$strings["Used to order links"] = "";
-App::$strings["Body"] = "";
-App::$strings["Workflow Settings"] = "";
-App::$strings["Workflow settings"] = "Instillinger for redigering";
-App::$strings["Possible adult content"] = "";
-App::$strings["%s - view"] = "";
-App::$strings["NSFW Settings saved."] = "";
-App::$strings["This app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter."] = "";
-App::$strings["Comma separated list of keywords to hide"] = "";
-App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = "";
-App::$strings["NSFW"] = "";
-App::$strings["Three Dimensional Tic-Tac-Toe"] = "";
-App::$strings["3D Tic-Tac-Toe"] = "";
-App::$strings["New game"] = "";
-App::$strings["New game with handicap"] = "";
-App::$strings["Three dimensional tic-tac-toe is just like the traditional game except that it is played on multiple levels simultaneously. "] = "";
-App::$strings["In this case there are three levels. You win by getting three in a row on any level, as well as up, down, and diagonally across the different levels."] = "";
-App::$strings["The handicap game disables the center position on the middle level because the player claiming this square often has an unfair advantage."] = "";
-App::$strings["You go first..."] = "";
-App::$strings["I'm going first this time..."] = "";
-App::$strings["You won!"] = "";
-App::$strings["\"Cat\" game!"] = "";
-App::$strings["I won!"] = "";
-App::$strings["Photo Cache settings saved."] = "";
-App::$strings["Saves a copy of images from external sites locally to increase your anonymity in the web."] = "";
-App::$strings["Minimal photo size for caching"] = "";
-App::$strings["In pixels. From 1 up to 1024, 0 will be replaced with system default."] = "";
-App::$strings["Photo Cache"] = "";
-App::$strings["Logfile archive directory"] = "";
-App::$strings["Directory to store rotated logs"] = "";
-App::$strings["Logfile size in bytes before rotating"] = "";
-App::$strings["Number of logfiles to retain"] = "";
-App::$strings["XMPP settings updated."] = "";
-App::$strings["XMPP App"] = "";
-App::$strings["Embedded XMPP (Jabber) client"] = "";
-App::$strings["Individual credentials"] = "";
-App::$strings["Jabber BOSH server"] = "";
-App::$strings["XMPP Settings"] = "";
-App::$strings["Jabber BOSH host"] = "";
-App::$strings["Use central userbase"] = "";
-App::$strings["If enabled, members will automatically login to an ejabberd server that has to be installed on this machine with synchronized credentials via the \"auth_ejabberd.php\" script."] = "";
-App::$strings["(No Title)"] = "(Ingen tittel)";
-App::$strings["Wiki page create failed."] = "Kunne ikke opprette wikiside.";
-App::$strings["Wiki not found."] = "Fant ikke wikien.";
-App::$strings["Destination name already exists"] = "Navnet finnes allerede";
-App::$strings["Page not found"] = "Fant ikke siden";
-App::$strings["Error reading page content"] = "Det oppstod en feil under lesing av innholder på siden";
-App::$strings["Error reading wiki"] = "Det oppstod en feil ved lesing av wikien";
-App::$strings["Page update failed."] = "Oppdatering av siden mislyktes.";
-App::$strings["Nothing deleted"] = "Ingenting ble slettet";
-App::$strings["Compare: object not found."] = "Sammanlign: fant ikke objektet.";
-App::$strings["Page updated"] = "Siden ble oppdatert";
-App::$strings["Wiki resource_id required for git commit"] = "Wiki ressurs_id er nødvendig for å lagre i git";
-App::$strings["__ctx:wiki_history__ Message"] = "Melding";
-App::$strings["Date"] = "Dato";
-App::$strings["Revert"] = "Tilbakestill";
-App::$strings["Compare"] = "Sammenlign";
-App::$strings["Wiki updated successfully"] = "Wikien ble oppdatert";
-App::$strings["Wiki files deleted successfully"] = "Wikifiler ble slettet";
-App::$strings["Error retrieving wiki"] = "Det oppstod en feil ved henting av wikien";
-App::$strings["Error creating zip file export folder"] = "Det oppstod en feil ved eksport av mappe til zip fil";
-App::$strings["Error downloading wiki: "] = "";
-App::$strings["Wikis"] = "Wikier";
-App::$strings["Wiki name"] = "";
-App::$strings["Content type"] = "";
-App::$strings["Any&nbsp;type"] = "";
-App::$strings["Lock content type"] = "";
-App::$strings["Create a status post for this wiki"] = "";
-App::$strings["Edit Wiki Name"] = "";
-App::$strings["Wiki not found"] = "Fant ikke wikien.";
-App::$strings["Rename page"] = "";
-App::$strings["Error retrieving page content"] = "";
-App::$strings["New page"] = "";
-App::$strings["Revision Comparison"] = "";
-App::$strings["Short description of your changes (optional)"] = "";
-App::$strings["New page name"] = "";
-App::$strings["Embed image from photo albums"] = "";
-App::$strings["History"] = "";
-App::$strings["Error creating wiki. Invalid name."] = "";
-App::$strings["A wiki with this name already exists."] = "";
-App::$strings["Wiki created, but error creating Home page."] = "";
-App::$strings["Error creating wiki"] = "";
-App::$strings["Error updating wiki. Invalid name."] = "";
-App::$strings["Error updating wiki"] = "";
-App::$strings["Wiki delete permission denied."] = "";
-App::$strings["Error deleting wiki"] = "";
-App::$strings["New page created"] = "";
-App::$strings["Cannot delete Home"] = "";
-App::$strings["Current Revision"] = "";
-App::$strings["Selected Revision"] = "";
-App::$strings["You must be authenticated."] = "";
-App::$strings["Add new page"] = "Legg til ny side";
-App::$strings["View Cards"] = "";
-App::$strings["Wiki Pages"] = "Wikisider";
-App::$strings["Page name"] = "Sidenavn";
-App::$strings["Who likes me?"] = "";
-App::$strings["Edit Card"] = "";
-App::$strings["Add Card"] = "";
-App::$strings["Libertree Crosspost Connector Settings saved."] = "";
-App::$strings["Libertree API token"] = "";
-App::$strings["Libertree site URL"] = "";
-App::$strings["Post to Libertree by default"] = "";
-App::$strings["Libertree Crosspost Connector"] = "";
-App::$strings["Post to Libertree"] = "";
-App::$strings["Send email to all members"] = "";
-App::$strings["%1\$d of %2\$d messages sent."] = "";
-App::$strings["Send email to all hub members."] = "";
-App::$strings["Sender Email address"] = "";
-App::$strings["Test mode (only send to hub administrator)"] = "";
-App::$strings["Use markdown for editing posts"] = "";
-App::$strings["We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."] = "Vi støtte på et problem under innloggingen med din OpenID. Vennligst sjekk at ID-en er stavet riktig.";
-App::$strings["The error message was:"] = "Feilmeldingen var:";
-App::$strings["OpenID protocol error. No ID returned."] = "OpenID protokollfeil. Ingen ID ble returnert.";
-App::$strings["Welcome %s. Remote authentication successful."] = "Velkommen %s. Ekstern autentisering er vellykket.";
-App::$strings["First Name"] = "Fornavn";
-App::$strings["Last Name"] = "Etternavn";
-App::$strings["Full Name"] = "Fullt navn";
-App::$strings["Profile Photo 16px"] = "Profilbilde 16px";
-App::$strings["Profile Photo 32px"] = "Profilbilde 32px";
-App::$strings["Profile Photo 48px"] = "Profilbilde 48px";
-App::$strings["Profile Photo 64px"] = "Profilbilde 64px";
-App::$strings["Profile Photo 80px"] = "Profilbilde 80px";
-App::$strings["Profile Photo 128px"] = "Profilbilde 128px";
-App::$strings["Birth Year"] = "Fødselsår";
-App::$strings["Birth Month"] = "Fødselsmåne";
-App::$strings["Birth Day"] = "Fødselsdag";
-App::$strings["Birthdate"] = "Fødselsdato";
-App::$strings["Popular Channels"] = "";
-App::$strings["Channels to auto connect"] = "";
-App::$strings["Comma separated list"] = "";
-App::$strings["IRC Settings"] = "";
-App::$strings["IRC settings saved."] = "";
-App::$strings["IRC Chatroom"] = "";
-App::$strings["__ctx:opensearch__ Search %1\$s (%2\$s)"] = "Søk %1\$s (%2\$s)";
-App::$strings["__ctx:opensearch__ \$Projectname"] = "\$Projectname";
-App::$strings["Search \$Projectname"] = "";
-App::$strings["ActivityPub Protocol Settings updated."] = "";
-App::$strings["The activitypub protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "";
-App::$strings["Send activities of type note instead of article"] = "";
-App::$strings["Microblog services such as Mastodon do not properly support articles"] = "";
-App::$strings["Activitypub Protocol"] = "";
-App::$strings["Not allowed."] = "";
-App::$strings["Insane Journal Crosspost Connector Settings saved."] = "";
-App::$strings["Insane Journal Crosspost Connector App"] = "";
-App::$strings["Relay public postings to Insane Journal"] = "";
-App::$strings["InsaneJournal username"] = "";
-App::$strings["InsaneJournal password"] = "";
-App::$strings["Post to InsaneJournal by default"] = "";
-App::$strings["Insane Journal Crosspost Connector"] = "";
-App::$strings["Post to Insane Journal"] = "";
-App::$strings["Random Planet App"] = "";
-App::$strings["Set a random planet from the Star Wars Empire as your location when posting"] = "";
-App::$strings["Project Servers and Resources"] = "";
-App::$strings["Project Creator and Tech Lead"] = "";
-App::$strings["And the hundreds of other people and organisations who helped make the Hubzilla possible."] = "";
-App::$strings["The Redmatrix/Hubzilla projects are provided primarily by volunteers giving their time and expertise - and often paying out of pocket for services they share with others."] = "";
-App::$strings["There is no corporate funding and no ads, and we do not collect and sell your personal information. (We don't control your personal information - <strong>you do</strong>.)"] = "";
-App::$strings["Help support our ground-breaking work in decentralisation, web identity, and privacy."] = "";
-App::$strings["Your donations keep servers and services running and also helps us to provide innovative new features and continued development."] = "";
-App::$strings["Donate"] = "";
-App::$strings["Choose a project, developer, or public hub to support with a one-time donation"] = "";
-App::$strings["Donate Now"] = "";
-App::$strings["<strong><em>Or</em></strong> become a project sponsor (Hubzilla Project only)"] = "";
-App::$strings["Please indicate if you would like your first name or full name (or nothing) to appear in our sponsor listing"] = "";
-App::$strings["Sponsor"] = "";
-App::$strings["Special thanks to: "] = "";
-App::$strings["Fediquest App"] = "Endre app";
-App::$strings["A distributed quest for a given word (game)."] = "";
-App::$strings["To start a game, enter [fediquest]your_word[/fediquest] somewhere in a toplevel post."] = "";
-App::$strings["Your contacts can post their guess in the comments."] = "";
-App::$strings["Your channel will evaluate the guess and automatically post the response."] = "";
-App::$strings["Correct letters"] = "";
-App::$strings["Letters contained in the word but at the wrong spot"] = "";
-App::$strings["Letters not contained in the word"] = "";
-App::$strings["ERROR: word length is not correct!"] = "";
-App::$strings["Post to WordPress"] = "";
-App::$strings["Wordpress Settings saved."] = "";
-App::$strings["WordPress username"] = "";
-App::$strings["WordPress password"] = "";
-App::$strings["WordPress API URL"] = "";
-App::$strings["Typically https://your-blog.tld/xmlrpc.php"] = "";
-App::$strings["WordPress blogid"] = "";
-App::$strings["For multi-user sites such as wordpress.com, otherwise leave blank"] = "";
-App::$strings["Post to WordPress by default"] = "";
-App::$strings["Forward comments (requires hubzilla_wp plugin)"] = "";
-App::$strings["Wordpress Post"] = "";
-App::$strings["Allow magic authentication only to websites of your immediate connections"] = "";
-App::$strings["Authchoose"] = "";
-App::$strings["Your channel has been upgraded to \$Projectname version"] = "";
-App::$strings["Please have a look at the"] = "";
-App::$strings["git history"] = "";
-App::$strings["change log"] = "";
-App::$strings["for further info."] = "";
-App::$strings["\$Projectname Upgrade Info"] = "\$Projectname";
-App::$strings["Do not show this again"] = "";
-App::$strings["Hide Aside App"] = "";
-App::$strings["Fade out aside areas after a while when using endless scroll"] = "";
-App::$strings["text to include in all outgoing posts from this site"] = "";
+App::$strings["Authentication failed."] = "Autentisering mislyktes.";
+App::$strings["Edit Block"] = "Endre byggekloss";
+App::$strings["Email Verification Required"] = "";
+App::$strings["A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message."] = "En verifikasjonskode ble sendt til epostadressen din [%s]. Skriv inn koden du mottok her for å fullføre kontoverifikasjonen. Det kan ta noen minutter før du mottar koden. Sjekk også at eposten ikke har havnet i mappen for søppelpost om du ikke ser den etter en stund.";
+App::$strings["Resend Email"] = "";
+App::$strings["Validation token"] = "";
+App::$strings["Nothing to import."] = "Ingenting å importere.";
+App::$strings["Unable to download data from old server"] = "Ikke i stand til å laste ned data fra gammel tjener";
+App::$strings["Your service plan only allows %d channels."] = "Din tjenesteplan tillater bare %d kanaler.";
+App::$strings["No channel. Import failed."] = "Ingen kanal. Import mislyktes.";
+App::$strings["Channel exists but has been marked removed on this hub. Import failed."] = "";
+App::$strings["Automatic content and files import was not possible due to API version incompatiblity. Please import content and files manually!"] = "";
+App::$strings["You must be logged in to use this feature."] = "Du må være innlogget for å bruke denne funksjonen.";
+App::$strings["Channel Import"] = "";
+App::$strings["Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file."] = "Bruk dette skjemaet for å importere en eksisterende kanal fra en annen tjener/hub. Du kan hente inn kanalidentiteten fra den gamle tjeneren/huben via nettverket eller ved å bruke en eksportfil.";
+App::$strings["Or provide the old server/hub details"] = "Eller oppgi detaljene fra den gamle tjeneren/hub-en";
+App::$strings["Your old identity address (xyz@example.com)"] = "Din gamle identitetsadresse (xyz@example.com)";
+App::$strings["Your old login email address"] = "Din gamle innloggings e-postadresse";
+App::$strings["Your old login password"] = "Ditt gamle innloggingspassord";
+App::$strings["Import your items and files (limited by available memory)"] = "";
+App::$strings["For either option, please choose whether to make this hub your new primary address, or whether your old location should continue this role. You will be able to post from either location, but only one can be marked as the primary location for files, photos, and media."] = "Enten du tar det ene eller det andre valget, vennligst angi om du vil at denne hubben skal være din nye primære adresse, eller om din gamle plassering skal fortsette å ha denne rollen. Du kan lage innlegg fra den ene eller den andre plasseringen, men bare en av dem kan markeres som den primære plasseringen for filer, bilder og media.";
+App::$strings["Make this hub my primary location"] = "Gjør dette nettstedet til min primære plassering";
+App::$strings["Move this channel (disable all previous locations)"] = "";
+App::$strings["Use this channel nickname instead of the one provided"] = "";
+App::$strings["Leave blank to keep your existing channel nickname. You will be randomly assigned a similar nickname if either name is already allocated on this site."] = "";
+App::$strings["This process may take several minutes to complete. Please submit the form only once and leave this page open until finished."] = "Denne prosessen kan ta flere minutter å fullføre. Vennligst send inn dette skjemaet bare en gang og la siden være åpen inntil den er ferdig.";
+App::$strings["network"] = "nettverk";
+App::$strings["Enter a folder name"] = "";
+App::$strings["or select an existing folder (doubleclick)"] = "";
+App::$strings["Poll not found."] = "Fant ikke spørreskjema.";
+App::$strings["Invalid response."] = "";
+App::$strings["Response submitted. Updates may not appear instantly."] = "";
diff --git a/view/tpl/conv_frame.tpl b/view/tpl/conv_frame.tpl
index b40585a46..0acf7b24c 100644
--- a/view/tpl/conv_frame.tpl
+++ b/view/tpl/conv_frame.tpl
@@ -30,6 +30,9 @@
<div class="modal-body list-group" id="reactions_body">
{{$wait}}
</div>
+ <div class="ps-3 pe-3" id="reactions_extra_top"></div>
+ <div class="ps-3 pe-3" id="reactions_extra_middle"></div>
+ <div class="ps-3 pe-3" id="reactions_extra_bottom"></div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl
index 6d8aa6abc..4abb34a31 100644
--- a/view/tpl/conv_item.tpl
+++ b/view/tpl/conv_item.tpl
@@ -1,4 +1,4 @@
-{{if $item.comment_firstcollapsed}}
+{{if !$item.threaded && $item.comment_firstcollapsed}}
<div id="hide-comments-outer-{{$item.parent}}" class="hide-comments-outer fakelink small" onclick="showHideComments({{$item.id}});">
<i id="hide-comments-icon-{{$item.id}}" class="bi bi-chevron-down align-middle hide-comments-icon"></i> <span id="hide-comments-label-{{$item.id}}" class="hide-comments-label align-middle" data-expanded="{{$item.collapse_comments}}" data-collapsed="{{$item.expand_comments}}">{{$item.expand_comments}}</span>{{if !$item.threaded}}&nbsp;<span id="hide-comments-total-{{$item.id}}" class="hide-comments-label align-middle">{{$item.num_comments}}</span>{{/if}}
</div>
@@ -111,10 +111,16 @@
<div class="wall-item-tools-left hstack gap-1" id="wall-item-tools-left-{{$item.id}}">
{{foreach $item.responses as $verb=>$response}}
{{if !($verb == 'comment' && (($item.toplevel && !$item.blog_mode) || $response.count == 0))}}
- <button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.observer_activity.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}" onclick="request({{$item.id}}, '{{$item.rawmid}}', '{{$verb}}', {{$item.parent}}, '{{$item.mid}}'); return false;" id="wall-item-{{$verb}}-{{$item.id}}">
+ {{if !$item.threaded && $item.blog_mode && $verb == 'comment'}}
+ <a href="{{$item.viewthread}}" target="_thread" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.observer_activity.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}" id="wall-item-{{$verb}}-{{$item.id}}">
+ <i class="bi bi-chat generic-icons"></i>{{if $response.count}}<span style="display: inline-block; margin-top: -.25rem;" class="align-top">{{$response.count}}</span>{{/if}}
+ </a>
+ {{else}}
+ <button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.observer_activity.$verb}} link-secondary{{/if}} wall-item-reaction wall-item-{{$response.button.class}}" id="wall-item-{{$verb}}-{{$item.id}}" data-item-id="{{$item.id}}" data-item-mid="{{$item.rawmid}}" data-item-verb="{{$verb}}" data-item-parent="{{$item.parent}}" data-item-uuid="{{$item.mid}}" data-item-reaction-count="{{$response.count}}">
<i class="bi bi-{{$response.button.icon}} generic-icons"></i>{{if $response.count}}<span style="display: inline-block; margin-top: -.25rem;" class="align-top">{{$response.count}}</span>{{/if}}
</button>
{{/if}}
+ {{/if}}
{{/foreach}}
{{if $item.toplevel && $item.emojis && $item.reactions}}
<div class="">
@@ -191,6 +197,9 @@
{{if $item.star}}
<a class="dropdown-item" href="#" onclick="dostar({{$item.id}}); return false;"><i id="starred-{{$item.id}}" class="generic-icons-nav bi{{if $item.star.isstarred}} starred bi-star-fill{{else}} unstarred bi-star{{/if}}" title="{{$item.star.toggle}}"></i>{{$item.star.toggle}}</a>
{{/if}}
+ {{if $item.expand}}
+ <a class="dropdown-item dropdown-item-expand" href="#" data-item-id="{{$item.id}}" data-item-uuid="{{$item.mid}}"><i id="expand-{{$item.id}}" class="generic-icons-nav bi bi-arrows-angle-expand" title="{{$item.expand}}"></i>{{$item.expand}}</a>
+ {{/if}}
{{if $item.thread_action_menu}}
{{foreach $item.thread_action_menu as $mitem}}
<a class="dropdown-item" {{if $mitem.href}}href="{{$mitem.href}}"{{/if}} {{if $mitem.action}}onclick="{{$mitem.action}}"{{/if}} {{if $mitem.title}}title="{{$mitem.title}}"{{/if}} ><i class="generic-icons-nav bi bi-{{$mitem.icon}}"></i>{{$mitem.title}}</a>
@@ -209,7 +218,7 @@
{{/if}}
{{if $item.settings}}
<div class="dropdown-divider"></div>
- <a class="dropdown-item conversation-settings-link" href="" data-bs-toggle="modal" data-bs-target="#conversation_settings">{{$item.settings}}</a>
+ <a class="dropdown-item conversation-settings-link" href="#" data-bs-toggle="modal" data-bs-target="#conversation_settings">{{$item.settings}}</a>
{{/if}}
</div>
</div>
@@ -219,6 +228,14 @@
</div>
</div>
{{if $item.thread_level == 1}}
+ {{if $item.toplevel && $item.load_more && $item.threaded}}
+ <div id="load-more-progress-wrapper-{{$item.id}}" class="progress{{if $item.blog_mode}} d-none{{/if}}" role="progressbar" aria-valuenow="{{$item.comments_total_percent}}" aria-valuemin="0" aria-valuemax="100" style="height: 1px">
+ <div id="load-more-progress-{{$item.id}}" class="progress-bar bg-info" style="width: {{$item.comments_total_percent}}%; margin-left: auto; margin-right: auto;" data-comments-total="{{$item.comments_total}}"></div>
+ </div>
+ <div id="load-more-{{$item.id}}" class="load-more text-center text-secondary cursor-pointer{{if $item.blog_mode}} d-none{{/if}}" title="{{$item.load_more_title}}" onclick="request(0, '{{$item.rawmid}}', 'load', {{$item.parent}}, ''); return false;">
+ <span id="load-more-dots-{{$item.id}}" class="load-more-dots rounded"><span class="dot-1">-</span> <span class="dot-2">-</span> <span class="dot-3">-</span></span>
+ </div>
+ {{/if}}
<div id="wall-item-sub-thread-wrapper-{{$item.id}}" class="wall-item-sub-thread-wrapper">
{{foreach $item.children as $child}}
{{include file="{{$child.template}}" item=$child}}
@@ -238,6 +255,6 @@
</div>
{{/if}}
</div>
-{{if $item.comment_lastcollapsed}}
+{{if !$item.threaded && $item.comment_lastcollapsed}}
</div>
{{/if}}
diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl
index b01fa6609..b9b8b3012 100644
--- a/view/tpl/jot-header.tpl
+++ b/view/tpl/jot-header.tpl
@@ -11,6 +11,8 @@
var activeCommentID = 0;
var activeCommentText = '';
+ var isModalAction = false;
+
var postSaveTimer = null;
function initEditor(cb){
@@ -414,19 +416,41 @@
// Fetch the photo album list
getPhotoAlbumList();
- // Remove any existing click event listeners on the modal body
- const modalBodyAlbumDialog = document.getElementById('embedPhotoModalBodyAlbumDialog');
- modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners
+ if (activeCommentID) {
+ const modalEl = document.getElementById('reactions');
+ isModalAction = modalEl.classList.contains('show');
+ }
- // Show the modal
- const modalEl = document.getElementById('embedPhotoModal');
- const modal = new bootstrap.Modal(modalEl);
- modal.show();
+ if (isModalAction) {
+ // Remove any existing click event listeners on the modal body
+ const modalBodyAlbumDialog = document.getElementById('reactions_extra_middle');
+ modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners
- // Reset activeCommentID when the modal is closed
- modalEl.addEventListener('hide.bs.modal', event => {
- activeCommentID = 0;
- });
+ const modalEl = document.getElementById('reactions');
+
+ // Reset activeCommentID when the modal is closed
+ modalEl.addEventListener('hide.bs.modal', event => {
+ activeCommentID = 0;
+ isModalAction = false;
+ document.getElementById('reactions_extra_middle').innerHTML = '';
+ document.getElementById('reactions_extra_top').innerHTML = '';
+ });
+ }
+ else {
+ // Remove any existing click event listeners on the modal body
+ const modalBodyAlbumDialog = document.getElementById('embedPhotoModalBodyAlbumDialog');
+ modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners
+
+ // Show the modal
+ const modalEl = document.getElementById('embedPhotoModal');
+ const modal = new bootstrap.Modal(modalEl);
+ modal.show();
+
+ // Reset activeCommentID when the modal is closed
+ modalEl.addEventListener('hide.bs.modal', event => {
+ activeCommentID = 0;
+ });
+ }
};
const choosePhotoFromAlbum = (album) => {
@@ -442,15 +466,16 @@
.then(data => {
if (data.status) {
- const modalLabel = document.getElementById('embedPhotoModalLabel');
- const modalBody = document.getElementById('embedPhotoModalBodyAlbumDialog');
+ const modalLabel = isModalAction ? document.getElementById('reactions_extra_top') : document.getElementById('embedPhotoModalLabel');
+ const modalBody = isModalAction ? document.getElementById('reactions_extra_middle') : document.getElementById('embedPhotoModalBodyAlbumDialog');
- modalLabel.innerHTML = '{{$modalchooseimages}}';
+ modalLabel.innerHTML = '<h3>{{$modalchooseimages}}</h3>';
modalBody.innerHTML = '<div><div class="nav nav-pills flex-column"><li class="nav-item"><a class="nav-link" href="#" onclick="initializeEmbedPhotoDialog(); return false;"><i class="bi bi-chevron-left"></i>&nbsp;{{$modaldiffalbum}}</a></li></div><br></div>';
modalBody.innerHTML += data.content;
// Make sure the loaded script is executed
const scripts = modalBody.querySelectorAll('script');
+
scripts.forEach(script => {
const scriptContent = script.textContent || script.innerText;
eval(scriptContent); // Execute the script
@@ -475,7 +500,6 @@
.then(ddata => {
if (ddata.status) {
addActiveEditorText(ddata.photolink);
- preview_post();
} else {
console.error("{{$modalerrorlink}}: " + ddata.errormsg);
}
@@ -502,10 +526,10 @@
.then(data => {
if (data.status) {
const albums = data.albumlist;
- const modalLabel = document.getElementById('embedPhotoModalLabel');
- const modalBodyList = document.getElementById('embedPhotoModalBodyAlbumList');
+ const modalLabel = isModalAction ? document.getElementById('reactions_extra_top') : document.getElementById('embedPhotoModalLabel');
+ const modalBodyList = isModalAction ? document.getElementById('reactions_extra_middle') : document.getElementById('embedPhotoModalBodyAlbumList');
- modalLabel.innerHTML = '{{$modalchoosealbum}}';
+ modalLabel.innerHTML = '<h3>{{$modalchoosealbum}}</h3>';
modalBodyList.innerHTML = '<ul class="nav nav-pills flex-column"></ul>';
albums.forEach(album => {
@@ -551,7 +575,9 @@
textarea.value = currentText + data;
textarea.focus();
textarea.click();
- preview_comment(activeCommentID);
+ if (!isModalAction) {
+ preview_comment(activeCommentID);
+ }
}
} else {
addeditortext(data);
diff --git a/view/tpl/js_strings.tpl b/view/tpl/js_strings.tpl
index 2a374fdf8..0e438f450 100644
--- a/view/tpl/js_strings.tpl
+++ b/view/tpl/js_strings.tpl
@@ -36,6 +36,7 @@
'pinned' : "{{$pinned}}",
'pin_item' : "{{$pin_item}}",
'unpin_item' : "{{$unpin_item}}",
+ 'dblclick_to_exit_zoom' : "{{$dblclick_to_exit_zoom}}",
'monthNames' : [ "{{$January}}","{{$February}}","{{$March}}","{{$April}}","{{$May}}","{{$June}}","{{$July}}","{{$August}}","{{$September}}","{{$October}}","{{$November}}","{{$December}}" ],
'monthNamesShort' : [ "{{$Jan}}","{{$Feb}}","{{$Mar}}","{{$Apr}}","{{$MayShort}}","{{$Jun}}","{{$Jul}}","{{$Aug}}","{{$Sep}}","{{$Oct}}","{{$Nov}}","{{$Dec}}" ],
diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl
index 113660c7e..be66c00d4 100644
--- a/view/tpl/notifications_widget.tpl
+++ b/view/tpl/notifications_widget.tpl
@@ -88,6 +88,7 @@
}
else {
if (!document.hidden) {
+ sse_fallback();
sse_fallback_interval = setInterval(sse_fallback, updateInterval);
}
diff --git a/view/tpl/pinned_item.tpl b/view/tpl/pinned_item.tpl
index b5b5c931e..db3a175da 100644
--- a/view/tpl/pinned_item.tpl
+++ b/view/tpl/pinned_item.tpl
@@ -102,7 +102,7 @@
<div class="p-2 wall-item-tools d-flex justify-content-between">
<div class="wall-item-tools-left hstack gap-1" id="pinned-item-tools-left-{{$id}}">
{{foreach $responses as $verb=>$response}}
- <button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$observer_activity.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}" onclick="request({{$id}}, '{{$rawmid}}', '{{$verb}}', {{$parent}}, '{{$mid}}'); return false;" id="pinned-item-{{$verb}}-{{$id}}">
+ <button type="button" title="{{$response.count}} {{$response.button.label}}" class="disabled btn btn-sm btn-link{{if !$observer_activity.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}" id="pinned-item-{{$verb}}-{{$id}}">
<i class="bi bi-{{$response.button.icon}} generic-icons"></i>{{if $response.count}}<span style="display: inline-block; margin-top: -.25rem;" class="align-top">{{$response.count}}</span>{{/if}}
</button>
{{/foreach}}