Compare commits

..

59 Commits
2.1.1 ... 2.1.4

Author SHA1 Message Date
Jonathan White
cb283bb95a Release 2.1.4 2017-04-09 10:20:52 -04:00
Jonathan White
b81d8bf97a Update translations 2017-04-09 10:20:52 -04:00
Jonathan White
918db68c52 Bumped version to 2.1.4, updated CHANGELOG 2017-04-09 09:54:55 -04:00
Weslly
aba0633a78 Bring HTTP Confirmation window to the front 2017-04-08 17:40:43 -04:00
Jonathan White
e684e230a6 Bumped up version of KeePassHTTP to 1.8.4.2
* No protocol changes required
* Fixes #439
2017-04-04 22:58:33 -04:00
Janek Bevendorff
76dcfb5ed0 Release 2.1.3
- Fix possible overflow in zxcvbn library [#363]
- Revert HiDPI setting to avoid problems on laptop screens [#332]
- Set file meta properties in Windows executable [#330]
- Suppress error message when auto-reloading a locked database [#345]
- Improve usability of question dialog when database is already locked by a different instance [#346]
- Fix compiler warnings in QHttp library [#351]
- Use unified toolbar on Mac OS X [#361]
- Fix an issue on X11 where the main window would be raised instead of closed on Alt+F4 [#362]
2017-03-02 21:56:54 +01:00
Janek Bevendorff
8a69421dc9 Update translations 2017-03-02 21:56:51 +01:00
Janek Bevendorff
e2d098dd9b Bump version to 2.1.3, update CHANGELOG 2017-03-02 21:56:22 +01:00
John Lindgren
d45c2cf0f2 closeEvent() should always hide the window, never raise it.
This fixes an issue on X11 where Alt-F4 would not close the window, due
to toggleWindow() believing the window is inactive and trying to raise
it.  Avoid the problem by closing the window unconditionally.
2017-03-02 13:52:57 +01:00
Hanno
3e76f7af0f Fix stack buffer overflow in zxcvbn.
The array PossChars is filled with a 48 byte string plus a trailing zero
byte. Therefore it needs to be 49 bytes long.
2017-03-02 13:38:14 +01:00
Janek Bevendorff
52ab7b8865 Use unified toolbar on OS X 2017-03-01 23:36:27 +01:00
Janek Bevendorff
a31c423d9e Fix compiler warnings in QHttp library 2017-02-26 22:13:22 -05:00
Janek Bevendorff
04b3b3dbc5 Assign role 'NoRole' instead of 'Accept' to not mess with button order 2017-02-26 13:33:28 -05:00
Janek Bevendorff
4ec2fe556a Fix impossible dialog by providing a proper question with approriate answers, resolves #202 2017-02-24 22:10:19 -05:00
Janek Bevendorff
311e7802e5 Don't show error message when trying to reload a locked database 2017-02-25 03:15:32 +01:00
Edward Jones
873871a42c Update text in HTTP settings and a hard-to-translate command line hint 2017-02-21 22:36:11 +01:00
Jonathan White
070ad695ec Updated snapcraft build to include networking 2017-02-20 14:16:04 +01:00
Janek Bevendorff
a56bcc8903 Set windows EXE resource properties, resolves #329 2017-02-20 00:33:11 +01:00
TheZ3ro
7613f1b726 Merge pull request #332 from keepassxreboot/hotfix/323-revert-hidpi-scaling
Revert "Enable High DPI scaling", resolves #323
2017-02-20 00:18:11 +01:00
Janek Bevendorff
cc44a833d5 Revert "Enable High DPI scaling", resolves #323
This reverts commit 188cac34ce.
2017-02-19 23:42:05 +01:00
Janek Bevendorff
b9279f73fa Release 2.1.2
- Ask for save location when creating a new database [#302]
- Remove Libmicrohttpd dependency to clean up the code and ensure better OS X compatibility [#317, #265]
- Prevent Qt from degrading Wifi network performance on certain platforms [#318]
- Visually refine user interface on OS X and other platforms [#299]
- Remove unusable tray icon setting on OS X [#293]
- Fix compositing glitches on Ubuntu and prevent flashing when minimizing to the tray at startup [#307]
- Fix AppImage tray icon on Ubuntu [#277, #273]
- Fix global menu disappearing after restoring KeePassXC from the tray on Ubuntu [#276]
- Fix result order in entry search [#320]
- Enable HiDPI scaling on supported platforms [#315]
- Remove empty directories from installation target [#282]
2017-02-17 16:21:39 +01:00
Janek Bevendorff
d8923123fa Update translations 2017-02-17 16:21:35 +01:00
Janek Bevendorff
9d55369c57 Update CHANGELOG 2017-02-17 16:20:35 +01:00
Janek Bevendorff
217e95e425 Bump version to 2.1.2 2017-02-17 15:56:05 +01:00
louib
d44b811b0d Merge pull request #320 from keepassxreboot/hotfix/319-search-sort-order
Fix sort order when searching
2017-02-17 09:48:38 -05:00
Janek Bevendorff
b61ecabed3 Fix sort order when searching, resolves #319 2017-02-17 14:18:18 +01:00
Janek Bevendorff
ebdb10e7f9 Add release folder, CLion and KDevelop project files 2017-02-16 21:25:04 -05:00
Janek Bevendorff
c2f3396753 Re-implement favicon fetching with QHttp, resolves #306 2017-02-16 21:25:04 -05:00
Janek Bevendorff
1b8366f040 Enable High DPI scaling, resolves #221 2017-02-16 20:57:17 -05:00
Janek Bevendorff
7df6d27900 Fix and clean up CMake files 2017-02-17 02:43:25 +01:00
Jonathan White
e9e92d0892 Fully functional http plugin with qhttp 2017-02-17 02:43:25 +01:00
Janek Bevendorff
86f2c9d350 Always release socket to allow consecutive HTTPS connections 2017-02-17 02:43:25 +01:00
Janek Bevendorff
a0ebbf997d Fix Host header always having port 65535 when URI does not contain explicit port 2017-02-17 02:43:25 +01:00
Janek Bevendorff
9d5d3081dc Implement basic SSL client and server sockets 2017-02-17 02:43:25 +01:00
Jonathan White
5274826e5c Implemented qhttp in server protocol 2017-02-17 02:43:25 +01:00
Janek Bevendorff
6dcb83f913 Disable 'Cancel' button on ChangeMasterKeyWidget when setting an initial password for a new database, follow-up to #302 2017-02-17 00:09:36 +01:00
Janek Bevendorff
daf0b72eed Right-align expires checkbox 2017-02-16 10:35:39 +01:00
Janek Bevendorff
ac52f73af2 Pixel-perfect entry edit widgets 2017-02-16 10:35:39 +01:00
Janek Bevendorff
832465a2a3 Correct link in about dialog, fix typo and make text selectable 2017-02-16 10:35:39 +01:00
Janek Bevendorff
3768145c9b Make 'General' the default tab in HTTP settings (was 'Advanced' before) 2017-02-16 10:35:39 +01:00
Janek Bevendorff
337161be02 Change size policy of alphabet buttons to avoid tiny button sizes 2017-02-16 10:35:39 +01:00
Janek Bevendorff
510904ebea Use proper layout for database settings and fix spacings on OS X 2017-02-16 10:35:39 +01:00
Janek Bevendorff
a37b98d95d Fix edit entry form alignment and autotype settings '+'/'-' buttons 2017-02-16 10:35:39 +01:00
Janek Bevendorff
35788f8654 Fix DatabaseOpenWidget alignment on Mac OS X 2017-02-16 10:35:39 +01:00
Janek Bevendorff
ec17199feb Ask for save location when creating new DB and change default name to 'Passwords.kdbx', resolves #285 2017-02-15 22:52:43 -05:00
Janek Bevendorff
012d0ee885 Remove lambda to be compatible with Qt 5.2 2017-02-14 22:21:53 +01:00
Janek Bevendorff
9b211928a9 Fix tabbar with only a single tab not hidden anymore when minimizing to tray at startup 2017-02-14 22:21:53 +01:00
Janek Bevendorff
fed210dc38 Also show when minimize on startup is enabled, but not minimize to tray 2017-02-14 22:21:53 +01:00
Janek Bevendorff
b73549fd35 Fix a bug where the window would sometimes not show up after restoring from tray
After this patch, the window will not have the window manager's restore animation anymore, but will be reliably shown
2017-02-14 22:21:53 +01:00
Janek Bevendorff
5bb6c4d9e4 Check for isVisible() instead of isNativeMenuBar() to make global menu hack work with appmenu-qt5, follow-up fix for #271 2017-02-14 22:21:53 +01:00
Janek Bevendorff
7a344930ec Don't try to show window when 'Minimize at startup' is enabled, prevents rendering glitches in Unity, resolves #304 2017-02-14 22:21:53 +01:00
Janek Bevendorff
ef53600d74 Merge pull request #293 from louib/fix/systrayOptionVisibleMac
Fix : systray option visible mac
2017-02-11 20:10:19 +01:00
Louis-Bertrand Varin
81cdcb4b62 Hide systray options on Mac 2017-02-11 13:49:19 -05:00
Janek Bevendorff
415b114dac Don't install empty icon directories, resolves #281 2017-02-11 08:20:45 -05:00
Janek Bevendorff
ef082c2e1f Remove check for Qt >= 5.7 2017-02-09 21:23:46 -05:00
Janek Bevendorff
8a26cfad79 Re-register global D-Bus menu when restoring window from tray, resolves #271 2017-02-09 21:23:46 -05:00
Janek Bevendorff
38d64a34a1 Improve minimize to tray 2017-02-09 21:23:46 -05:00
Janek Bevendorff
d1b403333c Unset XDG_DATA_DIRS before launching app, resolves #194, reverts #273 2017-02-10 01:55:09 +01:00
Janek Bevendorff
6c45fcbfc7 Don't try to use theme icons for the system tray, resolves #194
Qt also looks in the program's working directory for icons, but apparently, the Ubuntu system tray doesn't, resulting in missing tray icons
2017-02-08 17:55:50 +01:00
94 changed files with 13368 additions and 3234 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
CMakeLists.txt.*
build*/
release*/
.idea/
*.iml
*.kdev4

View File

@@ -49,8 +49,6 @@ cp -a ../../bin-release/* .
cp -a ./usr/local/* ./usr
rm -R ./usr/local
rmdir ./opt 2> /dev/null
patch_strings_in_file /usr/local ././
patch_strings_in_file /usr ./
# bundle Qt platform plugins and themes
QXCB_PLUGIN="$(find /usr/lib -name 'libqxcb.so' 2> /dev/null)"
@@ -76,6 +74,11 @@ cat << EOF > ./usr/bin/keepassxc_env
#export QT_QPA_PLATFORMTHEME=gtk2
export LD_LIBRARY_PATH="../opt/qt58/lib:\${LD_LIBRARY_PATH}"
export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}"
# unset XDG_DATA_DIRS to make tray icon work in Ubuntu Unity
# see https://github.com/probonopd/AppImageKit/issues/351
unset XDG_DATA_DIRS
exec keepassxc "\$@"
EOF
chmod +x ./usr/bin/keepassxc_env

View File

@@ -1,3 +1,36 @@
2.1.4 (2017-04-09)
=========================
- Bumped KeePassHTTP version to 1.8.4.2
- KeePassHTTP confirmation window comes to foreground [#466]
2.1.3 (2017-03-03)
=========================
- Fix possible overflow in zxcvbn library [#363]
- Revert HiDPI setting to avoid problems on laptop screens [#332]
- Set file meta properties in Windows executable [#330]
- Suppress error message when auto-reloading a locked database [#345]
- Improve usability of question dialog when database is already locked by a different instance [#346]
- Fix compiler warnings in QHttp library [#351]
- Use unified toolbar on Mac OS X [#361]
- Fix an issue on X11 where the main window would be raised instead of closed on Alt+F4 [#362]
2.1.2 (2017-02-17)
=========================
- Ask for save location when creating a new database [#302]
- Remove Libmicrohttpd dependency to clean up the code and ensure better OS X compatibility [#317, #265]
- Prevent Qt from degrading Wifi network performance on certain platforms [#318]
- Visually refine user interface on OS X and other platforms [#299]
- Remove unusable tray icon setting on OS X [#293]
- Fix compositing glitches on Ubuntu and prevent flashing when minimizing to the tray at startup [#307]
- Fix AppImage tray icon on Ubuntu [#277, #273]
- Fix global menu disappearing after restoring KeePassXC from the tray on Ubuntu [#276]
- Fix result order in entry search [#320]
- Enable HiDPI scaling on supported platforms [#315]
- Remove empty directories from installation target [#282]
2.1.1 (2017-02-06)
=========================

View File

@@ -38,8 +38,8 @@ option(WITH_XC_AUTOTYPE "Include Autotype." OFF)
option(WITH_XC_HTTP "Include KeePassHTTP." OFF)
option(WITH_XC_YUBIKEY "Include Yubikey support." OFF)
set(KEEPASSXC_VERSION "2.1.1")
set(KEEPASSXC_VERSION_NUM "2.1.1")
set(KEEPASSXC_VERSION "2.1.4")
set(KEEPASSXC_VERSION_NUM "2.1.4")
if("${CMAKE_C_COMPILER}" MATCHES "clang$" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1)
@@ -166,6 +166,9 @@ find_package(Qt5Widgets 5.2 REQUIRED)
find_package(Qt5Test 5.2 REQUIRED)
find_package(Qt5LinguistTools 5.2 REQUIRED)
find_package(Qt5Network 5.2 REQUIRED)
if (UNIX AND NOT APPLE)
find_package(Qt5DBus 5.2 REQUIRED)
endif()
set(CMAKE_AUTOMOC ON)
# Debian sets the the build type to None for package builds.
@@ -176,10 +179,6 @@ find_package(LibGPGError REQUIRED)
find_package(Gcrypt 1.6.0 REQUIRED)
if (WITH_XC_HTTP)
find_package(LibMicroHTTPD REQUIRED)
endif(WITH_XC_HTTP)
find_package(ZLIB REQUIRED)
check_cxx_source_compiles("
@@ -217,7 +216,7 @@ if(UNIX)
endif()
endif()
include_directories(SYSTEM ${GCRYPT_INCLUDE_DIR} ${MHD_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
include_directories(SYSTEM ${GCRYPT_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
include(FeatureSummary)

View File

@@ -0,0 +1,118 @@
# The MIT License (MIT)
#
# Copyright (c) 2015, by [halex2005](mailto:akharlov@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.
include (CMakeParseArguments)
set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR})
# generate_product_version() function
#
# This function uses VersionInfo.in template file and VersionResource.rc file
# to generate WIN32 resource with version information and general resource strings.
#
# Usage:
# generate_product_version(
# SomeOutputResourceVariable
# NAME MyGreatProject
# ICON ${PATH_TO_APP_ICON}
# VERSION_MAJOR 2
# VERSION_MINOR 3
# VERSION_PATH ${BUILD_COUNTER}
# VERSION_REVISION ${BUILD_REVISION}
# )
# where BUILD_COUNTER and BUILD_REVISION could be values from your CI server.
#
# You can use generated resource for your executable targets:
# add_executable(target-name ${target-files} ${SomeOutputResourceVariable})
#
# You can specify resource strings in arguments:
# NAME - name of executable (no defaults, ex: Microsoft Word)
# BUNDLE - bundle (${NAME} is default, ex: Microsoft Office)
# VERSION_MAJOR - 1 is default
# VERSION_MINOR - 0 is default
# VERSION_PATCH - 0 is default
# COMPANY_NAME - your company name (no defaults)
# COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default
# COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default
# ORIGINAL_FILENAME - ${NAME} is default
# INTERNAL_NAME - ${NAME} is default
# FILE_DESCRIPTION - ${NAME} is default
function(generate_product_version outfiles)
set (options)
set (oneValueArgs
NAME
BUNDLE
VERSION_MAJOR
VERSION_MINOR
VERSION_PATCH
COMPANY_NAME
COMPANY_COPYRIGHT
COMMENTS
ORIGINAL_FILENAME
INTERNAL_NAME
FILE_DESCRIPTION)
set (multiValueArgs)
cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "")
set(PRODUCT_BUNDLE "${PRODUCT_NAME}")
endif()
if (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "")
set(PRODUCT_VERSION_MAJOR 1)
endif()
if (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "")
set(PRODUCT_VERSION_MINOR 0)
endif()
if (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "")
set(PRODUCT_VERSION_PATCH 0)
endif()
if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "")
string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y")
set(PRODUCT_COMPANY_COPYRIGHT "Copyright (C) ${PRODUCT_CURRENT_YEAR} ${PRODUCT_COMPANY_NAME}")
endif()
if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "")
set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}")
endif()
if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "")
set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}")
endif()
if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "")
set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}")
endif()
if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "")
set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}")
endif()
set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h)
set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc)
configure_file(
${GenerateProductVersionCurrentDir}/VersionInfo.in
${_VersionInfoFile}
@ONLY)
configure_file(
${GenerateProductVersionCurrentDir}/VersionResource.rc
${_VersionResourceFile}
COPYONLY)
list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile})
set (${outfiles} ${${outfiles}} PARENT_SCOPE)
endfunction()

68
cmake/VersionInfo.in Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
#ifndef PRODUCT_VERSION_MAJOR
#define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@
#endif
#ifndef PRODUCT_VERSION_MINOR
#define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@
#endif
#ifndef PRODUCT_VERSION_PATCH
#define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@
#endif
#ifndef FILE_VERSION_MAJOR
#define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@
#endif
#ifndef FILE_VERSION_MINOR
#define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@
#endif
#ifndef FILE_VERSION_PATCH
#define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@
#endif
#ifndef __TO_STRING
#define __TO_STRING_IMPL(x) #x
#define __TO_STRING(x) __TO_STRING_IMPL(x)
#endif
#define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR)
#define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH)
#define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,0
#define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "\0"
#define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR)
#define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH)
#define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,0
#define FILE_VERSION_RESOURCE_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "\0"
#ifndef PRODUCT_COMMENTS
#define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0"
#endif
#ifndef PRODUCT_COMPANY_NAME
#define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0"
#endif
#ifndef PRODUCT_COMPANY_COPYRIGHT
#define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0"
#endif
#ifndef PRODUCT_FILE_DESCRIPTION
#define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0"
#endif
#ifndef PRODUCT_INTERNAL_NAME
#define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0"
#endif
#ifndef PRODUCT_ORIGINAL_FILENAME
#define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0"
#endif
#ifndef PRODUCT_BUNDLE
#define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0"
#endif

36
cmake/VersionResource.rc Normal file
View File

@@ -0,0 +1,36 @@
#include "VersionInfo.h"
#include "winresrc.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION_RESOURCE
PRODUCTVERSION PRODUCT_VERSION_RESOURCE
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", PRODUCT_COMMENTS
VALUE "CompanyName", PRODUCT_COMPANY_NAME
VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_RESOURCE_STR
VALUE "InternalName", PRODUCT_INTERNAL_NAME
VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT
VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_BUNDLE
VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -21,9 +21,11 @@ install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database)
if(UNIX AND NOT APPLE)
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
FILES_MATCHING PATTERN "keepassx*.png" PATTERN "keepassx*.svgz")
FILES_MATCHING PATTERN "keepassx*.png" PATTERN "keepassx*.svgz"
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE)
install(DIRECTORY icons/application/ DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz")
FILES_MATCHING PATTERN "application-x-keepassxc.png" PATTERN "application-x-keepassxc.svgz"
PATTERN "status" EXCLUDE PATTERN "actions" EXCLUDE)
install(FILES linux/keepassxc.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES linux/keepassxc.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages)
endif(UNIX AND NOT APPLE)

View File

@@ -13,16 +13,16 @@
<source>About KeePassXC</source>
<translation>O aplikaci KeePassXC</translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC je šířeno pod GNU obecnou veřejnou licencí (GPL) verze 2 a (případně) 3.</translation>
</message>
<message>
<source>Extensions:
</source>
<translation>Rozšíření:
</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC je šířeno pod GNU obecnou veřejnou licencí (GPL) verze 2 a (případně) 3.</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -32,28 +32,28 @@
</message>
<message>
<source>Allow</source>
<translation>Povolit</translation>
<translation>Umožnit</translation>
</message>
<message>
<source>Deny</source>
<translation>Zamítnout</translation>
<translation>Odepřít</translation>
</message>
<message>
<source>%1 has requested access to passwords for the following item(s).
Please select whether you want to allow access.</source>
<translation>%1 si vyžádalo přístup k heslům u následujících položek.
Umožnit přístup? ()</translation>
<translation>%1 si vyžádalo přístup k heslům u následujících záznamů.
Umožnit přístup?</translation>
</message>
<message>
<source>KeePassXC HTTP Confirm Access</source>
<translation>Potvrzení přístupu KeePassXC HTTP</translation>
<translation>Schválení přístupu KeePassXC HTTP</translation>
</message>
</context>
<context>
<name>AutoType</name>
<message>
<source>Couldn&apos;t find an entry that matches the window title:</source>
<translation>Nedaří se nalézt položku, která by se shodovala s titulkem okna:</translation>
<translation>Nedaří se nalézt záznam který by se shodoval s titulkem okna:</translation>
</message>
<message>
<source>Auto-Type - KeePassXC</source>
@@ -79,7 +79,7 @@ Umožnit přístup? ()</translation>
<name>AutoTypeSelectDialog</name>
<message>
<source>Select entry to Auto-Type:</source>
<translation>Vyberte položku, kterou se bude automaticky vyplňovat:</translation>
<translation>Vyberte záznam, kterým se bude automaticky vyplňovat:</translation>
</message>
<message>
<source>Auto-Type - KeePassXC</source>
@@ -100,10 +100,6 @@ Umožnit přístup? ()</translation>
<source>Repeat password:</source>
<translation>Zopakujte heslo:</translation>
</message>
<message>
<source>Key file</source>
<translation>Soubor s klíčem</translation>
</message>
<message>
<source>Browse</source>
<translation>Procházet</translation>
@@ -142,22 +138,26 @@ Umožnit přístup? ()</translation>
</message>
<message>
<source>Do you really want to use an empty string as password?</source>
<translation>Opravdu chcete ponechat bez hesla, tedy nechráněné?</translation>
<translation>Opravdu ponechat bez hesla, tedy nechráněné?</translation>
</message>
<message>
<source>Different passwords supplied.</source>
<translation>Nepodařilo se vám zadat heslo do obou kolonek stejně.</translation>
<translation>Nepodařilo se vám zadat heslo stejně do obou kolonek.</translation>
</message>
<message>
<source>Failed to set key file</source>
<translation>Nezdařilo se nastavit soubor s klíčem</translation>
<translation>Nepodařilo se nastavit soubor s klíčem</translation>
</message>
<message>
<source>Failed to set %1 as the Key file:
%2</source>
<translation>Nezdařilo se nastavit %1 jako soubor s klíčem:
<translation>Nepodařilo se nastavit %1 jako soubor s klíčem:
%2</translation>
</message>
<message>
<source>&amp;Key file</source>
<translation>Soubor s &amp;klíčem</translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -231,8 +231,8 @@ Umožnit přístup? ()</translation>
<message>
<source>The database has been successfully repaired
You can now save it.</source>
<translation>Databáze je úspěšně opravená
Nyní jí můžete uložit.</translation>
<translation>Databáze je úspěšně opravená.
Nyní je možné ji uložit.</translation>
</message>
<message>
<source>Unable to repair the database.</source>
@@ -257,10 +257,6 @@ Nyní jí můžete uložit.</translation>
<source>Default username:</source>
<translation>Výchozí uživatelské jméno:</translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation>Namísto mazání přesouvat do Koše:</translation>
</message>
<message>
<source> MiB</source>
<translation> MiB</translation>
@@ -271,11 +267,15 @@ Nyní jí můžete uložit.</translation>
</message>
<message>
<source>Max. history items:</source>
<translation>Omezit počet uchovávaných předchozích verzí položky na:</translation>
<translation>Omezit počet uchovávaných předchozích verzí záznamů na:</translation>
</message>
<message>
<source>Max. history size:</source>
<translation>Omezit datový objem předchozích verzí položek na:</translation>
<translation>Omezit datový objem uchovávaných předchozích verzí záznamů na:</translation>
</message>
<message>
<source>Use recycle bin</source>
<translation>Namísto mazání přesouvat do Koše</translation>
</message>
</context>
<context>
@@ -394,13 +394,7 @@ Přesto zavřít a zahodit změny?</translation>
</message>
<message>
<source>Unable to open the database.</source>
<translation>Nepodařilo se otevřít databázi.</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation>Databáze kterou se pokoušíte otevřít je právě používaná jinou instancí KeePassXC.
Chcete ji přesto otevřít? Případně je možné databázi otevřít pouze pro čtení.</translation>
<translation>Databázi se nedaří otevřít.</translation>
</message>
<message>
<source>Merge database</source>
@@ -412,6 +406,26 @@ Do you want to save it anyway?</source>
<translation>Databáze kterou se pokoušíte uložit je právě používaná jinou instancí KeePassXC.
Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Passwords</source>
<translation>Hesla</translation>
</message>
<message>
<source>Database already opened</source>
<translation>Tato databáze je už otevřená</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation>Databáze kterou se pokoušíte otevřít je právě používaná jinou instancí KeePassXC.
Chcete ji přesto otevřít?</translation>
</message>
<message>
<source>Open read-only</source>
<translation>Otevřít pouze pro čtení</translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -421,27 +435,27 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Delete entry?</source>
<translation>Smazat položku?</translation>
<translation>Smazat záznam?</translation>
</message>
<message>
<source>Do you really want to delete the entry &quot;%1&quot; for good?</source>
<translation>Opravdu chcete nenávratně smazat položku „%1“?</translation>
<translation>Opravdu chcete nenávratně smazat záznam „%1“?</translation>
</message>
<message>
<source>Delete entries?</source>
<translation>Smazat položky?</translation>
<translation>Smazat záznamy?</translation>
</message>
<message>
<source>Do you really want to delete %1 entries for good?</source>
<translation>Opravdu chcete nenávratně smazat %1 položek?</translation>
<translation>Opravdu chcete nenávratně smazat %1 záznamů?</translation>
</message>
<message>
<source>Move entries to recycle bin?</source>
<translation>Přesunout položky do Koše?</translation>
<translation>Přesunout záznamy do Koše?</translation>
</message>
<message numerus="yes">
<source>Do you really want to move %n entry(s) to the recycle bin?</source>
<translation><numerusform>Opravdu chcete přesunout %n položku do Koše?</numerusform><numerusform>Opravdu chcete přesunout %n položky do Koše?</numerusform><numerusform>Opravdu chcete přesunout %n položek do Koše?</numerusform></translation>
<translation><numerusform>Opravdu přesunout %n záznam do Koše? ()</numerusform><numerusform>Opravdu přesunout %n záznamy do Koše? ()</numerusform><numerusform>Opravdu přesunout %n záznamů do Koše?</numerusform></translation>
</message>
<message>
<source>Delete group?</source>
@@ -461,11 +475,11 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Move entry to recycle bin?</source>
<translation>Přesunout položku do koše?</translation>
<translation>Přesunout záznam do Koše?</translation>
</message>
<message>
<source>Do you really want to move entry &quot;%1&quot; to the recycle bin?</source>
<translation>Opravdu si přejete přesunout položku &quot;%1&quot; do koše?</translation>
<translation>Opravdu přesunout záznam &quot;%1&quot; do Koše?</translation>
</message>
<message>
<source>Searching...</source>
@@ -485,7 +499,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>No Results</source>
<translation>Žádné výsledky</translation>
<translation>Nic nenalezeno</translation>
</message>
<message>
<source>Execute command?</source>
@@ -493,7 +507,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Do you really want to execute the following command?&lt;br&gt;&lt;br&gt;%1&lt;br&gt;</source>
<translation>Opravdu chcete spustit následující příkaz?&lt;br&gt;&lt;br&gt;%1&lt;br&gt;</translation>
<translation>Opravdu spustit následující příkaz?&lt;br&gt;&lt;br&gt;%1&lt;br&gt;</translation>
</message>
<message>
<source>Remember my choice</source>
@@ -505,7 +519,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>The database file has changed. Do you want to load the changes?</source>
<translation>Soubor s databází byl změněn. Přejete si načíst změny?</translation>
<translation>Soubor s databází byl změněn. Načíst změny?</translation>
</message>
<message>
<source>Merge Request</source>
@@ -513,26 +527,22 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>The database file has changed and you have unsaved changes.Do you want to merge your changes?</source>
<translation>Soubor s databází byl mezitím změněn a vaše změny nejsou uložené. Přejete si vaše změny sloučit?</translation>
<translation>Soubor s databází byl změněn a vaše změny do něj nejsou uloženy. Přejete si své změny začlenit?</translation>
</message>
<message>
<source>Autoreload Failed</source>
<translation>Automatické opětovné načtení se nezdařilo</translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation>Nepodařilo se zpracovat nebo odemknout nový soubor s databází během pokusu o její opětovné načtení.</translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation>Nepodařilo se otevřít nový soubor s databází během pokusu o její opětovné načtení.</translation>
<translation>Nepodařilo se otevřít nový soubor s databází během pokusu o opětovné načtení této.</translation>
</message>
</context>
<context>
<name>EditEntryWidget</name>
<message>
<source>Entry</source>
<translation>Položka</translation>
<translation>Záznam</translation>
</message>
<message>
<source>Advanced</source>
@@ -556,15 +566,15 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Entry history</source>
<translation>Historie položky</translation>
<translation>Historie záznamu</translation>
</message>
<message>
<source>Add entry</source>
<translation>Přidat položku</translation>
<translation>Přidat záznam</translation>
</message>
<message>
<source>Edit entry</source>
<translation>Upravit položku</translation>
<translation>Upravit záznam</translation>
</message>
<message>
<source>Error</source>
@@ -572,7 +582,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Different passwords supplied.</source>
<translation>Zadání hesla se neshodují.</translation>
<translation>Nepodařilo se vám zadat heslo stejně do obou kolonek.</translation>
</message>
<message>
<source>New attribute</source>
@@ -648,15 +658,7 @@ Chcete ji přesto uložit?</translation>
<name>EditEntryWidgetAutoType</name>
<message>
<source>Enable Auto-Type for this entry</source>
<translation>Zapnout automatické vyplňování této položky</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation>Převzít výchozí posloupnost automatického vyplňování od skupiny</translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation>Použít vlastní posloupnost automatického vyplňování:</translation>
<translation>Zapnout automatické vyplňování tohoto záznamu</translation>
</message>
<message>
<source>+</source>
@@ -671,11 +673,19 @@ Chcete ji přesto uložit?</translation>
<translation>Titulek okna:</translation>
</message>
<message>
<source>Use default sequence</source>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Převzít výchozí posloupnost automatického vyplňování od skupiny</translation>
</message>
<message>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>Po&amp;užít vlastní posloupnost automatického vyplňování:</translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation>Použít výchozí posloupnost</translation>
</message>
<message>
<source>Set custom sequence:</source>
<source>Set custo&amp;m sequence:</source>
<translation>Nastavit vlastní posloupnost:</translation>
</message>
</context>
@@ -792,7 +802,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Use default auto-type sequence of parent group</source>
<translation>Použít výchozí posloupnost automatického vyplňování z nadřazené skupiny</translation>
<translation>Použít výchozí posloupnost automatického vyplňování od nadřazené skupiny</translation>
</message>
<message>
<source>Set default auto-type sequence</source>
@@ -801,14 +811,6 @@ Chcete ji přesto uložit?</translation>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation>Použít výchozí ikonu</translation>
</message>
<message>
<source>Use custom icon</source>
<translation>Použít svou vlastní ikonu</translation>
</message>
<message>
<source>Add custom icon</source>
<translation>Přidat svou vlastní ikonu</translation>
@@ -839,19 +841,27 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Download favicon</source>
<translation>Stáhnout favicon</translation>
<translation>Stáhnout ikonu webu (favicon)</translation>
</message>
<message>
<source>Unable to fetch favicon.</source>
<translation>Favicon se nepodařilo stáhnout.</translation>
<translation>Ikonu webu (favicon) se nedaří stáhnout.</translation>
</message>
<message>
<source>Can&apos;t read icon</source>
<translation>Nepodařilo se načíst ikonu</translation>
<translation>Ikonu se nedaří načíst</translation>
</message>
<message>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation>Ikonu nelze smazat. Ještě ji používá %1 položek.</translation>
<translation>Ikonu nelze smazat, protože je používaná ještě %1 dalšími záznamy.</translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation>Po&amp;užít výchozí ikonu</translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation>Použít svou vlastní ikonu</translation>
</message>
</context>
<context>
@@ -862,11 +872,11 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Modified:</source>
<translation>Okamžik minulé úpravy:</translation>
<translation>Okamžik nejnovější úpravy:</translation>
</message>
<message>
<source>Accessed:</source>
<translation>Okamžik minulého přístupu:</translation>
<translation>Okamžik nejnovějšího přístupu:</translation>
</message>
<message>
<source>Uuid:</source>
@@ -891,7 +901,7 @@ Chcete ji přesto uložit?</translation>
<name>EntryHistoryModel</name>
<message>
<source>Last modified</source>
<translation>Okamžik minulé změny</translation>
<translation>Okamžik nejnovější změny</translation>
</message>
<message>
<source>Title</source>
@@ -960,7 +970,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Numbers</source>
<translation>Čísla</translation>
<translation>Číslice</translation>
</message>
<message>
<source>0-9</source>
@@ -972,15 +982,15 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>/*_&amp; ...</source>
<translation>/*_&amp; ...</translation>
<translation>/*_&amp; </translation>
</message>
<message>
<source>Exclude look-alike characters</source>
<translation>Vyloučit podobné znaky</translation>
<translation>Vynechat podobně vypadající znaky (předejití záměně)</translation>
</message>
<message>
<source>Ensure that the password contains characters from every group</source>
<translation>Heslo musí obsahovat znak z každé skupiny</translation>
<translation>Zajistit aby heslo obsahovalo znaky ze všech zvolených skupin znaků</translation>
</message>
<message>
<source>Accept</source>
@@ -1006,7 +1016,7 @@ Chcete ji přesto uložit?</translation>
<name>KeePass1Reader</name>
<message>
<source>Unable to read keyfile.</source>
<translation>Nedaří se načíst soubor s klíčem.</translation>
<translation>Soubor s klíčem se nedaří načíst.</translation>
</message>
<message>
<source>Not a KeePass database.</source>
@@ -1045,7 +1055,7 @@ Chcete ji přesto uložit?</translation>
</message>
<message>
<source>Wrong key or database file is corrupt.</source>
<translation>Chybný klíč nebo je databáze poškozená.</translation>
<translation>Byl zadán chybný klíč, nebo je poškozen databázový soubor.</translation>
</message>
<message>
<source>Unable to calculate master key</source>
@@ -1058,7 +1068,7 @@ You can import it by clicking on Database &gt; 'Import KeePass 1 database'.
This is a one-way migration. You won&apos;t be able to open the imported database with the old KeePassX 0.4 version.</source>
<translation>Zvolený soubor je databáze ve starém formátu KeePass 1 (.kdb).
Můžete ho importovat pomocí Databáze Importovat databázi KeePass 1.
Můžete ho importovat pomocí Databáze Importovat databázi ve formátu KeePass 1.
Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otevřít ve staré verzi KeePassX 0.4.</translation>
</message>
</context>
@@ -1066,7 +1076,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
<name>Main</name>
<message>
<source>Fatal error while testing the cryptographic functions.</source>
<translation>Při ověřová šifrovacích funkcí byl zjištěn fatální nedostatek.</translation>
<translation>Při zkouše šifrovacích funkcí byl zjištěn fatální nedostatek.</translation>
</message>
<message>
<source>KeePassXC - Error</source>
@@ -1121,7 +1131,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>All files</source>
<translation>Všechny soubory</translation>
<translation>Veškeré soubory</translation>
</message>
<message>
<source>Save repaired database</source>
@@ -1145,7 +1155,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>E&amp;ntries</source>
<translation>Položky</translation>
<translation>Záz&amp;namy</translation>
</message>
<message>
<source>Copy att&amp;ribute to clipboard</source>
@@ -1153,7 +1163,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>&amp;Groups</source>
<translation>Skupniny</translation>
<translation>Skupiny</translation>
</message>
<message>
<source>&amp;View</source>
@@ -1185,19 +1195,19 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>Merge from KeePassX database</source>
<translation>Sloučit s databází KeePassX</translation>
<translation>Sloučit z databáze KeePassX</translation>
</message>
<message>
<source>&amp;Add new entry</source>
<translation>Přid&amp;at novou položku</translation>
<translation>Přid&amp;at nový záznam</translation>
</message>
<message>
<source>&amp;View/Edit entry</source>
<translation>Zobrazit/Upra&amp;vit položku</translation>
<translation>Zobrazit/Upra&amp;vit záznam</translation>
</message>
<message>
<source>&amp;Delete entry</source>
<translation>Smazat položku</translation>
<translation>Smazat záznam</translation>
</message>
<message>
<source>&amp;Add new group</source>
@@ -1229,7 +1239,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>&amp;Clone entry</source>
<translation>Klonovat položku</translation>
<translation>Klonovat záznam</translation>
</message>
<message>
<source>&amp;Find</source>
@@ -1257,7 +1267,7 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
</message>
<message>
<source>&amp;Lock databases</source>
<translation>Uzamknout databázi</translation>
<translation>Uzamknout databáze</translation>
</message>
<message>
<source>&amp;Title</source>
@@ -1298,25 +1308,15 @@ Jedná se o jednosměrný převod. Databázi, vzniklou z importu, nepůjde otev
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation>Z&amp;obrazit oznámení když jsou požadovány přihlašovací údaje</translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation>V%rátit pouze položky, které nejlépe odpovídají dané
URL adrese namísto položek pro celou doménu</translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
<translation>&amp;Odpovídající schémata URL adres
Jsou vráceny pouze položky se stejným schématem (http://, https://, ftp://, atp.)</translation>
Je odpovídáno pouze záznamy se stejným schématem (http://, https://, ftp://, atp.)</translation>
</message>
<message>
<source>Sort matching entries by &amp;username</source>
<translation>Seřadit odpovídající položky dle &amp;uživatelského jména</translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation>Z právě otevřené databáze od&amp;ebrat veškeré sdílené šifrovací klíče</translation>
<translation>Seřadit odpovídající záznamy dle &amp;uživatelského jména</translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
@@ -1330,34 +1330,22 @@ Jsou vráceny pouze položky se stejným schématem (http://, https://, ftp://,
<source>Advanced</source>
<translation>Pokročilé</translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation>Následující zapněte pouze pokud víte, co děláte!</translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation>Vždy umožnit přístup k veškerým položkám</translation>
<translation>Vždy umožnit přístup ke všem zázn&amp;amům</translation>
</message>
<message>
<source>Always allow &amp;updating entries</source>
<translation>Vždy umožnit akt&amp;ualizovat položky</translation>
<translation>Vždy umožnit akt&amp;ualizovat záznamy</translation>
</message>
<message>
<source>Searc&amp;h in all opened databases for matching entries</source>
<translation>Vy&amp;hledat odpovídající položky ve všech otevřených databázích</translation>
<translation>Vy&amp;hledat odpovídající záznamy ve všech otevřených databázích</translation>
</message>
<message>
<source>Only the selected database has to be connected with a client!</source>
<translation>Je třeba, aby ke klientovi byly připojené pouze vybrané databáze!</translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation>V&amp;rátit také pokročilé textové řetězce které začínají na KPH:</translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation>Automatická vytváření nebo aktualizace nejsou u textových kolonek podporované!</translation>
</message>
<message>
<source>HTTP Port:</source>
<translation>HTTP port:</translation>
@@ -1372,17 +1360,17 @@ Jsou vráceny pouze položky se stejným schématem (http://, https://, ftp://,
</message>
<message>
<source>Sort &amp;matching entries by title</source>
<translation>Seřadit odpovídající položky dle názvu</translation>
<translation>Seřadit odpovídající záznamy dle názvu</translation>
</message>
<message>
<source>Enable KeepassXC HTTP protocol
This is required for accessing your databases from ChromeIPass or PassIFox</source>
<translation>Zapnout protokol KeePassXC HTTP
Toto je zapotřebí pro přístup do databáze z ChromeIPass nebo PassIFox</translation>
Toto je zapotřebí pro přístup do databáze z doplňku ChromeIPass nebo PassIFox pro webové prohlížeče</translation>
</message>
<message>
<source>KeePassXC will listen to this port on 127.0.0.1</source>
<translation>Na tomto portu bude KeePassXC očekávat spojení na adrese 127.0.0.1</translation>
<translation>KeePassXC bude očekávat spojení na tomto portu na adrese 127.0.0.1 (localhost)</translation>
</message>
<message>
<source>Cannot bind to privileged ports</source>
@@ -1394,6 +1382,28 @@ Using default port 19455.</source>
<translation>Není možné navázat na porty s číslem nižším, než 1024!
Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation>Odpovědět pouze záznamy, které nejlépe odpovídají dané
URL ad&amp;rese namísto záznamů pro celou doménu</translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation>Z právě otevřené databáze od&amp;ebrat veškeré sdílené šifrovací klíče</translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation>Následující předvolby mohou být nebezpečné. Měňte je pouze pokud víte, co děláte!</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>Odpovědět také kolonkami pok&amp;ročilých textových řetězců které začínají na KPH:</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation>Automatická vytváření nebo aktualizace nejsou u textových kolonek podporované!</translation>
</message>
</context>
<context>
<name>PasswordGeneratorWidget</name>
@@ -1439,7 +1449,7 @@ Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>entropy</source>
<translation>náhodnost</translation>
<translation>nahodilost</translation>
</message>
<message>
<source>&amp;Length:</source>
@@ -1447,11 +1457,11 @@ Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>Pick characters from every group</source>
<translation>Použít znaky ze všech skupin</translation>
<translation>Zajistit přítomnost znaků ze všech zvolených skupin</translation>
</message>
<message>
<source>Generate</source>
<translation>Vytvořit</translation>
<translation>Tvoř</translation>
</message>
<message>
<source>Close</source>
@@ -1463,27 +1473,27 @@ Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>Entropy: %1 bit</source>
<translation>Náhodnost: %1 bit</translation>
<translation>Nahodilost: %1 bitů</translation>
</message>
<message>
<source>Password Quality: %1</source>
<translation>Odolnost hesla: %1</translation>
<translation>Kvalita hesla: %1</translation>
</message>
<message>
<source>Poor</source>
<translation>Velmi nízká</translation>
<translation>Velmi slabé</translation>
</message>
<message>
<source>Weak</source>
<translation>Slabá</translation>
<translation>Slabé</translation>
</message>
<message>
<source>Good</source>
<translation>Dobrá</translation>
<translation>Dobré</translation>
</message>
<message>
<source>Excellent</source>
<translation>Skvělá</translation>
<translation>Skvělé</translation>
</message>
</context>
<context>
@@ -1513,7 +1523,7 @@ Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>Internal zlib error when decompressing: </source>
<translation>Vnitřní chyba knihovny zlib při rozbalování:</translation>
<translation>Během rozbalování se vyskytla vnitřní chyba v knihovně zlib:</translation>
</message>
</context>
<context>
@@ -1524,7 +1534,7 @@ Náhradně bude použit port 19455.</translation>
</message>
<message>
<source>Internal zlib error: </source>
<translation>Vnitřní chyba knihovny zlib:</translation>
<translation>Vnitřní chyba v knihovně zlib:</translation>
</message>
</context>
<context>
@@ -1552,11 +1562,11 @@ Náhradně bude použit port 19455.</translation>
<source>A shared encryption-key with the name &quot;%1&quot; already exists.
Do you want to overwrite it?</source>
<translation> existuje sdílený šifrovací klíč s názvem %1.
Přejete si ho přepsat?</translation>
Chcete ho přepsat?</translation>
</message>
<message>
<source>Do you want to update the information in %1 - %2?</source>
<translation>Přejete si aktualizovat informaci v %1 %2?</translation>
<translation>Chcete aktualizovat informaci v %1 %2?</translation>
</message>
<message>
<source>The active database is locked!
@@ -1574,11 +1584,11 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou.</translation>
</message>
<message>
<source>The active database does not contain an entry of KeePassHttp Settings.</source>
<translation>Právě otevřená databáze neobsahuje žádnou položku nastavení KeePassHttp.</translation>
<translation>Právě otevřená databáze neobsahuje žádný záznam nastavení KeePassHttp.</translation>
</message>
<message>
<source>Removing stored permissions...</source>
<translation>Odstraňuji uložená oprávnění...</translation>
<translation>Odstraňování uložených oprávnění</translation>
</message>
<message>
<source>Abort</source>
@@ -1590,7 +1600,7 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou.</translation>
</message>
<message>
<source>The active database does not contain an entry with permissions.</source>
<translation>Právě otevřená databáze neobsahuje položku s oprávněními.</translation>
<translation>Právě otevřená databáze neobsahuje záznam s oprávněními.</translation>
</message>
<message>
<source>KeePassXC: New key association request</source>
@@ -1600,9 +1610,9 @@ Buď jí odemkněte, nebo vyberte jinou, odemčenou.</translation>
<source>You have received an association request for the above key.
If you would like to allow it access to your KeePassXC database
give it a unique name to identify and accept it.</source>
<translation>Pokud jste obdrželi požadavek na přiřazení pro výše uvedený klíč.
Pokud chcete umožnit přístup do KeePassXC databáze, dejte jí
jedinečný název pro její identifikaci a potvrďte ho.</translation>
<translation>Obdrželi jste požadavek na přiřazení výše uvedeného klíče.
Pokud jím chcete umožnit přístup do KeePassXC databáze, zadejte pro j
jedinečný název pro identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>KeePassXC: Overwrite existing key?</source>
@@ -1610,7 +1620,7 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>KeePassXC: Update Entry</source>
<translation>KeePassXC: Aktualizovat položku</translation>
<translation>KeePassXC: Aktualizovat záznam</translation>
</message>
<message>
<source>KeePassXC: Database locked!</source>
@@ -1618,7 +1628,7 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>KeePassXC: Removed keys from database</source>
<translation>KeePassXC: Odebrány klíče z databáze</translation>
<translation>KeePassXC: Klíče odebrány z databáze</translation>
</message>
<message>
<source>KeePassXC: No keys found</source>
@@ -1634,7 +1644,7 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>KeePassXC: No entry with permissions found!</source>
<translation>KeePassXC: Nebyla nalezena položka s oprávněními!</translation>
<translation>KeePassXC: Nebyl nalezen záznam s oprávněními!</translation>
</message>
</context>
<context>
@@ -1668,11 +1678,11 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>Minimize when copying to clipboard</source>
<translation>Po zkopírování údaje do schránky automaticky minimalizovat</translation>
<translation>Po zkopírování údaje do schránky odklidit okno aplikace jeho automatickou minimalizací</translation>
</message>
<message>
<source>Use group icon on entry creation</source>
<translation>Pro vytvářenou položku použít ikonu skupiny, do které spadá</translation>
<translation>Pro vytvářený záznam použít ikonu skupiny, do které spadá</translation>
</message>
<message>
<source>Global Auto-Type shortcut</source>
@@ -1680,7 +1690,7 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>Use entry title to match windows for global auto-type</source>
<translation>Všeobecné automatické vyplňování provádět na základě shody titulku položky s titulkem okna.</translation>
<translation>Všeobecné automatické vyplňování provádět na základě shody titulku záznamu s titulkem okna.</translation>
</message>
<message>
<source>Language</source>
@@ -1700,15 +1710,15 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>Load previous databases on startup</source>
<translation>Při spuštění aplikace načíst minule otevřené databáze</translation>
<translation>Při spouštění aplikace načíst minule otevřené databáze</translation>
</message>
<message>
<source>Automatically reload the database when modified externally</source>
<translation>V okamžiku zásahu do databáze zvenčí ji načíst znovu</translation>
<translation>V případě úpravy zvenčí, automaticky opětovně načíst databázi</translation>
</message>
<message>
<source>Hide window to system tray instead of app exit</source>
<translation>Namísto zavření minimalizovat okno aplikace do oznamovací oblasti systémového panelu</translation>
<translation>Namísto ukončení aplikace skrýt její okno do oznamovací oblasti systémového panelu</translation>
</message>
<message>
<source>Minimize window at application startup</source>
@@ -1743,7 +1753,7 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
</message>
<message>
<source>Don&apos;t require password repeat when it is visible</source>
<translation>Pokud jsou zobrazená, nevyžadovat zopakování hesel</translation>
<translation>Pokud je viditelné, nevyžadovat zopakování zadání hesla</translation>
</message>
</context>
<context>
@@ -1764,16 +1774,12 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
<name>main</name>
<message>
<source>path to a custom config file</source>
<translation>umístění souboru s vlastními nastaveními</translation>
<translation>umístění vlastního souboru s nastaveními</translation>
</message>
<message>
<source>key file of the database</source>
<translation>soubor s klíčem k databázi</translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation>názvy databází s hesly (*.kdbx) které otevřít ()()</translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation>KeePassXC aplikace pro správu hesel, fungující na vícero operačních systémech</translation>
@@ -1782,5 +1788,9 @@ jedinečný název pro její identifikaci a potvrďte ho.</translation>
<source>read password of the database from stdin</source>
<translation>načíst heslo k databázi ze standardního vstupu</translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation>soubory s databázemi hesel k otevření (*.kdbx)</translation>
</message>
</context>
</TS>

View File

@@ -3,26 +3,26 @@
<name>AboutDialog</name>
<message>
<source>Revision</source>
<translation>Überarbeitung</translation>
<translation>Revision</translation>
</message>
<message>
<source>Using:</source>
<translation>In Benutzung:</translation>
<translation>Verwendet:</translation>
</message>
<message>
<source>About KeePassXC</source>
<translation>Über KeePassXC</translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC steht unter der GNU General Public License (GPL) version 2 (version 3).</translation>
</message>
<message>
<source>Extensions:
</source>
<translation>Erweiterungen:
</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC wird unter den Bedingungen der GNU General Public License (GPL) Version 2 oder Version 3 (je nach Ihrer Auswahl) vertrieben.</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -46,7 +46,7 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten.</translation>
</message>
<message>
<source>KeePassXC HTTP Confirm Access</source>
<translation>KeePassXC HTTP Erlaube Zugriff</translation>
<translation>Erlaube KeePassXC HTTP Zugriff</translation>
</message>
</context>
<context>
@@ -100,10 +100,6 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten.</translation>
<source>Repeat password:</source>
<translation>Passwort wiederholen:</translation>
</message>
<message>
<source>Key file</source>
<translation>Schlüsseldatei</translation>
</message>
<message>
<source>Browse</source>
<translation>Durchsuchen</translation>
@@ -157,6 +153,10 @@ Bitte wählen Sie, ob Sie den Zugriff erlauben möchten.</translation>
%2</source>
<translation>Festlegen von %1 als Schlüsseldatei nicht möglich: %2</translation>
</message>
<message>
<source>&amp;Key file</source>
<translation>&amp;Schlüsseldatei</translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -256,10 +256,6 @@ sie kann nun gespeichert werden.</translation>
<source>Default username:</source>
<translation>Standardbenutzername:</translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation>Verwende Papierkorb:</translation>
</message>
<message>
<source> MiB</source>
<translation> MiB</translation>
@@ -276,6 +272,10 @@ sie kann nun gespeichert werden.</translation>
<source>Max. history size:</source>
<translation>Max. Verlaufsgröße:</translation>
</message>
<message>
<source>Use recycle bin</source>
<translation>Papierkorb verwenden</translation>
</message>
</context>
<context>
<name>DatabaseTabWidget</name>
@@ -395,12 +395,6 @@ Discard changes and close anyway?</source>
<source>Unable to open the database.</source>
<translation>Öffnen der Datenbank ist nicht möglich.</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation>Die Datenbank, die geöffnet werden soll, ist aktuell von einer anderen Instanz von KeePassXC blockiert.
Soll sie dennoch geöffnet werden? Alternativ wird die Datenbank schreibgeschützt geöffnet.</translation>
</message>
<message>
<source>Merge database</source>
<translation>Datenbank zusammenführen</translation>
@@ -408,9 +402,29 @@ Soll sie dennoch geöffnet werden? Alternativ wird die Datenbank schreibgeschüt
<message>
<source>The database you are trying to save as is locked by another instance of KeePassXC.
Do you want to save it anyway?</source>
<translation>Die Datenbank, die gespeichert werden soll, ist von einer anderen Instanz von KeePassXC blockiert.
<translation>Die Datenbank, die gespeichert werden soll, wird von einer anderen KeePassXC-Instanz verwendet.
Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Passwords</source>
<translation>Passwörter</translation>
</message>
<message>
<source>Database already opened</source>
<translation>Datenbank bereits geöffnet</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation>Die Datenbank, die Sie versuchen, zu öffnen, wird bereits von einer anderen KeePassXC-Instanz verwendet.
Möchten Sie diese dennoch öffnen?</translation>
</message>
<message>
<source>Open read-only</source>
<translation>Schreibgeschützt öffnen</translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -464,11 +478,11 @@ Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Do you really want to move entry &quot;%1&quot; to the recycle bin?</source>
<translation>Möchten Sie wirklich Eintrag &quot;%1&quot; in den Papierkorb verschieben?</translation>
<translation>Möchten Sie wirklich den Eintrag &quot;%1&quot; in den Papierkorb verschieben?</translation>
</message>
<message>
<source>Searching...</source>
<translation>Am suchen...</translation>
<translation>Suche…</translation>
</message>
<message>
<source>No current database.</source>
@@ -492,7 +506,7 @@ Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Do you really want to execute the following command?&lt;br&gt;&lt;br&gt;%1&lt;br&gt;</source>
<translation>Den folgenden Befehl wirklich ausführen?&lt;br&gt;&lt;br&gt;%1&lt;br&gt;</translation>
<translation>Folgenden Befehl ausführen? &lt;br&gt;&lt;br&gt;%1&lt;br&gt;</translation>
</message>
<message>
<source>Remember my choice</source>
@@ -500,31 +514,27 @@ Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Autoreload Request</source>
<translation>Autoreload Anfrage</translation>
<translation>Automatisches Neuladen angefordert</translation>
</message>
<message>
<source>The database file has changed. Do you want to load the changes?</source>
<translation>Die Datenbank wurde verändert. Möchten Sie die Änderungen laden?</translation>
<translation>Die Datenbank hat sich geändert. Sollen die Änderungen geladen werden?</translation>
</message>
<message>
<source>Merge Request</source>
<translation>Zusammenführ-Anfrage</translation>
<translation>Zusammenführung angefragt</translation>
</message>
<message>
<source>The database file has changed and you have unsaved changes.Do you want to merge your changes?</source>
<translation>Die Datenbank wurde verändert und Sie haben ungespeicherte Änderungen. Möchten Sie Ihre Änderungen zusammenfügen?</translation>
<translation>Die Datenbankdatei wurde geändert und Sie haben noch nicht gespeicherte Änderungen. Wollen Sie Ihre Änderungen zusammenführen?</translation>
</message>
<message>
<source>Autoreload Failed</source>
<translation>Autoreload fehlgeschlagen</translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation>Kann Datenbank nicht durchsuchen oder entsperren während dem Autoreload-Versuch dieser Datenbank.</translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation>Kann die Datenbank nicht öffnen während dem Versuch, diese Datenbank automatisch neu zu laden.</translation>
<translation>Die neue Datenbankdatei konnte nicht geöffnet werden, während versucht wurde, diese neu zu laden.</translation>
</message>
</context>
<context>
@@ -648,14 +658,6 @@ Soll sie dennoch gespeichert werden? </translation>
<source>Enable Auto-Type for this entry</source>
<translation>Auto-Type für diesen Eintrag aktivieren</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation>Standard-Auto-Type-Sequenz von der Gruppe erben</translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation>Benutzerdefinierte Auto-Type-Sequenz benutzen:</translation>
</message>
<message>
<source>+</source>
<translation>+</translation>
@@ -669,12 +671,20 @@ Soll sie dennoch gespeichert werden? </translation>
<translation>Fenstertitel:</translation>
</message>
<message>
<source>Use default sequence</source>
<translation>Standardsequenz benutzen</translation>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Standard-Auto-Type-Sequenz der &amp;Gruppe erben</translation>
</message>
<message>
<source>Set custom sequence:</source>
<translation>Benutzerdefinierte Sequenz verwenden:</translation>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>Ben&amp;utzerdefinierte Auto-Type-Sequenz verwenden:</translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation>Standardse&amp;quenz verwenden</translation>
</message>
<message>
<source>Set custo&amp;m sequence:</source>
<translation>B&amp;enutzerdefinierte Sequenz:</translation>
</message>
</context>
<context>
@@ -786,27 +796,19 @@ Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Auto-type</source>
<translation>Auto-type</translation>
<translation>Auto-Type</translation>
</message>
<message>
<source>Use default auto-type sequence of parent group</source>
<translation>Auto-Type-Sequenz der übergeordneten Gruppe anwenden.</translation>
<translation>Verwende Standard-A&amp;uto-Type-Sequenz der übergeordneten Gruppe</translation>
</message>
<message>
<source>Set default auto-type sequence</source>
<translation>Standard Auto-Type-Sequenz setzen</translation>
<translation>Standard-Auto-Type-Se&amp;quenz setzen</translation>
</message>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation>Standardsymbol verwenden</translation>
</message>
<message>
<source>Use custom icon</source>
<translation>Benutzerdefiniertes Symbol verwenden</translation>
</message>
<message>
<source>Add custom icon</source>
<translation>Benutzerdefiniertes Symbol hinzufügen</translation>
@@ -849,7 +851,15 @@ Soll sie dennoch gespeichert werden? </translation>
</message>
<message>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation>Symbol kann nicht gelöscht werden. Es wird noch von %1 Einträgen verwendet.</translation>
<translation>Icon kann nicht gelöscht werden. Es wird noch von %1 Einträgen verwendet.</translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation>&amp;Standardsymbol verwenden</translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation>B&amp;enutzerdefiniertes Symbol verwenden</translation>
</message>
</context>
<context>
@@ -875,7 +885,7 @@ Soll sie dennoch gespeichert werden? </translation>
<name>Entry</name>
<message>
<source> - Clone</source>
<translation>- Klone</translation>
<translation>- Klonen</translation>
</message>
</context>
<context>
@@ -1135,7 +1145,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
</message>
<message>
<source>&amp;Recent databases</source>
<translation>&amp;Letzte Datenbanken</translation>
<translation>&amp;Zuletzt verwendete Datenbanken</translation>
</message>
<message>
<source>He&amp;lp</source>
@@ -1147,7 +1157,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
</message>
<message>
<source>Copy att&amp;ribute to clipboard</source>
<translation>Eigenschaft in &amp;Zwischenablage kopieren</translation>
<translation>Feld in &amp;Zwischenablage kopieren</translation>
</message>
<message>
<source>&amp;Groups</source>
@@ -1235,7 +1245,7 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
</message>
<message>
<source>Copy &amp;username</source>
<translation>&amp;Benutzername kopieren</translation>
<translation>&amp;Benutzernamen kopieren</translation>
</message>
<message>
<source>Cop&amp;y password</source>
@@ -1296,29 +1306,19 @@ Dieser Vorgang ist nur in eine Richtung möglich. Die importierte Datenbank kann
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation>Zeig&amp;e eine Benachrichtigung, wenn Anmeldedaten angefordert werden.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation>Zeige nur die passendsten Einträge für eine URL, statt
alle Einträge der ganzen Domäne. </translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
<translation>Passendes URL Schema
Nur Einträge mit dem gleichen Schema (hhtp://, https://, ftp://, ...) werden angezeigt</translation>
Nur Einträge mit dem gleichen Schema (http://, https://, ftp://, ...) werden angezeigt</translation>
</message>
<message>
<source>Sort matching entries by &amp;username</source>
<translation>Sortiere gefundene Einträge nach &amp;Benutzername</translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation>Entferne alle freigegebenen Chiffrierschlüssel aus der aktiven Datenbank</translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
<translation>Entferne alle gespeicherten Berechtigungen von Einträgen in der aktiven Datenbank</translation>
<translation>Entferne alle gespeicherten Berechtigungen für Einträge in der aktiven Datenbank</translation>
</message>
<message>
<source>Password generator</source>
@@ -1328,10 +1328,6 @@ Nur Einträge mit dem gleichen Schema (hhtp://, https://, ftp://, ...) werden an
<source>Advanced</source>
<translation>Fortgeschritten</translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation>Aktivieren Sie das Nachfolgende nur dann, wenn Sie sich sicher sind, was sie tun! </translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation>&amp;Zugriff auf Einträge immer erlauben</translation>
@@ -1348,21 +1344,13 @@ Nur Einträge mit dem gleichen Schema (hhtp://, https://, ftp://, ...) werden an
<source>Only the selected database has to be connected with a client!</source>
<translation>Nur die ausgewählte Datenbank muss mit dem Client verbunden sein.</translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation>Zeige auch erweiterte Zeichenfelder, welche mit &quot;KPH: &quot; beginnen</translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation>Automatisches Erstellen und Aktualisieren von Zeichenfeldern wird nicht unterstützt!</translation>
</message>
<message>
<source>HTTP Port:</source>
<translation>HTTP Port:</translation>
<translation>HTTP-Port:</translation>
</message>
<message>
<source>Default port: 19455</source>
<translation>Standard Port:19455</translation>
<translation>Standard-Port: 19455</translation>
</message>
<message>
<source>Re&amp;quest to unlock the database if it is locked</source>
@@ -1375,22 +1363,44 @@ Nur Einträge mit dem gleichen Schema (hhtp://, https://, ftp://, ...) werden an
<message>
<source>Enable KeepassXC HTTP protocol
This is required for accessing your databases from ChromeIPass or PassIFox</source>
<translation>Aktiviere KeePassXC HTTP Protokoll
Dies wird benötigt um von ChromeIPass oder PassIFox auf Deine Datenbank zu zugreifen</translation>
<translation>KeepassXC-HTTP-Protokoll aktivieren
Dies ist für den Zugriff auf Ihre Datenbanken von ChromeIPass oder Passifox notwendig.</translation>
</message>
<message>
<source>KeePassXC will listen to this port on 127.0.0.1</source>
<translation>KeePassXC überwacht diesen Port auf 127.0..0.1</translation>
<translation>KeePassXC überwacht diesen Port auf 127.0.0.1</translation>
</message>
<message>
<source>Cannot bind to privileged ports</source>
<translation>Kann nicht zu privilegierte Ports verbinden</translation>
<translation>Privilegierte Ports können nicht überwacht werden</translation>
</message>
<message>
<source>Cannot bind to privileged ports below 1024!
Using default port 19455.</source>
<translation>Kann nicht zu privilegierten Ports unter 1024 verbinden!
Benutze Standardport 19455.</translation>
<translation>Privilegierte Ports unterhalb von 1024 können nicht überwacht werden.
Es wird der Standard-Port 19455 verwendet.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation>Zeige nur die am besten passenden Einträge für eine URL anstatt aller Einträge der ganzen Domäne.</translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation>&amp;Entferne alle freigegebenen Chiffrierschlüssel aus der aktiven Datenbank</translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation>Die folgenden Optionen können gefährlich sein!
Ändern Sie diese nur, wenn Sie wissen, was Sie tun.</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>Zeige auch erweiterte Zeichenfelder, welche mit &quot;KPH: &quot; beginnen</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation>Automatisches Erstellen und Aktualisieren von Zeichenfeldern wird nicht unterstützt!</translation>
</message>
</context>
<context>
@@ -1449,7 +1459,7 @@ Benutze Standardport 19455.</translation>
</message>
<message>
<source>Generate</source>
<translation>Generiere</translation>
<translation>Generieren</translation>
</message>
<message>
<source>Close</source>
@@ -1481,7 +1491,7 @@ Benutze Standardport 19455.</translation>
</message>
<message>
<source>Excellent</source>
<translation>Exzellent</translation>
<translation>Ausgezeichnet</translation>
</message>
</context>
<context>
@@ -1541,7 +1551,7 @@ Benutze Standardport 19455.</translation>
</message>
<message>
<source>Clear</source>
<translation>löschen</translation>
<translation>Löschen</translation>
</message>
</context>
<context>
@@ -1550,7 +1560,7 @@ Benutze Standardport 19455.</translation>
<source>A shared encryption-key with the name &quot;%1&quot; already exists.
Do you want to overwrite it?</source>
<translation>Ein freigegebener Chiffrierschlüssel mit dem Namen &quot;%1&quot; existiert schon.
Willst Du ihn überschreiben?</translation>
Möchten Sie ihn überschreiben?</translation>
</message>
<message>
<source>Do you want to update the information in %1 - %2?</source>
@@ -1560,11 +1570,11 @@ Willst Du ihn überschreiben?</translation>
<source>The active database is locked!
Please unlock the selected database or choose another one which is unlocked.</source>
<translation>Die aktive Datenbank ist gesperrt!
Bitte entsperren Sie die markierte Datenbank oder wählen Sie eine andere, die entsperrt ist.</translation>
Bitte entsperren Sie die ausgewählte Datenbank oder wählen Sie eine andere, die entsperrt ist.</translation>
</message>
<message>
<source>Successfully removed %1 encryption-%2 from KeePassX/Http Settings.</source>
<translation>%1 Verschlüsselungs-%2 erfolgreich von KeePassX/Http Einstellungen entfernt.</translation>
<translation>%1 Verschlüsselungs-%2 erfolgreich aus KeePassX/Http Einstellungen entfernt.</translation>
</message>
<message>
<source>No shared encryption-keys found in KeePassHttp Settings.</source>
@@ -1617,7 +1627,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>KeePassXC: Removed keys from database</source>
<translation>KeePassXC: Entfernte Schlüssel von der Datenbank</translation>
<translation>KeePassXC: Schlüssel aus der Datenbank entfernt</translation>
</message>
<message>
<source>KeePassXC: No keys found</source>
@@ -1629,7 +1639,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>KeePassXC: Removed permissions</source>
<translation>KeePassXC: Entfernte Zugangsdaten</translation>
<translation>KeePassXC: Zugangsdaten entfernt</translation>
</message>
<message>
<source>KeePassXC: No entry with permissions found!</source>
@@ -1679,7 +1689,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>Use entry title to match windows for global auto-type</source>
<translation>Verwende den Eintragstitel für entsprechende Fenster für den globale Auto-Typ</translation>
<translation>Verwende Eintragstitel, um entsprechende Fenster für globales Auto-Type zu finden</translation>
</message>
<message>
<source>Language</source>
@@ -1703,7 +1713,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>Automatically reload the database when modified externally</source>
<translation>Datenbank nach externer Modifikation automatisch neu laden.</translation>
<translation>Datenbank nach externer Änderung automatisch neu laden.</translation>
</message>
<message>
<source>Hide window to system tray instead of app exit</source>
@@ -1734,7 +1744,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>Always ask before performing auto-type</source>
<translation>Immer vor einem Auto-type fragen</translation>
<translation>Immer vor einem Auto-Type fragen</translation>
</message>
<message>
<source>Lock databases after minimizing the window</source>
@@ -1742,7 +1752,7 @@ Namen und akzeptieren Sie.</translation>
</message>
<message>
<source>Don&apos;t require password repeat when it is visible</source>
<translation>Keine erneute Eingabe verlangen wenn Passwort sichtbar.</translation>
<translation>Keine erneute Passworteingabe verlangen wenn das Passwort sichtbar ist.</translation>
</message>
</context>
<context>
@@ -1769,17 +1779,17 @@ Namen und akzeptieren Sie.</translation>
<source>key file of the database</source>
<translation>Schlüsseldatei der Datenbank</translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation>Dateiname(n) der zu öffnenden Passwortdatenbank(en) (*.kdbx)</translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation>KeePassXC - plattformübergreifender Passwortmanager</translation>
</message>
<message>
<source>read password of the database from stdin</source>
<translation>passwort der datenbank von stdin lesen</translation>
<translation>Passwort der Datenbank von stdin lesen</translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation>Dateinamen der zu öffnenden Datenbanken (*.kdbx)</translation>
</message>
</context>
</TS>

View File

@@ -16,12 +16,12 @@
<translation type="unfinished"></translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<source>Extensions:
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Extensions:
</source>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -100,10 +100,6 @@ Please select whether you want to allow access.</source>
<source>Repeat password:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Key file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browse</source>
<translation type="unfinished"></translation>
@@ -157,6 +153,10 @@ Please select whether you want to allow access.</source>
%2</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Key file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -255,10 +255,6 @@ You can now save it.</source>
<source>Default username:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> MiB</source>
<translation type="unfinished"></translation>
@@ -275,6 +271,10 @@ You can now save it.</source>
<source>Max. history size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use recycle bin</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseTabWidget</name>
@@ -388,11 +388,6 @@ Discard changes and close anyway?</source>
<source>Unable to open the database.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Merge database</source>
<translation type="unfinished"></translation>
@@ -402,6 +397,24 @@ Do you want to open it anyway? Alternatively the database is opened read-only.</
Do you want to save it anyway?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Passwords</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database already opened</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open read-only</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -512,10 +525,6 @@ Do you want to save it anyway?</source>
<source>Autoreload Failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation type="unfinished"></translation>
@@ -648,14 +657,6 @@ Do you want to save it anyway?</source>
<source>Enable Auto-Type for this entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>+</source>
<translation type="unfinished"></translation>
@@ -669,11 +670,19 @@ Do you want to save it anyway?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use default sequence</source>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Set custom sequence:</source>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Set custo&amp;m sequence:</source>
<translation type="unfinished"></translation>
</message>
</context>
@@ -799,14 +808,6 @@ Do you want to save it anyway?</source>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use custom icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add custom icon</source>
<translation type="unfinished"></translation>
@@ -851,6 +852,14 @@ Do you want to save it anyway?</source>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
@@ -1293,11 +1302,6 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
@@ -1307,10 +1311,6 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<source>Sort matching entries by &amp;username</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
<translation type="unfinished"></translation>
@@ -1323,10 +1323,6 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<source>Advanced</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation type="unfinished"></translation>
@@ -1343,14 +1339,6 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<source>Only the selected database has to be connected with a client!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>HTTP Port:</source>
<translation type="unfinished"></translation>
@@ -1385,6 +1373,27 @@ This is required for accessing your databases from ChromeIPass or PassIFox</sour
Using default port 19455.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PasswordGeneratorWidget</name>
@@ -1757,10 +1766,6 @@ give it a unique name to identify and accept it.</source>
<source>key file of the database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation type="unfinished"></translation>
@@ -1769,5 +1774,9 @@ give it a unique name to identify and accept it.</source>
<source>read password of the database from stdin</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,16 +13,16 @@
<source>About KeePassXC</source>
<translation>Apie KeePassXC</translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC yra platinama GNU Bendrosios Viešosios Licencijos (GPL) versijos 2 arba (jūsų pasirinkimu) versijos 3 sąlygomis.</translation>
</message>
<message>
<source>Extensions:
</source>
<translation>Plėtiniai:
</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC yra platinama GNU Bendrosios Viešosios Licencijos (GPL) versijos 2 arba (jūsų pasirinkimu) versijos 3 sąlygomis.</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -100,10 +100,6 @@ Pasirinkite, ar norite leisti prieigą.</translation>
<source>Repeat password:</source>
<translation>Pakartokite slaptažodį:</translation>
</message>
<message>
<source>Key file</source>
<translation>Rakto failas</translation>
</message>
<message>
<source>Browse</source>
<translation>Naršyti</translation>
@@ -158,6 +154,10 @@ Pasirinkite, ar norite leisti prieigą.</translation>
<translation>Nepavyko nustatyti %1 kaip rakto failą:
%2</translation>
</message>
<message>
<source>&amp;Key file</source>
<translation>&amp;Rakto failas</translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -257,10 +257,6 @@ Dabar galite ją įrašyti.</translation>
<source>Default username:</source>
<translation>Numatytasis naudotojo vardas:</translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation>Naudoti šiukšlinę:</translation>
</message>
<message>
<source> MiB</source>
<translation> MiB</translation>
@@ -277,6 +273,10 @@ Dabar galite ją įrašyti.</translation>
<source>Max. history size:</source>
<translation>Didžiausias istorijos dydis:</translation>
</message>
<message>
<source>Use recycle bin</source>
<translation>Naudoti šiukšlinę</translation>
</message>
</context>
<context>
<name>DatabaseTabWidget</name>
@@ -396,12 +396,6 @@ Vis tiek atmesti pakeitimus ir užverti?</translation>
<source>Unable to open the database.</source>
<translation>Nepavyko atverti duomenų bazės.</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation>Duomenų bazė, kurią bandote atverti, yra užrakinta kito KeePassXC egzemplioriaus.
Ar vis tiek norite ją atverti? Tokiu atveju duomenų bazė bus atverta tik skaitymui.</translation>
</message>
<message>
<source>Merge database</source>
<translation>Sulieti duomenų bazę</translation>
@@ -412,6 +406,25 @@ Do you want to save it anyway?</source>
<translation>Duomenų bazė, kurią bandote įrašyti yra užrakinta kito KeePassXC programos egzemplioriaus.
Ar vis tiek norite ją įrašyti?</translation>
</message>
<message>
<source>Passwords</source>
<translation>Slaptažodžiai</translation>
</message>
<message>
<source>Database already opened</source>
<translation>Duomenų bazė jau atverta</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation>Duomenų bazė, kurią bandote atverti yra užrakinta kito KeePassXC programos egzemplioriaus.
Ar vis tiek norite ją atverti?</translation>
</message>
<message>
<source>Open read-only</source>
<translation>Atverti tik skaitymui</translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -519,10 +532,6 @@ Ar vis tiek norite ją įrašyti?</translation>
<source>Autoreload Failed</source>
<translation>Automatinis įkėlimas iš naujo nepavyko</translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation>Nepavyko išanalizuoti ar atrakinti naujos duomenų bazės failo, bandant automatiškai iš naujo įkelti šią duomenų bazę.</translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation>Nepavyko atverti naujos duomenų bazės failo, bandant automatiškai iš naujo įkelti šią duomenų bazę.</translation>
@@ -650,14 +659,6 @@ Ar vis tiek norite ją įrašyti?</translation>
<source>Enable Auto-Type for this entry</source>
<translation>Įjungti šiam įrašui automatinį rinkimą</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation>Paveldėti numatytąją automatinio rinkimo seką iš grupės</translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation>Naudoti tinkintą automatinio rinkimo seka:</translation>
</message>
<message>
<source>+</source>
<translation>+</translation>
@@ -671,12 +672,20 @@ Ar vis tiek norite ją įrašyti?</translation>
<translation>Lango antraštė:</translation>
</message>
<message>
<source>Use default sequence</source>
<translation>Naudoti numatytąją seką</translation>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Paveldėti numatytąją automatinio rinkimo seką iš &amp;grupės</translation>
</message>
<message>
<source>Set custom sequence:</source>
<translation>Nustatyti tinkintą seką:</translation>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>Na&amp;udoti tinkintą automatinio rinkimo seka:</translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation>Naudoti numatytąją se&amp;ką</translation>
</message>
<message>
<source>Set custo&amp;m sequence:</source>
<translation>Nustatyti tinkintą s&amp;eką:</translation>
</message>
</context>
<context>
@@ -801,14 +810,6 @@ Ar vis tiek norite ją įrašyti?</translation>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation>Naudoti numatytąją piktogramą</translation>
</message>
<message>
<source>Use custom icon</source>
<translation>Naudoti tinkintą piktogramą</translation>
</message>
<message>
<source>Add custom icon</source>
<translation>Pridėti tinkintą piktogramą</translation>
@@ -853,6 +854,14 @@ Ar vis tiek norite ją įrašyti?</translation>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation>Nepavyksta ištrinti piktogramos. Vis dar naudojama %1 elementų.</translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation>Na&amp;udoti numatytąją piktogramą</translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation>Naudoti tinkintą piktogra&amp;mą</translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
@@ -1298,12 +1307,6 @@ Tai yra vienakryptis perkėlimas. Jūs negalėsite atverti importuotos duomenų
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation>R&amp;odyti pranešimą, kai reikalaujama prisijungimo duomenų</translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation>&amp;Vietoj visų įrašų, skirtų visai sričiai,
grąžinti tik geriausiai atitinkančius įrašus, skirtus URL</translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
@@ -1314,10 +1317,6 @@ Bus grąžinami įrašai tik su ta pačia schema (http://, https://, ftp://, ...
<source>Sort matching entries by &amp;username</source>
<translation>Rikiuoti atitinkančius įrašus pagal na&amp;udotojo vardą</translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation>Ša&amp;linti aktyvios duomenų bazės visus bendrinamus šifravimo raktus</translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
<translation>Šal&amp;inti įrašų aktyvioje duomenų bazėje visus saugomus leidimus</translation>
@@ -1330,10 +1329,6 @@ Bus grąžinami įrašai tik su ta pačia schema (http://, https://, ftp://, ...
<source>Advanced</source>
<translation>Išplėstiniai</translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation>Aktyvuokite tai tik tuo atveju, jeigu žinote darote!</translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation>Visada leisti &amp;prieigą prie įrašų</translation>
@@ -1350,14 +1345,6 @@ Bus grąžinami įrašai tik su ta pačia schema (http://, https://, ftp://, ...
<source>Only the selected database has to be connected with a client!</source>
<translation>Su klientu turi būti sujungta tik pasirinkta duomenų bazė!</translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation>&amp;Taip pat grąžinti ir išplėstines eilutes, kurios prasideda &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation>Šiems eilutės laukams automatiniai kūrimai ir atnaujinimai neprieinami!</translation>
</message>
<message>
<source>HTTP Port:</source>
<translation>HTTP prievadas:</translation>
@@ -1394,6 +1381,28 @@ Using default port 19455.</source>
<translation>Nepavyksta susieti su privilegijuotais prievadais žemiau 1024!
Naudojamas numatytasis prievadas 19455.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation>&amp;Vietoj visų įrašų, skirtų visai sričiai,
grąžinti tik geriausiai atitinkančius įrašus, skirtus URL</translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation>Ša&amp;linti aktyvios duomenų bazės visus bendrinamus šifravimo raktus</translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation>Šios parinktys gali būti pavojingos. Keiskite jas tik tuo atveju, jeigu žinote darote!</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>&amp;Grąžinti išplėstines eilutes, kurios prasideda &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation>Automatinis eilutės laukų kūrimas ar atnaujinimas nėra palaikomas.</translation>
</message>
</context>
<context>
<name>PasswordGeneratorWidget</name>
@@ -1463,7 +1472,7 @@ Naudojamas numatytasis prievadas 19455.</translation>
</message>
<message>
<source>Entropy: %1 bit</source>
<translation>Entropija: %1 bit</translation>
<translation>Entropija: %1 bitų</translation>
</message>
<message>
<source>Password Quality: %1</source>
@@ -1535,7 +1544,7 @@ Naudojamas numatytasis prievadas 19455.</translation>
</message>
<message>
<source>Search</source>
<translation>Ieškoti</translation>
<translation>Paieška</translation>
</message>
<message>
<source>Find</source>
@@ -1771,10 +1780,6 @@ ir priimtumėte jį.</translation>
<source>key file of the database</source>
<translation>duomenų bazės rakto failas</translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation>norimos atverti slaptažodžių duomenų bazės(-) failo pavadinimas(-ai) (*.kdbx)</translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation>KeePassXC - daugiaplatformė slaptažodžių tvarkytuvė</translation>
@@ -1783,5 +1788,9 @@ ir priimtumėte jį.</translation>
<source>read password of the database from stdin</source>
<translation>nuskaityti duomenų bazės slaptažodį stdin</translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation>norimų atverti slaptažodžių duomenų bazių failų pavadinimai (*.kdbx)</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,16 +13,16 @@
<source>About KeePassXC</source>
<translation>Sobre KeePassXC</translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC é distribuído nos termos da Licença Pública Geral (GPL), versão 2 ou (à sua escolha) versão 3, do GNU.</translation>
</message>
<message>
<source>Extensions:
</source>
<translation>Extensões:
</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC é distribuído nos termos da Licença Pública Geral (GPL), versão 2 ou (à sua escolha) versão 3, do GNU.</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -100,10 +100,6 @@ Selecione se deseja permitir o acesso.</translation>
<source>Repeat password:</source>
<translation>Repita senha:</translation>
</message>
<message>
<source>Key file</source>
<translation>Arquivo-Chave</translation>
</message>
<message>
<source>Browse</source>
<translation>Navegar</translation>
@@ -158,6 +154,10 @@ Selecione se deseja permitir o acesso.</translation>
<translation>Falha ao definir %1 como o Arquivo-Chave:
%2</translation>
</message>
<message>
<source>&amp;Key file</source>
<translation>&amp;Arquivo-Chave</translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -257,10 +257,6 @@ Você pode salvá-lo agora.</translation>
<source>Default username:</source>
<translation>Usuário padrão:</translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation>Usar lixeira:</translation>
</message>
<message>
<source> MiB</source>
<translation> MB</translation>
@@ -277,6 +273,10 @@ Você pode salvá-lo agora.</translation>
<source>Max. history size:</source>
<translation>Tamanho máx. do histórico:</translation>
</message>
<message>
<source>Use recycle bin</source>
<translation>Usar lixeira</translation>
</message>
</context>
<context>
<name>DatabaseTabWidget</name>
@@ -396,12 +396,6 @@ Descartar alterações e fechar mesmo assim?</translation>
<source>Unable to open the database.</source>
<translation>Não foi possível abrir o banco de dados.</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation>A base de dados que você está tentando abrir está bloqueada por outra instância de KeePassXC.
Mesmo assim deseja abrir? Alternativamente, a base de dados é aberta somente para leitura.</translation>
</message>
<message>
<source>Merge database</source>
<translation>Juntar banco de dados</translation>
@@ -412,6 +406,26 @@ Do you want to save it anyway?</source>
<translation>A base de dados que você está tentando abrir está bloqueada por outra instância de KeePassXC.
Mesmo assim deseja salvá-la?</translation>
</message>
<message>
<source>Passwords</source>
<translation>Senhas</translation>
</message>
<message>
<source>Database already opened</source>
<translation>Banco de dados já aberto</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation>O banco de dados que você está tentando abrir está bloqueada por outra instância do KeePassXC.
Mesmo assim deseja salvá-la?</translation>
</message>
<message>
<source>Open read-only</source>
<translation>Abrir somente leitura</translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -519,10 +533,6 @@ Mesmo assim deseja salvá-la?</translation>
<source>Autoreload Failed</source>
<translation>Carregamento Automático Falhou</translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation>Não foi possível analisar ou desbloquear o novo arquivo da base de dados ao tentar recarregar automaticamente essa base de dados.</translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation>Não foi possível abrir a nova base de dados ao tentar recarregar automaticamente essa base de dados.</translation>
@@ -650,14 +660,6 @@ Mesmo assim deseja salvá-la?</translation>
<source>Enable Auto-Type for this entry</source>
<translation>Habilitar Auto-Digitação para esta entrada</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation>Herdar sequência pré-definida de Auto-Digitação do grupo</translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation>Usar sequência de Auto-Digitação personalizada:</translation>
</message>
<message>
<source>+</source>
<translation>+</translation>
@@ -671,12 +673,20 @@ Mesmo assim deseja salvá-la?</translation>
<translation>Título da Janela:</translation>
</message>
<message>
<source>Use default sequence</source>
<translation>Usar sequência pré-definida</translation>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Herdar sequência pré-definida de Auto-Digitação do &amp;grupo</translation>
</message>
<message>
<source>Set custom sequence:</source>
<translation>Definir sequência personalizada:</translation>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>&amp;Usar sequência de Auto-Digitação personalizada:</translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation>Usar se&amp;quência pré-definida</translation>
</message>
<message>
<source>Set custo&amp;m sequence:</source>
<translation>Definir sequência &amp;personalizada:</translation>
</message>
</context>
<context>
@@ -801,14 +811,6 @@ Mesmo assim deseja salvá-la?</translation>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation>Usar ícone padrão</translation>
</message>
<message>
<source>Use custom icon</source>
<translation>Usar ícone personalizado</translation>
</message>
<message>
<source>Add custom icon</source>
<translation>Adicionar ícone personalizado</translation>
@@ -853,6 +855,14 @@ Mesmo assim deseja salvá-la?</translation>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation>Não é possível apagar ícone. Ainda usado por %1 itens.</translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation>&amp;Usar ícone padrão</translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation>Usar ícone &amp;personalizado</translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
@@ -1298,12 +1308,6 @@ Esta é uma migração de uma via. Você não poderá abrir o banco de dados imp
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation>M&amp;ostrar uma notificação quando as credenciais forem solicitadas</translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation>&amp;Mostrar apenas as melhores entradas correspondentes para um URL em vez de
todas as entradas para o domínio completo</translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
@@ -1314,10 +1318,6 @@ Somente entradas com o mesmo esquema (http://, https://, ftp://, ...) são mostr
<source>Sort matching entries by &amp;username</source>
<translation>Ordenar entradas coincidentes por nome de &amp;usuário</translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation>R&amp;emover todas as chaves criptografadas compartilhadas da base de dados ativa</translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
<translation>R&amp;emover todas as permissões armazenadas de entradas na base de dados ativa</translation>
@@ -1330,10 +1330,6 @@ Somente entradas com o mesmo esquema (http://, https://, ftp://, ...) são mostr
<source>Advanced</source>
<translation>Avançado</translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation>Ativar apenas os seguintes, se souber o que está fazendo!</translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation>Permitir sempre &amp;acesso as entradas</translation>
@@ -1350,14 +1346,6 @@ Somente entradas com o mesmo esquema (http://, https://, ftp://, ...) são mostr
<source>Only the selected database has to be connected with a client!</source>
<translation>Somente a base de dados selecionada tem que ser conectada com um cliente!</translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation>&amp;Mostrar também campos avançados que começam com &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation>Criação automática ou atualizações não são suportadas para os valores dos campos!</translation>
</message>
<message>
<source>HTTP Port:</source>
<translation>Porta HTTP:</translation>
@@ -1377,8 +1365,8 @@ Somente entradas com o mesmo esquema (http://, https://, ftp://, ...) são mostr
<message>
<source>Enable KeepassXC HTTP protocol
This is required for accessing your databases from ChromeIPass or PassIFox</source>
<translation>Habilitar KeepassXC protocolo HTTP
Isso é necessário para acessar os seus bancos de dados de ChromeIPass ou de PassIFox</translation>
<translation>Habilitar o protocolo KeepassXC HTTP
Isso é necessário para acessar os seus bancos de dados usando o ChromeIPass ou PassIFox</translation>
</message>
<message>
<source>KeePassXC will listen to this port on 127.0.0.1</source>
@@ -1394,6 +1382,28 @@ Using default port 19455.</source>
<translation>Não é possível ligar a portas privilegiadas abaixo de 1024!
Usando porta padrão 19455.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation>&amp;Mostrar apenas as melhores entradas correspondentes para um URL em vez de
todas as entradas para o domínio completo</translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation>R&amp;emover todas as chaves criptografadas compartilhadas da base de dados ativa</translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation>As configurações abaixo podem ser perigosas. Altere-as somente se souber o que está fazendo.</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>&amp;Mostrar também campos avançados que começam com &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation>Criação automática ou atualizações não são suportadas para os valores dos campos.</translation>
</message>
</context>
<context>
<name>PasswordGeneratorWidget</name>
@@ -1700,7 +1710,7 @@ dar-lhe um nome único para identificá-lo e aceitá-lo.</translation>
</message>
<message>
<source>Load previous databases on startup</source>
<translation>Abrir bancos de dados anteriores na inicialização</translation>
<translation>Carregar bancos de dados anteriores na inicialização</translation>
</message>
<message>
<source>Automatically reload the database when modified externally</source>
@@ -1770,10 +1780,6 @@ dar-lhe um nome único para identificá-lo e aceitá-lo.</translation>
<source>key file of the database</source>
<translation>arquivo-chave do banco de dados</translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation>nome(s) de arquivo(s) do banco de dados de senhas a ser aberto (*.kdbx)</translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation>KeePassXC - gerenciador de senhas multiplataforma</translation>
@@ -1782,5 +1788,9 @@ dar-lhe um nome único para identificá-lo e aceitá-lo.</translation>
<source>read password of the database from stdin</source>
<translation>ler a senha do banco de dados da entrada padrão</translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation>nome de arquivo do banco de dados de senhas a ser aberto (*.kdbx)</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

View File

@@ -13,16 +13,16 @@
<source>About KeePassXC</source>
<translation>О KeePassXC</translation>
</message>
<message>
<source>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC распространяется на условиях Стандартной общественной лицензии GNU (GPL) версии 2 или (на ваше усмотрение) версии 3.</translation>
</message>
<message>
<source>Extensions:
</source>
<translation>Расширения:
</translation>
</message>
<message>
<source>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</source>
<translation>KeePassXC распространяется на условиях Стандартной общественной лицензии GNU (GPL) версии 2 или (на ваше усмотрение) версии 3.</translation>
</message>
</context>
<context>
<name>AccessControlDialog</name>
@@ -99,10 +99,6 @@ Please select whether you want to allow access.</source>
<source>Repeat password:</source>
<translation>Повторите пароль:</translation>
</message>
<message>
<source>Key file</source>
<translation>Файл-ключ</translation>
</message>
<message>
<source>Browse</source>
<translation>Обзор</translation>
@@ -157,6 +153,10 @@ Please select whether you want to allow access.</source>
<translation>Не удалось установить %1 как файл-ключ:
%2</translation>
</message>
<message>
<source>&amp;Key file</source>
<translation>Файл&amp;ключ</translation>
</message>
</context>
<context>
<name>DatabaseOpenWidget</name>
@@ -256,10 +256,6 @@ You can now save it.</source>
<source>Default username:</source>
<translation>Имя пользователя по умолчанию:</translation>
</message>
<message>
<source>Use recycle bin:</source>
<translation>Использовать корзину:</translation>
</message>
<message>
<source> MiB</source>
<translation>МиБ</translation>
@@ -276,6 +272,10 @@ You can now save it.</source>
<source>Max. history size:</source>
<translation>Максимальный размер истории:</translation>
</message>
<message>
<source>Use recycle bin</source>
<translation>Использовать корзину</translation>
</message>
</context>
<context>
<name>DatabaseTabWidget</name>
@@ -395,12 +395,6 @@ Discard changes and close anyway?</source>
<source>Unable to open the database.</source>
<translation>Не удаётся открыть хранилище.</translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway? Alternatively the database is opened read-only.</source>
<translation>Хранилище, которое Вы хотите открыть, заблокировано другой запущенной копией KeePassXC.
Всё равно открыть? В качестве альтернативы хранилище будет открыто в режиме для чтения.</translation>
</message>
<message>
<source>Merge database</source>
<translation>Объединить хранилище</translation>
@@ -411,6 +405,25 @@ Do you want to save it anyway?</source>
<translation>Хранилище, которые вы пытаетесь сохранить, заблокировано другим экземпляром KeePassXC.
Хотите сохранить во всех случаях?</translation>
</message>
<message>
<source>Passwords</source>
<translation>Пароли</translation>
</message>
<message>
<source>Database already opened</source>
<translation>Хранилище уже открыто </translation>
</message>
<message>
<source>The database you are trying to open is locked by another instance of KeePassXC.
Do you want to open it anyway?</source>
<translation>Хранилище, которые вы пытаетесь открыть, заблокировано другим экземпляром KeePassXC.
Хотите открыть во всех случаях?</translation>
</message>
<message>
<source>Open read-only</source>
<translation>Открыть в режиме &quot;только чтение&quot;</translation>
</message>
</context>
<context>
<name>DatabaseWidget</name>
@@ -518,10 +531,6 @@ Do you want to save it anyway?</source>
<source>Autoreload Failed</source>
<translation>Ошибка автоматической загрузки</translation>
</message>
<message>
<source>Could not parse or unlock the new database file while attempting to autoreload this database.</source>
<translation>Не удаётся разобрать или разблокировать новый файл хранилища при попытке автоматической загрузки этого хранилища.</translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload this database.</source>
<translation>Не удаётся открыть новый файл хранилища при попытке автоматической загрузки этого файла.</translation>
@@ -649,14 +658,6 @@ Do you want to save it anyway?</source>
<source>Enable Auto-Type for this entry</source>
<translation>Включить автоввод для этой записи</translation>
</message>
<message>
<source>Inherit default Auto-Type sequence from the group</source>
<translation>Унаследовать стандартную последовательность автоввода от группы</translation>
</message>
<message>
<source>Use custom Auto-Type sequence:</source>
<translation>Использовать свою последовательность автоввода:</translation>
</message>
<message>
<source>+</source>
<translation>+</translation>
@@ -670,12 +671,20 @@ Do you want to save it anyway?</source>
<translation>Заголовок окна:</translation>
</message>
<message>
<source>Use default sequence</source>
<translation>Использовать стандартную последовательность</translation>
<source>Inherit default Auto-Type sequence from the &amp;group</source>
<translation>Унаследовать стандартную последовательность автоввода от &amp;группы</translation>
</message>
<message>
<source>Set custom sequence:</source>
<translation>Установить свою последовательность:</translation>
<source>&amp;Use custom Auto-Type sequence:</source>
<translation>Использовать сво&amp;ю последовательность автоввода:</translation>
</message>
<message>
<source>Use default se&amp;quence</source>
<translation>Использовать стан&amp;дартную последовательность</translation>
</message>
<message>
<source>Set custo&amp;m sequence:</source>
<translation>Установить сво&amp;ю последовательность:</translation>
</message>
</context>
<context>
@@ -800,14 +809,6 @@ Do you want to save it anyway?</source>
</context>
<context>
<name>EditWidgetIcons</name>
<message>
<source>Use default icon</source>
<translation>Использовать стандартный значок</translation>
</message>
<message>
<source>Use custom icon</source>
<translation>Использовать свой значок</translation>
</message>
<message>
<source>Add custom icon</source>
<translation>Добавить свой значок</translation>
@@ -852,6 +853,14 @@ Do you want to save it anyway?</source>
<source>Can&apos;t delete icon. Still used by %1 items.</source>
<translation>Не удается удалить значок, она продолжает использоваться %1 записями.</translation>
</message>
<message>
<source>&amp;Use default icon</source>
<translation>Использовать с&amp;тандартный значок</translation>
</message>
<message>
<source>Use custo&amp;m icon</source>
<translation>Использовать св&amp;ой значок</translation>
</message>
</context>
<context>
<name>EditWidgetProperties</name>
@@ -1297,25 +1306,16 @@ This is a one-way migration. You won&apos;t be able to open the imported databas
<source>Sh&amp;ow a notification when credentials are requested</source>
<translation>Показывать уведомление при запросе данных для входа</translation>
</message>
<message>
<source>&amp;Return only best matching entries for an URL instead
of all entries for the whole domain</source>
<translation>Возвращать только наиболее совпавшие с URL записи, а не все записи для домена</translation>
</message>
<message>
<source>&amp;Match URL schemes
Only entries with the same scheme (http://, https://, ftp://, ...) are returned</source>
<translation>Совпадение со схемой URL
Возвращать только записи с соответствующей схемой (http://, https://, ftp://, ...) </translation>
Возвращат&amp;ь только записи с соответствующей схемой (http://, https://, ftp://, ...) </translation>
</message>
<message>
<source>Sort matching entries by &amp;username</source>
<translation>Сортировать совпавшие записи по &amp;имени пользователя</translation>
</message>
<message>
<source>R&amp;emove all shared encryption-keys from active database</source>
<translation>Удалить все общие ключи шифрования из активного хранилища</translation>
</message>
<message>
<source>Re&amp;move all stored permissions from entries in active database</source>
<translation>Удалить все сохраненные права доступа из активного хранилища </translation>
@@ -1328,10 +1328,6 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<source>Advanced</source>
<translation>Расширенные</translation>
</message>
<message>
<source>Activate the following only, if you know what you are doing!</source>
<translation>Включайте эти настройки только если знаете что делаете!</translation>
</message>
<message>
<source>Always allow &amp;access to entries</source>
<translation>Всегда разрешать доступ к записям</translation>
@@ -1348,14 +1344,6 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<source>Only the selected database has to be connected with a client!</source>
<translation>Только выбранное хранилище должно быть соединено с клиентом!</translation>
</message>
<message>
<source>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</source>
<translation>Возвращать дополнительные строковые поля, начинающиеся с &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatic creates or updates are not supported for string fields!</source>
<translation>Автоматическое создание или обновление не поддерживается строковыми полями!</translation>
</message>
<message>
<source>HTTP Port:</source>
<translation>Порт HTTP:</translation>
@@ -1389,8 +1377,29 @@ This is required for accessing your databases from ChromeIPass or PassIFox</sour
<message>
<source>Cannot bind to privileged ports below 1024!
Using default port 19455.</source>
<translation>Не удается привязать к привилегированные порты ниже 1024!
Используется порт по умолчанию 19455.</translation>
<translation>Не удается привязать к привилегированным портам с номерами меньше 1024!
Используется порт по умолчанию: 19455.</translation>
</message>
<message>
<source>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</source>
<translation>Возвращать толь&amp;ко наиболее совпавшие с URL записи, а не все записи для домена</translation>
</message>
<message>
<source>R&amp;emove all shared encryption keys from active database</source>
<translation>&amp;Удалить все общие ключи шифрования из активного хранилища</translation>
</message>
<message>
<source>The following options can be dangerous. Change them only if you know what you are doing.</source>
<translation>Используйте эти настройки только если знаете, что делаете!</translation>
</message>
<message>
<source>&amp;Return advanced string fields which start with &quot;KPH: &quot;</source>
<translation>Возвращать дополнительные стро&amp;ковые поля, начинающиеся с &quot;KPH: &quot;</translation>
</message>
<message>
<source>Automatically creating or updating string fields is not supported.</source>
<translation>Автоматическое создание или обновление полей, содержащих строки, не поддерживается.</translation>
</message>
</context>
<context>
@@ -1767,10 +1776,6 @@ give it a unique name to identify and accept it.</source>
<source>key file of the database</source>
<translation>файл-ключ хранилища</translation>
</message>
<message>
<source>filename(s) of the password database(s) to open (*.kdbx)</source>
<translation>имена(имя) файлов хранилищ(а) для открытия (*.kdbx)</translation>
</message>
<message>
<source>KeePassXC - cross-platform password manager</source>
<translation>KeePassXC кросс-платформенный менеджер паролей</translation>
@@ -1779,5 +1784,9 @@ give it a unique name to identify and accept it.</source>
<source>read password of the database from stdin</source>
<translation>читать пароли хранилища из стандартного ввода &quot;stdin&quot;</translation>
</message>
<message>
<source>filenames of the password databases to open (*.kdbx)</source>
<translation>имена файлов открываемого хранилища паролей (*.kdbx)</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
name: keepassxc
version: 2.1.0
version: 2.1.3
grade: stable
summary: community driven port of the windows application “Keepass Password Safe”
description: |
@@ -11,7 +11,7 @@ confinement: strict
apps:
keepassxc:
command: desktop-launch keepassxc
plugs: [unity7, opengl, gsettings, home]
plugs: [unity7, opengl, gsettings, home, network, network-bind]
parts:
keepassxc:
@@ -21,6 +21,7 @@ parts:
- -DCMAKE_BUILD_TYPE=Release
- -DWITH_TESTS=OFF
- -DWITH_XC_AUTOTYPE=ON
- -DWITH_XC_HTTP=ON
build-packages:
- g++
- libgcrypt20-dev

View File

@@ -149,24 +149,9 @@ set(keepassx_FORMS
add_feature_info(KeePassHTTP WITH_XC_HTTP "KeePassHTTP support for ChromeIPass and PassIFox")
add_feature_info(Autotype WITH_XC_AUTOTYPE "Auto-type passwords in Input fields")
add_subdirectory(http)
if(WITH_XC_HTTP)
set(keepasshttp_SOURCES
http/AccessControlDialog.cpp
http/EntryConfig.cpp
http/HttpPasswordGeneratorWidget.cpp
http/HttpSettings.cpp
http/OptionDialog.cpp
http/Protocol.cpp
http/Server.cpp
http/Service.cpp
)
set(keepasshttp_FORMS
http/AccessControlDialog.ui
http/HttpPasswordGeneratorWidget.ui
http/OptionDialog.ui
)
set(keepasshttp_LIB keepasshttp)
qt5_wrap_ui(keepasshttp_SOURCES ${keepasshttp_FORMS})
set(keepasshttp_LIB keepasshttp)
endif()
add_subdirectory(autotype)
@@ -195,24 +180,19 @@ qt5_wrap_ui(keepassx_SOURCES ${keepassx_FORMS})
add_library(zxcvbn STATIC zxcvbn/zxcvbn.cpp)
target_link_libraries(zxcvbn)
if(WITH_XC_HTTP)
add_library(keepasshttp STATIC ${keepasshttp_SOURCES})
target_link_libraries(keepasshttp ${MHD_LIBRARIES} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network)
endif()
add_library(autotype STATIC ${autotype_SOURCES})
target_link_libraries(autotype Qt5::Core Qt5::Widgets)
set(autotype_LIB autotype)
add_library(keepassx_core STATIC ${keepassx_SOURCES})
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
target_link_libraries(keepassx_core zxcvbn ${keepasshttp_LIB} ${autotype_LIB} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network)
add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE})
target_link_libraries(${PROGNAME}
keepassx_core
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
target_link_libraries(keepassx_core
${keepasshttp_LIB}
${autotype_LIB}
zxcvbn
qhttp
Qt5::Core
Qt5::Concurrent
Qt5::Widgets
@@ -220,6 +200,29 @@ target_link_libraries(${PROGNAME}
${GCRYPT_LIBRARIES}
${GPGERROR_LIBRARIES}
${ZLIB_LIBRARIES})
if (UNIX AND NOT APPLE)
target_link_libraries(keepassx_core Qt5::DBus)
endif()
if(MINGW)
string(REPLACE "." ";" VERSION_LIST ${KEEPASSXC_VERSION})
list(GET VERSION_LIST 0 KEEPASSXC_VERSION_MAJOR)
list(GET VERSION_LIST 1 KEEPASSXC_VERSION_MINOR)
list(GET VERSION_LIST 2 KEEPASSXC_VERSION_PATCH)
include(GenerateProductVersion)
generate_product_version(
WIN32_ProductVersionFiles
NAME "KeePassXC"
COMPANY_NAME "KeePassXC Team"
VERSION_MAJOR ${KEEPASSXC_VERSION_MAJOR}
VERSION_MINOR ${KEEPASSXC_VERSION_MINOR}
VERSION_PATCH ${KEEPASSXC_VERSION_PATCH}
)
endif()
add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE} ${WIN32_ProductVersionFiles})
target_link_libraries(${PROGNAME} keepassx_core)
set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON)

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>455</width>
<height>238</height>
<height>266</height>
</rect>
</property>
<property name="windowTitle">
@@ -53,7 +53,7 @@
</sizepolicy>
</property>
<property name="text">
<string notr="true">&lt;a href=&quot;https://www.keepassxc.org/&quot;&gt;https://www.keepassxc.org/&lt;/a&gt;</string>
<string notr="true">&lt;a href=&quot;https://keepassxc.org/&quot;&gt;https://keepassxc.org/&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
@@ -69,7 +69,7 @@
</sizepolicy>
</property>
<property name="text">
<string>KeePassXC is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3.</string>
<string>KeePassXC is distributed under the terms of the GNU General Public License (GPL) version 2 or (at your option) version 3.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -82,7 +82,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@@ -91,6 +91,9 @@
<property name="text">
<string>Using:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
@@ -99,6 +102,9 @@
<string>Extensions:
</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>

View File

@@ -136,3 +136,8 @@ void ChangeMasterKeyWidget::reject()
{
Q_EMIT editFinished(false);
}
void ChangeMasterKeyWidget::setCancelEnabled(bool enabled)
{
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(enabled);
}

View File

@@ -38,6 +38,7 @@ public:
void clearForms();
CompositeKey newMasterKey();
QLabel* headlineLabel();
void setCancelEnabled(bool enabled);
Q_SIGNALS:
void editFinished(bool accepted);

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>438</width>
<height>256</height>
<width>818</width>
<height>397</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -41,8 +41,8 @@
<property name="checked">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="enterPasswordLabel">
<property name="text">
<string>Enter password:</string>
@@ -67,7 +67,7 @@
</item>
</layout>
</item>
<item row="1" column="0">
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="repeatPasswordLabel">
<property name="text">
<string>Repeat password:</string>
@@ -87,7 +87,7 @@
<item>
<widget class="QGroupBox" name="keyFileGroup">
<property name="title">
<string>Key file</string>
<string>&amp;Key file</string>
</property>
<property name="checkable">
<bool>true</bool>

View File

@@ -52,6 +52,13 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent)
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase()));
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
#ifdef Q_OS_MACOS
// add random padding to layouts to align widgets properly
m_ui->dialogButtonsLayout->setContentsMargins(10, 0, 15, 0);
m_ui->gridLayout->setContentsMargins(10, 0, 0, 0);
m_ui->labelLayout->setContentsMargins(10, 0, 10, 0);
#endif
}
DatabaseOpenWidget::~DatabaseOpenWidget()

View File

@@ -10,7 +10,7 @@
<height>250</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,1,0,0,3">
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,1,0,0,3">
<property name="spacing">
<number>8</number>
</property>
@@ -28,14 +28,24 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="labelHeadline">
<property name="text">
<string>Enter master key</string>
<layout class="QVBoxLayout" name="labelLayout">
<property name="leftMargin">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelFilename"/>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="labelHeadline">
<property name="text">
<string>Enter master key</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelFilename"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
@@ -52,17 +62,20 @@
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>8</number>
</property>
<item row="1" column="0">
<item row="1" column="0" alignment="Qt::AlignVCenter">
<widget class="QCheckBox" name="checkKeyFile">
<property name="text">
<string>Key File:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<item row="0" column="0" alignment="Qt::AlignVCenter">
<widget class="QCheckBox" name="checkPassword">
<property name="text">
<string>Password:</string>
@@ -70,9 +83,18 @@
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="keyFileLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="comboKeyFile">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -94,7 +116,13 @@
</layout>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="passwordLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="PasswordEdit" name="editPassword">
<property name="echoMode">
@@ -114,14 +142,21 @@
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QHBoxLayout" name="dialogButtonsLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<property name="rightMargin">
<number>5</number>
</property>
</widget>
<item alignment="Qt::AlignRight">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
@@ -152,7 +187,6 @@
<tabstop>checkKeyFile</tabstop>
<tabstop>comboKeyFile</tabstop>
<tabstop>buttonBrowseFile</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -6,160 +6,204 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>399</height>
<width>600</width>
<height>340</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,2,5,1">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>3</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="dbNameLabel">
<property name="text">
<string>Database name:</string>
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1,0">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="maximumSize">
<size>
<width>800</width>
<height>16777215</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="transformRoundsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="transformBenchmarkButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>25</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Benchmark</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1" alignment="Qt::AlignRight">
<widget class="QLabel" name="dbNameLabel">
<property name="text">
<string>Database name:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="historyMaxSizeCheckBox">
<property name="text">
<string>Max. history size:</string>
</property>
</widget>
</item>
<item row="2" column="1" alignment="Qt::AlignRight">
<widget class="QLabel" name="transformRoundsLabel">
<property name="text">
<string>Transform rounds:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="historyMaxItemsCheckBox">
<property name="text">
<string>Max. history items:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="dbNameEdit"/>
</item>
<item row="5" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="historyMaxItemsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1" alignment="Qt::AlignRight">
<widget class="QLabel" name="defaultUsernameLabel">
<property name="text">
<string>Default username:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="dbDescriptionEdit"/>
</item>
<item row="6" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="historyMaxSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> MiB</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="2">
<widget class="QCheckBox" name="recycleBinEnabledCheckBox">
<property name="text">
<string>Use recycle bin</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="defaultUsernameEdit">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" alignment="Qt::AlignRight">
<widget class="QLabel" name="dbDescriptionLabel">
<property name="text">
<string>Database description:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="dbNameEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="dbDescriptionLabel">
<property name="text">
<string>Database description:</string>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="dbDescriptionEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="transformRoundsLabel">
<property name="text">
<string>Transform rounds:</string>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="defaultUsernameLabel">
<property name="text">
<string>Default username:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="defaultUsernameEdit">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Use recycle bin:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="historyMaxSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> MiB</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="historyMaxItemsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>2000000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="transformRoundsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="transformBenchmarkButton">
<property name="text">
<string>Benchmark</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="historyMaxItemsCheckBox">
<property name="text">
<string>Max. history items:</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="historyMaxSizeCheckBox">
<property name="text">
<string>Max. history size:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="recycleBinEnabledCheckBox"/>
</spacer>
</item>
</layout>
</item>

View File

@@ -21,6 +21,7 @@
#include <QLockFile>
#include <QSaveFile>
#include <QTabWidget>
#include <QPushButton>
#include "autotype/AutoType.h"
#include "core/Config.h"
@@ -77,14 +78,9 @@ DatabaseTabWidget::~DatabaseTabWidget()
void DatabaseTabWidget::toggleTabbar()
{
if (count() > 1) {
if (!tabBar()->isVisible()) {
tabBar()->show();
}
}
else {
if (tabBar()->isVisible()) {
tabBar()->hide();
}
tabBar()->show();
} else {
tabBar()->hide();
}
}
@@ -95,9 +91,17 @@ void DatabaseTabWidget::newDatabase()
db->rootGroup()->setName(tr("Root"));
dbStruct.dbWidget = new DatabaseWidget(db, this);
CompositeKey emptyKey;
db->setKey(emptyKey);
insertDatabase(db, dbStruct);
dbStruct.dbWidget->switchToMasterKeyChange();
if (!saveDatabaseAs(db)) {
closeDatabase(db);
return;
}
dbStruct.dbWidget->switchToMasterKeyChange(true);
}
void DatabaseTabWidget::openDatabase()
@@ -154,21 +158,29 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
// for now silently ignore if we can't create a lock file
// due to lack of permissions
if (lockFile->error() != QLockFile::PermissionError) {
QMessageBox::StandardButton result = MessageBox::question(this, tr("Open database"),
tr("The database you are trying to open is locked by another instance of KeePassXC.\n"
"Do you want to open it anyway? Alternatively the database is opened read-only."),
QMessageBox::Yes | QMessageBox::No);
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Database already opened"));
msgBox.setText(tr("The database you are trying to open is locked by another instance of KeePassXC.\n\n"
"Do you want to open it anyway?"));
msgBox.setIcon(QMessageBox::Question);
msgBox.addButton(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
auto readOnlyButton = msgBox.addButton(tr("Open read-only"), QMessageBox::NoRole);
msgBox.setDefaultButton(readOnlyButton);
msgBox.setEscapeButton(QMessageBox::No);
auto result = msgBox.exec();
if (result == QMessageBox::No) {
if (msgBox.clickedButton() == readOnlyButton) {
dbStruct.readOnly = true;
delete lockFile;
lockFile = nullptr;
}
else {
} else if (result == QMessageBox::Yes) {
// take over the lock file if possible
if (lockFile->removeStaleLockFile()) {
lockFile->tryLock();
}
} else {
return;
}
}
}
@@ -353,80 +365,78 @@ bool DatabaseTabWidget::saveDatabase(Database* db)
bool DatabaseTabWidget::saveDatabaseAs(Database* db)
{
DatabaseManagerStruct& dbStruct = m_dbList[db];
QString oldFileName;
if (dbStruct.saveToFilename) {
oldFileName = dbStruct.filePath;
}
else {
oldFileName = tr("New database").append(".kdbx");
}
QString fileName = fileDialog()->getSaveFileName(this, tr("Save database as"),
oldFileName, tr("KeePass 2 Database").append(" (*.kdbx)"),
nullptr, 0, "kdbx");
if (!fileName.isEmpty()) {
QFileInfo fileInfo(fileName);
QString lockFilePath;
if (fileInfo.exists()) {
// returns empty string when file doesn't exist
lockFilePath = fileInfo.canonicalPath();
while (true) {
DatabaseManagerStruct& dbStruct = m_dbList[db];
QString oldFileName;
if (dbStruct.saveToFilename) {
oldFileName = dbStruct.filePath;
} else {
oldFileName = tr("Passwords").append(".kdbx");
}
else {
lockFilePath = fileInfo.absolutePath();
}
QString lockFileName = QString("%1/.%2.lock").arg(lockFilePath, fileInfo.fileName());
QScopedPointer<QLockFile> lockFile(new QLockFile(lockFileName));
lockFile->setStaleLockTime(0);
if (!lockFile->tryLock()) {
// for now silently ignore if we can't create a lock file
// due to lack of permissions
if (lockFile->error() != QLockFile::PermissionError) {
QMessageBox::StandardButton result = MessageBox::question(this, tr("Save database as"),
tr("The database you are trying to save as is locked by another instance of KeePassXC.\n"
"Do you want to save it anyway?"),
QMessageBox::Yes | QMessageBox::No);
QString fileName = fileDialog()->getSaveFileName(this, tr("Save database as"),
oldFileName, tr("KeePass 2 Database").append(" (*.kdbx)"),
nullptr, 0, "kdbx");
if (!fileName.isEmpty()) {
QFileInfo fileInfo(fileName);
QString lockFilePath;
if (fileInfo.exists()) {
// returns empty string when file doesn't exist
lockFilePath = fileInfo.canonicalPath();
} else {
lockFilePath = fileInfo.absolutePath();
}
QString lockFileName = QString("%1/.%2.lock").arg(lockFilePath, fileInfo.fileName());
QScopedPointer<QLockFile> lockFile(new QLockFile(lockFileName));
lockFile->setStaleLockTime(0);
if (!lockFile->tryLock()) {
// for now silently ignore if we can't create a lock file
// due to lack of permissions
if (lockFile->error() != QLockFile::PermissionError) {
QMessageBox::StandardButton result = MessageBox::question(this, tr("Save database as"),
tr("The database you are trying to save as is locked by another instance of KeePassXC.\n"
"Do you want to save it anyway?"),
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::No) {
return false;
}
else {
// take over the lock file if possible
if (lockFile->removeStaleLockFile()) {
lockFile->tryLock();
if (result == QMessageBox::No) {
return false;
} else {
// take over the lock file if possible
if (lockFile->removeStaleLockFile()) {
lockFile->tryLock();
}
}
}
}
}
// setup variables so saveDatabase succeeds
dbStruct.saveToFilename = true;
dbStruct.canonicalFilePath = fileName;
// setup variables so saveDatabase succeeds
dbStruct.saveToFilename = true;
dbStruct.canonicalFilePath = fileName;
if (!saveDatabase(db)) {
// failed to save, revert back
dbStruct.saveToFilename = false;
dbStruct.canonicalFilePath = oldFileName;
if (!saveDatabase(db)) {
// failed to save, revert back
dbStruct.saveToFilename = false;
dbStruct.canonicalFilePath = oldFileName;
continue;
}
// refresh fileinfo since the file didn't exist before
fileInfo.refresh();
dbStruct.modified = false;
dbStruct.saveToFilename = true;
dbStruct.readOnly = false;
dbStruct.filePath = fileInfo.absoluteFilePath();
dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
dbStruct.fileName = fileInfo.fileName();
dbStruct.dbWidget->updateFilename(dbStruct.filePath);
delete dbStruct.lockFile;
dbStruct.lockFile = lockFile.take();
updateTabName(db);
updateLastDatabases(dbStruct.filePath);
return true;
} else {
return false;
}
// refresh fileinfo since the file didn't exist before
fileInfo.refresh();
dbStruct.modified = false;
dbStruct.saveToFilename = true;
dbStruct.readOnly = false;
dbStruct.filePath = fileInfo.absoluteFilePath();
dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
dbStruct.fileName = fileInfo.fileName();
dbStruct.dbWidget->updateFilename(dbStruct.filePath);
delete dbStruct.lockFile;
dbStruct.lockFile = lockFile.take();
updateTabName(db);
updateLastDatabases(dbStruct.filePath);
return true;
}
else {
return false;
}
}

View File

@@ -810,9 +810,10 @@ void DatabaseWidget::switchToGroupEdit()
switchToGroupEdit(group, false);
}
void DatabaseWidget::switchToMasterKeyChange()
void DatabaseWidget::switchToMasterKeyChange(bool disableCancel)
{
m_changeMasterKeyWidget->clearForms();
m_changeMasterKeyWidget->setCancelEnabled(!disableCancel);
setCurrentWidget(m_changeMasterKeyWidget);
}
@@ -1060,8 +1061,7 @@ void DatabaseWidget::reloadDatabaseFile()
// Merge the old database into the new one
m_db->setEmitModified(false);
db->merge(m_db);
}
else {
} else {
// Since we are accepting the new file as-is, internally mark as unmodified
// TODO: when saving is moved out of DatabaseTabWidget, this should be replaced
m_databaseModified = false;
@@ -1085,16 +1085,9 @@ void DatabaseWidget::reloadDatabaseFile()
restoreGroupEntryFocus(groupBeforeReload, entryBeforeReload);
}
else {
MessageBox::critical(this, tr("Autoreload Failed"),
tr("Could not parse or unlock the new database file while attempting"
" to autoreload this database."));
}
}
else {
} else {
MessageBox::critical(this, tr("Autoreload Failed"),
tr("Could not open the new database file while attempting to autoreload"
" this database."));
tr("Could not open the new database file while attempting to autoreload this database."));
}
// Rewatch the database file

View File

@@ -132,7 +132,7 @@ public Q_SLOTS:
void switchToView(bool accepted);
void switchToEntryEdit();
void switchToGroupEdit();
void switchToMasterKeyChange();
void switchToMasterKeyChange(bool disableCancel = false);
void switchToDatabaseSettings();
void switchToOpenDatabase(const QString& fileName);
void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile);

View File

@@ -49,11 +49,18 @@
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>5</number>
</property>
</widget>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@@ -66,7 +73,6 @@
</customwidgets>
<tabstops>
<tabstop>categoryList</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -28,6 +28,11 @@
#include "gui/IconModels.h"
#include "gui/MessageBox.h"
#include "http/qhttp/qhttpclient.hpp"
#include "http/qhttp/qhttpclientresponse.hpp"
using namespace qhttp::client;
IconStruct::IconStruct()
: uuid(Uuid())
, number(0)
@@ -40,8 +45,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
, m_database(nullptr)
, m_defaultIconModel(new DefaultIconModel(this))
, m_customIconModel(new CustomIconModel(this))
, m_networkAccessMngr(new QNetworkAccessManager(this))
, m_networkOperation(nullptr)
, m_httpClient(nullptr)
{
m_ui->setupUi(this);
@@ -59,8 +63,6 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
connect(m_ui->addButton, SIGNAL(clicked()), SLOT(addCustomIcon()));
connect(m_ui->deleteButton, SIGNAL(clicked()), SLOT(removeCustomIcon()));
connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon()));
connect(m_networkAccessMngr, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onRequestFinished(QNetworkReply*)) );
m_ui->faviconButton->setVisible(false);
}
@@ -103,7 +105,7 @@ void EditWidgetIcons::reset()
m_currentUuid = Uuid();
}
void EditWidgetIcons::load(Uuid currentUuid, Database* database, IconStruct iconStruct, const QString &url)
void EditWidgetIcons::load(const Uuid& currentUuid, Database* database, const IconStruct& iconStruct, const QString& url)
{
Q_ASSERT(database);
Q_ASSERT(!currentUuid.isNull());
@@ -134,11 +136,11 @@ void EditWidgetIcons::load(Uuid currentUuid, Database* database, IconStruct icon
}
}
void EditWidgetIcons::setUrl(const QString &url)
void EditWidgetIcons::setUrl(const QString& url)
{
m_url = url;
m_ui->faviconButton->setVisible(!url.isEmpty());
abortFaviconDownload();
resetFaviconDownload();
}
void EditWidgetIcons::downloadFavicon()
@@ -148,87 +150,99 @@ void EditWidgetIcons::downloadFavicon()
fetchFavicon(url);
}
void EditWidgetIcons::fetchFavicon(QUrl url)
void EditWidgetIcons::fetchFavicon(const QUrl& url)
{
if (m_networkOperation == nullptr) {
m_networkOperation = m_networkAccessMngr->get(QNetworkRequest(url));
m_ui->faviconButton->setDisabled(true);
if (nullptr == m_httpClient) {
m_httpClient = new QHttpClient(this);
}
bool requestMade = m_httpClient->request(qhttp::EHTTP_GET, url, [this, url](QHttpResponse* response) {
if (m_database == nullptr) {
return;
}
response->collectData();
response->onEnd([this, response, &url]() {
int status = response->status();
if (200 == status) {
QImage image;
image.loadFromData(response->collectedData());
if (!image.isNull()) {
//Set the image
Uuid uuid = Uuid::random();
m_database->metadata()->addCustomIcon(uuid, image.scaled(16, 16));
m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(),
m_database->metadata()->customIconsOrder());
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
m_ui->customIconsView->setCurrentIndex(index);
m_ui->customIconsRadio->setChecked(true);
resetFaviconDownload();
} else {
fetchFaviconFromGoogle(url.host());
}
} else if (301 == status || 302 == status) {
// Check if server has sent a redirect
QUrl possibleRedirectUrl(response->headers().value("location", ""));
if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != m_redirectUrl && m_redirectCount < 3) {
resetFaviconDownload(false);
m_redirectUrl = possibleRedirectUrl;
++m_redirectCount;
fetchFavicon(m_redirectUrl);
} else {
// website is trying to redirect to itself or
// maximum number of redirects has been reached, fall back to Google
fetchFaviconFromGoogle(url.host());
}
} else {
fetchFaviconFromGoogle(url.host());
}
});
});
if (!requestMade) {
resetFaviconDownload();
return;
}
m_httpClient->setConnectingTimeOut(5000, [this]() {
resetFaviconDownload();
MessageBox::warning(this, tr("Error"), tr("Unable to fetch favicon."));
});
m_ui->faviconButton->setDisabled(true);
}
void EditWidgetIcons::fetchFaviconFromGoogle(QString domain)
void EditWidgetIcons::fetchFaviconFromGoogle(const QString& domain)
{
if (m_fallbackToGoogle) {
abortFaviconDownload();
resetFaviconDownload();
m_fallbackToGoogle = false;
fetchFavicon(QUrl("http://www.google.com/s2/favicons?domain=" + domain));
}
else {
abortFaviconDownload();
resetFaviconDownload();
MessageBox::warning(this, tr("Error"), tr("Unable to fetch favicon."));
}
}
void EditWidgetIcons::abortFaviconDownload(bool clearRedirect)
void EditWidgetIcons::resetFaviconDownload(bool clearRedirect)
{
if (m_networkOperation != nullptr) {
m_networkOperation->abort();
m_networkOperation->deleteLater();
m_networkOperation = nullptr;
if (clearRedirect) {
m_redirectUrl.clear();
m_redirectCount = 0;
}
if (clearRedirect) {
if (!m_redirectUrl.isEmpty()) {
m_redirectUrl.clear();
}
m_redirectCount = 0;
if (nullptr != m_httpClient) {
m_httpClient->deleteLater();
m_httpClient = nullptr;
}
m_fallbackToGoogle = true;
m_ui->faviconButton->setDisabled(false);
}
void EditWidgetIcons::onRequestFinished(QNetworkReply *reply)
{
if (m_database == nullptr) {
return;
}
if (!reply->error()) {
QImage image;
image.loadFromData(reply->readAll());
if (!image.isNull()) {
//Set the image
Uuid uuid = Uuid::random();
m_database->metadata()->addCustomIcon(uuid, image.scaled(16, 16));
m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(),
m_database->metadata()->customIconsOrder());
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
m_ui->customIconsView->setCurrentIndex(index);
m_ui->customIconsRadio->setChecked(true);
abortFaviconDownload();
}
else {
// Check if server has sent a redirect
QUrl possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != m_redirectUrl && m_redirectCount < 3) {
abortFaviconDownload(false);
m_redirectUrl = possibleRedirectUrl;
++m_redirectCount;
fetchFavicon(m_redirectUrl);
}
else { // Webpage is trying to redirect back to itself or the maximum number of redirects has been reached, fallback to Google
fetchFaviconFromGoogle(reply->url().host());
}
}
}
else { // Request Error e.g. 404, fallback to Google
fetchFaviconFromGoogle(reply->url().host());
}
}
void EditWidgetIcons::addCustomIcon()
{
if (m_database) {

View File

@@ -20,9 +20,7 @@
#include <QWidget>
#include <QSet>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QUrl>
#include "core/Global.h"
#include "core/Uuid.h"
@@ -31,6 +29,11 @@ class Database;
class DefaultIconModel;
class CustomIconModel;
namespace qhttp {
namespace client {
class QHttpClient;
}
}
namespace Ui {
class EditWidgetIcons;
}
@@ -53,17 +56,16 @@ public:
IconStruct state();
void reset();
void load(Uuid currentUuid, Database* database, IconStruct iconStruct, const QString &url = QString());
void load(const Uuid& currentUuid, Database* database, const IconStruct& iconStruct, const QString& url = "");
public Q_SLOTS:
void setUrl(const QString &url);
void setUrl(const QString& url);
private Q_SLOTS:
void downloadFavicon();
void fetchFavicon(QUrl url);
void fetchFaviconFromGoogle(QString domain);
void abortFaviconDownload(bool clearRedirect = true);
void onRequestFinished(QNetworkReply *reply);
void fetchFavicon(const QUrl& url);
void fetchFaviconFromGoogle(const QString& domain);
void resetFaviconDownload(bool clearRedirect = true);
void addCustomIcon();
void removeCustomIcon();
void updateWidgetsDefaultIcons(bool checked);
@@ -81,8 +83,7 @@ private:
unsigned short m_redirectCount = 0;
DefaultIconModel* const m_defaultIconModel;
CustomIconModel* const m_customIconModel;
QNetworkAccessManager* const m_networkAccessMngr;
QNetworkReply* m_networkOperation;
qhttp::client::QHttpClient* m_httpClient;
Q_DISABLE_COPY(EditWidgetIcons)
};

View File

@@ -6,15 +6,27 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<width>437</width>
<height>300</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="defaultIconsRadio">
<property name="text">
<string>Use default icon</string>
<string>&amp;Use default icon</string>
</property>
</widget>
</item>
@@ -46,7 +58,7 @@
<item>
<widget class="QRadioButton" name="customIconsRadio">
<property name="text">
<string>Use custom icon</string>
<string>Use custo&amp;m icon</string>
</property>
</widget>
</item>

View File

@@ -14,6 +14,15 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="QLineEdit" name="createdEdit">
<property name="sizePolicy">

View File

@@ -22,6 +22,11 @@
#include <QShortcut>
#include <QTimer>
#if defined(Q_OS_LINUX) && ! defined(QT_NO_DBUS)
#include <QList>
#include <QtDBus/QtDBus>
#endif
#include "config-keepassx.h"
#include "autotype/AutoType.h"
@@ -277,6 +282,10 @@ MainWindow::MainWindow()
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
#ifdef Q_OS_MAC
setUnifiedTitleAndToolBarOnMac(true);
#endif
updateTrayIcon();
}
@@ -554,7 +563,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
if (minimizeOnClose && !appExitCalled)
{
event->ignore();
hide();
hideWindow();
if (config()->get("security/lockdatabaseminimize").toBool()) {
m_ui->tabWidget->lockDatabases();
@@ -719,21 +728,45 @@ void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
}
}
void MainWindow::hideWindow()
{
setWindowState(windowState() | Qt::WindowMinimized);
QTimer::singleShot(0, this, SLOT(hide()));
if (config()->get("security/lockdatabaseminimize").toBool()) {
m_ui->tabWidget->lockDatabases();
}
}
void MainWindow::toggleWindow()
{
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
hide();
if (config()->get("security/lockdatabaseminimize").toBool()) {
m_ui->tabWidget->lockDatabases();
}
}
else {
hideWindow();
} else {
ensurePolished();
setWindowState(windowState() & ~Qt::WindowMinimized);
show();
raise();
activateWindow();
#if defined(Q_OS_LINUX) && ! defined(QT_NO_DBUS)
// re-register global D-Bus menu (needed on Ubuntu with Unity)
// see https://github.com/keepassxreboot/keepassxc/issues/271
// and https://bugreports.qt.io/browse/QTBUG-58723
// check for !isVisible(), because isNativeMenuBar() does not work with appmenu-qt5
if (!m_ui->menubar->isVisible()) {
QDBusMessage msg = QDBusMessage::createMethodCall(
"com.canonical.AppMenu.Registrar",
"/com/canonical/AppMenu/Registrar",
"com.canonical.AppMenu.Registrar",
"RegisterWindow");
QList<QVariant> args;
args << QVariant::fromValue(static_cast<uint32_t>(winId()))
<< QVariant::fromValue(QDBusObjectPath("/MenuBar/1"));
msg.setArguments(args);
QDBusConnection::sessionBus().send(msg);
}
#endif
}
}

View File

@@ -68,6 +68,7 @@ private Q_SLOTS:
void rememberOpenDatabases(const QString& filePath);
void applySettingsChanges();
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
void hideWindow();
void toggleWindow();
void lockDatabasesAfterInactivity();
void repairDatabase();

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>278</height>
<width>575</width>
<height>284</height>
</rect>
</property>
<property name="sizePolicy">
@@ -16,10 +16,25 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>284</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QGridLayout" name="passwordFieldLayout">
<property name="bottomMargin">
@@ -232,13 +247,19 @@ QProgressBar::chunk {
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="alphabetLayout">
<layout class="QHBoxLayout" name="alphabetLayout" stretch="0,0,0,0,1">
<item>
<widget class="QToolButton" name="checkBoxUpper">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
<height>25</height>
</size>
</property>
<property name="focusPolicy">
@@ -260,10 +281,16 @@ QProgressBar::chunk {
</item>
<item>
<widget class="QToolButton" name="checkBoxLower">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
<height>25</height>
</size>
</property>
<property name="focusPolicy">
@@ -285,10 +312,16 @@ QProgressBar::chunk {
</item>
<item>
<widget class="QToolButton" name="checkBoxNumbers">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
<height>25</height>
</size>
</property>
<property name="focusPolicy">
@@ -310,10 +343,16 @@ QProgressBar::chunk {
</item>
<item>
<widget class="QToolButton" name="checkBoxSpecialChars">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
<height>25</height>
</size>
</property>
<property name="focusPolicy">

View File

@@ -65,6 +65,7 @@ SettingsWidget::SettingsWidget(QWidget* parent)
#ifdef Q_OS_MAC
// systray not useful on OS X
m_generalUi->systrayShowCheckBox->setVisible(false);
m_generalUi->systrayMinimizeOnCloseCheckBox->setVisible(false);
m_generalUi->systrayMinimizeToTrayCheckBox->setVisible(false);
#endif

View File

@@ -77,6 +77,8 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
connect(this, SIGNAL(accepted()), SLOT(saveEntry()));
connect(this, SIGNAL(rejected()), SLOT(cancel()));
m_mainUi->passwordGenerator->layout()->setContentsMargins(0, 0, 0, 0);
}
EditEntryWidget::~EditEntryWidget()

View File

@@ -7,10 +7,22 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>315</height>
<height>366</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="attributesBox">
<property name="title">

View File

@@ -7,10 +7,22 @@
<x>0</x>
<y>0</y>
<width>567</width>
<height>347</height>
<height>348</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="enableButton">
<property name="text">
@@ -37,14 +49,14 @@
<item>
<widget class="QRadioButton" name="inheritSequenceButton">
<property name="text">
<string>Inherit default Auto-Type sequence from the group</string>
<string>Inherit default Auto-Type sequence from the &amp;group</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="customSequenceButton">
<property name="text">
<string>Use custom Auto-Type sequence:</string>
<string>&amp;Use custom Auto-Type sequence:</string>
</property>
</widget>
</item>
@@ -90,39 +102,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="assocAddButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="assocRemoveButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,1,1">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
@@ -136,6 +116,50 @@
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="assocAddButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="assocRemoveButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -171,14 +195,14 @@
<item>
<widget class="QRadioButton" name="defaultWindowSequenceButton">
<property name="text">
<string>Use default sequence</string>
<string>Use default se&amp;quence</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="customWindowSequenceButton">
<property name="text">
<string>Set custom sequence:</string>
<string>Set custo&amp;m sequence:</string>
</property>
</widget>
</item>

View File

@@ -11,6 +11,18 @@
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTreeView" name="historyView">
<property name="sortingEnabled">

View File

@@ -6,35 +6,50 @@
<rect>
<x>0</x>
<y>0</y>
<width>372</width>
<height>364</height>
<width>692</width>
<height>323</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="titleLabel">
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="5" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="urlLabel">
<property name="text">
<string>Title:</string>
<string>URL:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="titleEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="usernameLabel">
<property name="text">
<string>Username:</string>
<item row="4" column="1">
<widget class="PasswordGeneratorWidget" name="passwordGenerator" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="usernameEdit"/>
</item>
<item row="3" column="0">
<item row="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="passwordLabel">
<property name="text">
<string>Password:</string>
@@ -42,31 +57,6 @@
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="PasswordEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="togglePasswordButton">
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="passwordRepeatLabel">
<property name="text">
<string>Repeat:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="PasswordEdit" name="passwordRepeatEdit">
@@ -84,24 +74,46 @@
</item>
</layout>
</item>
<item row="7" column="0">
<widget class="QLabel" name="urlLabel">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="PasswordEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="togglePasswordButton">
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="passwordRepeatLabel">
<property name="text">
<string>URL:</string>
<string>Repeat:</string>
</property>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item row="9" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="notesLabel">
<property name="text">
<string>Notes:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="urlEdit"/>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="expireCheck">
<property name="text">
<string>Expires</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QDateTimeEdit" name="expireDatePicker">
@@ -115,6 +127,12 @@
</item>
<item>
<widget class="QPushButton" name="expirePresets">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Presets</string>
</property>
@@ -122,13 +140,6 @@
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="notesLabel">
<property name="text">
<string>Notes:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QPlainTextEdit" name="notesEdit">
<property name="sizePolicy">
@@ -139,8 +150,28 @@
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="PasswordGeneratorWidget" name="passwordGenerator" native="true"/>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="usernameLabel">
<property name="text">
<string>Username:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="titleEdit"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="usernameEdit"/>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="urlEdit"/>
</item>
<item row="7" column="0" alignment="Qt::AlignRight">
<widget class="QCheckBox" name="expireCheck">
<property name="text">
<string>Expires</string>
</property>
</widget>
</item>
</layout>
</widget>
@@ -165,7 +196,6 @@
<tabstop>togglePasswordButton</tabstop>
<tabstop>togglePasswordGeneratorButton</tabstop>
<tabstop>urlEdit</tabstop>
<tabstop>expireCheck</tabstop>
<tabstop>expireDatePicker</tabstop>
<tabstop>expirePresets</tabstop>
<tabstop>notesEdit</tabstop>

View File

@@ -140,15 +140,21 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index)
void EntryView::switchToEntryListMode()
{
m_sortModel->hideColumn(0, false);
sortByColumn(1, Qt::AscendingOrder); // TODO: should probably be improved
m_sortModel->sort(1, Qt::AscendingOrder);
m_sortModel->sort(0, Qt::AscendingOrder);
sortByColumn(0, Qt::AscendingOrder);
m_inEntryListMode = true;
}
void EntryView::switchToGroupMode()
{
m_sortModel->hideColumn(0, true);
sortByColumn(-1, Qt::AscendingOrder);
m_sortModel->sort(-1, Qt::AscendingOrder);
m_sortModel->sort(0, Qt::AscendingOrder);
sortByColumn(0, Qt::AscendingOrder);
m_inEntryListMode = false;
}

View File

@@ -19,6 +19,8 @@ AccessControlDialog::AccessControlDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AccessControlDialog())
{
this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
ui->setupUi(this);
connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept()));
connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject()));

26
src/http/CMakeLists.txt Normal file
View File

@@ -0,0 +1,26 @@
add_subdirectory(qhttp)
if(WITH_XC_HTTP)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
set(keepasshttp_SOURCES
AccessControlDialog.cpp
EntryConfig.cpp
HttpPasswordGeneratorWidget.cpp
HttpSettings.cpp
OptionDialog.cpp
Protocol.cpp
Server.cpp
Service.cpp
)
set(keepasshttp_FORMS
AccessControlDialog.ui
HttpPasswordGeneratorWidget.ui
OptionDialog.ui
)
qt5_wrap_ui(keepasshttp_SOURCES ${keepasshttp_FORMS})
add_library(keepasshttp STATIC ${keepasshttp_SOURCES})
target_link_libraries(keepasshttp qhttp Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network)
endif()

View File

@@ -28,7 +28,7 @@ This is required for accessing your databases from ChromeIPass or PassIFox</stri
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@@ -45,7 +45,7 @@ This is required for accessing your databases from ChromeIPass or PassIFox</stri
<item>
<widget class="QCheckBox" name="bestMatchOnly">
<property name="text">
<string>&amp;Return only best matching entries for an URL instead
<string>&amp;Return only best matching entries for a URL instead
of all entries for the whole domain</string>
</property>
</widget>
@@ -82,7 +82,7 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<item>
<widget class="QPushButton" name="removeSharedEncryptionKeys">
<property name="text">
<string>R&amp;emove all shared encryption-keys from active database</string>
<string>R&amp;emove all shared encryption keys from active database</string>
</property>
</widget>
</item>
@@ -148,7 +148,7 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<string notr="true">color: rgb(255, 0, 0);</string>
</property>
<property name="text">
<string>Activate the following only, if you know what you are doing!</string>
<string>The following options can be dangerous. Change them only if you know what you are doing.</string>
</property>
</widget>
</item>
@@ -186,14 +186,14 @@ Only entries with the same scheme (http://, https://, ftp://, ...) are returned<
<item>
<widget class="QCheckBox" name="supportKphFields">
<property name="text">
<string>&amp;Return also advanced string fields which start with &quot;KPH: &quot;</string>
<string>&amp;Return advanced string fields which start with &quot;KPH: &quot;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Automatic creates or updates are not supported for string fields!</string>
<string>Automatically creating or updating string fields is not supported.</string>
</property>
<property name="indent">
<number>30</number>

View File

@@ -27,7 +27,7 @@ static const char * const STR_SET_LOGIN = "set-login";
static const char * const STR_ASSOCIATE = "associate";
static const char * const STR_TEST_ASSOCIATE = "test-associate";
static const char * const STR_GENERATE_PASSWORD = "generate-password";
static const char * const STR_VERSION = "1.8.4.1";
static const char * const STR_VERSION = "1.8.4.2";
}/*namespace KeepassHttpProtocol*/
@@ -110,7 +110,7 @@ static QByteArray encrypt2(const QByteArray & data, SymmetricCipherGcrypt & ciph
//Encrypt
QByteArray buffer = data + QByteArray(paddingSize, paddingSize);
cipher.reset();
cipher.processInPlace(buffer);
Q_UNUSED(cipher.processInPlace(buffer));
return buffer;
}

View File

@@ -11,41 +11,30 @@
***************************************************************************
*/
#include "Server.h"
#include <microhttpd.h>
#include "Protocol.h"
#include "HttpSettings.h"
#include "crypto/Crypto.h"
#include <QEventLoop>
#include <QtCore/QHash>
#include <QtCore/QCryptographicHash>
#include <QtWidgets/QMessageBox>
#include <QEventLoop>
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QHostAddress>
#ifdef Q_OS_WIN
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
#include "qhttp/qhttpserver.hpp"
#include "qhttp/qhttpserverresponse.hpp"
#include "qhttp/qhttpserverrequest.hpp"
#include "Server.h"
#include "Protocol.h"
#include "HttpSettings.h"
#include "crypto/Crypto.h"
using namespace KeepassHttpProtocol;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Request
////////////////////////////////////////////////////////////////////////////////////////////////////
using namespace qhttp::server;
Server::Server(QObject *parent) :
QObject(parent),
m_started(false)
m_started(false),
m_server(nullptr)
{
connect(this, SIGNAL(emitRequest(const QByteArray, QByteArray*)),
this, SLOT(handleRequest(const QByteArray, QByteArray*)));
connect(this, SIGNAL(emitOpenDatabase(bool*)),
this, SLOT(handleOpenDatabase(bool*)));
}
}
void Server::testAssociate(const Request& r, Response * protocolResp)
{
@@ -132,39 +121,6 @@ void Server::setLogin(const Request &r, Response *protocolResp)
protocolResp->setVerifier(key);
}
int Server::send_response(struct MHD_Connection *connection, const char *page)
{
int ret;
struct MHD_Response *response;
response = MHD_create_response_from_buffer(
strlen(page), static_cast<void*>(const_cast<char*>(page)),
MHD_RESPMEM_PERSISTENT);
if (!response) return MHD_NO;
MHD_add_response_header (response, "Content-Type", "application/json");
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
}
int Server::send_unavailable(struct MHD_Connection *connection)
{
int ret;
struct MHD_Response *response;
response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
if (!response) return MHD_NO;
ret = MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE, response);
MHD_destroy_response (response);
return ret;
}
void Server::generatePassword(const Request &r, Response *protocolResp)
{
QString key = getKey(r.id());
@@ -182,27 +138,10 @@ void Server::generatePassword(const Request &r, Response *protocolResp)
memset(password.data(), 0, password.length());
}
int Server::request_handler_wrapper(void *me, struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size, void **con_cls)
void Server::handleRequest(const QByteArray& data, QHttpResponse* response)
{
Server *myself = static_cast<Server*>(me);
if (myself)
return myself->request_handler(connection, url, method, version,
upload_data, upload_data_size, con_cls);
else
return MHD_NO;
}
void Server::handleRequest(const QByteArray in, QByteArray *out)
{
*out = QByteArray();
Request r;
if (!r.fromJson(in))
if (!r.fromJson(data))
return;
QByteArray hash = QCryptographicHash::hash(
@@ -221,7 +160,7 @@ void Server::handleRequest(const QByteArray in, QByteArray *out)
case GENERATE_PASSWORD: generatePassword(r, &protocolResp); break;
}
*out = protocolResp.toJson().toUtf8();
QString out = protocolResp.toJson().toUtf8();
// THIS IS A FAKE HACK!!!
// the real "error" is a misbehavior in the QJSON qobject2qvariant method
@@ -232,100 +171,17 @@ void Server::handleRequest(const QByteArray in, QByteArray *out)
//(4. ChromeIPass tries to access Entries.length and fails with null pointer exception)
// the fake workaround replaces the (wrong) "Entries":null with "Entries:[] to give
// chromeIPass (and passIFox) en empty list
QString tmp_out = QString(*out);
int tmp_pos1 = tmp_out.indexOf("\"Count\":0,");
int tmp_pos2 = tmp_out.indexOf("\"Entries\":null,");
if (tmp_pos1 != -1 && tmp_pos2 != -1) {
tmp_out.replace(tmp_pos2, 15, "\"Entries\":[],");
}
*out = tmp_out.toUtf8();
Q_EMIT donewrk();
}
void Server::handleOpenDatabase(bool *success)
{
*success = openDatabase();
Q_EMIT donewrk();
}
int Server::request_handler(struct MHD_Connection *connection,
const char *, const char *method, const char *,
const char *upload_data, size_t *upload_data_size, void **con_cls)
{
struct Server::connection_info_struct *con_info =
static_cast<struct Server::connection_info_struct*>(*con_cls);
if (!isDatabaseOpened()) {
bool success;
QEventLoop loop1;
loop1.connect(this, SIGNAL(donewrk()), SLOT(quit()));
Q_EMIT emitOpenDatabase(&success);
loop1.exec();
if (!success)
return send_unavailable(connection);
int pos1 = out.indexOf("\"Count\":0,");
int pos2 = out.indexOf("\"Entries\":null,");
if (pos1 != -1 && pos2 != -1) {
out.replace(pos2, 15, "\"Entries\":[],");
}
if (con_info == NULL) {
*con_cls = calloc(1, sizeof(*con_info));
return MHD_YES;
}
if (strcmp (method, MHD_HTTP_METHOD_POST) != 0)
return MHD_NO;
if (*upload_data_size == 0) {
if (con_info && con_info->response)
return send_response(connection, con_info->response);
else
return MHD_NO;
}
QString type = MHD_lookup_connection_value(connection,
MHD_HEADER_KIND, "Content-Type");
if (!type.contains("application/json", Qt::CaseInsensitive))
return MHD_NO;
// Now process the POST request
QByteArray post = QByteArray(upload_data, *upload_data_size);
QByteArray s;
QEventLoop loop;
loop.connect(this, SIGNAL(donewrk()), SLOT(quit()));
Q_EMIT emitRequest(post, &s);
loop.exec();
if (s.size() == 0)
return MHD_NO;
con_info->response = static_cast<char*>(calloc(1, s.size()+1));
memcpy(con_info->response, s.data(), s.size());
*upload_data_size = 0;
return MHD_YES;
response->setStatusCode(qhttp::ESTATUS_OK);
response->addHeader("Content-Type", "application/json");
response->end(out.toUtf8());
}
void Server::request_completed(void *, struct MHD_Connection *,
void **con_cls, enum MHD_RequestTerminationCode)
{
struct Server::connection_info_struct *con_info =
static_cast<struct Server::connection_info_struct*>(*con_cls);
if (con_info == NULL)
return;
if (con_info->response) free(con_info->response);
free(con_info);
*con_cls = NULL;
}
void Server::start(void)
{
if (m_started)
@@ -336,37 +192,36 @@ void Server::start(void)
QHostAddress address("127.0.0.1");
int port = HttpSettings::httpPort();
void* addrx = NULL;
unsigned int flags = MHD_USE_SELECT_INTERNALLY;
m_server = new QHttpServer(this);
m_server->listen(address, port);
connect(m_server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), this, SLOT(onNewRequest(QHttpRequest*, QHttpResponse*)));
struct sockaddr_in *addr = static_cast<struct sockaddr_in*>(calloc(1, sizeof(struct sockaddr_in)));
addrx = static_cast<void*>(addr);
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = htonl(address.toIPv4Address());
if (NULL == (daemon = MHD_start_daemon(flags, port, NULL, NULL,
&this->request_handler_wrapper, this,
MHD_OPTION_NOTIFY_COMPLETED,
this->request_completed, NULL,
MHD_OPTION_SOCK_ADDR,
addrx,
MHD_OPTION_END))) {
qWarning("HTTPPlugin: Failed to bind to localhost!");
} else {
m_started = true;
}
if (addrx != NULL)
free(addrx);
m_started = true;
}
void Server::stop(void)
{
if (!m_started)
return;
MHD_stop_daemon(daemon);
m_server->stopListening();
m_server->deleteLater();
m_started = false;
}
void Server::onNewRequest(QHttpRequest* request, QHttpResponse* response)
{
if (!isDatabaseOpened()) {
if (!openDatabase()) {
response->setStatusCode(qhttp::ESTATUS_SERVICE_UNAVAILABLE);
response->end();
return;
}
}
request->collectData(1024);
request->onEnd([=]() {
this->handleRequest(request->collectedData(), response);
});
}

View File

@@ -16,11 +16,19 @@
#include <QtCore/QObject>
#include <QtCore/QList>
#include <microhttpd.h>
namespace qhttp {
namespace server {
class QHttpServer;
class QHttpRequest;
class QHttpResponse;
}
}
namespace KeepassHttpProtocol {
using namespace qhttp::server;
class Request;
class Response;
class Entry;
@@ -31,7 +39,6 @@ class Server : public QObject
public:
explicit Server(QObject *parent = 0);
//TODO: use QByteArray?
virtual bool isDatabaseOpened() const = 0;
virtual bool openDatabase() = 0;
virtual QString getDatabaseRootUuid() = 0;
@@ -45,18 +52,13 @@ public:
virtual void updateEntry(const QString &id, const QString &uuid, const QString &login, const QString &password, const QString &url) = 0;
virtual QString generatePassword() = 0;
public Q_SLOTS:
public slots:
void start();
void stop();
private Q_SLOTS:
void handleRequest(const QByteArray in, QByteArray *out);
void handleOpenDatabase(bool *success);
Q_SIGNALS:
void emitRequest(const QByteArray in, QByteArray *out);
void emitOpenDatabase(bool *success);
void donewrk();
private slots:
void onNewRequest(QHttpRequest* request, QHttpResponse* response);
void handleRequest(const QByteArray& data, QHttpResponse* response);
private:
void testAssociate(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
@@ -67,25 +69,9 @@ private:
void setLogin(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
void generatePassword(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
static int request_handler_wrapper(void *me,
struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size, void **con_cls);
static void request_completed(void *, struct MHD_Connection *,
void **con_cls, enum MHD_RequestTerminationCode);
int request_handler(struct MHD_Connection *connection,
const char *, const char *method, const char *,
const char *upload_data, size_t *upload_data_size, void **con_cls);
int send_response(struct MHD_Connection *connection, const char *page);
int send_unavailable(struct MHD_Connection *connection);
bool m_started;
struct MHD_Daemon *daemon;
struct connection_info_struct {
char *response;
};
QHttpServer* m_server;
};
} /*namespace KeepassHttpProtocol*/

View File

@@ -14,7 +14,6 @@
#include <QInputDialog>
#include <QMessageBox>
#include <QProgressDialog>
#include <QDebug>
#include "Service.h"
#include "Protocol.h"

View File

@@ -0,0 +1,18 @@
project(qhttp)
set(qhttp_SOURCES
qhttpabstracts.cpp
qhttpserverconnection.cpp
qhttpserverrequest.cpp
qhttpserverresponse.cpp
qhttpserver.cpp
qhttpclientrequest.cpp
qhttpclientresponse.cpp
qhttpclient.cpp
http-parser/http_parser.c
)
add_library(qhttp STATIC ${qhttp_SOURCES})
target_compile_definitions(qhttp PUBLIC QHTTP_MEMORY_LOG=0 QHTTP_EXPORT)
target_include_directories(qhttp PRIVATE .)
target_link_libraries(qhttp Qt5::Core Qt5::Network)

22
src/http/qhttp/LICENSE Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2014 Amir Zamani
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.

229
src/http/qhttp/README.md Normal file
View File

@@ -0,0 +1,229 @@
# QHttp
### Table of contents
- [About](#about)
- [Sample codes](#sample-codes)
- [Features](#features)
- [Setup](#setup)
- [Multi-threading](#multi-threading)
- [Source tree](#source-tree)
- [Disclaimer](#disclaimer)
- [License](#license)
## About
[TOC](#table-of-contents)
`QHttp` is a lightweight, asynchronous and fast HTTP library, containing both server and client side classes for managing connections, parsing and building HTTP requests and responses. this project is inspired by [nikhilm/qhttpserver](https://github.com/nikhilm/qhttpserver) effort to implement a Qt HTTP server. `QHttp` pushes the idea further by implementing client classes and better memory management, a lot more Node.js-like API, ...
* the fantastic [nodejs/http-parser](https://github.com/nodejs/http-parser) is the core parser of HTTP requests (server mode) and responses (client mode).
* By using `std::function` and `c++11 lambda`, the API is intentionally similar to the [Node.js' http module](http://nodejs.org/api/http.html). Asynchronous and non-blocking HTTP programming is quite easy with `QHttp`. have a look at [sample codes](#sample-codes).
* the objective of `QHttp` is being light weight with a simple API for Qt developers to implement RESTful web services in private (internal) zones. [more](#disclaimer)
## Sample codes
[TOC](#table-of-contents)
a HelloWorld **HTTP server** by `QHttp` looks like:
``` cpp
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
using namespace qhttp::server;
QHttpServer server(&app);
// listening on 0.0.0.0:8080
server.listen(QHostAddress::Any, 8080, [](QHttpRequest* req, QHttpResponse* res) {
res->setStatusCode(qhttp::ESTATUS_OK); // http status 200
//res->addHeader("connection", "close"); // optional, it's the default header
res->end("Hello World!\n"); // the response body data
// by "connection: close", the req and res objects will be deleted automatically.
});
if ( !server.isListening() ) {
fprintf(stderr, "failed. can not listen at port 8080!\n");
return -1;
}
return app.exec();
}
```
to request weather information by **HTTP client**:
```cpp
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
using namespace qhttp::client;
QHttpClient client(&app);
QByteArray httpBody;
QUrl weatherUrl("http://api.openweathermap.org/data/2.5/weather?q=tehran,ir&units=metric&mode=xml");
client.request(qhttp::EHTTP_GET, weatherUrl, [&httpBody](QHttpResponse* res) {
// response handler, called when the HTTP headers of the response are ready
// gather HTTP response data
res->onData([&httpBody](const QByteArray& chunk) {
httpBody.append(chunk);
});
// called when all data in HTTP response have been read.
res->onEnd([&httpBody]() {
// print the XML body of the response
puts("\n[incoming response:]");
puts(httpBody.constData());
puts("\n\n");
QCoreApplication::instance()->quit();
});
// just for fun! print incoming headers:
puts("\n[Headers:]");
const qhttp::THeaderHash& hs = res->headers();
for ( auto cit = hs.constBegin(); cit != hs.constEnd(); cit++) {
printf("%s : %s\n", cit.key().constData(), cit.value().constData());
}
});
// set a timeout for making the request
client.setConnectingTimeOut(10000, []{
qDebug("connecting to HTTP server timed out!");
QCoreApplication::quit();
});
return app.exec();
}
```
## Features
[TOC](#table-of-contents)
* the only dependencies are: [Qt 5](http://qt-project.org/downloads), [c++11](http://en.wikipedia.org/wiki/C%2B%2B11) and [joyent/http-parser](https://github.com/joyent/http-parser)
* both TCP and UNIX (local) sockets are supported as backend.
* separate `namespace`s for server and client classes.
* HTTP server classes: [QHttpServer](./src/qhttpserver.hpp), [QHttpConnection](./src/qhttpserverconnection.hpp), [QHttpRequest](./src/qhttpserverrequest.hpp) and [QHttpResponse](./src/qhttpserverresponse.hpp).
* HTTP client classes: [QHttpClient](./src/qhttpclient.hpp), [QHttpRequest](./src/qhttpclientrequest.hpp) and [QHttpResponse](./src/qhttpclientresponse.hpp).
* **automatic memory management** of objects. Instances of connections, requests and replies will be deleted automatically when socket drops or disconnected.
* **PIMPL** (Private classes) to achieve better ABI compatibility and cleaner API.
* **Asynchronous** and **non-blocking**. You can handle thousands of concurrent HTTP connections efficiently by a single thread, although a multi-threaded HTTP server is easy to implement.
* **high throughput**, I have tried the `QHttp` and [gason++](https://github.com/azadkuh/gason--) to implement a REST/Json web service on an Ubuntu VPS (dual core + 512MB ram) with more than **5800** connections per second (stress test). On a MacBook Pro (i5 4258U 4cores with HT + 8096MB ram), `QHttp` easily reaches to more than **11700** connections / second. Generally `QHttp` is **1.5x ~ 3x** faster than `Node.js` depending on your machine / OS. check [benchmark app](./example/benchmard/README.md) to measure your system.
* Tested under **Linux** (Ubuntu 12.04 LTS, 14.04 LTS, g++) and **OS X** (10.9/10.10/10.11, clang). Easily portable where ever Qt 5 works. (tested by some users on Windows7/msvc2013 and Windows8.1/msvc2015)
## Setup
[TOC](#table-of-contents)
instructions:
```bash
# first clone this repository:
$> git clone --depth=1 https://github.com/azadkuh/qhttp.git -b master
$> cd qhttp
# prepare dependencies:
$> ./update-dependencies.sh
# now build the library and the examples
$> qmake qhttp.pro
$> make -j 8
```
## Multi-threading
[TOC](#table-of-contents)
As `QHttp` is **asynchronous** and **non-blocking**, your app can handle thousands of concurrent HTTP connections by a single thread.
in some rare scenarios you may want to use multiple handler threads (although it's not the best solution):
* there are some blocking APIs (QSql, system calls, ...) in your connection handler (adopting asynchronous layer over the blocking API is a better approach).
* the hardware has lots of free cores and the measurement shows that the load on the main `QHttp` thread is close to highest limit. There you can spawn some other handler threads.
[benchmark example](./example/benchmark/README.md) shows how to implement a single or multi threaded HTTP app (both server and client). This example uses worker `QThread` and `QObject::moveToThread()` for worker objects. see also: [Subclassing no longer recommended way of using QThread](http://qt-project.org/doc/note_revisions/5/8/view).
**Note**:
> moving objects between threads is an expensive job, more ever the locking/unlocking mechanism, creating or stopping threads, ... cost even more! so using multiple threads in an application is not guaranteed to get better performance, but it's guaranteed to add more complexity, nasty bugs and headache!
see why other top performer networking libraries as ZeroMQ are concurrent but not multi-threaded by default:
* [ZeroMQ : Multithreading Magic](http://zeromq.org/blog:multithreading-magic)
* [Node.js : about](http://nodejs.org/about/)
## Source tree
[TOC](#table-of-contents)
* **`3rdparty/`**:
will contain `http-parser` source tree as the only dependency.
this directory is created by setup. see also: [setup](#setup).
* **`example/`**:
contains some sample applications representing the `QHttp` usage:
* **`helloworld/`**:
the HelloWorld example of `QHttp`, both server + client are represented.
see: [README@helloworld](./example/helloworld/README.md)
* **`basic-server/`**:
a basic HTTP server shows how to collect the request body, and respond to the clients.
see: [README@basic-server](./example/basic-server/README.md)
* **`benchmark/`**:
a simple utility to measure the throughput (requests per second) of `QHttp` as a REST/Json server. this app provides both the server and attacking clients.
see: [README@benchmark](./example/benchmark/README.md)
* **`nodejs/`**:
Node.js implementation of `benchmark/` in server mode. Provided for benchmarking `QHttp` with `Node.js` as a RESTFul service app.
see: [README@nodejs](./example/nodejs/README.md)
* **`src/`**:
holds the source code of `QHttp`. server classes are prefixed by `qhttpserver*` and client classes by `qhttpclient*`.
* **`private/`**:
Private classes of the library. see: [d-pointers](https://qt-project.org/wiki/Dpointer).
* **`tmp/`**:
a temporary directory which is created while `make`ing the library and holds all the `.o`, `moc files`, etc.
* **`xbin/`**:
all the executable and binaries will be placed on this folder by `make`.
## Disclaimer
[TOC](#table-of-contents)
* Implementing a lightweight and simple HTTP server/client in Qt with Node.js like API, is the main purpose of `QHttp`.
* There are lots of features in a full blown HTTP server which are out of scope of this small library, although those can be added on top of `QHttp`.
* The client classes are by no mean designed as a `QNetworkAccessManager` replacement. `QHttpClient` is simpler and lighter, for serious scenarios just use `QNetworkAccessManager`.
* I'm a busy person.
> If you have any ideas, critiques, suggestions or whatever you want to call it, please open an issue. I'll be happy to hear from you what you'd see in this lib. I think about all suggestions, and I try to add those that make sense.
## License
[TOC](#table-of-contents)
Distributed under the MIT license. Copyright (c) 2014, Amir Zamani.

View File

@@ -0,0 +1,23 @@
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
Igor Sysoev.
Additional changes are licensed under the same terms as NGINX and
copyright Joyent, Inc. and other Node contributors. All rights reserved.
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.

View File

@@ -0,0 +1,246 @@
HTTP Parser
===========
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
This is a parser for HTTP messages written in C. It parses both requests and
responses. The parser is designed to be used in performance HTTP
applications. It does not make any syscalls nor allocations, it does not
buffer data, it can be interrupted at anytime. Depending on your
architecture, it only requires about 40 bytes of data per message
stream (in a web server that is per connection).
Features:
* No dependencies
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Upgrade support
* Defends against buffer overflow attacks.
The parser extracts the following information from HTTP messages:
* Header fields and values
* Content-Length
* Request method
* Response status code
* Transfer-Encoding
* HTTP version
* Request URL
* Message body
Usage
-----
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this for a request parser:
```c
http_parser_settings settings;
settings.on_url = my_url_callback;
settings.on_header_field = my_header_field_callback;
/* ... */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST);
parser->data = my_socket;
```
When data is received on the socket execute the parser and check for errors.
```c
size_t len = 80*1024, nparsed;
char buf[len];
ssize_t recved;
recved = recv(fd, buf, len, 0);
if (recved < 0) {
/* Handle error. */
}
/* Start up / continue the parser.
* Note we pass recved==0 to signal that EOF has been received.
*/
nparsed = http_parser_execute(parser, &settings, buf, recved);
if (parser->upgrade) {
/* handle new protocol */
} else if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
}
```
HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
Scalar valued message information such as `status_code`, `method`, and the
HTTP version are stored in the parser structure. This data is only
temporally stored in `http_parser` and gets reset on each new message. If
this information is needed later, copy it out of the structure during the
`headers_complete` callback.
The parser decodes the transfer-encoding for both requests and responses
transparently. That is, a chunked encoding is decoded before being sent to
the on_body callback.
The Special Problem of Upgrade
------------------------------
HTTP supports upgrading the connection to a different protocol. An
increasingly common example of this is the WebSocket protocol which sends
a request like
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
followed by non-HTTP data.
(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
WebSocket protocol.)
To support this, the parser will treat this as a normal HTTP message without a
body, issuing both on_headers_complete and on_message_complete callbacks. However
http_parser_execute() will stop parsing at the end of the headers and return.
The user is expected to check if `parser->upgrade` has been set to 1 after
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
offset by the return value of `http_parser_execute()`.
Callbacks
---------
During the `http_parser_execute()` call, the callbacks set in
`http_parser_settings` will be executed. The parser maintains state and
never looks behind, so buffering the data is not necessary. If you need to
save certain data for later usage, you can do that from the callbacks.
There are two types of callbacks:
* notification `typedef int (*http_cb) (http_parser*);`
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
Callbacks: (requests only) on_url,
(common) on_header_field, on_header_value, on_body;
Callbacks must return 0 on success. Returning a non-zero value indicates
error to the parser, making it exit immediately.
For cases where it is necessary to pass local information to/from a callback,
the `http_parser` object's `data` field can be used.
An example of such a case is when using threads to handle a socket connection,
parse a request, and then give a response over that socket. By instantiation
of a thread-local struct containing relevant data (e.g. accepted socket,
allocated memory for callbacks to write into, etc), a parser's callbacks are
able to communicate data between the scope of the thread and the scope of the
callback in a threadsafe manner. This allows http-parser to be used in
multi-threaded contexts.
Example:
```c
typedef struct {
socket_t sock;
void* buffer;
int buf_len;
} custom_data_t;
int my_url_callback(http_parser* parser, const char *at, size_t length) {
/* access to thread local custom_data_t struct.
Use this access save parsed data for later use into thread local
buffer, or communicate over socket
*/
parser->data;
...
return 0;
}
...
void http_parser_thread(socket_t sock) {
int nparsed = 0;
/* allocate memory for user data */
custom_data_t *my_data = malloc(sizeof(custom_data_t));
/* some information for use by callbacks.
* achieves thread -> callback information flow */
my_data->sock = sock;
/* instantiate a thread-local parser */
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
/* this custom data reference is accessible through the reference to the
parser supplied to callback functions */
parser->data = my_data;
http_parser_settings settings; /* set up callbacks */
settings.on_url = my_url_callback;
/* execute parser */
nparsed = http_parser_execute(parser, &settings, buf, recved);
...
/* parsed information copied from callback.
can now perform action on data copied into thread-local memory from callbacks.
achieves callback -> thread information flow */
my_data->buffer;
...
}
```
In case you parse HTTP message in chunks (i.e. `read()` request line
from socket, parse, read half headers, parse, etc) your data callbacks
may be called more than once. Http-parser guarantees that data pointer is only
valid for the lifetime of callback. You can also `read()` into a heap allocated
buffer to avoid copying memory around if this fits your application.
Reading headers may be a tricky task if you read/parse headers partially.
Basically, you need to remember whether last header callback was field or value
and apply the following logic:
(on_header_field and on_header_value shortened to on_h_*)
------------------------ ------------ --------------------------------------------
| State (prev. callback) | Callback | Description/action |
------------------------ ------------ --------------------------------------------
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
| | | into it |
------------------------ ------------ --------------------------------------------
| value | on_h_field | New header started. |
| | | Copy current name,value buffers to headers |
| | | list and allocate new buffer for new name |
------------------------ ------------ --------------------------------------------
| field | on_h_field | Previous name continues. Reallocate name |
| | | buffer and append callback data to it |
------------------------ ------------ --------------------------------------------
| field | on_h_value | Value for current header started. Allocate |
| | | new buffer and copy callback data to it |
------------------------ ------------ --------------------------------------------
| value | on_h_value | Value continues. Reallocate value buffer |
| | | and append callback data to it |
------------------------ ------------ --------------------------------------------
Parsing URLs
------------
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
Users of this library may wish to use it to parse URLs constructed from
consecutive `on_url` callbacks.
See examples of reading in headers:
* [partial example](http://gist.github.com/155877) in C
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,432 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* 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.
*/
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_PATCH 1
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
#include <stddef.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTPPARSER_HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required)
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTPPARSER_HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,119 @@
/** @file httpparser.hxx
*
* @copyright (C) 2016
* @date 2016.05.26
* @version 1.0.0
* @author amir zamani <azadkuh@live.com>
*
*/
#ifndef __QHTTP_HTTPPARSER_HXX__
#define __QHTTP_HTTPPARSER_HXX__
#include "qhttpbase.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace details {
///////////////////////////////////////////////////////////////////////////////
/// base HttpParser based on joyent http_parser
template<class TImpl>
class HttpParser
{
public:
explicit HttpParser(http_parser_type type) {
// create http_parser object
iparser.data = static_cast<TImpl*>(this);
http_parser_init(&iparser, type);
memset(&iparserSettings, 0, sizeof(http_parser_settings));
iparserSettings.on_message_begin = onMessageBegin;
iparserSettings.on_url = onUrl;
iparserSettings.on_status = onStatus;
iparserSettings.on_header_field = onHeaderField;
iparserSettings.on_header_value = onHeaderValue;
iparserSettings.on_headers_complete = onHeadersComplete;
iparserSettings.on_body = onBody;
iparserSettings.on_message_complete = onMessageComplete;
}
size_t parse(const char* data, size_t length) {
return http_parser_execute(&iparser,
&iparserSettings,
data,
length);
}
public: // callback functions for http_parser_settings
static int onMessageBegin(http_parser* p) {
return me(p)->messageBegin(p);
}
static int onUrl(http_parser* p, const char* at, size_t length) {
return me(p)->url(p, at, length);
}
static int onStatus(http_parser* p, const char* at, size_t length) {
return me(p)->status(p, at, length);
}
static int onHeaderField(http_parser* p, const char* at, size_t length) {
return me(p)->headerField(p, at, length);
}
static int onHeaderValue(http_parser* p, const char* at, size_t length) {
return me(p)->headerValue(p, at, length);
}
static int onHeadersComplete(http_parser* p) {
return me(p)->headersComplete(p);
}
static int onBody(http_parser* p, const char* at, size_t length) {
return me(p)->body(p, at, length);
}
static int onMessageComplete(http_parser* p) {
return me(p)->messageComplete(p);
}
protected:
// The ones we are reading in from the parser
QByteArray itempHeaderField;
QByteArray itempHeaderValue;
// if connection has a timeout, these fields will be used
quint32 itimeOut = 0;
QBasicTimer itimer;
// uniform socket object
QSocket isocket;
// if connection should persist
bool ikeepAlive = false;
// joyent http_parser
http_parser iparser;
http_parser_settings iparserSettings;
static TImpl* me(http_parser* p) {
return static_cast<TImpl*>(p->data);
}
}; //
/// basic request parser (server)
template<class TImpl>
struct HttpRequestParser : public HttpParser<TImpl> {
HttpRequestParser() : HttpParser<TImpl>(HTTP_REQUEST) {}
};
/// basic response parser (clinet)
template<class TImpl>
struct HttpResponseParser : public HttpParser<TImpl> {
HttpResponseParser() : HttpParser<TImpl>(HTTP_RESPONSE) {}
};
///////////////////////////////////////////////////////////////////////////////
} // namespace details
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // __QHTTP_HTTPPARSER_HXX__

View File

@@ -0,0 +1,78 @@
/** @file httpreader.hxx
*
* @copyright (C) 2016
* @date 2016.05.26
* @version 1.0.0
* @author amir zamani <azadkuh@live.com>
*
*/
#ifndef __QHTTP_HTTPREADER_HXX__
#define __QHTTP_HTTPREADER_HXX__
#include "qhttpbase.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace details {
///////////////////////////////////////////////////////////////////////////////
// usage in client::QHttpResponse, server::QHttpRequest
template<class TBase>
class HttpReader : public TBase
{
public:
enum TReadState {
EEmpty,
EPartial,
EComplete,
ESent
};
public:
void collectData(int atMost) {
icollectRequired = true;
icollectCapacity = atMost;
icollectedData.clear();
if ( atMost > 0 )
icollectedData.reserve(atMost);
}
bool append(const char* data, size_t length) {
if ( !icollectRequired ) // not allowed to collect data
return false;
int newLength = icollectedData.length() + static_cast<int>(length);
if ( icollectCapacity > 0 && newLength > icollectCapacity )
return false; // the capacity is full
icollectedData.append(data, length);
return true;
}
// call cb if the message is not finalized yet
template<class Func>
void finalizeSending(Func cb) {
if ( ireadState != EComplete ) {
ireadState = EComplete;
isuccessful = true;
cb();
}
}
public:
bool isuccessful = false;
TReadState ireadState = EEmpty;
/// shall I collect incoming body data by myself?
bool icollectRequired = false;
int icollectCapacity = 0;
QByteArray icollectedData;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace details
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // __QHTTP_HTTPREADER_HXX__

View File

@@ -0,0 +1,112 @@
/** @file httpwriter.hxx
*
* @copyright (C) 2016
* @date 2016.05.26
* @version 1.0.0
* @author amir zamani <azadkuh@live.com>
*
*/
#ifndef __QHTTP_HTTPWRITER_HXX__
#define __QHTTP_HTTPWRITER_HXX__
#include "qhttpbase.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace details {
///////////////////////////////////////////////////////////////////////////////
// usage in client::QHttpRequest, server::QHttpResponse
template<class TBase, class TImpl>
class HttpWriter : public TBase
{
public:
bool addHeader(const QByteArray &field, const QByteArray &value) {
if ( ifinished )
return false;
TBase::iheaders.insert(field.toLower(), value);
return true;
}
bool writeHeader(const QByteArray& field, const QByteArray& value) {
if ( ifinished )
return false;
QByteArray buffer = QByteArray(field)
.append(": ")
.append(value)
.append("\r\n");
isocket.writeRaw(buffer);
return true;
}
bool writeData(const QByteArray& data) {
if ( ifinished )
return false;
ensureWritingHeaders();
isocket.writeRaw(data);
return true;
}
bool endPacket(const QByteArray& data) {
if ( !writeData(data) )
return false;
isocket.flush();
ifinished = true;
return true;
}
void ensureWritingHeaders() {
if ( ifinished || iheaderWritten )
return;
TImpl* me = static_cast<TImpl*>(this);
isocket.writeRaw(me->makeTitle());
writeHeaders();
iheaderWritten = true;
}
void writeHeaders(bool doFlush = false) {
if ( ifinished || iheaderWritten )
return;
if ( TBase::iheaders.keyHasValue("connection", "keep-alive") )
ikeepAlive = true;
else
TBase::iheaders.insert("connection", "close");
TImpl* me = static_cast<TImpl*>(this);
me->prepareHeadersToWrite();
for ( auto cit = TBase::iheaders.constBegin(); cit != TBase::iheaders.constEnd(); cit++ ) {
const QByteArray& field = cit.key();
const QByteArray& value = cit.value();
writeHeader(field, value);
}
isocket.writeRaw("\r\n");
if ( doFlush )
isocket.flush();
}
public:
QSocket isocket;
bool ifinished = false;
bool iheaderWritten = false;
bool ikeepAlive = false;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace details
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // __QHTTP_HTTPWRITER_HXX__

View File

@@ -0,0 +1,52 @@
/** base classes for private implementations.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPBASE_HPP
#define QHTTPBASE_HPP
#include "qhttpfwd.hpp"
#include "qsocket.hpp"
#include <QHostAddress>
#include <QBasicTimer>
#include "http-parser/http_parser.h"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace details {
///////////////////////////////////////////////////////////////////////////////
struct HttpBase
{
QString iversion;
THeaderHash iheaders;
}; // struct HttpBase
///////////////////////////////////////////////////////////////////////////////
struct HttpRequestBase : public HttpBase
{
QUrl iurl;
THttpMethod imethod;
}; // HttpRequestBase
///////////////////////////////////////////////////////////////////////////////
struct HttpResponseBase : public HttpBase
{
TStatusCode istatus = ESTATUS_BAD_REQUEST;
HttpResponseBase() { iversion = "1.1"; }
}; // HttpResponseBase
///////////////////////////////////////////////////////////////////////////////
} // namespace details
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPBASE_HPP

View File

@@ -0,0 +1,222 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_PRIVATE_HPP
#define QHTTPCLIENT_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpclient.hpp"
#include "httpparser.hxx"
#include "qhttpclientrequest_private.hpp"
#include "qhttpclientresponse_private.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
class QHttpClientPrivate :
public details::HttpResponseParser<QHttpClientPrivate>
{
Q_DECLARE_PUBLIC(QHttpClient)
public:
explicit QHttpClientPrivate(QHttpClient* q) : q_ptr(q) {
QObject::connect(
q_func(), &QHttpClient::disconnected,
[this](){ release(); }
);
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpClientPrivate() {
QHTTP_LINE_DEEPLOG
}
void release() {
// if socket drops and http_parser can not call messageComplete,
// dispatch the ilastResponse
finalizeConnection();
isocket.disconnectAllQtConnections();
isocket.release();
if ( ilastRequest ) {
ilastRequest->deleteLater();
ilastRequest = nullptr;
}
if ( ilastResponse ) {
ilastResponse->deleteLater();
ilastResponse = nullptr;
}
// must be called! or the later http_parser_execute() may fail
http_parser_init(&iparser, HTTP_RESPONSE);
}
void initializeSocket() {
// no need to reconnect. do nothing and simply return
if ( ikeepAlive )
return;
// close previous connection now!
// instead being called by emitted disconnected signal
release();
ikeepAlive = false;
// create a tcp connection
if ( isocket.ibackendType == ETcpSocket ) {
initTcpSocket();
} else if ( isocket.ibackendType == ESslSocket ) {
initSslSocket();
} else if ( isocket.ibackendType == ELocalSocket ) {
initLocalSocket();
}
}
public:
int messageBegin(http_parser* parser);
int url(http_parser*, const char*, size_t) {
return 0; // not used in parsing incoming response.
}
int status(http_parser* parser, const char* at, size_t length) ;
int headerField(http_parser* parser, const char* at, size_t length);
int headerValue(http_parser* parser, const char* at, size_t length);
int headersComplete(http_parser* parser);
int body(http_parser* parser, const char* at, size_t length);
int messageComplete(http_parser* parser);
protected:
void onConnected() {
iconnectingTimer.stop();
if ( itimeOut > 0 )
itimer.start(itimeOut, Qt::CoarseTimer, q_func());
if ( ireqHandler )
ireqHandler(ilastRequest);
else
q_func()->onRequestReady(ilastRequest);
}
void onReadyRead() {
while ( isocket.bytesAvailable() > 0 ) {
char buffer[4097] = {0};
size_t readLength = static_cast<size_t>(isocket.readRaw(buffer, 4096));
parse(buffer, readLength);
}
}
void finalizeConnection() {
if ( ilastResponse == nullptr )
return;
ilastResponse->d_func()->finalizeSending([this]{
emit ilastResponse->end();
});
}
private:
void initTcpSocket() {
QTcpSocket* sok = new QTcpSocket(q_func());
isocket.itcpSocket = sok;
QObject::connect(
sok, &QTcpSocket::connected,
[this](){ onConnected(); }
);
QObject::connect(
sok, &QTcpSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QTcpSocket::bytesWritten,
[this](qint64){
const auto& ts = isocket.itcpSocket;
if ( ts->bytesToWrite() == 0 && ilastRequest )
emit ilastRequest->allBytesWritten();
});
QObject::connect(
sok, &QTcpSocket::disconnected,
q_func(), &QHttpClient::disconnected
);
}
void initSslSocket() {
QSslSocket* sok = new QSslSocket(q_func());
isocket.itcpSocket = sok;
QObject::connect(
sok, &QSslSocket::encrypted,
[this](){ onConnected(); }
);
QObject::connect(
sok, &QSslSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QSslSocket::bytesWritten,
[this](qint64){
const auto& ts = isocket.itcpSocket;
if ( ts->bytesToWrite() == 0 && ilastRequest )
emit ilastRequest->allBytesWritten();
});
QObject::connect(
sok, &QSslSocket::disconnected,
q_func(), &QHttpClient::disconnected
);
}
void initLocalSocket() {
QLocalSocket* sok = new QLocalSocket(q_func());
isocket.ilocalSocket = sok;
QObject::connect(
sok, &QLocalSocket::connected,
[this](){ onConnected(); }
);
QObject::connect(
sok, &QLocalSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QLocalSocket::bytesWritten,
[this](qint64){
const auto* ls = isocket.ilocalSocket;
if ( ls->bytesToWrite() == 0 && ilastRequest )
emit ilastRequest->allBytesWritten();
});
QObject::connect(
sok, &QLocalSocket::disconnected,
q_func(), &QHttpClient::disconnected
);
}
protected:
QHttpClient* const q_ptr;
QHttpRequest* ilastRequest = nullptr;
QHttpResponse* ilastResponse = nullptr;
TRequstHandler ireqHandler;
TResponseHandler irespHandler;
QBasicTimer iconnectingTimer;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPCLIENT_PRIVATE_HPP

View File

@@ -0,0 +1,57 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_REQUEST_PRIVATE_HPP
#define QHTTPCLIENT_REQUEST_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "httpwriter.hxx"
#include "qhttpclient.hpp"
#include "qhttpclientrequest.hpp"
#include <QTcpSocket>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
class QHttpRequestPrivate :
public details::HttpWriter<details::HttpRequestBase, QHttpRequestPrivate>
{
Q_DECLARE_PUBLIC(QHttpRequest)
public:
explicit QHttpRequestPrivate(QHttpClient* cli, QHttpRequest* q) : q_ptr(q), iclient(cli) {
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpRequestPrivate() {
QHTTP_LINE_DEEPLOG
}
void initialize() {
iversion = "1.1";
isocket.ibackendType = iclient->backendType();
isocket.itcpSocket = iclient->tcpSocket();
isocket.ilocalSocket = iclient->localSocket();
}
QByteArray makeTitle();
void prepareHeadersToWrite();
protected:
QHttpRequest* const q_ptr;
QHttpClient* const iclient;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPCLIENT_REQUEST_PRIVATE_HPP

View File

@@ -0,0 +1,51 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_RESPONSE_PRIVATE_HPP
#define QHTTPCLIENT_RESPONSE_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "httpreader.hxx"
#include "qhttpclient.hpp"
#include "qhttpclientresponse.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
class QHttpResponsePrivate :
public details::HttpReader<details::HttpResponseBase>
{
Q_DECLARE_PUBLIC(QHttpResponse)
QHttpResponse* const q_ptr;
public:
explicit QHttpResponsePrivate(QHttpClient* cli, QHttpResponse* q)
: q_ptr(q), iclient(cli) {
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpResponsePrivate() {
QHTTP_LINE_DEEPLOG
}
void initialize() {
}
public:
QString icustomStatusMessage;
protected:
QHttpClient* const iclient;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPCLIENT_RESPONSE_PRIVATE_HPP

View File

@@ -0,0 +1,90 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_PRIVATE_HPP
#define QHTTPSERVER_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpserver.hpp"
#include "qhttpserverconnection.hpp"
#include "qhttpserverrequest.hpp"
#include "qhttpserverresponse.hpp"
#include <QTcpServer>
#include <QLocalServer>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
class QHttpServerPrivate
{
public:
template<class TServer>
class BackendServer : public TServer
{
public:
QHttpServer* iserver;
explicit BackendServer(QHttpServer* s) : TServer(s), iserver(s) {
}
protected:
// if it's a QTcpServer
virtual void incomingConnection(qintptr socketDescriptor) {
iserver->incomingConnection(socketDescriptor);
}
// if it's a QLocalServer
virtual void incomingConnection(quintptr socketDescriptor) {
iserver->incomingConnection(static_cast<qintptr>(socketDescriptor));
}
};
using TTcpServer = QScopedPointer<BackendServer<QTcpServer>>;
using TLocalServer = QScopedPointer<BackendServer<QLocalServer>>;
public:
quint32 itimeOut = 0;
TServerHandler ihandler = nullptr;
TBackend ibackend = ETcpSocket;
TTcpServer itcpServer;
TLocalServer ilocalServer;
public:
explicit QHttpServerPrivate() {
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpServerPrivate() {
QHTTP_LINE_DEEPLOG
}
void initialize(TBackend backend, QHttpServer* parent) {
ibackend = backend;
if ( ibackend == ETcpSocket || ibackend == ESslSocket ) {
itcpServer.reset( new BackendServer<QTcpServer>(parent) );
ilocalServer.reset( nullptr );
} else if ( ibackend == ELocalSocket ) {
itcpServer.reset( nullptr );
ilocalServer.reset( new BackendServer<QLocalServer>(parent) );
}
}
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPSERVER_PRIVATE_HPP

View File

@@ -0,0 +1,201 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_CONNECTION_PRIVATE_HPP
#define QHTTPSERVER_CONNECTION_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpserverconnection.hpp"
#include "httpparser.hxx"
#include "qhttpserverrequest.hpp"
#include "qhttpserverresponse.hpp"
#include "private/qhttpserverrequest_private.hpp"
#include "private/qhttpserverresponse_private.hpp"
#include <QBasicTimer>
#include <QFile>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
class QHttpConnectionPrivate :
public details::HttpRequestParser<QHttpConnectionPrivate>
{
Q_DECLARE_PUBLIC(QHttpConnection)
public:
explicit QHttpConnectionPrivate(QHttpConnection* q) : q_ptr(q) {
QObject::connect(
q_func(), &QHttpConnection::disconnected,
[this](){ release(); }
);
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpConnectionPrivate() {
QHTTP_LINE_DEEPLOG
}
void createSocket(qintptr sokDesc, TBackend bend) {
isocket.ibackendType = bend;
if ( bend == ETcpSocket ) {
initTcpSocket(sokDesc);
} else if ( bend == ESslSocket) {
initSslSocket(sokDesc);
} else if ( bend == ELocalSocket ) {
initLocalSocket(sokDesc);
}
}
void release() {
// if socket drops and http_parser can not call
// messageComplete, dispatch the ilastRequest
finalizeConnection();
isocket.disconnectAllQtConnections();
isocket.release();
if ( ilastRequest ) {
ilastRequest->deleteLater();
ilastRequest = nullptr;
}
if ( ilastResponse ) {
ilastResponse->deleteLater();
ilastResponse = nullptr;
}
q_func()->deleteLater();
}
public:
void onReadyRead() {
while ( isocket.bytesAvailable() > 0 ) {
char buffer[4097] = {0};
size_t readLength = static_cast<size_t>(isocket.readRaw(buffer, 4096));
parse(buffer, readLength);
}
}
void finalizeConnection() {
if ( ilastRequest == nullptr )
return;
ilastRequest->d_func()->finalizeSending([this]{
emit ilastRequest->end();
});
}
public:
int messageBegin(http_parser* parser);
int url(http_parser* parser, const char* at, size_t length);
int status(http_parser*, const char*, size_t) {
return 0; // not used in parsing incoming request.
}
int headerField(http_parser* parser, const char* at, size_t length);
int headerValue(http_parser* parser, const char* at, size_t length);
int headersComplete(http_parser* parser);
int body(http_parser* parser, const char* at, size_t length);
int messageComplete(http_parser* parser);
private:
void initTcpSocket(qintptr sokDesc) {
QTcpSocket* sok = new QTcpSocket( q_func() );
isocket.itcpSocket = sok;
sok->setSocketDescriptor(sokDesc);
QObject::connect(
sok, &QTcpSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QTcpSocket::bytesWritten,
[this](){
auto btw = isocket.itcpSocket->bytesToWrite();
if ( btw == 0 && ilastResponse )
emit ilastResponse->allBytesWritten();
});
QObject::connect(
sok, &QTcpSocket::disconnected,
q_func(), &QHttpConnection::disconnected,
Qt::QueuedConnection
);
}
void initSslSocket(qintptr sokDesc) {
QSslSocket* sok = new QSslSocket( q_func() );
isocket.itcpSocket = sok;
sok->setSocketDescriptor(sokDesc);
QObject::connect(
sok, &QSslSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QSslSocket::bytesWritten,
[this](){
auto btw = isocket.itcpSocket->bytesToWrite();
if ( btw == 0 && ilastResponse )
emit ilastResponse->allBytesWritten();
});
QObject::connect(
sok, &QSslSocket::disconnected,
q_func(), &QHttpConnection::disconnected,
Qt::QueuedConnection
);
}
void initLocalSocket(qintptr sokDesc) {
QLocalSocket* sok = new QLocalSocket( q_func() );
isocket.ilocalSocket = sok;
sok->setSocketDescriptor(sokDesc);
QObject::connect(
sok, &QLocalSocket::readyRead,
[this](){ onReadyRead(); }
);
QObject::connect(
sok, &QLocalSocket::bytesWritten,
[this](){
auto btw = isocket.ilocalSocket->bytesToWrite();
if ( btw == 0 && ilastResponse )
emit ilastResponse->allBytesWritten();
});
QObject::connect(
sok, &QLocalSocket::disconnected,
q_func(), &QHttpConnection::disconnected,
Qt::QueuedConnection
);
}
protected:
QHttpConnection* const q_ptr;
QByteArray itempUrl;
// Since there can only be one request/response pair per connection at any
// time even with pipelining.
QHttpRequest* ilastRequest = nullptr;
QHttpResponse* ilastResponse = nullptr;
TServerHandler ihandler = nullptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPSERVER_CONNECTION_PRIVATE_HPP

View File

@@ -0,0 +1,51 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_REQUEST_PRIVATE_HPP
#define QHTTPSERVER_REQUEST_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "httpreader.hxx"
#include "qhttpserverrequest.hpp"
#include "qhttpserverconnection.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
class QHttpRequestPrivate :
public details::HttpReader<details::HttpRequestBase>
{
protected:
Q_DECLARE_PUBLIC(QHttpRequest)
QHttpRequest* const q_ptr;
public:
explicit QHttpRequestPrivate(QHttpConnection* conn, QHttpRequest* q) : q_ptr(q), iconnection(conn) {
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpRequestPrivate() {
QHTTP_LINE_DEEPLOG
}
void initialize() {
}
public:
QString iremoteAddress;
quint16 iremotePort = 0;
QHttpConnection* const iconnection = nullptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPSERVER_REQUEST_PRIVATE_HPP

View File

@@ -0,0 +1,62 @@
/** private imeplementation.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_RESPONSE_PRIVATE_HPP
#define QHTTPSERVER_RESPONSE_PRIVATE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "httpwriter.hxx"
#include "qhttpserverresponse.hpp"
#include "qhttpserver.hpp"
#include "qhttpserverconnection.hpp"
#include <QDateTime>
#include <QLocale>
#include <QTcpSocket>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
class QHttpResponsePrivate :
public details::HttpWriter<details::HttpResponseBase, QHttpResponsePrivate>
{
Q_DECLARE_PUBLIC(QHttpResponse)
public:
explicit QHttpResponsePrivate(QHttpConnection* conn, QHttpResponse* q)
: q_ptr(q), iconnection(conn) {
QHTTP_LINE_DEEPLOG
}
virtual ~QHttpResponsePrivate() {
QHTTP_LINE_DEEPLOG
}
void initialize() {
isocket.ibackendType = iconnection->backendType();
isocket.ilocalSocket = iconnection->localSocket();
isocket.itcpSocket = iconnection->tcpSocket();
QObject::connect(iconnection, &QHttpConnection::disconnected,
q_func(), &QHttpResponse::deleteLater);
}
QByteArray makeTitle();
void prepareHeadersToWrite();
protected:
QHttpResponse* const q_ptr;
QHttpConnection* const iconnection;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPSERVER_RESPONSE_PRIVATE_HPP

View File

@@ -0,0 +1,131 @@
/** @file qsocket.hpp
*
* @copyright (C) 2016
* @date 2016.05.26
* @version 1.0.0
* @author amir zamani <azadkuh@live.com>
*
*/
#ifndef __QHTTP_SOCKET_HPP__
#define __QHTTP_SOCKET_HPP__
#include "qhttpfwd.hpp"
#include <QTcpSocket>
#include <QSslSocket>
#include <QLocalSocket>
#include <QUrl>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace details {
///////////////////////////////////////////////////////////////////////////////
/** an adapter for different socket types.
* the main purpose of QHttp was to create a small HTTP server with ability to
* support UNIX sockets (QLocalSocket)
*/
class QSocket
{
public:
void close() {
if ( itcpSocket )
itcpSocket->close();
if ( ilocalSocket )
ilocalSocket->close();
}
void release() {
close();
if ( itcpSocket )
itcpSocket->deleteLater();
if ( ilocalSocket )
ilocalSocket->deleteLater();
itcpSocket = nullptr;
ilocalSocket = nullptr;
}
void flush() {
if ( itcpSocket )
itcpSocket->flush();
else if ( ilocalSocket )
ilocalSocket->flush();
}
bool isOpen() const {
if ( ibackendType == ETcpSocket && itcpSocket )
return itcpSocket->isOpen()
&& itcpSocket->state() == QTcpSocket::ConnectedState;
else if ( ibackendType == ELocalSocket && ilocalSocket )
return ilocalSocket->isOpen()
&& ilocalSocket->state() == QLocalSocket::ConnectedState;
return false;
}
void connectTo(const QUrl& url) {
if ( ilocalSocket )
ilocalSocket->connectToServer(url.path());
}
void connectTo(const QString& host, quint16 port) {
if ( itcpSocket ) {
if ( ibackendType == ESslSocket )
static_cast<QSslSocket*>(itcpSocket)->connectToHostEncrypted(host, port);
else
itcpSocket->connectToHost(host, port);
}
}
qint64 readRaw(char* buffer, int maxlen) {
if ( itcpSocket )
return itcpSocket->read(buffer, maxlen);
else if ( ilocalSocket )
return ilocalSocket->read(buffer, maxlen);
return 0;
}
void writeRaw(const QByteArray& data) {
if ( itcpSocket )
itcpSocket->write(data);
else if ( ilocalSocket )
ilocalSocket->write(data);
}
qint64 bytesAvailable() {
if ( itcpSocket )
return itcpSocket->bytesAvailable();
else if ( ilocalSocket )
return ilocalSocket->bytesAvailable();
return 0;
}
void disconnectAllQtConnections() {
if ( itcpSocket )
QObject::disconnect(itcpSocket, 0, 0, 0);
if ( ilocalSocket )
QObject::disconnect(ilocalSocket, 0, 0, 0);
}
public:
TBackend ibackendType = ETcpSocket;
QTcpSocket* itcpSocket = nullptr;
QLocalSocket* ilocalSocket = nullptr;
}; // class QSocket
///////////////////////////////////////////////////////////////////////////////
} // namespace details
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // __QHTTP_SOCKET_HPP__

View File

@@ -0,0 +1,114 @@
#include "qhttpabstracts.hpp"
#include "http-parser/http_parser.h"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
///////////////////////////////////////////////////////////////////////////////
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
# error "to compile QHttp classes, Qt 5.0 or later is needed."
#endif
#define QHTTPABSTRACTS_HTTP_STATUS_MAP(XX) \
XX(100, "Continue") \
XX(101, "Switching Protocols") \
/* RFC 2518) obsoleted by RFC 4918 */ \
XX(102, "Processing") \
XX(200, "OK") \
XX(201, "Created") \
XX(202, "Accepted") \
XX(203, "Non-Authoritative Information") \
XX(204, "No Content") \
XX(205, "Reset Content") \
XX(206, "Partial Content") \
/* RFC 4918 */ \
XX(207, "Multi-Status") \
XX(300, "Multiple Choices") \
XX(301, "Moved Permanently") \
XX(302, "Moved Temporarily") \
XX(303, "See Other") \
XX(304, "Not Modified") \
XX(305, "Use Proxy") \
XX(307, "Temporary Redirect") \
XX(400, "Bad Request") \
XX(401, "Unauthorized") \
XX(402, "Payment Required") \
XX(403, "Forbidden") \
XX(404, "Not Found") \
XX(405, "Method Not Allowed") \
XX(406, "Not Acceptable") \
XX(407, "Proxy Authentication Required") \
XX(408, "Request Time-out") \
XX(409, "Conflict") \
XX(410, "Gone") \
XX(411, "Length Required") \
XX(412, "Precondition Failed") \
XX(413, "Request Entity Too Large") \
XX(414, "Request-URI Too Large") \
XX(415, "Unsupported Media Type") \
XX(416, "Requested Range Not Satisfiable") \
XX(417, "Expectation Failed") \
/* RFC 2324 */ \
XX(418, "I\"m a teapot") \
/* RFC 4918 */ \
XX(422, "Unprocessable Entity") \
/* RFC 4918 */ \
XX(423, "Locked") \
/* RFC 4918 */ \
XX(424, "Failed Dependency") \
/* RFC 4918 */ \
XX(425, "Unordered Collection") \
/* RFC 2817 */ \
XX(426, "Upgrade Required") \
XX(500, "Internal Server Error") \
XX(501, "Not Implemented") \
XX(502, "Bad Gateway") \
XX(503, "Service Unavailable") \
XX(504, "Gateway Time-out") \
XX(505, "HTTP Version not supported") \
/* RFC 2295 */ \
XX(506, "Variant Also Negotiates") \
/* RFC 4918 */ \
XX(507, "Insufficient Storage") \
XX(509, "Bandwidth Limit Exceeded") \
/* RFC 2774 */ \
XX(510, "Not Extended")
#define PATCH_STATUS_CODES(n,s) {n, s},
static struct {
int code;
const char* message;
} g_status_codes[] {
QHTTPABSTRACTS_HTTP_STATUS_MAP(PATCH_STATUS_CODES)
};
#undef PATCH_STATUS_CODES
///////////////////////////////////////////////////////////////////////////////
const char*
Stringify::toString(TStatusCode code) {
size_t count = sizeof(g_status_codes) / sizeof(g_status_codes[0]);
for ( size_t i = 0; i < count; i++ ) {
if ( g_status_codes[i].code == code )
return g_status_codes[i].message;
}
return nullptr;
}
const char*
Stringify::toString(THttpMethod method) {
return http_method_str(static_cast<http_method>(method));
}
///////////////////////////////////////////////////////////////////////////////
QHttpAbstractInput::QHttpAbstractInput(QObject* parent) : QObject(parent) {
}
QHttpAbstractOutput::QHttpAbstractOutput(QObject *parent) : QObject(parent) {
}
///////////////////////////////////////////////////////////////////////////////
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,190 @@
/** interfaces of QHttp' incomming and outgoing classes.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPABSTRACTS_HPP
#define QHTTPABSTRACTS_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpfwd.hpp"
#include <QObject>
#include <functional>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
///////////////////////////////////////////////////////////////////////////////
/** a utility class to give the string representation of qhttp types. */
class QHTTP_API Stringify
{
public:
/** returns the standard message for an HTTP status code. */
static const char* toString(TStatusCode);
/** returns the standars name of an HTTP method. */
static const char* toString(THttpMethod);
};
///////////////////////////////////////////////////////////////////////////////
/** an interface for input (incoming) HTTP packets.
* server::QHttpRequest or client::QHttpResponse inherit from this class. */
class QHTTP_API QHttpAbstractInput : public QObject
{
Q_OBJECT
public:
/** Return all the headers in the incomming packet.
* This returns a reference. If you want to store headers
* somewhere else, where the request may be deleted,
* make sure you store them as a copy.
* @note All header names are <b>lowercase</b> . */
virtual const THeaderHash& headers() const=0;
/** The HTTP version of the packet.
* @return A string in the form of "x.x" */
virtual const QString& httpVersion() const=0;
/** If this packet was successfully received.
* Set before end() has been emitted, stating whether
* the message was properly received. This is false
* until the receiving the full request has completed. */
virtual bool isSuccessful() const=0;
signals:
/** Emitted when new body data has been received.
* @param data Received data.
* @note This may be emitted zero or more times depending on the transfer type.
* @see onData();
*/
void data(QByteArray data);
/** Emitted when the incomming packet has been fully received.
* @note The no more data() signals will be emitted after this.
* @see onEnd();
*/
void end();
public:
/** optionally set a handler for data() signal.
* @param dataHandler a std::function or lambda handler to receive incoming data.
* @note if you set this handler, the data() signal won't be emitted anymore.
*/
template<class Func>
void onData(Func f) {
QObject::connect(this, &QHttpAbstractInput::data, f);
}
/** optionally set a handler for end() signal.
* @param endHandler a std::function or lambda handler to receive end notification.
* @note if you set this handler, the end() signal won't be emitted anymore.
*/
template<class Func>
void onEnd(Func f) {
QObject::connect(this, &QHttpAbstractInput::end, f);
}
public:
/** tries to collect all the incoming data internally.
* @note if you call this method, data() signal won't be emitted and
* onData() will have no effect.
*
* @param atMost maximum acceptable incoming data. if the incoming data
* exceeds this value, the connection won't read any more data and
* end() signal will be emitted.
* default value (-1) means read data as "content-length" or unlimited if
* the body size is unknown.
*/
virtual void collectData(int atMost = -1) =0;
/** returns the collected data requested by collectData(). */
virtual const QByteArray& collectedData()const =0;
public:
virtual ~QHttpAbstractInput() = default;
explicit QHttpAbstractInput(QObject* parent);
Q_DISABLE_COPY(QHttpAbstractInput)
};
///////////////////////////////////////////////////////////////////////////////
/** an interface for output (outgoing) HTTP packets.
* server::QHttpResponse or client::QHttpRequest inherit from this class. */
class QHTTP_API QHttpAbstractOutput : public QObject
{
Q_OBJECT
public:
/** changes the HTTP version string ex: "1.1" or "1.0".
* version is "1.1" set by default. */
virtual void setVersion(const QString& versionString)=0;
/** helper function. @sa addHeader */
template<typename T>
void addHeaderValue(const QByteArray &field, T value);
/** adds an HTTP header to the packet.
* @note this method does not actually write anything to socket, just prepares the headers(). */
virtual void addHeader(const QByteArray& field, const QByteArray& value)=0;
/** returns all the headers that already been set. */
virtual THeaderHash& headers()=0;
/** writes a block of data into the HTTP packet.
* @note headers are written (flushed) before any data.
* @warning after calling this method add a new header, set staus code, set Url have no effect! */
virtual void write(const QByteArray &data)=0;
/** ends (finishes) the outgoing packet by calling write().
* headers and data will be flushed to the underlying socket.
*
* @sa write() */
virtual void end(const QByteArray &data = QByteArray())=0;
signals:
/** Emitted when all the data has been sent.
* this signal indicates that the underlaying socket has transmitted all
* of it's buffered data. */
void allBytesWritten();
/** Emitted when the packet is finished and reports if it was the last packet.
* if it was the last packet (google for "Connection: keep-alive / close")
* the http connection (socket) will be closed automatically. */
void done(bool wasTheLastPacket);
public:
virtual ~QHttpAbstractOutput() = default;
protected:
explicit QHttpAbstractOutput(QObject* parent);
Q_DISABLE_COPY(QHttpAbstractOutput)
};
template<> inline void
QHttpAbstractOutput::addHeaderValue<int>(const QByteArray &field, int value) {
addHeader(field, QString::number(value).toLatin1());
}
template<> inline void
QHttpAbstractOutput::addHeaderValue<size_t>(const QByteArray &field, size_t value) {
addHeader(field, QString::number(value).toLatin1());
}
template<> inline void
QHttpAbstractOutput::addHeaderValue<QString>(const QByteArray &field, QString value) {
addHeader(field, value.toUtf8());
}
///////////////////////////////////////////////////////////////////////////////
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // QHTTPABSTRACTS_HPP

View File

@@ -0,0 +1,286 @@
#include "private/qhttpclient_private.hpp"
#include <QTimerEvent>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
QHttpClient::QHttpClient(QObject *parent)
: QObject(parent), d_ptr(new QHttpClientPrivate(this)) {
QHTTP_LINE_LOG
}
QHttpClient::QHttpClient(QHttpClientPrivate &dd, QObject *parent)
: QObject(parent), d_ptr(&dd) {
QHTTP_LINE_LOG
}
QHttpClient::~QHttpClient() {
QHTTP_LINE_LOG
}
quint32
QHttpClient::timeOut() const {
return d_func()->itimeOut;
}
void
QHttpClient::setTimeOut(quint32 t) {
d_func()->itimeOut = t;
}
bool
QHttpClient::isOpen() const {
return d_func()->isocket.isOpen();
}
void
QHttpClient::killConnection() {
Q_D(QHttpClient);
d->iconnectingTimer.stop();
d->itimer.stop();
d->isocket.close();
}
TBackend
QHttpClient::backendType() const {
return d_func()->isocket.ibackendType;
}
QTcpSocket*
QHttpClient::tcpSocket() const {
return d_func()->isocket.itcpSocket;
}
QLocalSocket*
QHttpClient::localSocket() const {
return d_func()->isocket.ilocalSocket;
}
void
QHttpClient::setConnectingTimeOut(quint32 timeout) {
Q_D(QHttpClient);
if ( timeout == 0 ) {
d->iconnectingTimer.stop();
} else {
d->iconnectingTimer.start(timeout,
Qt::CoarseTimer,
this
);
}
}
bool
QHttpClient::request(THttpMethod method, QUrl url,
const TRequstHandler &reqHandler,
const TResponseHandler &resHandler) {
Q_D(QHttpClient);
d->ireqHandler = nullptr;
d->irespHandler = nullptr;
// if url is a local file (UNIX socket) the host could be empty!
if ( !url.isValid() || url.isEmpty() /*|| url.host().isEmpty()*/ )
return false;
// process handlers
if ( resHandler ) {
d->irespHandler = resHandler;
if ( reqHandler )
d->ireqHandler = reqHandler;
else
d->ireqHandler = [](QHttpRequest* req) ->void {
req->addHeader("connection", "close");
req->end();
};
}
auto requestCreator = [this, method, url]() {
// create request object
if ( d_ptr->ilastRequest )
d_ptr->ilastRequest->deleteLater();
d_ptr->ilastRequest = new QHttpRequest(this);
QObject::connect(d_ptr->ilastRequest, &QHttpRequest::done, [this](bool wasTheLastPacket){
d_ptr->ikeepAlive = !wasTheLastPacket;
});
d_ptr->ilastRequest->d_ptr->imethod = method;
d_ptr->ilastRequest->d_ptr->iurl = url;
};
// connecting to host/server must be the last thing. (after all function handlers and ...)
// check for type
if ( url.scheme().toLower() == QLatin1String("file") ) {
d->isocket.ibackendType = ELocalSocket;
d->initializeSocket();
requestCreator();
if ( d->isocket.isOpen() )
d->onConnected();
else
d->isocket.connectTo(url);
} else {
bool ssl = url.scheme() == "https";
d->isocket.ibackendType = ssl ? ESslSocket : ETcpSocket;
d->initializeSocket();
requestCreator();
if ( d->isocket.isOpen() )
d->onConnected();
else
d->isocket.connectTo(url.host(), url.port(ssl ? 443 : 80));
}
return true;
}
void
QHttpClient::timerEvent(QTimerEvent *e) {
Q_D(QHttpClient);
if ( e->timerId() == d->itimer.timerId() ) {
killConnection();
} else if ( e->timerId() == d->iconnectingTimer.timerId() ) {
d->iconnectingTimer.stop();
emit connectingTimeOut();
}
}
void
QHttpClient::onRequestReady(QHttpRequest *req) {
emit httpConnected(req);
}
void
QHttpClient::onResponseReady(QHttpResponse *res) {
emit newResponse(res);
}
///////////////////////////////////////////////////////////////////////////////
// if server closes the connection, ends the response or by any other reason
// the socket disconnects, then the irequest and iresponse instances may have
// been deleted. In these situations reading more http body or emitting end()
// for incoming request are not possible:
// if ( ilastRequest == nullptr )
// return 0;
int
QHttpClientPrivate::messageBegin(http_parser*) {
itempHeaderField.clear();
itempHeaderValue.clear();
return 0;
}
int
QHttpClientPrivate::status(http_parser* parser, const char* at, size_t length) {
if ( ilastResponse )
ilastResponse->deleteLater();
ilastResponse = new QHttpResponse(q_func());
ilastResponse->d_func()->istatus = static_cast<TStatusCode>(parser->status_code);
ilastResponse->d_func()->iversion = QString("%1.%2")
.arg(parser->http_major)
.arg(parser->http_minor);
ilastResponse->d_func()->icustomStatusMessage = QString::fromUtf8(at, length);
return 0;
}
int
QHttpClientPrivate::headerField(http_parser*, const char* at, size_t length) {
if ( ilastResponse == nullptr )
return 0;
// insert the header we parsed previously
// into the header map
if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
// header names are always lower-cased
ilastResponse->d_func()->iheaders.insert(
itempHeaderField.toLower(),
itempHeaderValue.toLower()
);
// clear header value. this sets up a nice
// feedback loop where the next time
// HeaderValue is called, it can simply append
itempHeaderField.clear();
itempHeaderValue.clear();
}
itempHeaderField.append(at, length);
return 0;
}
int
QHttpClientPrivate::headerValue(http_parser*, const char* at, size_t length) {
itempHeaderValue.append(at, length);
return 0;
}
int
QHttpClientPrivate::headersComplete(http_parser*) {
if ( ilastResponse == nullptr )
return 0;
// Insert last remaining header
ilastResponse->d_func()->iheaders.insert(
itempHeaderField.toLower(),
itempHeaderValue.toLower()
);
if ( irespHandler )
irespHandler(ilastResponse);
else
q_func()->onResponseReady(ilastResponse);
return 0;
}
int
QHttpClientPrivate::body(http_parser*, const char* at, size_t length) {
if ( ilastResponse == nullptr )
return 0;
ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EPartial;
if ( ilastResponse->d_func()->icollectRequired ) {
if ( !ilastResponse->d_func()->append(at, length) ) {
// forcefully dispatch the ilastResponse
finalizeConnection();
}
return 0;
}
emit ilastResponse->data(QByteArray(at, length));
return 0;
}
int
QHttpClientPrivate::messageComplete(http_parser*) {
if ( ilastResponse == nullptr )
return 0;
// response is done
finalizeConnection();
return 0;
}
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,179 @@
/** HTTP client class.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_HPP
#define QHTTPCLIENT_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpfwd.hpp"
#include <QTcpSocket>
#include <QSslSocket>
#include <QUrl>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
using TRequstHandler = std::function<void (QHttpRequest*)>;
using TResponseHandler = std::function<void (QHttpResponse*)>;
/** a simple and async HTTP client class which sends a request to an HTTP server and parses the
* corresponding response.
* This class internally handles the memory management and life cycle of QHttpRequest and
* QHttpResponse instances. you do not have to manually delete or keep their pointers.
* in fact the QHttpRequest and QHttpResponse object will be deleted when the internal socket
* disconnects.
*/
class QHTTP_API QHttpClient : public QObject
{
Q_OBJECT
Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut)
public:
explicit QHttpClient(QObject *parent = nullptr);
virtual ~QHttpClient();
/** tries to connect to a HTTP server.
* when the connection is made, the reqHandler will be called
* and when the response is ready, resHandler will be called.
* @note httpConnected() and newResponse() won't be emitted.
*
* @param method an HTTP method, ex: GET, POST, ...
* @param url specifies server's address, port and optional path and query strings.
* if url starts with socket:// the request will be made on QLocalSocket, otherwise
* normal QTcpSocket will be used.
* @param resHandler response handler (a lambda, std::function object, ...)
* @return true if the url is valid or false (no connection will be made).
*/
bool request(THttpMethod method, QUrl url,
const TRequstHandler& reqHandler,
const TResponseHandler& resHandler);
/** tries to connect to a HTTP server.
* when the connection is made, a default request handler is called automatically (
* simply calls req->end()) and when the response is ready, resHandler will be called.
* @note httpConnected() and newResponse() won't be emitted.
*
* @param method an HTTP method, ex: GET, POST, ...
* @param url specifies server's address, port and optional path and query strings.
* @param resHandler response handler (a lambda, std::function object, ...)
* @return true if the url is valid or false (no connection will be made).
*/
inline bool request(THttpMethod method, QUrl url, const TResponseHandler& resHandler) {
return request(method, url, nullptr, resHandler);
}
/** tries to connect to a HTTP server.
* when the connection is made, creates and emits a QHttpRequest instance
* by @sa httpConnected(QHttpRequest*).
* @note both httpConnected() and newResponse() may be emitted.
*
* @param method an HTTP method, ex: GET, POST, ...
* @param url specifies server's address, port and optional path and query strings.
* @return true if the url is valid or false (no connection will be made).
*/
inline bool request(THttpMethod method, QUrl url) {
return request(method, url, nullptr, nullptr);
}
/** checks if the connetion to the server is open. */
bool isOpen() const;
/** forcefully close the connection. */
void killConnection();
/** returns time-out value [mSec] for ESTABLISHED connections (sockets).
* @sa setTimeOut(). */
quint32 timeOut()const;
/** set time-out for ESTABLISHED connections in miliseconds [mSec].
* each (already opened) connection will be forcefully closed after this timeout.
* a zero (0) value disables timer for new connections. */
void setTimeOut(quint32);
/** set a time-out [mSec] for making a new connection (make a request).
* if connecting to server takes more than this time-out value,
* the @sa timedOut(quint32) signal will be emitted and connection will be killed.
* 0 (default) timeout value means to disable this timer.
*/
void setConnectingTimeOut(quint32);
template<class Handler>
void setConnectingTimeOut(quint32 timeout, Handler&& func) {
setConnectingTimeOut(timeout);
QObject::connect(this, &QHttpClient::connectingTimeOut,
std::forward<Handler&&>(func)
);
}
/** returns the backend type of this client. */
TBackend backendType() const;
/** returns tcp socket of the connection if backend() == ETcpSocket. */
QTcpSocket* tcpSocket() const;
/** returns local socket of the connection if backend() == ELocalSocket. */
QLocalSocket* localSocket() const;
signals:
/** emitted when a new HTTP connection to the server is established.
* if you overload onRequestReady this signal won't be emitted.
* @sa onRequestReady
* @sa QHttpRequest
*/
void httpConnected(QHttpRequest* req);
/** emitted when a new response is received from the server.
* if you overload onResponseReady this signal won't be emitted.
* @sa onResponseReady
* @sa QHttpResponse
*/
void newResponse(QHttpResponse* res);
/** emitted when the HTTP connection drops or being disconnected. */
void disconnected();
/// emitted when fails to connect to a HTTP server. @sa setConnectingTimeOut()
void connectingTimeOut();
protected:
/** called when a new HTTP connection is established.
* you can overload this method, the default implementaion only emits connected().
* @param req use this request instance for assinging the
* request headers and sending optional body.
* @see httpConnected(QHttpRequest*)
*/
virtual void onRequestReady(QHttpRequest* req);
/** called when a new response is received from the server.
* you can overload this method, the default implementaion only emits newResponse().
* @param res use this instance for reading incoming response.
* @see newResponse(QHttpResponse*)
*/
virtual void onResponseReady(QHttpResponse* res);
protected:
explicit QHttpClient(QHttpClientPrivate&, QObject*);
void timerEvent(QTimerEvent*) override;
Q_DECLARE_PRIVATE(QHttpClient)
Q_DISABLE_COPY(QHttpClient)
QScopedPointer<QHttpClientPrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPCLIENT_HPP

View File

@@ -0,0 +1,97 @@
#include "private/qhttpclientrequest_private.hpp"
#include "qhttpclient.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
QHttpRequest::QHttpRequest(QHttpClient* cli)
: QHttpAbstractOutput(cli) , d_ptr(new QHttpRequestPrivate(cli, this)) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpRequest::QHttpRequest(QHttpRequestPrivate& dd, QHttpClient* cli)
: QHttpAbstractOutput(cli) , d_ptr(&dd) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpRequest::~QHttpRequest() {
QHTTP_LINE_LOG
}
void
QHttpRequest::setVersion(const QString &versionString) {
d_func()->iversion = versionString;
}
void
QHttpRequest::addHeader(const QByteArray &field, const QByteArray &value) {
d_func()->addHeader(field, value);
}
THeaderHash&
QHttpRequest::headers() {
return d_func()->iheaders;
}
void
QHttpRequest::write(const QByteArray &data) {
d_func()->writeData(data);
}
void
QHttpRequest::end(const QByteArray &data) {
Q_D(QHttpRequest);
if ( d->endPacket(data) )
emit done(!d->ikeepAlive);
}
QHttpClient*
QHttpRequest::connection() const {
return d_func()->iclient;
}
///////////////////////////////////////////////////////////////////////////////
QByteArray
QHttpRequestPrivate::makeTitle() {
QByteArray title;
title.reserve(512);
title.append(qhttp::Stringify::toString(imethod))
.append(" ");
QByteArray path = iurl.path(QUrl::FullyEncoded).toLatin1();
if ( path.size() == 0 )
path = "/";
title.append(path);
if ( iurl.hasQuery() )
title.append("?").append(iurl.query(QUrl::FullyEncoded).toLatin1());
title.append(" HTTP/")
.append(iversion.toLatin1())
.append("\r\n");
return title;
}
void
QHttpRequestPrivate::prepareHeadersToWrite() {
if ( !iheaders.contains("host") ) {
QString portStr = ( -1 != iurl.port() ) ?
QString(":%1").arg(iurl.port()) : "";
iheaders.insert("host",
QString("%1%2").arg(iurl.host()).arg(portStr).toLatin1()
);
}
}
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,63 @@
/** HTTP request from a client.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_REQUEST_HPP
#define QHTTPCLIENT_REQUEST_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpabstracts.hpp"
#include <QUrl>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
/** a class for building a new HTTP request.
* the life cycle of this class and the memory management is handled by QHttpClient.
* @sa QHttpClient
*/
class QHTTP_API QHttpRequest : public QHttpAbstractOutput
{
Q_OBJECT
public:
virtual ~QHttpRequest();
public: // QHttpAbstractOutput methods:
/** @see QHttpAbstractOutput::setVersion(). */
void setVersion(const QString& versionString) override;
/** @see QHttpAbstractOutput::addHeader(). */
void addHeader(const QByteArray& field, const QByteArray& value) override;
/** @see QHttpAbstractOutput::headers(). */
THeaderHash& headers() override;
/** @see QHttpAbstractOutput::write(). */
void write(const QByteArray &data) override;
/** @see QHttpAbstractOutput::end(). */
void end(const QByteArray &data = QByteArray()) override;
public:
/** returns parent QHttpClient object. */
QHttpClient* connection() const;
protected:
explicit QHttpRequest(QHttpClient*);
explicit QHttpRequest(QHttpRequestPrivate&, QHttpClient*);
friend class QHttpClient;
Q_DECLARE_PRIVATE(QHttpRequest)
QScopedPointer<QHttpRequestPrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPCLIENT_REQUEST_HPP

View File

@@ -0,0 +1,66 @@
#include "private/qhttpclientresponse_private.hpp"
#include "qhttpclient.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
QHttpResponse::QHttpResponse(QHttpClient *cli)
: QHttpAbstractInput(cli), d_ptr(new QHttpResponsePrivate(cli, this)) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpResponse::QHttpResponse(QHttpResponsePrivate &dd, QHttpClient *cli)
: QHttpAbstractInput(cli), d_ptr(&dd) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpResponse::~QHttpResponse() {
QHTTP_LINE_LOG
}
TStatusCode
QHttpResponse::status() const {
return d_func()->istatus;
}
const QString&
QHttpResponse::statusString() const {
return d_func()->icustomStatusMessage;
}
const QString&
QHttpResponse::httpVersion() const {
return d_func()->iversion;
}
const THeaderHash&
QHttpResponse::headers() const {
return d_func()->iheaders;
}
bool
QHttpResponse::isSuccessful() const {
return d_func()->isuccessful;
}
void
QHttpResponse::collectData(int atMost) {
d_func()->collectData(atMost);
}
const QByteArray&
QHttpResponse::collectedData() const {
return d_func()->icollectedData;
}
QHttpClient*
QHttpResponse::connection() const {
return d_func()->iclient;
}
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,73 @@
/** HTTP response received by client.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPCLIENT_RESPONSE_HPP
#define QHTTPCLIENT_RESPONSE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpabstracts.hpp"
#include <QUrl>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace client {
///////////////////////////////////////////////////////////////////////////////
/** a class for reading incoming HTTP response from a server.
* the life cycle of this class and the memory management is handled by QHttpClient.
* @sa QHttpClient
*/
class QHTTP_API QHttpResponse : public QHttpAbstractInput
{
Q_OBJECT
public:
virtual ~QHttpResponse();
public: // QHttpAbstractInput methods:
/** @see QHttpAbstractInput::headers(). */
const THeaderHash& headers() const override;
/** @see QHttpAbstractInput::httpVersion(). */
const QString& httpVersion() const override;
/** @see QHttpAbstractInput::isSuccessful(). */
bool isSuccessful() const override;
/** @see QHttpAbstractInput::collectData(). */
void collectData(int atMost = -1) override;
/** @see QHttpAbstractInput::collectedData(). */
const QByteArray& collectedData()const override;
public:
/** The status code of this response. */
TStatusCode status() const ;
/** The server status message as string.
* may be slightly different than: @code qhttp::Stringify::toString(status()); @endcode
* depending on implementation of HTTP server. */
const QString& statusString() const;
/** returns parent QHttpClient object. */
QHttpClient* connection() const;
protected:
explicit QHttpResponse(QHttpClient*);
explicit QHttpResponse(QHttpResponsePrivate&, QHttpClient*);
friend class QHttpClientPrivate;
Q_DECLARE_PRIVATE(QHttpResponse)
QScopedPointer<QHttpResponsePrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace client
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPCLIENT_RESPONSE_HPP

216
src/http/qhttp/qhttpfwd.hpp Normal file
View File

@@ -0,0 +1,216 @@
/** forward declarations and general definitions of the QHttp.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPFWD_HPP
#define QHTTPFWD_HPP
///////////////////////////////////////////////////////////////////////////////
#include <QHash>
#include <QString>
#include <QtGlobal>
#include <functional>
///////////////////////////////////////////////////////////////////////////////
// Qt
class QTcpServer;
class QTcpSocket;
class QLocalServer;
class QLocalSocket;
// http_parser
struct http_parser_settings;
struct http_parser;
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
///////////////////////////////////////////////////////////////////////////////
/** A map of request or response headers. */
class THeaderHash : public QHash<QByteArray, QByteArray>
{
public:
/** checks for a header item, regardless of the case of the characters. */
bool has(const QByteArray& key) const {
return contains(key.toLower());
}
/** checks if a header has the specified value ignoring the case of the characters. */
bool keyHasValue(const QByteArray& key, const QByteArray& value) const {
if ( !contains(key) )
return false;
const QByteArray& v = QHash<QByteArray, QByteArray>::value(key);
return qstrnicmp(value.constData(), v.constData(), v.size()) == 0;
}
};
/// QHash/QMap iterators are incompatibility with range for
template<class Iterator, class Func>
void for_each(Iterator first, Iterator last, Func&& f) {
while ( first != last ) {
f( first );
++first;
}
}
/** Request method enumeration.
* @note Taken from http_parser.h */
enum THttpMethod {
EHTTP_DELETE = 0, ///< DELETE
EHTTP_GET = 1, ///< GET
EHTTP_HEAD = 2, ///< HEAD
EHTTP_POST = 3, ///< POST
EHTTP_PUT = 4, ///< PUT
/* pathological */
EHTTP_CONNECT = 5, ///< CONNECT
EHTTP_OPTIONS = 6, ///< OPTIONS
EHTTP_TRACE = 7, ///< TRACE
/* webdav */
EHTTP_COPY = 8, ///< COPY
EHTTP_LOCK = 9, ///< LOCK
EHTTP_MKCOL = 10, ///< MKCOL
EHTTP_MOVE = 11, ///< MOVE
EHTTP_PROPFIND = 12, ///< PROPFIND
EHTTP_PROPPATCH = 13, ///< PROPPATCH
EHTTP_SEARCH = 14, ///< SEARCH
EHTTP_UNLOCK = 15, ///< UNLOCK
EHTTP_BIND = 16, ///< BIND
EHTTP_REBIND = 17, ///< REBIND
EHTTP_UNBIND = 18, ///< UNBIND
EHTTP_ACL = 19, ///< ACL
/* subversion */
EHTTP_REPORT = 20, ///< REPORT
EHTTP_MKACTIVITY = 21, ///< MKACTIVITY
EHTTP_CHECKOUT = 22, ///< CHECKOUT
EHTTP_MERGE = 23, ///< MERGE
/* upnp */
EHTTP_MSEARCH = 24, ///< M-SEARCH
EHTTP_NOTIFY = 25, ///< NOTIFY
EHTTP_SUBSCRIBE = 26, ///< SUBSCRIBE
EHTTP_UNSUBSCRIBE = 27, ///< UNSUBSCRIBE
/* RFC-5789 */
EHTTP_PATCH = 28, ///< PATCH
EHTTP_PURGE = 29, ///< PURGE
/* CalDAV */
EHTTP_MKCALENDAR = 30, ///< MKCALENDAR
/* RFC-2068, section 19.6.1.2 */
EHTTP_LINK = 31, ///< LINK
EHTTP_UNLINK = 32, ///< UNLINK
};
/** HTTP status codes. */
enum TStatusCode {
ESTATUS_CONTINUE = 100,
ESTATUS_SWITCH_PROTOCOLS = 101,
ESTATUS_OK = 200,
ESTATUS_CREATED = 201,
ESTATUS_ACCEPTED = 202,
ESTATUS_NON_AUTHORITATIVE_INFORMATION = 203,
ESTATUS_NO_CONTENT = 204,
ESTATUS_RESET_CONTENT = 205,
ESTATUS_PARTIAL_CONTENT = 206,
ESTATUS_MULTI_STATUS = 207,
ESTATUS_MULTIPLE_CHOICES = 300,
ESTATUS_MOVED_PERMANENTLY = 301,
ESTATUS_FOUND = 302,
ESTATUS_SEE_OTHER = 303,
ESTATUS_NOT_MODIFIED = 304,
ESTATUS_USE_PROXY = 305,
ESTATUS_TEMPORARY_REDIRECT = 307,
ESTATUS_BAD_REQUEST = 400,
ESTATUS_UNAUTHORIZED = 401,
ESTATUS_PAYMENT_REQUIRED = 402,
ESTATUS_FORBIDDEN = 403,
ESTATUS_NOT_FOUND = 404,
ESTATUS_METHOD_NOT_ALLOWED = 405,
ESTATUS_NOT_ACCEPTABLE = 406,
ESTATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
ESTATUS_REQUEST_TIMEOUT = 408,
ESTATUS_CONFLICT = 409,
ESTATUS_GONE = 410,
ESTATUS_LENGTH_REQUIRED = 411,
ESTATUS_PRECONDITION_FAILED = 412,
ESTATUS_REQUEST_ENTITY_TOO_LARGE = 413,
ESTATUS_REQUEST_URI_TOO_LONG = 414,
ESTATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415,
ESTATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
ESTATUS_EXPECTATION_FAILED = 417,
ESTATUS_INTERNAL_SERVER_ERROR = 500,
ESTATUS_NOT_IMPLEMENTED = 501,
ESTATUS_BAD_GATEWAY = 502,
ESTATUS_SERVICE_UNAVAILABLE = 503,
ESTATUS_GATEWAY_TIMEOUT = 504,
ESTATUS_HTTP_VERSION_NOT_SUPPORTED = 505
};
/** The backend of QHttp library. */
enum TBackend {
ETcpSocket = 0, ///< client / server work on top of TCP/IP stack. (default)
ESslSocket = 1, ///< client / server work on SSL/TLS tcp stack.
ELocalSocket = 2 ///< client / server work on local socket (unix socket).
};
///////////////////////////////////////////////////////////////////////////////
namespace server {
///////////////////////////////////////////////////////////////////////////////
class QHttpServer;
class QHttpConnection;
class QHttpRequest;
class QHttpResponse;
// Privte classes
class QHttpServerPrivate;
class QHttpConnectionPrivate;
class QHttpRequestPrivate;
class QHttpResponsePrivate;
using TServerHandler = std::function<void (QHttpRequest*, QHttpResponse*)>;
///////////////////////////////////////////////////////////////////////////////
} // namespace server
///////////////////////////////////////////////////////////////////////////////
namespace client {
///////////////////////////////////////////////////////////////////////////////
class QHttpClient;
class QHttpRequest;
class QHttpResponse;
// Private classes
class QHttpClientPrivate;
class QHttpRequestPrivate;
class QHttpResponsePrivate;
///////////////////////////////////////////////////////////////////////////////
} // namespace client
///////////////////////////////////////////////////////////////////////////////
#ifdef Q_OS_WIN
# if defined(QHTTP_EXPORT)
# define QHTTP_API __declspec(dllexport)
# else
# define QHTTP_API __declspec(dllimport)
# endif
#else
# define QHTTP_API
#endif
#if QHTTP_MEMORY_LOG > 0
# define QHTTP_LINE_LOG fprintf(stderr, "%s(): obj = %p @ %s[%d]\n",\
__FUNCTION__, this, __FILE__, __LINE__);
#else
# define QHTTP_LINE_LOG
#endif
#if QHTTP_MEMORY_LOG > 1
# define QHTTP_LINE_DEEPLOG QHTTP_LINE_LOG
#else
# define QHTTP_LINE_DEEPLOG
#endif
///////////////////////////////////////////////////////////////////////////////
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPFWD_HPP

View File

@@ -0,0 +1,118 @@
#include "private/qhttpserver_private.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
QHttpServer::QHttpServer(QObject *parent)
: QObject(parent), d_ptr(new QHttpServerPrivate) {
}
QHttpServer::QHttpServer(QHttpServerPrivate &dd, QObject *parent)
: QObject(parent), d_ptr(&dd) {
}
QHttpServer::~QHttpServer() {
stopListening();
}
bool
QHttpServer::listen(const QString &socketOrPort, const TServerHandler &handler) {
Q_D(QHttpServer);
bool isNumber = false;
quint16 tcpPort = socketOrPort.toUShort(&isNumber);
if ( isNumber && tcpPort > 0 )
return listen(QHostAddress::Any, tcpPort, handler);
d->initialize(ELocalSocket, this);
d->ihandler = handler;
return d->ilocalServer->listen(socketOrPort);
}
bool
QHttpServer::listen(const QHostAddress& address, quint16 port, const qhttp::server::TServerHandler& handler) {
Q_D(QHttpServer);
d->initialize(d->ibackend, this);
d->ihandler = handler;
return d->itcpServer->listen(address, port);
}
bool
QHttpServer::isListening() const {
const Q_D(QHttpServer);
if ( ( d->ibackend == ETcpSocket || d->ibackend == ESslSocket ) && d->itcpServer )
return d->itcpServer->isListening();
else if ( d->ibackend == ELocalSocket && d->ilocalServer )
return d->ilocalServer->isListening();
return false;
}
void
QHttpServer::stopListening() {
Q_D(QHttpServer);
if ( d->itcpServer )
d->itcpServer->close();
if ( d->ilocalServer ) {
d->ilocalServer->close();
QLocalServer::removeServer( d->ilocalServer->fullServerName() );
}
}
quint32
QHttpServer::timeOut() const {
return d_func()->itimeOut;
}
void
QHttpServer::setTimeOut(quint32 newValue) {
d_func()->itimeOut = newValue;
}
TBackend
QHttpServer::backendType() const {
return d_func()->ibackend;
}
QTcpServer*
QHttpServer::tcpServer() const {
return d_func()->itcpServer.data();
}
QLocalServer*
QHttpServer::localServer() const {
return d_func()->ilocalServer.data();
}
void
QHttpServer::incomingConnection(qintptr handle) {
QHttpConnection* conn = new QHttpConnection(this);
conn->setSocketDescriptor(handle, backendType());
conn->setTimeOut(d_func()->itimeOut);
emit newConnection(conn);
Q_D(QHttpServer);
if ( d->ihandler )
QObject::connect(conn, &QHttpConnection::newRequest, d->ihandler);
else
incomingConnection(conn);
}
void
QHttpServer::incomingConnection(QHttpConnection *connection) {
QObject::connect(connection, &QHttpConnection::newRequest,
this, &QHttpServer::newRequest);
}
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,131 @@
/** HTTP server class.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_HPP
#define QHTTPSERVER_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpfwd.hpp"
#include <QObject>
#include <QHostAddress>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
/** The QHttpServer class is a fast, async (non-blocking) HTTP server. */
class QHTTP_API QHttpServer : public QObject
{
Q_OBJECT
Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut)
public:
/** construct a new HTTP Server. */
explicit QHttpServer(QObject *parent = nullptr);
virtual ~QHttpServer();
/** starts a TCP or Local (unix domain socket) server.
* if you provide a server handler, the newRequest() signal won't be emitted.
*
* @param socketOrPort could be a tcp port number as "8080" or a unix socket name as
* "/tmp/sample.socket" or "sample.socket".
* @param handler optional server handler (a lambda, std::function, ...)
* @return false if listening fails.
*/
bool listen(const QString& socketOrPort,
const TServerHandler& handler = nullptr);
/** starts a TCP server on specified address and port.
* if you provide a server handler, the newRequest() signal won't be emitted.
*
* @param address listening address as QHostAddress::Any.
* @param port listening port.
* @param handler optional server handler (a lambda, std::function, ...)
* @return false if listening fails.
*/
bool listen(const QHostAddress& address, quint16 port,
const TServerHandler& handler = nullptr);
/** @overload listen() */
bool listen(quint16 port) {
return listen(QHostAddress::Any, port);
}
/** returns true if server successfully listens. @sa listen() */
bool isListening() const;
/** closes the server and stops from listening. */
void stopListening();
/** returns timeout value [mSec] for open connections (sockets).
* @sa setTimeOut(). */
quint32 timeOut()const;
/** set time-out for new open connections in miliseconds [mSec].
* new incoming connections will be forcefully closed after this time out.
* a zero (0) value disables timer for new connections. */
void setTimeOut(quint32);
/** returns the QHttpServer's backend type. */
TBackend backendType() const;
signals:
/** emitted when a client makes a new request to the server if you do not override
* incomingConnection(QHttpConnection *connection);
* @sa incommingConnection(). */
void newRequest(QHttpRequest *request, QHttpResponse *response);
/** emitted when a new connection comes to the server if you do not override
* incomingConnection(QHttpConnection *connection);
* @sa incomingConnection(); */
void newConnection(QHttpConnection* connection);
protected:
/** returns the tcp server instance if the backend() == ETcpSocket. */
QTcpServer* tcpServer() const;
/** returns the local server instance if the backend() == ELocalSocket. */
QLocalServer* localServer() const;
/** is called when server accepts a new connection.
* you can override this function for using a thread-pool or ... some other reasons.
*
* the default implementation just connects QHttpConnection::newRequest signal.
* @note if you override this method, the signal won't be emitted by QHttpServer.
* (perhaps, you do not need it anymore).
*
* @param connection New incoming connection. */
virtual void incomingConnection(QHttpConnection* connection);
/** overrides QTcpServer::incomingConnection() to make a new QHttpConnection.
* override this function if you like to create your derived QHttpConnection instances.
*
* @note if you override this method, incomingConnection(QHttpConnection*) or
* newRequest(QHttpRequest *, QHttpResponse *) signal won't be called.
*
* @see example/benchmark/server.cpp to see how to override.
*/
virtual void incomingConnection(qintptr handle);
private:
explicit QHttpServer(QHttpServerPrivate&, QObject *parent);
Q_DECLARE_PRIVATE(QHttpServer)
Q_DISABLE_COPY(QHttpServer)
QScopedPointer<QHttpServerPrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPSERVER_HPP

View File

@@ -0,0 +1,217 @@
#include "private/qhttpserverconnection_private.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
QHttpConnection::QHttpConnection(QObject *parent)
: QObject(parent), d_ptr(new QHttpConnectionPrivate(this)) {
QHTTP_LINE_LOG
}
QHttpConnection::QHttpConnection(QHttpConnectionPrivate& dd, QObject* parent)
: QObject(parent), d_ptr(&dd) {
QHTTP_LINE_LOG
}
void
QHttpConnection::setSocketDescriptor(qintptr sokDescriptor, TBackend backendType) {
d_ptr->createSocket(sokDescriptor, backendType);
}
QHttpConnection::~QHttpConnection() {
QHTTP_LINE_LOG
}
void
QHttpConnection::setTimeOut(quint32 miliSeconds) {
if ( miliSeconds != 0 ) {
d_func()->itimeOut = miliSeconds;
d_func()->itimer.start(miliSeconds, Qt::CoarseTimer, this);
}
}
void
QHttpConnection::killConnection() {
d_func()->isocket.close();
}
TBackend
QHttpConnection::backendType() const {
return d_func()->isocket.ibackendType;
}
QTcpSocket*
QHttpConnection::tcpSocket() const {
return d_func()->isocket.itcpSocket;
}
QLocalSocket*
QHttpConnection::localSocket() const {
return d_func()->isocket.ilocalSocket;
}
void
QHttpConnection::onHandler(const TServerHandler &handler) {
d_func()->ihandler = handler;
}
void
QHttpConnection::timerEvent(QTimerEvent *) {
killConnection();
}
///////////////////////////////////////////////////////////////////////////////
// if user closes the connection, ends the response or by any other reason the
// socket disconnects, then the irequest and iresponse instances may have
// been deleted. In these situations reading more http body or emitting end()
// for incoming request are not possible:
// if ( ilastRequest == nullptr )
// return 0;
int
QHttpConnectionPrivate::messageBegin(http_parser*) {
itempUrl.clear();
itempUrl.reserve(128);
if ( ilastRequest )
ilastRequest->deleteLater();
ilastRequest = new QHttpRequest(q_func());
return 0;
}
int
QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) {
Q_ASSERT(ilastRequest);
itempUrl.append(at, length);
return 0;
}
int
QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) {
if ( ilastRequest == nullptr )
return 0;
// insert the header we parsed previously
// into the header map
if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
// header names are always lower-cased
ilastRequest->d_func()->iheaders.insert(
itempHeaderField.toLower(),
itempHeaderValue.toLower()
);
// clear header value. this sets up a nice
// feedback loop where the next time
// HeaderValue is called, it can simply append
itempHeaderField.clear();
itempHeaderValue.clear();
}
itempHeaderField.append(at, length);
return 0;
}
int
QHttpConnectionPrivate::headerValue(http_parser*, const char* at, size_t length) {
if ( ilastRequest == nullptr )
return 0;
itempHeaderValue.append(at, length);
return 0;
}
int
QHttpConnectionPrivate::headersComplete(http_parser* parser) {
if ( ilastRequest == nullptr )
return 0;
ilastRequest->d_func()->iurl = QUrl(itempUrl);
// set method
ilastRequest->d_func()->imethod =
static_cast<THttpMethod>(parser->method);
// set version
ilastRequest->d_func()->iversion = QString("%1.%2")
.arg(parser->http_major)
.arg(parser->http_minor);
// Insert last remaining header
ilastRequest->d_func()->iheaders.insert(
itempHeaderField.toLower(),
itempHeaderValue.toLower()
);
// set client information
if ( isocket.ibackendType == ETcpSocket || isocket.ibackendType == ESslSocket ) {
ilastRequest->d_func()->iremoteAddress = isocket.itcpSocket->peerAddress().toString();
ilastRequest->d_func()->iremotePort = isocket.itcpSocket->peerPort();
} else if ( isocket.ibackendType == ELocalSocket ) {
ilastRequest->d_func()->iremoteAddress = isocket.ilocalSocket->fullServerName();
ilastRequest->d_func()->iremotePort = 0; // not used in local sockets
}
if ( ilastResponse )
ilastResponse->deleteLater();
ilastResponse = new QHttpResponse(q_func());
if ( parser->http_major < 1 || parser->http_minor < 1 )
ilastResponse->d_func()->ikeepAlive = false;
// close the connection if response was the last packet
QObject::connect(ilastResponse, &QHttpResponse::done, [this](bool wasTheLastPacket){
ikeepAlive = !wasTheLastPacket;
if ( wasTheLastPacket ) {
isocket.flush();
isocket.close();
}
});
// we are good to go!
if ( ihandler )
ihandler(ilastRequest, ilastResponse);
else
emit q_ptr->newRequest(ilastRequest, ilastResponse);
return 0;
}
int
QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) {
if ( ilastRequest == nullptr )
return 0;
ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial;
if ( ilastRequest->d_func()->icollectRequired ) {
if ( !ilastRequest->d_func()->append(at, length) ) {
// forcefully dispatch the ilastRequest
finalizeConnection();
}
return 0;
}
emit ilastRequest->data(QByteArray(at, length));
return 0;
}
int
QHttpConnectionPrivate::messageComplete(http_parser*) {
if ( ilastRequest == nullptr )
return 0;
// request is done
finalizeConnection();
return 0;
}
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,87 @@
/** HTTP connection class.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_CONNECTION_HPP
#define QHTTPSERVER_CONNECTION_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpfwd.hpp"
#include <QObject>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
/** a HTTP connection in the server.
* this class controls the HTTP connetion and handles life cycle and the memory management
* of QHttpRequest and QHttpResponse instances autoamtically.
*/
class QHTTP_API QHttpConnection : public QObject
{
Q_OBJECT
public:
virtual ~QHttpConnection();
/** set an optional timer event to close the connection. */
void setTimeOut(quint32 miliSeconds);
/** forcefully kills (closes) a connection. */
void killConnection();
/** optionally set a handler for connection class.
* @note if you set this handler, the newRequest() signal won't be emitted.
*/
void onHandler(const TServerHandler& handler);
/** returns the backend type of the connection. */
TBackend backendType() const;
/** returns connected socket if the backend() == ETcpSocket. */
QTcpSocket* tcpSocket() const;
/** returns connected socket if the backend() == ELocalSocket. */
QLocalSocket* localSocket() const;
/** creates a new QHttpConnection based on arguments. */
static
QHttpConnection* create(qintptr sokDescriptor, TBackend backendType, QObject* parent) {
QHttpConnection* conn = new QHttpConnection(parent);
conn->setSocketDescriptor(sokDescriptor, backendType);
return conn;
}
signals:
/** emitted when a pair of HTTP request and response are ready to interact.
* @param req incoming request by the client.
* @param res outgoing response to the client.
*/
void newRequest(QHttpRequest* req, QHttpResponse* res);
/** emitted when the tcp/local socket, disconnects. */
void disconnected();
protected:
explicit QHttpConnection(QObject *parent);
explicit QHttpConnection(QHttpConnectionPrivate&, QObject *);
void setSocketDescriptor(qintptr sokDescriptor, TBackend backendType);
void timerEvent(QTimerEvent*) override;
Q_DISABLE_COPY(QHttpConnection)
Q_DECLARE_PRIVATE(QHttpConnection)
QScopedPointer<QHttpConnectionPrivate> d_ptr;
friend class QHttpServer;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // #define QHTTPSERVER_CONNECTION_HPP

View File

@@ -0,0 +1,81 @@
#include "private/qhttpserverrequest_private.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
QHttpRequest::QHttpRequest(QHttpConnection *conn)
: QHttpAbstractInput(conn), d_ptr(new QHttpRequestPrivate(conn, this)) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpRequest::QHttpRequest(QHttpRequestPrivate &dd, QHttpConnection *conn)
: QHttpAbstractInput(conn), d_ptr(&dd) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpRequest::~QHttpRequest() {
QHTTP_LINE_LOG
}
THttpMethod
QHttpRequest::method() const {
return d_func()->imethod;
}
const QString
QHttpRequest::methodString() const {
return http_method_str(static_cast<http_method>(d_func()->imethod));
}
const QUrl&
QHttpRequest::url() const {
return d_func()->iurl;
}
const QString&
QHttpRequest::httpVersion() const {
return d_func()->iversion;
}
const THeaderHash&
QHttpRequest::headers() const {
return d_func()->iheaders;
}
const QString&
QHttpRequest::remoteAddress() const {
return d_func()->iremoteAddress;
}
quint16
QHttpRequest::remotePort() const {
return d_func()->iremotePort;
}
bool
QHttpRequest::isSuccessful() const {
return d_func()->isuccessful;
}
void
QHttpRequest::collectData(int atMost) {
d_func()->collectData(atMost);
}
const QByteArray&
QHttpRequest::collectedData() const {
return d_func()->icollectedData;
}
QHttpConnection*
QHttpRequest::connection() const {
return d_ptr->iconnection;
}
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,82 @@
/** HTTP request which is received by the server.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_REQUEST_HPP
#define QHTTPSERVER_REQUEST_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpabstracts.hpp"
#include <QUrl>
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
/** The QHttpRequest class represents the header and body data sent by the client.
* The class is <b>read-only</b>.
* @sa QHttpConnection
*/
class QHTTP_API QHttpRequest : public QHttpAbstractInput
{
Q_OBJECT
public:
virtual ~QHttpRequest();
public: // QHttpAbstractInput methods:
/** @see QHttpAbstractInput::headers(). */
const THeaderHash& headers() const override;
/** @see QHttpAbstractInput::httpVersion(). */
const QString& httpVersion() const override;
/** @see QHttpAbstractInput::isSuccessful(). */
bool isSuccessful() const override;
/** @see QHttpAbstractInput::collectData(). */
void collectData(int atMost = -1) override;
/** @see QHttpAbstractInput::collectedData(). */
const QByteArray& collectedData()const override;
public:
/** The method used for the request. */
THttpMethod method() const ;
/** Returns the method string for the request.
* @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */
const QString methodString() const;
/** The complete URL for the request.
* This includes the path and query string. @sa path(). */
const QUrl& url() const;
/** IP Address of the client in dotted decimal format. */
const QString& remoteAddress() const;
/** Outbound connection port for the client. */
quint16 remotePort() const;
/** returns the parent QHttpConnection object. */
QHttpConnection* connection() const;
protected:
explicit QHttpRequest(QHttpConnection*);
explicit QHttpRequest(QHttpRequestPrivate&, QHttpConnection*);
friend class QHttpConnectionPrivate;
Q_DECLARE_PRIVATE(QHttpRequest)
QScopedPointer<QHttpRequestPrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPSERVER_REQUEST_HPP

View File

@@ -0,0 +1,90 @@
#include "private/qhttpserverresponse_private.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
QHttpResponse::QHttpResponse(QHttpConnection* conn)
: QHttpAbstractOutput(conn) , d_ptr(new QHttpResponsePrivate(conn, this)) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpResponse::QHttpResponse(QHttpResponsePrivate& dd, QHttpConnection* conn)
: QHttpAbstractOutput(conn) , d_ptr(&dd) {
d_ptr->initialize();
QHTTP_LINE_LOG
}
QHttpResponse::~QHttpResponse() {
QHTTP_LINE_LOG
}
void
QHttpResponse::setStatusCode(TStatusCode code) {
d_func()->istatus = code;
}
void
QHttpResponse::setVersion(const QString &versionString) {
d_func()->iversion = versionString;
}
void
QHttpResponse::addHeader(const QByteArray &field, const QByteArray &value) {
d_func()->addHeader(field, value);
}
THeaderHash&
QHttpResponse::headers() {
return d_func()->iheaders;
}
void
QHttpResponse::write(const QByteArray &data) {
d_func()->writeData(data);
}
void
QHttpResponse::end(const QByteArray &data) {
Q_D(QHttpResponse);
if ( d->endPacket(data) )
emit done(!d->ikeepAlive);
}
QHttpConnection*
QHttpResponse::connection() const {
return d_func()->iconnection;
}
///////////////////////////////////////////////////////////////////////////////
QByteArray
QHttpResponsePrivate::makeTitle() {
QString title = QString("HTTP/%1 %2 %3\r\n")
.arg(iversion)
.arg(istatus)
.arg(Stringify::toString(istatus));
return title.toLatin1();
}
void
QHttpResponsePrivate::prepareHeadersToWrite() {
if ( !iheaders.contains("date") ) {
// Sun, 06 Nov 1994 08:49:37 GMT - RFC 822. Use QLocale::c() so english is used for month and
// day.
QString dateString = QLocale::c().
toString(QDateTime::currentDateTimeUtc(),
"ddd, dd MMM yyyy hh:mm:ss")
.append(" GMT");
addHeader("date", dateString.toLatin1());
}
}
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,70 @@
/** HTTP response from a server.
* https://github.com/azadkuh/qhttp
*
* @author amir zamani
* @version 2.0.0
* @date 2014-07-11
*/
#ifndef QHTTPSERVER_RESPONSE_HPP
#define QHTTPSERVER_RESPONSE_HPP
///////////////////////////////////////////////////////////////////////////////
#include "qhttpabstracts.hpp"
///////////////////////////////////////////////////////////////////////////////
namespace qhttp {
namespace server {
///////////////////////////////////////////////////////////////////////////////
/** The QHttpResponse class handles sending data back to the client as a response to a request.
* @sa QHttpConnection
*/
class QHTTP_API QHttpResponse : public QHttpAbstractOutput
{
Q_OBJECT
public:
virtual ~QHttpResponse();
public:
/** set the response HTTP status code. @sa TStatusCode.
* default value is ESTATUS_BAD_REQUEST.
* @sa write()
*/
void setStatusCode(TStatusCode code);
public: // QHttpAbstractOutput methods:
/** @see QHttpAbstractOutput::setVersion(). */
void setVersion(const QString& versionString) override;
/** @see QHttpAbstractOutput::addHeader(). */
void addHeader(const QByteArray& field, const QByteArray& value) override;
/** @see QHttpAbstractOutput::headers(). */
THeaderHash& headers() override;
/** @see QHttpAbstractOutput::write(). */
void write(const QByteArray &data) override;
/** @see QHttpAbstractOutput::end(). */
void end(const QByteArray &data = QByteArray()) override;
public:
/** returns the parent QHttpConnection object. */
QHttpConnection* connection() const;
protected:
explicit QHttpResponse(QHttpConnection*);
explicit QHttpResponse(QHttpResponsePrivate&, QHttpConnection*);
friend class QHttpConnectionPrivate;
Q_DECLARE_PRIVATE(QHttpResponse)
QScopedPointer<QHttpResponsePrivate> d_ptr;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace server
} // namespace qhttp
///////////////////////////////////////////////////////////////////////////////
#endif // define QHTTPSERVER_RESPONSE_HPP

View File

@@ -64,7 +64,7 @@ int main(int argc, char** argv)
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager"));
parser.addPositionalArgument("filename", QCoreApplication::translate("main", "filename(s) of the password database(s) to open (*.kdbx)"), "[filename(s)]");
parser.addPositionalArgument("filename", QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
QCommandLineOption configOption("config",
QCoreApplication::translate("main", "path to a custom config file"),
@@ -97,14 +97,18 @@ int main(int argc, char** argv)
MainWindow mainWindow;
app.setMainWindow(&mainWindow);
mainWindow.show();
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
// start minimized if configured
if (config()->get("GUI/MinimizeOnStartup").toBool()) {
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool();
if (minimizeOnStartup) {
mainWindow.setWindowState(Qt::WindowMinimized);
}
if (!(minimizeOnStartup && minimizeToTray)) {
mainWindow.show();
}
for (int ii=0; ii < args.length(); ii++) {
QString filename = args[ii];

View File

@@ -496,7 +496,7 @@ typedef struct
uint8_t LeetCnv[sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1];
/* uint8_t LeetChr[3]; */
uint8_t First;
uint8_t PossChars[48];
uint8_t PossChars[49];
} DictWork_t;
/**********************************************************************************