├── resources ├── icon.rc ├── PXM_Icon.ico ├── message.wav ├── PXMessenger.png ├── PXMessenger_wBackground.png ├── PXMessenger_TightCrop_100px.png ├── pxmessenger.desktop ├── resources.qrc └── updates.json ├── include ├── QSimpleUpdater │ ├── .gitattributes │ ├── etc │ │ ├── resources │ │ │ ├── update.png │ │ │ └── qsimpleupdater.qrc │ │ ├── screenshots │ │ │ ├── tutorial.png │ │ │ ├── downloading.png │ │ │ └── download-complete.png │ │ └── scripts │ │ │ ├── format-code.sh │ │ │ └── format-code.bat │ ├── .gitignore │ ├── .travis.yml │ ├── COPYING.md │ ├── QSimpleUpdater.pro │ ├── QSimpleUpdater.pri │ ├── src │ │ ├── Downloader.h │ │ ├── Updater.h │ │ ├── Downloader.ui │ │ ├── Downloader.cpp │ │ ├── Updater.cpp │ │ └── QSimpleUpdater.cpp │ ├── README.md │ └── include │ │ └── QSimpleUpdater.h ├── pxmsync.h ├── pxmagent.h ├── pxminireader.h ├── netcompression.h ├── pxmsettingsdialog.h ├── pxmconsts.h ├── pxmclient.h ├── pxmpeerworker.h ├── pxmconsole.h ├── pxmpeers.h ├── timedvector.h ├── pxmstackwidget.h ├── pxmserver.h └── pxmmainwindow.h ├── .gitignore ├── .travis.yml ├── docker └── Dockerfile ├── src ├── pxmsync.cpp ├── netcompression.cpp ├── pxmessenger.cpp ├── pxmpeers.cpp ├── pxmconsole.cpp ├── pxminireader.cpp ├── pxmclient.cpp ├── pxmstackwidget.cpp └── pxmagent.cpp ├── README.md ├── PXMessenger.pro └── ui ├── manualconnect.ui ├── pxmaboutdialog.ui ├── pxmmainwindow.ui └── pxmsettingsdialog.ui /resources/icon.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "PXM_Icon.ico" 2 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/.gitattributes: -------------------------------------------------------------------------------- 1 | doc/* linguist-documentation 2 | -------------------------------------------------------------------------------- /resources/PXM_Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/resources/PXM_Icon.ico -------------------------------------------------------------------------------- /resources/message.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/resources/message.wav -------------------------------------------------------------------------------- /resources/PXMessenger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/resources/PXMessenger.png -------------------------------------------------------------------------------- /resources/PXMessenger_wBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/resources/PXMessenger_wBackground.png -------------------------------------------------------------------------------- /resources/PXMessenger_TightCrop_100px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/resources/PXMessenger_TightCrop_100px.png -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/resources/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/include/QSimpleUpdater/etc/resources/update.png -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/screenshots/tutorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/include/QSimpleUpdater/etc/screenshots/tutorial.png -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/screenshots/downloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/include/QSimpleUpdater/etc/screenshots/downloading.png -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/screenshots/download-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corybolar/PXMessenger/HEAD/include/QSimpleUpdater/etc/screenshots/download-complete.png -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/resources/qsimpleupdater.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | update.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /PXMessenger.pro.user 2 | /*.exe 3 | /PXMessenger 4 | /object_script* 5 | /build-unix 6 | /build-win* 7 | /.qmake.stash 8 | /Makefile* 9 | /*_plugin_import.cpp 10 | /PXMessenger*_resource.rc 11 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Fortran module files 12 | *.mod 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | 19 | *.user 20 | -------------------------------------------------------------------------------- /resources/pxmessenger.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Exec=PXMessenger 4 | Terminal=false 5 | Name=PXMessenger 6 | Icon=/usr/share/pixmaps/PXMessenger.png 7 | GenericName=Instant Messenger 8 | Comment=P2P Instant Messenger 9 | Categories=Network;InstantMessaging;Chat 10 | -------------------------------------------------------------------------------- /resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | PXM_Icon.ico 4 | PXMessenger_TightCrop_100px.png 5 | PXMessenger_wBackground.png 6 | message.wav 7 | icon.rc 8 | 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: none 2 | sudo: required 3 | 4 | services: 5 | - docker 6 | 7 | before_install: 8 | - docker build -t xenial-pxm ./docker 9 | - docker run -t -d -p 127.0.0.1:80:80 --name build xenial-pxm /bin/sh 10 | 11 | script: 12 | - docker ps -a 13 | - docker exec build sh -c "cd pxmessenger && qmake && make -j4" 14 | - docker stop build 15 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/scripts/format-code.sh: -------------------------------------------------------------------------------- 1 | # Style and format recursively 2 | astyle --style=linux --indent=spaces --align-pointer=type --indent-preproc-block --indent-preproc-define --indent-col1-comments --pad-first-paren-out --pad-oper --attach-namespaces --remove-brackets --convert-tabs --close-templates --max-code-length=100 --max-instatement-indent=50 --lineend=windows --suffix=none --recursive ../../*.h ../../*.c ../../*.cpp ../../*.cc 3 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | 3 | RUN apt-get update -q && apt-get install -y software-properties-common 4 | RUN add-apt-repository ppa:ubuntu-toolchain-r/test -y 5 | RUN apt-get update -q && apt-get install -y libqt5gui5 \ 6 | qtbase5-dev \ 7 | libqt5multimedia5 \ 8 | qtmultimedia5-dev \ 9 | libevent-2.0-5 \ 10 | libevent-dev \ 11 | g++-5 \ 12 | make \ 13 | git \ 14 | qt5-default \ 15 | g++ 16 | 17 | RUN git clone https://github.com/cbpeckles/pxmessenger 18 | 19 | ENTRYPOINT /bin/bash 20 | -------------------------------------------------------------------------------- /resources/updates.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": { 3 | "windows32": { 4 | "open-url": "", 5 | "latest-version": "1.6.0", 6 | "download-url": "https://github.com/cbpeckles/PXMessenger/releases/download/v1.6.0/PXMessenger-Setup-v1.6.0-x86.exe", 7 | "changelog": "" 8 | }, 9 | "windows64": { 10 | "open-url": "", 11 | "latest-version": "1.6.0", 12 | "download-url": "https://github.com/cbpeckles/PXMessenger/releases/download/v1.6.0/PXMessenger-Setup-v1.6.0-x86_64.exe", 13 | "changelog": "" 14 | } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | 4 | dist: trusty 5 | sudo: required 6 | 7 | before_install: 8 | - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test 9 | - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa 10 | - sudo apt-get update -qq 11 | 12 | install: 13 | - sudo apt-get install -y --force-yes build-essential g++-4.8 -y 14 | - sudo apt-get install -y --force-yes libudev-dev libts-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev -y 15 | - sudo apt-get install -y --force-yes qtbase5-dev qtdeclarative5-dev libqt5gui5 qttools5-dev-tools qttools5-dev qtmultimedia5-dev 16 | 17 | script: 18 | - qmake -qt=qt5 QSimpleUpdater.pro 19 | - make -j4 20 | -------------------------------------------------------------------------------- /include/pxmsync.h: -------------------------------------------------------------------------------- 1 | #ifndef PXMSYNC_H 2 | #define PXMSYNC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "pxmpeers.h" 10 | 11 | namespace Peers 12 | { 13 | class BevWrapper; 14 | class PeerData; 15 | } 16 | 17 | class PXMSync : public QObject 18 | { 19 | Q_OBJECT 20 | QHash syncHash; 21 | 22 | public: 23 | PXMSync(QObject* parent, QHash &hash); 24 | void setsyncHash(const QHash &hash); 25 | QHash::iterator hashIterator; 26 | void setIteratorToStart(); 27 | public slots: 28 | void syncNext(); 29 | signals: 30 | void requestIps(QSharedPointer, QUuid); 31 | void syncComplete(); 32 | }; 33 | 34 | #endif // MESSSYNC_H 35 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/etc/scripts/format-code.bat: -------------------------------------------------------------------------------- 1 | :: Description: This script changes the style format of 2 | :: all the source code of the project. 3 | 4 | :: Setup the command line 5 | @echo off 6 | title Code Formatter 7 | 8 | :: Go to the directory where the script is run 9 | cd /d %~dp0 10 | 11 | :: Style and format the source code recursively 12 | astyle --style=linux --indent=spaces --align-pointer=type --indent-preproc-block --indent-preproc-define --indent-col1-comments --pad-first-paren-out --pad-oper --attach-namespaces --remove-brackets --convert-tabs --close-templates --max-code-length=100 --max-instatement-indent=50 --lineend=windows --suffix=none --recursive ../../*.h ../../*.cpp ../../*.c 13 | 14 | :: Notify the user that we have finished 15 | echo. 16 | echo Code styling complete! 17 | echo. 18 | 19 | :: Let the user see the output 20 | pause 21 | -------------------------------------------------------------------------------- /src/pxmsync.cpp: -------------------------------------------------------------------------------- 1 | #include "pxmsync.h" 2 | 3 | #include 4 | 5 | #include "pxmpeers.h" 6 | 7 | PXMSync::PXMSync(QObject* parent, QHash& hash) : QObject(parent), syncHash(hash) 8 | { 9 | } 10 | void PXMSync::syncNext() 11 | { 12 | while (hashIterator != syncHash.end() && !(hashIterator.value().isAuthed)) { 13 | hashIterator++; 14 | } 15 | if (hashIterator == syncHash.end()) { 16 | emit syncComplete(); 17 | return; 18 | } else { 19 | emit requestIps(hashIterator.value().bw, hashIterator.value().uuid); 20 | } 21 | hashIterator++; 22 | } 23 | 24 | void PXMSync::setsyncHash(const QHash& hash) 25 | { 26 | syncHash = hash; 27 | hashIterator = QHash::iterator(); 28 | } 29 | void PXMSync::setIteratorToStart() 30 | { 31 | hashIterator = syncHash.begin(); 32 | } 33 | -------------------------------------------------------------------------------- /include/pxmagent.h: -------------------------------------------------------------------------------- 1 | #ifndef PXMAGENT_H 2 | #define PXMAGENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | class PXMAgentPrivate; 12 | class PXMAgent : public QObject 13 | { 14 | Q_OBJECT 15 | 16 | QScopedPointer d_ptr; 17 | 18 | public: 19 | // Default 20 | PXMAgent(QObject* parent = 0); 21 | // Copy 22 | PXMAgent(const PXMAgent& agent) = delete; 23 | // Copy Assignment 24 | PXMAgent& operator=(const PXMAgent& agent) = delete; 25 | // Move 26 | PXMAgent(PXMAgent&& agent) noexcept = delete; 27 | // Move Assignment 28 | PXMAgent& operator=(PXMAgent&& agent) noexcept = delete; 29 | // Destructor 30 | ~PXMAgent(); 31 | 32 | static QPalette defaultPalette; 33 | static void changeToDark(); 34 | 35 | public slots: 36 | void doneChkUpdt(const QString &); 37 | int init(); 38 | private slots: 39 | int postInit(); 40 | signals: 41 | void alreadyRunning(); 42 | }; 43 | 44 | #endif // PXMAGENT_H 45 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/COPYING.md: -------------------------------------------------------------------------------- 1 | # DON'T BE A DICK PUBLIC LICENSE 2 | 3 | > Version 1, December 2009 4 | 5 | > Copyright (C) 2009 Philip Sturgeon 6 | 7 | Everyone is permitted to copy and distribute verbatim or modified 8 | copies of this license document, and changing it is allowed as long 9 | as the name is changed. 10 | 11 | > DON'T BE A DICK PUBLIC LICENSE 12 | > TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 13 | 14 | 1. Do whatever you like with the original work, just don't be a dick. 15 | 16 | Being a dick includes - but is not limited to - the following instances: 17 | 18 | 1a. Outright copyright infringement - Don't just copy this and change the name. 19 | 1b. Selling the unmodified original with no work done what-so-ever, that's REALLY being a dick. 20 | 1c. Modifying the original work to contain hidden harmful content. That would make you a PROPER dick. 21 | 22 | 2. If you become rich through modifications, related works/services, or supporting the original work, 23 | share the love. Only a dick would make loads off this work and not buy the original work's 24 | creator(s) a pint. 25 | 26 | 3. Code is provided with no warranty. Using somebody else's code and bitching when it goes wrong makes 27 | you a DONKEY dick. Fix the problem yourself. A non-dick would submit the fix back. 28 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/QSimpleUpdater.pro: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014-2016 Alex Spataru 3 | # 4 | # This file is part of the QSimpleUpdater library, which is released under 5 | # the DBAD license, you can read a copy of it below: 6 | # 7 | # DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION 8 | # AND MODIFICATION: 9 | # 10 | # Do whatever you like with the original work, just don't be a dick. 11 | # Being a dick includes - but is not limited to - the following instances: 12 | # 13 | # 1a. Outright copyright infringement - Don't just copy this and change the 14 | # name. 15 | # 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | # REALLY being a dick. 17 | # 1c. Modifying the original work to contain hidden harmful content. 18 | # That would make you a PROPER dick. 19 | # 20 | # If you become rich through modifications, related works/services, or 21 | # supporting the original work, share the love. 22 | # Only a dick would make loads off this work and not buy the original works 23 | # creator(s) a pint. 24 | # 25 | # Code is provided with no warranty. Using somebody else's code and bitching 26 | # when it goes wrong makes you a DONKEY dick. 27 | # Fix the problem yourself. A non-dick would submit the fix back. 28 | 29 | TEMPLATE = lib 30 | DEFINES += QSU_SHARED 31 | include ($$PWD/QSimpleUpdater.pri) 32 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/QSimpleUpdater.pri: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014-2016 Alex Spataru 3 | # 4 | # This file is part of the QSimpleUpdater library, which is released under 5 | # the DBAD license, you can read a copy of it below: 6 | # 7 | # DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION 8 | # AND MODIFICATION: 9 | # 10 | # Do whatever you like with the original work, just don't be a dick. 11 | # Being a dick includes - but is not limited to - the following instances: 12 | # 13 | # 1a. Outright copyright infringement - Don't just copy this and change the 14 | # name. 15 | # 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | # REALLY being a dick. 17 | # 1c. Modifying the original work to contain hidden harmful content. 18 | # That would make you a PROPER dick. 19 | # 20 | # If you become rich through modifications, related works/services, or 21 | # supporting the original work, share the love. 22 | # Only a dick would make loads off this work and not buy the original works 23 | # creator(s) a pint. 24 | # 25 | # Code is provided with no warranty. Using somebody else's code and bitching 26 | # when it goes wrong makes you a DONKEY dick. 27 | # Fix the problem yourself. A non-dick would submit the fix back. 28 | 29 | QT += gui 30 | QT += core 31 | QT += network 32 | QT += widgets 33 | 34 | INCLUDEPATH += $$PWD/include 35 | 36 | SOURCES += \ 37 | $$PWD/src/Updater.cpp \ 38 | $$PWD/src/Downloader.cpp \ 39 | $$PWD/src/QSimpleUpdater.cpp 40 | 41 | HEADERS += \ 42 | $$PWD/include/QSimpleUpdater.h \ 43 | $$PWD/src/Updater.h \ 44 | $$PWD/src/Downloader.h 45 | 46 | FORMS += $$PWD/src/Downloader.ui 47 | RESOURCES += $$PWD/etc/resources/qsimpleupdater.qrc 48 | -------------------------------------------------------------------------------- /src/netcompression.cpp: -------------------------------------------------------------------------------- 1 | #include "netcompression.h" 2 | 3 | #include 4 | #ifdef _WIN32 5 | #include 6 | #elif __unix__ 7 | #include 8 | #else 9 | #error "include header that defines sockaddr_in" 10 | #endif 11 | 12 | static_assert(sizeof(uint8_t) == 1, "uint8_t not defined as 1 byte"); 13 | static_assert(sizeof(uint16_t) == 2, "uint16_t not defined as 2 bytes"); 14 | static_assert(sizeof(uint32_t) == 4, "uint32_t not defined as 4 bytes"); 15 | 16 | size_t NetCompression::unpackUUID(const unsigned char* src, QUuid& uuid) 17 | { 18 | // Use QT implementation of converting to NBO, its better 19 | uuid = QUuid::fromRfc4122(QByteArray::fromRawData(reinterpret_cast(&src[0]), PACKED_UUID_LENGTH)); 20 | return PACKED_UUID_LENGTH; 21 | } 22 | size_t NetCompression::packUUID(unsigned char* buf, const QUuid& uuid) 23 | { 24 | // Use QT implementation of converting from NBO, its better 25 | // because it will return a null uuid if it fails 26 | memcpy(&buf[0], uuid.toRfc4122().constData(), PACKED_UUID_LENGTH); 27 | return PACKED_UUID_LENGTH; 28 | } 29 | 30 | size_t NetCompression::packSockaddr_in(unsigned char* buf, const sockaddr_in& addr) 31 | { 32 | size_t index = 0; 33 | memcpy(&buf[index], &(addr.sin_addr.s_addr), sizeof(uint32_t)); 34 | index += sizeof(uint32_t); 35 | memcpy(&buf[index], &(addr.sin_port), sizeof(uint16_t)); 36 | index += sizeof(uint16_t); 37 | return index; 38 | } 39 | 40 | size_t NetCompression::unpackSockaddr_in(const unsigned char* buf, sockaddr_in& addr) 41 | { 42 | size_t index = 0; 43 | memcpy(&(addr.sin_addr.s_addr), &buf[index], sizeof(uint32_t)); 44 | index += sizeof(uint32_t); 45 | memcpy(&(addr.sin_port), &buf[index], sizeof(uint16_t)); 46 | index += sizeof(uint16_t); 47 | 48 | return index; 49 | } 50 | -------------------------------------------------------------------------------- /include/pxminireader.h: -------------------------------------------------------------------------------- 1 | /** @file pxminireader.h 2 | * @brief Public header for pxminireader.cpp 3 | * 4 | * pxminireader reads our ini for initial values. I bet you couldn't have 5 | * guessed that. The .ini should be located under ~/.config/PXMessenger on 6 | * unix, c:/User/[username]/AppData/Roaming/PXMessenger on windows. 7 | */ 8 | 9 | #ifndef MESSINIREADER_H 10 | #define MESSINIREADER_H 11 | 12 | #include "pxmconsts.h" 13 | 14 | #include 15 | 16 | class QSettings; 17 | class QString; 18 | class QSize; 19 | class PXMIniReader { 20 | QScopedPointer iniFile; 21 | 22 | public: 23 | PXMIniReader(); 24 | ~PXMIniReader(); 25 | bool checkAllowMoreThanOne(); 26 | unsigned int getUUIDNumber() const; 27 | QUuid getUUID(unsigned int num, bool takeIt) const; 28 | int resetUUID(unsigned int num, QUuid uuid); 29 | unsigned short getPort(QString protocol); 30 | QString getHostname(QString defaultHostname); 31 | void setHostname(QString hostname); 32 | void setPort(QString protocol, int portNumber); 33 | void setWindowSize(QSize windowSize); 34 | QSize getWindowSize(QSize defaultSize) const; 35 | void setMute(bool mute); 36 | bool getMute() const; 37 | void setFocus(bool focus); 38 | bool getFocus() const; 39 | QString getFont(); 40 | void setFont(QString font); 41 | int getFontSize() const; 42 | void setFontSize(int size); 43 | void setAllowMoreThanOne(bool value); 44 | int setMulticastAddress(QString ip); 45 | QString getMulticastAddress() const; 46 | int getVerbosity() const; 47 | void setVerbosity(int level) const; 48 | bool getLogActive() const; 49 | void setLogActive(bool status) const; 50 | bool getDarkColorScheme() const; 51 | void setDarkColorScheme(bool status) const; 52 | bool getUpdates() const; 53 | void setUpdates(bool status) const; 54 | }; 55 | 56 | #endif // MESSINIREADER_H 57 | -------------------------------------------------------------------------------- /include/netcompression.h: -------------------------------------------------------------------------------- 1 | #ifndef NETCOMPRESSION_H 2 | #define NETCOMPRESSION_H 3 | 4 | #include 5 | 6 | #ifdef __WIN32 7 | #include 8 | #elif __unix__ 9 | #include 10 | #else 11 | #error "include header for sockaddr_in" 12 | #endif 13 | 14 | class QUuid; 15 | namespace NetCompression 16 | { 17 | const size_t PACKED_UUID_LENGTH = 16; 18 | /*! 19 | * \brief packUUID 20 | * Compresses and packs a UUID by converting it into Network Byte Order. 21 | * \param buf supplied buffer to pack into 22 | * \param uuid uuid to compress 23 | * \return the size of the packed UUID that was added to buf 24 | */ 25 | size_t packUUID(unsigned char* buf, const QUuid& uuid); 26 | /*! 27 | * \brief unpackUUID 28 | * Convert a UUID that was in Network Byte Order back to normal format 29 | * \param src a buffer where a packed UUID is stored 30 | * \param uuid a supplied UUID to store the unpacked UUID to 31 | * \return the size of the unpacked UUID that was read from src 32 | */ 33 | size_t unpackUUID(const unsigned char* src, QUuid &uuid); 34 | 35 | const size_t PACKED_SOCKADDR_IN_LENGTH = 6; 36 | /*! 37 | * \brief packSockaddr_in 38 | * Pack a sockaddr_in structure into a supplied buffer. Only the in_addr_t and 39 | * in_port_t are packed. IPv4 only! 40 | * \param buf supplied buffer to store data to 41 | * \param addr sockaddr_in to pack 42 | * \return the size of the packed sockaddr_in that was stored in buf 43 | */ 44 | size_t packSockaddr_in(unsigned char* buf, const sockaddr_in &addr); 45 | /*! 46 | * \brief unpackSockaddr_in 47 | * Unpacks a sockaddr_in structure that was packed using packSockaddr_in. IPv4 48 | * only! 49 | * \param buf supplied buffer which contains the packed sockaddr_in 50 | * \param addr a supplied sockaddr_in structure to store the result to 51 | * \return the size of the unpacked sockaddr_in that was unpacked from buf 52 | */ 53 | size_t unpackSockaddr_in(const unsigned char* buf, sockaddr_in &addr); 54 | } 55 | 56 | #endif // NETCOMPRESSION_H 57 | -------------------------------------------------------------------------------- /include/pxmsettingsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef DIALOG_20__2D__20_UNTITLEDXQ7926_H 2 | #define DIALOG_20__2D__20_UNTITLEDXQ7926_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "pxminireader.h" 22 | #include "pxmdefinitions.h" 23 | #include "pxmconsolewindow.h" 24 | 25 | #ifdef _WIN32 26 | #include 27 | #else 28 | #include 29 | #include 30 | #include 31 | #endif 32 | 33 | class PXMSettingsDialog : public QDialog 34 | { 35 | Q_OBJECT 36 | 37 | bool AllowMoreThanOneInstance; 38 | QString hostname; 39 | int tcpPort; 40 | int udpPort; 41 | QFont iniFont; 42 | int fontSize; 43 | 44 | QGridLayout *gridLayout; 45 | QSpacerItem *verticalSpacer; 46 | QCheckBox *checkBox; 47 | QLabel *label_2; 48 | QLineEdit *lineEdit; 49 | QLabel *label; 50 | QLabel *label_3; 51 | QLabel *label_4; 52 | QLabel *label_5; 53 | QLabel *label_6; 54 | QLabel *label_7; 55 | QDialogButtonBox *buttonBox; 56 | QSpinBox *spinBox; 57 | QSpinBox *spinBox_2; 58 | QFontComboBox *fontComboBox; 59 | QSpinBox *spinBox_3; 60 | QSpinBox *spinBox_4; 61 | private slots: 62 | void clickedme(QAbstractButton *button); 63 | void accept(); 64 | void currentFontChanged(QFont font); 65 | void valueChanged(int size); 66 | 67 | public: 68 | explicit PXMSettingsDialog(QWidget* parent); 69 | 70 | void setupUi(); 71 | void retranslateUi(); 72 | void readIni(); 73 | signals: 74 | void nameChange(QString); 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/pxmconsts.h: -------------------------------------------------------------------------------- 1 | /** @file pxmconsts.h 2 | * @brief various constants used throughout pxmessenger 3 | */ 4 | 5 | #ifndef PXMCONSTS_H 6 | #define PXMCONSTS_H 7 | 8 | #include "netcompression.h" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | static_assert(sizeof(uint32_t) == 4, "uint32_t not defined as 4 bytes"); 17 | 18 | namespace PXMConsts 19 | { 20 | const char DEFAULT_MULTICAST_ADDRESS[] = "239.192.13.13"; 21 | const int MESSAGE_HISTORY_LENGTH = 500; 22 | #ifdef QT_DEBUG 23 | const size_t DEBUG_PADDING = 23; 24 | #else 25 | const size_t DEBUG_PADDING = 0; 26 | #endif 27 | const unsigned short DEFAULT_UDP_PORT = 53273; 28 | const int TEXT_EDIT_MAX_LENGTH = 2000; 29 | const size_t MAX_HOSTNAME_LENGTH = 24; 30 | const size_t MAX_COMPUTER_NAME_LENGTH = 36; 31 | const char AUTH_SEPERATOR[] = ":::"; 32 | enum MESSAGE_TYPE : const uint32_t { 33 | MSG_TEXT = 0x11111111, 34 | MSG_GLOBAL = 0x22222222, 35 | MSG_SYNC = 0x33333333, 36 | MSG_SYNC_REQUEST = 0x44444444, 37 | MSG_AUTH = 0x55555555, 38 | MSG_NAME = 0x66666666, 39 | MSG_DISOVER = 0x77777777, 40 | MSG_ID = 0x88888888, 41 | MSG_TYPING = 0x99999999, 42 | MSG_TEXTENTERED = 0x21132398, 43 | MSG_ENDTEXTENTERED = 0x29932398 44 | }; 45 | //This works around a compiler warning in pxmserver when building a reply 46 | //message for udp discover packets. See updReceive in pxmserver.cpp 47 | constexpr size_t ct_strlen(const char* s) noexcept 48 | { 49 | return *s ? 1 + ct_strlen(s + 1) : 0; 50 | } 51 | const size_t MAX_AUTH_PACKET_LEN = 52 | sizeof(MESSAGE_TYPE) + 53 | NetCompression::PACKED_UUID_LENGTH + 54 | MAX_HOSTNAME_LENGTH + 55 | MAX_COMPUTER_NAME_LENGTH + 56 | strlen(AUTH_SEPERATOR) + 57 | 5 /*port number*/ + 58 | strlen(AUTH_SEPERATOR) + 59 | strlen("001.001.001") + 60 | 1 /*null*/; 61 | } 62 | Q_DECLARE_METATYPE(PXMConsts::MESSAGE_TYPE) 63 | 64 | #endif // PXMCONSTS_H 65 | -------------------------------------------------------------------------------- /include/pxmclient.h: -------------------------------------------------------------------------------- 1 | /** @file pxmclient.h 2 | * @brief Public header for pxmclient.cpp 3 | * 4 | * Primary class used to send messages over the network 5 | */ 6 | 7 | #ifndef PXMCLIENT_H 8 | #define PXMCLIENT_H 9 | 10 | #include "pxmconsts.h" 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | struct bufferevent; 17 | namespace Peers 18 | { 19 | class BevWrapper; 20 | } 21 | 22 | struct PXMClientPrivate; 23 | class PXMClient : public QObject 24 | { 25 | Q_OBJECT 26 | QScopedPointer d_ptr; 27 | 28 | public: 29 | PXMClient(QObject* parent, in_addr multicast, QUuid localUUID); 30 | ~PXMClient(); 31 | /** Sets the uuid for use in comms 32 | * @brief setLocalUUID 33 | * @param uuid QUuid to set 34 | */ 35 | void setLocalUUID(QUuid uuid); 36 | public slots: 37 | /*! 38 | * \brief sendMsgSlot 39 | * Function for sending data to already connected peers. 40 | * \param bw pointer containing a Bevwrapper, which contains a bufferevent 41 | * \param msg bytes to send, does not need to be null terminated 42 | * \param len length of bytes to send 43 | * \param type type of message to send in header 44 | * \param theiruuid recipients uuid, only used when confirming message was 45 | * sent 46 | */ 47 | void sendMsgSlot(QSharedPointer bw, 48 | QByteArray msg, 49 | size_t len, 50 | PXMConsts::MESSAGE_TYPE type, 51 | QUuid theiruuid = QUuid()); 52 | /*! 53 | * \brief sendUDP 54 | * Sends a UDP packet to the multicast group 55 | * \param msg message to send, must be null-terminated 56 | * \param port port on the multicast to send to 57 | * \return 0 on success, -1 on error 58 | */ 59 | int sendUDP(const char* msg, unsigned short port); 60 | void sendSingleType(QSharedPointer bw, PXMConsts::MESSAGE_TYPE type); 61 | signals: 62 | /*! 63 | * \brief resultOfTCPSend 64 | * This returns the result of the sendMsgSlot, with information about how 65 | * successful it was contained in the first integer. 66 | */ 67 | void resultOfTCPSend(int, QUuid, QUuid, QString, bool, QSharedPointer); 68 | }; 69 | 70 | #endif // PXMCLIENT_H 71 | -------------------------------------------------------------------------------- /include/pxmpeerworker.h: -------------------------------------------------------------------------------- 1 | /** @file pxmpeerworker.h 2 | * @brief public header for pxmpeerworker.cpp 3 | * 4 | * This is the manager for pxmserver and pxmclient. Meant to be used in it own 5 | * thread but will spawn another thread for the server to work from. 6 | */ 7 | 8 | #ifndef PXMPEERWORKER_H 9 | #define PXMPEERWORKER_H 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "pxmpeers.h" 17 | #include "pxmconsts.h" 18 | 19 | class PXMPeerWorkerPrivate; 20 | 21 | class PXMPeerWorker : public QObject 22 | { 23 | Q_OBJECT 24 | 25 | friend class PXMPeerWorkerPrivate; 26 | const QScopedPointer d_ptr; 27 | public: 28 | explicit PXMPeerWorker(QObject* parent, 29 | QString username, 30 | QUuid selfUUID, 31 | QString multicast, 32 | unsigned short tcpPort, 33 | unsigned short udpPort, 34 | QUuid globaluuid); 35 | ~PXMPeerWorker(); 36 | PXMPeerWorker(PXMPeerWorker const&) = delete; 37 | PXMPeerWorker& operator=(PXMPeerWorker const&) = delete; 38 | PXMPeerWorker& operator=(PXMPeerWorker&&) noexcept = delete; 39 | PXMPeerWorker(PXMPeerWorker&&) noexcept = delete; 40 | public slots: 41 | int addMessageToPeer(QString str, QUuid ruuid, QUuid suuid, bool alert, bool fromServer); 42 | 43 | void sendMsgAccessor(QByteArray msg, PXMConsts::MESSAGE_TYPE type, 44 | QUuid uuid = QUuid()); 45 | void sendUDPAccessor(const char* msg); 46 | 47 | //void setupTimers(); 48 | void printInfoToDebug(); 49 | void beginPeerSync(); 50 | void typing(QUuid uuid); 51 | void textEntered(QUuid uuid); 52 | void endOfTextEntered(QUuid uuid); 53 | // void restartServer(); 54 | signals: 55 | void msgRecieved(QSharedPointer, QString, QUuid, QUuid, bool, bool, bool); 56 | void newAuthedPeer(QUuid, QString); 57 | void sendMsg(QSharedPointer, QByteArray, size_t, 58 | PXMConsts::MESSAGE_TYPE, QUuid = QUuid()); 59 | void sendUDP(const char*, unsigned short); 60 | void sendSingleType(QSharedPointer, PXMConsts::MESSAGE_TYPE); 61 | void connectionStatusChange(QUuid, bool); 62 | void ipsReceivedFrom(QUuid); 63 | void warnBox(QString, QString); 64 | void typingAlert(QUuid); 65 | void textEnteredAlert(QUuid); 66 | void endOfTextEnteredAlert(QUuid); 67 | 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /include/pxmconsole.h: -------------------------------------------------------------------------------- 1 | /** @file pxmconsole.h 2 | * @brief Public header for pxmconsole.cpp 3 | * 4 | * PXMConsole is what handles all logging in PXMessenger as well as drive the 5 | * debug console available through the menu bar. It needs to be used in the 6 | * main gui thread as it updates a textbox by default. 7 | */ 8 | 9 | #ifndef PXMCONSOLE_H 10 | #define PXMCONSOLE_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class QPushButton; 20 | namespace PXMConsole 21 | { 22 | const int HISTORY_LIMIT = 2000; 23 | static QFile file; 24 | struct WindowPrivate; 25 | class Window : public QMainWindow 26 | { 27 | Q_OBJECT 28 | QScopedPointer d_ptr; 29 | 30 | public: 31 | explicit Window(QWidget* parent = 0); 32 | ~Window(); 33 | QPushButton* pushButton; 34 | QPushButton* pushButton1; 35 | static QTextEdit* textEdit; 36 | public slots: 37 | void verbosityChanged(); 38 | private slots: 39 | void adjustScrollBar(int i); 40 | void rangeChanged(int, int i2); 41 | }; 42 | 43 | class AppendTextEvent : public QEvent 44 | { 45 | public: 46 | AppendTextEvent(QString text, QColor color) : QEvent(static_cast(type)), text(text), textColor(color) {} 47 | QString getText() { return text; } 48 | QColor getColor() { return textColor; } 49 | private: 50 | static int type; 51 | QString text; 52 | QColor textColor; 53 | }; 54 | 55 | class Logger : public QObject 56 | { 57 | public: 58 | static Logger* getInstance() 59 | { 60 | if (!loggerInstance) { 61 | loggerInstance = new Logger; 62 | #ifdef __WIN32 63 | loggerInstance->logFile.reset(new QFile(QDir::currentPath() + "/log.txt", loggerInstance)); 64 | #else 65 | loggerInstance->logFile.reset(new QFile(QDir::homePath() + "/.pxmessenger-log", loggerInstance)); 66 | #endif 67 | } 68 | 69 | return loggerInstance; 70 | } 71 | void setTextEdit(QTextEdit* textEdit) { logTextEdit = textEdit; } 72 | int getVerbosityLevel() { return verbosityLevel; } 73 | void setVerbosityLevel(int value) { verbosityLevel = value; } 74 | bool getLogStatus() { return logActive; } 75 | void setLogStatus(bool stat); 76 | QScopedPointer logFile; 77 | 78 | private: 79 | Logger() : QObject() {} 80 | static Logger* loggerInstance; 81 | QTextEdit* logTextEdit = 0; 82 | int verbosityLevel = 0; 83 | bool logActive = 0; 84 | 85 | protected: 86 | virtual void customEvent(QEvent* event); 87 | }; 88 | } 89 | 90 | #endif // PXMCONSOLE_H 91 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/Downloader.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #ifndef DOWNLOAD_DIALOG_H 31 | #define DOWNLOAD_DIALOG_H 32 | 33 | #include 34 | #include 35 | 36 | namespace Ui { 37 | class Downloader; 38 | } 39 | 40 | class QNetworkReply; 41 | class QNetworkAccessManager; 42 | 43 | /** 44 | * \brief Implements an integrated file downloader with a nice UI 45 | */ 46 | class Downloader : public QWidget 47 | { 48 | Q_OBJECT 49 | 50 | signals: 51 | void downloadFinished (const QString& url, const QString& filepath); 52 | void installerOpened(); 53 | void downloadCanceled(); 54 | 55 | public: 56 | explicit Downloader (QWidget* parent = 0); 57 | ~Downloader(); 58 | 59 | bool useCustomInstallProcedures() const; 60 | 61 | public slots: 62 | void setUrlId (const QString& url); 63 | void startDownload (const QUrl& url); 64 | void setFileName (const QString& file); 65 | void setUseCustomInstallProcedures (const bool custom); 66 | 67 | private slots: 68 | void openDownload(); 69 | void installUpdate(); 70 | void cancelDownload(); 71 | void saveFile (qint64 received, qint64 total); 72 | void calculateSizes (qint64 received, qint64 total); 73 | void updateProgress (qint64 received, qint64 total); 74 | void calculateTimeRemaining (qint64 received, qint64 total); 75 | 76 | private: 77 | qreal round (const qreal& input); 78 | 79 | private: 80 | QString m_url; 81 | uint m_startTime; 82 | QString m_fileName; 83 | Ui::Downloader* m_ui; 84 | QNetworkReply* m_reply; 85 | bool m_useCustomProcedures; 86 | QNetworkAccessManager* m_manager; 87 | }; 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

P2P cross platform home or small office instant messenger. Written in C++ with Qt.

5 |

6 | 7 |

8 | 9 | #### Status 10 | 11 | [![Build Status](https://travis-ci.org/cbpeckles/PXMessenger.svg?branch=master)](https://travis-ci.org/cbpeckles/PXMessenger) 12 | 13 | #### Dependencies: 14 | 15 | qt5 >= qt5.5.0 16 | 17 | qtmultimedia 18 | 19 | gcc > 4.9.0 or clang > 3.5.0 20 | 21 | libevent >= 2.0.22 22 | 23 | 24 | #### BUILD INSTRUCTIONS 25 | 26 | git clone 27 | 28 | cd ./PXMessenger 29 | 30 | qmake 31 | 32 | make 33 | 34 | sudo make install (optional; installs a desktop entry as well) 35 | 36 | ./build-*/PXMessenger 37 | 38 | If compiling on Windows, the .pro file will have to be edited to point to your 39 | installation of libevent. Specifically the lines 40 | 41 | ``` 42 | win32 { 43 | LIBS += -L$$PWD/../libevent/build/lib -levent -levent_core 44 | INCLUDEPATH += $$PWD/../libevent/include \ 45 | $$PWD/../libevent/build/include 46 | } 47 | ``` 48 | 49 | #### USAGE 50 | 51 | PXMessenger is a cross platform instant messaging client that does not need a 52 | central server. It will automatically discover other users of the client 53 | through the use of a multicast group. All running clients have a UUID 54 | associated with them for verification purposes and history. The UUID's are 55 | saved in an .ini file in the users home directory (See QSettings in the Qt5 docs 56 | for more details). This program can be run multiple times on the same computer 57 | and login. The Global send item will send the message to all known peers. It 58 | is essentially a global chat room. 59 | 60 | ##### Settings 61 | 62 | The multicast group that PXMessenger uses is 239.192.13.13. This can be changed 63 | in the settings window. 64 | 65 | Adjustments can be made to the ports that are used for PXMessenger if firewall 66 | rules only allow specific ones. By default, PXMessenger allows the operating 67 | system to choose the TCP port. The UDP port defaults to 53723. The UDP port 68 | must be the same for all connected computers however the TCP port can be 69 | whichever you prefer for each. 70 | 71 | The "Bloom" setting should not be needed under normal circumstances and only 72 | resends the discovery packed to the multicast group. This is useful if you 73 | believe that an exisiting group of computers have missed all discovery packets. 74 | (very rare) 75 | 76 | PXMessenger will minimize to a tray if the system supports one and will alert 77 | itself in the event of receiving a message. 78 | 79 | ##### Notes 80 | 81 | Bugs are possible, please report them here under the issues tab and they will be 82 | responded to. 83 | 84 | Windows executable and setup included in the releases section 85 | 86 | While this should theoretically work under Mac OSX, it has never been 87 | tested as I do not have access to a Mac. 88 | 89 | Artwork created by Scott Bolar. 90 | 91 | QSimpleUpdater credit to https://github.com/alex-spataru 92 | -------------------------------------------------------------------------------- /include/pxmpeers.h: -------------------------------------------------------------------------------- 1 | /** @file pxmpeers.h 2 | * @brief public header for pxmpeers.cpp 3 | * 4 | * Defines multiple structures and classes used for storing other connected 5 | * computers information such as hostname, connection information, etc. 6 | * 7 | * Bevwrapper is a mutex wrapped bufferevent 8 | * PeerData is everything else we know about them 9 | */ 10 | 11 | #ifndef PXMPEERS_H 12 | #define PXMPEERS_H 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | struct bufferevent; 23 | 24 | Q_DECLARE_METATYPE(struct sockaddr_in) 25 | Q_DECLARE_METATYPE(size_t) 26 | Q_DECLARE_OPAQUE_POINTER(bufferevent*) 27 | Q_DECLARE_METATYPE(bufferevent*) 28 | Q_DECLARE_OPAQUE_POINTER(const bufferevent*) 29 | Q_DECLARE_METATYPE(const bufferevent*) 30 | 31 | class QMutex; 32 | namespace Peers { 33 | /* 34 | const QString selfColor = "#6495ED"; // Cornflower Blue 35 | const QString peerColor = "#FF0000"; // Red 36 | const QVector textColors = { 37 | "#808000", // Olive 38 | "#FFA500", // Orange 39 | "#FF00FF", // Fuschia 40 | "#DC143C", // Crimson 41 | "#FF69B4", // HotPink 42 | "#708090", // SlateGrey 43 | "#008000", // Green 44 | "#00FF00" // Lime 45 | }; 46 | extern int textColorsNext; 47 | */ 48 | class BevWrapper { 49 | bufferevent* bev; 50 | QMutex* locker; 51 | 52 | public: 53 | // Default Constructor 54 | BevWrapper(); 55 | // Constructor with a bufferevent 56 | BevWrapper(bufferevent* buf); 57 | // Destructor 58 | ~BevWrapper(); 59 | // Copy Constructor 60 | BevWrapper(const BevWrapper& b) : bev(b.bev), locker(b.locker) {} 61 | // Move Constructor 62 | BevWrapper(BevWrapper&& b) noexcept; 63 | // Move Assignment 64 | BevWrapper& operator=(BevWrapper&& b) noexcept; 65 | // Eqaul 66 | bool operator==(const BevWrapper& b) { return bev == b.bev; } 67 | // Not Equal 68 | bool operator!=(const BevWrapper& b) { return !(bev == b.bev); } 69 | 70 | void setBev(bufferevent* buf) { bev = buf; } 71 | bufferevent* getBev() const { return bev; } 72 | void lockBev(); 73 | void unlockBev(); 74 | int freeBev(); 75 | }; 76 | 77 | class PeerData { 78 | public: 79 | QUuid uuid; 80 | struct sockaddr_in addrRaw; 81 | QString hostname; 82 | //QString textColor; 83 | QString progVersion; 84 | QSharedPointer bw; 85 | evutil_socket_t socket; 86 | qint64 timeOfTyping; 87 | bool connectTo; 88 | bool isAuthed; 89 | 90 | // Default Constructor 91 | PeerData(); 92 | 93 | // Copy 94 | PeerData(const PeerData& pd); 95 | 96 | // Move 97 | PeerData(PeerData&& pd) noexcept; 98 | 99 | // Destructor 100 | ~PeerData() noexcept {} 101 | 102 | // Move assignment 103 | PeerData& operator=(PeerData&& pd) noexcept; 104 | 105 | // Copy assignment 106 | PeerData& operator=(const PeerData& pd); 107 | 108 | // Return data of this struct as a string padded with the value in 'pad' 109 | QString toInfoString(); 110 | }; 111 | } 112 | Q_DECLARE_METATYPE(QSharedPointer) 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /include/timedvector.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMEDVECTOR_H 2 | #define TIMEDVECTOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | class TimedVector 12 | { 13 | struct TimedStruct { 14 | T* t; 15 | qint64 epoch; 16 | 17 | TimedStruct() : t(new T()), epoch(QDateTime::currentMSecsSinceEpoch()) {} 18 | TimedStruct(T t1) : t(new T(t1)), epoch(QDateTime::currentMSecsSinceEpoch()) {} 19 | ~TimedStruct() noexcept { delete t; } 20 | TimedStruct(TimedStruct&& v) noexcept : t(v.t), epoch(v.epoch) { v.t = nullptr; } 21 | TimedStruct(const TimedStruct& v) : t(new T(*(v.t))), epoch(v.epoch) {} 22 | TimedStruct& operator=(const TimedStruct& v) 23 | { 24 | TimedStruct tmp(v); 25 | *this = std::move(tmp); 26 | return *this; 27 | } 28 | 29 | TimedStruct& operator=(TimedStruct&& v) noexcept 30 | { 31 | delete t; 32 | t = v.t; 33 | v.t = nullptr; 34 | epoch = v.epoch; 35 | return *this; 36 | } 37 | 38 | inline bool operator==(TimedStruct v) { return (*v.t == *t && v.epoch == epoch); } 39 | }; 40 | QVector rawVector; 41 | unsigned int ItemLifeMsecs; 42 | 43 | public: 44 | TimedVector(unsigned int itemLife) : ItemLifeMsecs(itemLife) {} 45 | ~TimedVector() {} 46 | TimedVector(const TimedVector& tv) : ItemLifeMsecs(tv.ItemLifeMsecs) 47 | { 48 | for (TimedStruct itr : tv.rawVector) { 49 | rawVector.append(TimedStruct(itr)); 50 | } 51 | } 52 | 53 | // TimedVector(TimedVector&& tv) noexcept {} 54 | 55 | inline void append(const T t) { rawVector.append(TimedStruct(t)); } 56 | bool contains(T t) 57 | { 58 | if (rawVector.isEmpty()) 59 | return false; 60 | 61 | for (TimedStruct &itr : rawVector) { 62 | if (*(itr.t) == t) { 63 | if (itr.epoch + ItemLifeMsecs > QDateTime::currentMSecsSinceEpoch()) 64 | return true; 65 | else { 66 | rawVector.takeAt(rawVector.indexOf(itr)); 67 | return false; 68 | } 69 | } 70 | } 71 | return false; 72 | } 73 | 74 | int length() 75 | { 76 | pruneItems(); 77 | return rawVector.length(); 78 | } 79 | 80 | int pruneItems() 81 | { 82 | int count = 0; 83 | for (TimedVector::TimedStruct itr : rawVector) { 84 | if (itr.epoch + ItemLifeMsecs < QDateTime::currentMSecsSinceEpoch()) { 85 | int index = rawVector.indexOf(itr); 86 | rawVector.takeAt(index); 87 | count++; 88 | } 89 | } 90 | return count; 91 | } 92 | 93 | int remove(T t) 94 | { 95 | for (TimedVector::TimedStruct itr : rawVector) { 96 | if (*(itr.t) == t) { 97 | rawVector.takeAt(rawVector.indexOf(itr)); 98 | return 0; 99 | } 100 | } 101 | return -1; 102 | } 103 | }; 104 | #endif // TIMEDVECTOR_H 105 | -------------------------------------------------------------------------------- /PXMessenger.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = PXMessenger 3 | VERSION = 1.6.0 4 | QMAKE_TARGET_COMPANY = Bolar Code Solutions 5 | QMAKE_TARGET_PRODUCT = PXMessenger 6 | QMAKE_TARGET_DESCRIPTION = Instant Messenger 7 | QMAKE_TARGET_COPYRIGHT = GPLv3 8 | 9 | target.path = /usr/local/bin 10 | desktop.path = /usr/share/applications 11 | desktop.files += $$PWD/resources/pxmessenger.desktop 12 | icon.path = /usr/share/pixmaps 13 | icon.files += $$PWD/resources/PXMessenger.png 14 | INSTALLS += target desktop icon 15 | 16 | QT = core gui widgets multimedia 17 | CONFIG += DEBUG \ 18 | RELEASE 19 | win32: CONFIG += windows 20 | 21 | unix: LIBS += -levent -levent_pthreads 22 | 23 | win32 { 24 | LIBS += -L$$PWD/../libevent/build/lib -levent -levent_core 25 | INCLUDEPATH += $$PWD/../libevent/include \ 26 | $$PWD/../libevent/build/include 27 | } 28 | 29 | INCLUDEPATH += $$PWD/include 30 | 31 | win32 { 32 | LIBS += -lws2_32 33 | RC_ICONS = $$PWD/resources/PXM_Icon.ico 34 | } 35 | 36 | win32 { 37 | contains(QMAKE_HOST.arch, x86_64):{ 38 | TARGET =$$TARGET-x86_64 39 | BUILDDIR = build-win64 40 | } 41 | contains(QMAKE_HOST.arch, x86):{ 42 | TARGET =$$TARGET-x86 43 | BUILDDIR = build-win32 44 | } 45 | } 46 | 47 | QMAKE_CXXFLAGS += -Wall \ 48 | -std=c++14 49 | 50 | SOURCES += \ 51 | $$PWD/src/pxmclient.cpp \ 52 | $$PWD/src/pxmpeerworker.cpp \ 53 | $$PWD/src/pxmsync.cpp \ 54 | $$PWD/src/pxminireader.cpp \ 55 | $$PWD/src/pxmserver.cpp \ 56 | $$PWD/src/pxmmainwindow.cpp \ 57 | $$PWD/src/pxmessenger.cpp \ 58 | $$PWD/src/netcompression.cpp \ 59 | $$PWD/src/pxmstackwidget.cpp \ 60 | $$PWD/src/pxmconsole.cpp \ 61 | $$PWD/src/pxmpeers.cpp \ 62 | $$PWD/src/pxmagent.cpp 63 | 64 | HEADERS += \ 65 | $$PWD/include/pxmpeerworker.h \ 66 | $$PWD/include/pxmmainwindow.h \ 67 | $$PWD/include/pxmsync.h \ 68 | $$PWD/include/pxminireader.h \ 69 | $$PWD/include/pxmserver.h \ 70 | $$PWD/include/pxmclient.h \ 71 | $$PWD/include/netcompression.h \ 72 | $$PWD/include/timedvector.h \ 73 | $$PWD/include/pxmstackwidget.h \ 74 | $$PWD/include/pxmconsole.h \ 75 | $$PWD/include/pxmconsts.h \ 76 | $$PWD/include/pxmpeers.h \ 77 | $$PWD/include/pxmagent.h 78 | 79 | FORMS += \ 80 | $$PWD/ui/pxmmainwindow.ui \ 81 | $$PWD/ui/pxmaboutdialog.ui \ 82 | $$PWD/ui/pxmsettingsdialog.ui \ 83 | ui/manualconnect.ui 84 | 85 | DISTFILES += \ 86 | resources/updates.json 87 | 88 | RESOURCES += $$PWD/resources/resources.qrc 89 | 90 | win32 { 91 | release:DESTDIR = $$PWD/$$BUILDDIR/ 92 | debug:DESTDIR = $$PWD/$$BUILDDIR/ 93 | OBJECTS_DIR = $$PWD/$$BUILDDIR/obj 94 | MOC_DIR = $$PWD/$$BUILDDIR/moc 95 | RCC_DIR = $$PWD/$$BUILDDIR/rcc 96 | UI_DIR = $$PWD/$$BUILDDIR/ui 97 | QMAKE_CLEAN += $$PWD/object_script.* \ 98 | $$PWD/PXMessenger_resource.rc 99 | } 100 | unix { 101 | release:DESTDIR = $$PWD/ 102 | debug:DESTDIR = $$PWD/ 103 | OBJECTS_DIR = $$PWD/build-unix/obj 104 | MOC_DIR = $$PWD/build-unix/moc 105 | RCC_DIR = $$PWD/build-unix/rcc 106 | UI_DIR = $$PWD/build-unix/ui 107 | } 108 | 109 | INCLUDEPATH += MOC_DIR 110 | 111 | win32 { 112 | include($$PWD/include/QSimpleUpdater/QSimpleUpdater.pri) 113 | } 114 | #QMAKE_CXXFLAGS+="-fsanitize=undefined -fno-omit-frame-pointer" 115 | #QMAKE_CFLAGS+="-fsanitize=address -fno-omit-frame-pointer" 116 | #QMAKE_LFLAGS+="-fsanitize=undefined" 117 | -------------------------------------------------------------------------------- /ui/manualconnect.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ManualConnect 4 | 5 | 6 | 7 | 0 8 | 0 9 | 399 10 | 188 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 23 | 24 | 25 | 26 | 27 | 28 | 29 | Manually connect to address 30 | 31 | 32 | Qt::AlignCenter 33 | 34 | 35 | 36 | 37 | 38 | 39 | Ip Address 40 | 41 | 42 | 43 | 44 | 45 | 46 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 47 | 48 | 49 | 65535 50 | 51 | 52 | 53 | 54 | 55 | 56 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 57 | 58 | 59 | 60 | 61 | 62 | 63 | Listener Port 64 | 65 | 66 | 67 | 68 | 69 | 70 | Qt::Horizontal 71 | 72 | 73 | QSizePolicy::Preferred 74 | 75 | 76 | 77 | 30 78 | 20 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | Qt::Horizontal 87 | 88 | 89 | QSizePolicy::Preferred 90 | 91 | 92 | 93 | 100 94 | 20 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /include/pxmstackwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef PXMSTACKWIDGET_H 2 | #define PXMSTACKWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace PXMMessageViewer { 16 | 17 | const QString infoTyping = " is typing..."; 18 | const QString infoEntered = " has entered text."; 19 | const int typeTimerInterval = 1500; 20 | const QString infoBarStyle = "QLineEdit { background-color : rgb(255,255,160); color : black; }"; 21 | class History { 22 | QUuid identifier; 23 | QString text; 24 | public: 25 | History() : identifier(QUuid()), text(QString()) {} 26 | History(QUuid id, QString text = QString()) : identifier(id), text(text) {} 27 | QString getText() const {return text;} 28 | QUuid getUuid() const {return identifier;} 29 | void append(const QString& str); 30 | }; 31 | 32 | // subclass this to allow the stackwidget to search for it by uuid 33 | class MVBase { 34 | QUuid identifier; 35 | 36 | public: 37 | explicit MVBase(const QUuid& uuid) : identifier(uuid) {} 38 | QUuid getIdentifier() const { return identifier; } 39 | }; 40 | class LabelWidget : public QLabel, public MVBase { 41 | Q_OBJECT 42 | public: 43 | explicit LabelWidget(QWidget* parent, const QUuid& uuid); 44 | }; 45 | 46 | class TextWidget : public QTextBrowser, public MVBase { 47 | Q_OBJECT 48 | bool typing; 49 | void resizeEvent(QResizeEvent *event) 50 | { 51 | rlabel(); 52 | QTextBrowser::resizeEvent(event); 53 | } 54 | 55 | void rlabel(); 56 | 57 | public: 58 | qint64 typingTime; 59 | QLineEdit* info; 60 | bool textEntered; 61 | explicit TextWidget(QWidget* parent, const QUuid& uuid); 62 | //Copy 63 | TextWidget (const TextWidget& other) : 64 | QTextBrowser(other.parentWidget()), 65 | MVBase(other.getIdentifier()), 66 | typing(other.typing), 67 | info(new QLineEdit(other.info)), 68 | textEntered(other.textEntered) 69 | { 70 | 71 | } 72 | //Move 73 | TextWidget (TextWidget&& other) noexcept : 74 | MVBase(other.getIdentifier()), 75 | typing(other.typing), 76 | info(other.info), 77 | textEntered(other.textEntered) 78 | { 79 | other.info = nullptr; 80 | } 81 | ~TextWidget() noexcept 82 | { 83 | } 84 | //Copy Assignment 85 | TextWidget& operator= (const TextWidget& other) 86 | { 87 | TextWidget tmp(other); 88 | *this = std::move(tmp); 89 | return *this; 90 | } 91 | //Move Assignment 92 | TextWidget& operator= (TextWidget&& other) noexcept 93 | { 94 | info->deleteLater(); 95 | info = other.info; 96 | other.info = nullptr; 97 | textEntered = other.textEntered; 98 | typing = other.typing; 99 | return *this; 100 | } 101 | 102 | void showTyping(QString hostname); 103 | void showEntered(QString hostname); 104 | void clearInfoLine(); 105 | void timerCallback(); 106 | void invert(); 107 | }; 108 | 109 | class StackedWidget : public QStackedWidget { 110 | Q_OBJECT 111 | QHash history; 112 | int removeLastWidget(); 113 | QTimer *typingTimer; 114 | public: 115 | StackedWidget(QWidget* parent); 116 | int append(QString str, QUuid& uuid); 117 | int switchToUuid(QUuid& uuid); 118 | int newHistory(QUuid &uuid); 119 | TextWidget *getItem(QUuid &uuid); 120 | int showTyping(QUuid &uuid, QString hostname); 121 | int showEntered(QUuid &uuid, QString hostname); 122 | int clearInfoLine(QUuid &uuid); 123 | void invert(QUuid uuid); 124 | private slots: 125 | void timerCallback(); 126 | }; 127 | 128 | } 129 | 130 | #endif // PXMMESSAGEVIEWER_H 131 | -------------------------------------------------------------------------------- /ui/pxmaboutdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PXMAboutDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 250 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 400 22 | 250 23 | 24 | 25 | 26 | 27 | 400 28 | 300 29 | 30 | 31 | 32 | Qt::NoFocus 33 | 34 | 35 | About PXMessenger 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 30 44 | 200 45 | 341 46 | 32 47 | 48 | 49 | 50 | Qt::Horizontal 51 | 52 | 53 | QDialogButtonBox::Close|QDialogButtonBox::Ok 54 | 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 0 63 | 0 64 | 400 65 | 250 66 | 67 | 68 | 69 | 70 | 0 71 | 0 72 | 73 | 74 | 75 | 76 | 400 77 | 250 78 | 79 | 80 | 81 | 82 | 600 83 | 350 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 30 94 | 30 95 | 64 96 | 64 97 | 98 | 99 | 100 | 101 | 102 | 103 | label 104 | buttonBox 105 | label_2 106 | 107 | 108 | 109 | 110 | buttonBox 111 | accepted() 112 | PXMAboutDialog 113 | accept() 114 | 115 | 116 | 248 117 | 254 118 | 119 | 120 | 157 121 | 274 122 | 123 | 124 | 125 | 126 | buttonBox 127 | rejected() 128 | PXMAboutDialog 129 | reject() 130 | 131 | 132 | 316 133 | 260 134 | 135 | 136 | 286 137 | 274 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /include/pxmserver.h: -------------------------------------------------------------------------------- 1 | /*! @file pxmserver.h 2 | * @brief public header for for pxmserver.cpp 3 | * 4 | * Manages the listener socket and all subsequent connections made to this 5 | * device. 6 | */ 7 | 8 | #ifndef PXMSERVER_H 9 | #define PXMSERVER_H 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | struct bufferevent; 22 | struct event_base; 23 | class ServerThreadPrivate; 24 | 25 | namespace PXMServer 26 | { 27 | const timeval READ_TIMEOUT = {1, 0}; /*!< timeout if we receive partial 28 | packets */ 29 | const timeval READ_TIMEOUT_RESET = {3600, 0}; /*!< workaround for libevent 30 | bug, fixed in 2.1 */ 31 | const uint8_t PACKET_HEADER_LEN = 2; 32 | const size_t INTERNAL_MSG_LENGTH = 200; 33 | enum INTERNAL_MSG : uint16_t { 34 | ADD_DEFAULT_BEV = 0x1111, 35 | EXIT = 0x2222, 36 | CONNECT_TO_ADDR = 0x3333, 37 | TCP_PORT_CHANGE = 0x8888, // Under Construction 38 | UDP_PORT_CHANGE = 0x9999 // Under Construction 39 | }; 40 | class ServerThread : public QThread 41 | { 42 | Q_OBJECT 43 | QScopedPointer d_ptr; 44 | 45 | public: 46 | // Default 47 | ServerThread(QObject* parent, 48 | QUuid uuid, 49 | in_addr multicast, 50 | unsigned short tcpPort = 0, 51 | unsigned short udpPort = 0); 52 | 53 | // Copy 54 | ServerThread(ServerThread const&) = delete; 55 | // Copy assignment 56 | ServerThread& operator=(ServerThread const&) = delete; 57 | // Move 58 | ServerThread(ServerThread&& st) noexcept = delete; 59 | // Move assignment 60 | ServerThread& operator=(ServerThread&& st) noexcept = delete; 61 | // Destructor 62 | ~ServerThread(); 63 | 64 | void run() Q_DECL_OVERRIDE; 65 | signals: 66 | /*! 67 | * \brief packetHandler 68 | * Send a received packet to a handler to be dealt with 69 | */ 70 | void packetHandler(const QSharedPointer, const size_t, const PXMConsts::MESSAGE_TYPE, const QUuid, const bufferevent*); 71 | /*! 72 | * \brief newTCPConnection 73 | * New connection has been received via accept(). No action has been taken 74 | * with it yet. 75 | */ 76 | void newTCPConnection(bufferevent*); 77 | /*! 78 | * \brief authHandler 79 | * Authentication packet has been received and needs to be dealt with. 80 | */ 81 | void authHandler(QString, unsigned short, QString, 82 | evutil_socket_t, QUuid, bufferevent*); 83 | /*! 84 | * \brief peerQuit 85 | * A connection has been terminated 86 | */ 87 | void peerQuit(evutil_socket_t, bufferevent*); 88 | /*! 89 | * \brief attemptConnection 90 | * Recieved a udp "name" packet, we probably want to connect to the address 91 | * that came with it. Pass this info along to see if we are already 92 | * connected to it. 93 | */ 94 | void attemptConnection(struct sockaddr_in, QUuid); 95 | void sendName(bufferevent*, QString, QString); 96 | void setPeerHostname(QString, QUuid); 97 | void sendUDP(const char*, unsigned short); 98 | void setListenerPorts(unsigned short, unsigned short); 99 | void libeventBackend(QString, QString); 100 | void setInternalBufferevent(bufferevent*); 101 | void setSelfCommsBufferevent(bufferevent*); 102 | void multicastIsFunctional(); 103 | void serverSetupFailure(QString); 104 | void resultOfConnectionAttempt(evutil_socket_t, bool, bufferevent*, QUuid); 105 | }; 106 | } 107 | 108 | #endif // MESS_SERV_H 109 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/Updater.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #ifndef _QSIMPLEUPDATER_UPDATER_H 31 | #define _QSIMPLEUPDATER_UPDATER_H 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | class Downloader; 41 | 42 | /** 43 | * \brief Downloads and interprests the update definition file 44 | */ 45 | class QSU_DECL Updater : public QObject 46 | { 47 | Q_OBJECT 48 | 49 | signals: 50 | void checkingFinished (const QString& url); 51 | void downloadFinished (const QString& url, const QString& filepath); 52 | void appcastDownloaded (const QString& url, const QByteArray& data); 53 | void installerOpened(); 54 | void updateDeclined(); 55 | 56 | public: 57 | Updater(); 58 | ~Updater(); 59 | 60 | QString url() const; 61 | QString openUrl() const; 62 | QString changelog() const; 63 | QString moduleName() const; 64 | QString downloadUrl() const; 65 | QString platformKey() const; 66 | QString moduleVersion() const; 67 | QString latestVersion() const; 68 | 69 | bool customAppcast() const; 70 | bool notifyOnUpdate() const; 71 | bool notifyOnFinish() const; 72 | bool updateAvailable() const; 73 | bool downloaderEnabled() const; 74 | bool useCustomInstallProcedures() const; 75 | 76 | public slots: 77 | void checkForUpdates(); 78 | void setUrl (const QString& url); 79 | void setModuleName (const QString& name); 80 | void setNotifyOnUpdate (const bool notify); 81 | void setNotifyOnFinish (const bool notify); 82 | void setModuleVersion (const QString& version); 83 | void setDownloaderEnabled (const bool enabled); 84 | void setPlatformKey (const QString& platformKey); 85 | void setUseCustomAppcast (const bool customAppcast); 86 | void setUseCustomInstallProcedures (const bool custom); 87 | 88 | private slots: 89 | void onReply (QNetworkReply* reply); 90 | void setUpdateAvailable (const bool available); 91 | 92 | private: 93 | bool compare (const QString& x, const QString& y); 94 | 95 | private: 96 | QString m_url; 97 | 98 | bool m_customAppcast; 99 | bool m_notifyOnUpdate; 100 | bool m_notifyOnFinish; 101 | bool m_updateAvailable; 102 | bool m_downloaderEnabled; 103 | 104 | QString m_openUrl; 105 | QString m_platform; 106 | QString m_changelog; 107 | QString m_moduleName; 108 | QString m_downloadUrl; 109 | QString m_moduleVersion; 110 | QString m_latestVersion; 111 | 112 | Downloader* m_downloader; 113 | QNetworkAccessManager* m_manager; 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/pxmessenger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pxmagent.h" 9 | #include "pxmconsole.h" 10 | 11 | #ifdef _WIN32 12 | static QLatin1Char seperator = QLatin1Char('\\'); 13 | #define WIN32_LEAN_AND_MEAN 14 | #else 15 | QLatin1Char seperator = QLatin1Char('/'); 16 | #endif 17 | 18 | 19 | #ifdef QT_DEBUG 20 | void debugMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) 21 | #else 22 | void debugMessageOutput(QtMsgType type, const QMessageLogContext&, const QString& msg) 23 | #endif 24 | { 25 | using namespace PXMConsole; 26 | Logger* logger = Logger::getInstance(); 27 | 28 | switch (logger->getVerbosityLevel()) { 29 | case 0: 30 | if (type == QtDebugMsg || type == QtWarningMsg) 31 | return; 32 | break; 33 | case 1: 34 | if (type == QtDebugMsg) 35 | return; 36 | break; 37 | default: 38 | break; 39 | } 40 | 41 | QByteArray localMsg = QByteArray(); 42 | localMsg.append(QDateTime::currentDateTime().time().toString(QStringLiteral("[hh:mm:ss] "))); 43 | 44 | QColor msgColor; 45 | switch (type) { 46 | case QtDebugMsg: 47 | localMsg.append(QString("%1").arg(QStringLiteral("DEBUG:"), -7, QChar(' '))); 48 | msgColor = Qt::gray; 49 | break; 50 | case QtWarningMsg: 51 | localMsg.append(QString("%1").arg(QStringLiteral("WARN:"), -7, QChar(' '))); 52 | msgColor = Qt::darkYellow; 53 | break; 54 | case QtCriticalMsg: 55 | localMsg.append(QString("%1").arg(QStringLiteral("CRIT:"), -7, QChar(' '))); 56 | msgColor = Qt::red; 57 | break; 58 | case QtFatalMsg: 59 | localMsg.append(QString("%1").arg(QStringLiteral("FATAL:"), -7, QChar(' '))); 60 | abort(); 61 | case QtInfoMsg: 62 | localMsg.append(QString("%1").arg(QStringLiteral("INFO:"), -7, QChar(' '))); 63 | msgColor = QGuiApplication::palette().foreground().color(); 64 | break; 65 | } 66 | 67 | QString filename = QString(); 68 | #ifdef QT_DEBUG 69 | filename = QString::fromUtf8(context.file); 70 | filename = filename.right(filename.length() - filename.lastIndexOf(seperator) - 1); 71 | filename = QString("%1").arg(filename.append(':' + QString::number(context.line)), -(static_cast(PXMConsts::DEBUG_PADDING)), 72 | QChar(' ')); 73 | #endif /* end QT_DEBUG */ 74 | localMsg.append(filename.toUtf8() % msg.toLatin1()); 75 | 76 | localMsg.append(QChar('\n')); 77 | const char* cmsg = localMsg.constData(); 78 | fprintf(stderr, "%s", cmsg); 79 | if (Window::textEdit) { 80 | qApp->postEvent(logger, new AppendTextEvent(localMsg, msgColor), Qt::LowEventPriority); 81 | } 82 | if (logger->getLogStatus() && logger->logFile->isOpen()) { 83 | logger->logFile->write(cmsg, strlen(cmsg)); 84 | logger->logFile->flush(); 85 | } 86 | } 87 | 88 | int main(int argc, char** argv) 89 | { 90 | qInstallMessageHandler(debugMessageOutput); 91 | 92 | QApplication app(argc, argv); 93 | QObject::connect(&app, &QApplication::lastWindowClosed, &app, &QApplication::quit); 94 | 95 | app.setApplicationName("PXMessenger"); 96 | app.setOrganizationName("PXMessenger"); 97 | app.setOrganizationDomain("PXMessenger"); 98 | app.setApplicationVersion("1.6.0"); 99 | 100 | //QFontDatabase::addApplicationFont(":/resources/EmojiOneColor-SVGinOT.ttf"); 101 | int result; 102 | { 103 | PXMAgent overlord; 104 | 105 | QMetaObject::invokeMethod(&overlord, "init", Qt::QueuedConnection); 106 | QObject::connect(&overlord, &PXMAgent::alreadyRunning, &app, &QApplication::quit); 107 | 108 | result = app.exec(); 109 | 110 | qInfo().noquote() << QStringLiteral("Exiting PXMessenger"); 111 | } 112 | qInfo().noquote() << QStringLiteral("Successful Shutdown with code:") << result; 113 | 114 | return result; 115 | } 116 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/README.md: -------------------------------------------------------------------------------- 1 | # QSimpleUpdater 2 | 3 | [![Build Status](https://img.shields.io/travis/alex-spataru/QSimpleUpdater.svg?style=flat-square)](https://travis-ci.org/alex-spataru/QSimpleUpdater) 4 | [![Donate button](https://img.shields.io/badge/bitcoin-donate-brightgreen.svg?style=flat-square)](https://blockchain.info/address/1K85yLxjuqUmhkjP839R7C23XFhSxrefMx "Donate once-off to this project using BitCoin") 5 | [![Github downloads](https://img.shields.io/github/downloads/alex-spataru/qsimpleupdater/total.svg?style=flat-square)](https://github.com/alex-spataru/qsimpleupdater/releases/latest) 6 | 7 | QSimpleUpdater is an implementation of an auto-updating system to be used with Qt projects. It allows you to easily check for updates, download them and install them. Additionally, the QSimpleUpdater allows you to check for updates for different "modules" of your application. Check the [WTFs Section](#wtfs-section) for more information. 8 | 9 | Online documentation can be found [here](http://frc-utilities.github.io/documentation/qsimpleupdater/). 10 | 11 | [![Downloading](etc/screenshots/downloading.png)](etc/screenshots/) 12 | 13 | ## Integrating QSimpleUpdater with your projects 14 | 1. Copy the QSimpleUpdater folder in your "3rd-party" folder. 15 | 2. Include the QSimpleUpdater project include (*pri*) file using the include() function. 16 | 3. That's all! Check the [tutorial project](/tutorial) as a reference for your project. 17 | 18 | ## WTFs Section 19 | 20 | ### 1. How does the QSimpleUpdater check for updates? 21 | 22 | The QSimpleUpdater downloads an update definition file stored in JSON format. This file specifies the latest version, the download links and changelogs for each platform (you can also register your own platform easily if needed). 23 | 24 | After downloading this file, the library analyzes the local version and the remote version. If the remote version is greater than the local version, then the library infers that there is an update available and notifies the user. 25 | 26 | An example update definition file can be found [here](https://github.com/alex-spataru/QSimpleUpdater/blob/master/tutorial/definitions/updates.json). 27 | 28 | ### 2. Can I customize the update notifications shown to the user? 29 | 30 | Yes! You can "toggle" which notifications to show using the library's functions or re-implement by yourself the notifications by "reacting" to the signals emitted by the QSimpleUpdater. 31 | 32 | ```c++ 33 | QString url = "https://MyBadassApplication.com/updates.json"; 34 | 35 | QSimpleUpdater::getInstance()->setNotifyOnUpdate (url, true); 36 | QSimpleUpdater::getInstance()->setNotifyOnFinish (url, false); 37 | 38 | QSimpleUpdater::getInstance()->checkForUpdates (url); 39 | ``` 40 | 41 | ### 3. Is the application able to download the updates directly? 42 | 43 | Yes. If there is an update available, the library will prompt the user if he/she wants to download the update. You can enable or disable the integrated downloader with the following code: 44 | 45 | ```c++ 46 | QString url = "https://MyBadassApplication.com/updates.json"; 47 | QSimpleUpdater::getInstance()->setDownloaderEnabled (url, true); 48 | ``` 49 | 50 | ### 4. Why do I need to specify an URL for each function of the library? 51 | 52 | The QSimpleUpdater allows you to use different updater instances, which can be accessed with the URL of the update definitions. 53 | While it is not obligatory to use multiple updater instances, this can be useful for applications that make use of plugins or different modules. 54 | 55 | Say that you are developing a game, in this case, you could use the following code: 56 | 57 | ```c++ 58 | // Update the game textures 59 | QString textures_url = "https://MyBadassGame.com/textures.json" 60 | QSimpleUpdater::getInstance()->setModuleName (textures_url, "textures"); 61 | QSimpleUpdater::getInstance()->setModuleVersion (textures_url, "0.4"); 62 | QSimpleUpdater::getInstance()->checkForUpdates (textures_url); 63 | 64 | // Update the game sounds 65 | QString sounds_url = "https://MyBadassGame.com/sounds.json" 66 | QSimpleUpdater::getInstance()->setModuleName (sounds_url, "sounds"); 67 | QSimpleUpdater::getInstance()->setModuleVersion (sounds_url, "0.6"); 68 | QSimpleUpdater::getInstance()->checkForUpdates (sounds_url); 69 | 70 | // Update the client (name & versions are already stored in qApp) 71 | QString client_url = "https://MyBadassGame.com/client.json" 72 | QSimpleUpdater::getInstance()->checkForUpdates (client_url); 73 | ``` 74 | 75 | ## License 76 | QSimpleUpdater is free and open-source software, it is released under the [DBAD](COPYING.md) license. 77 | -------------------------------------------------------------------------------- /src/pxmpeers.cpp: -------------------------------------------------------------------------------- 1 | #include "pxmpeers.h" 2 | 3 | #ifdef _WIN32 4 | #include 5 | #include 6 | Q_DECLARE_METATYPE(intptr_t) 7 | #else 8 | #include 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | using namespace Peers; 18 | 19 | // int Peers::textColorsNext = 0; 20 | 21 | PeerData::PeerData() 22 | : uuid(QUuid()), 23 | addrRaw(sockaddr_in()), 24 | hostname(QString()), 25 | progVersion(QString()), 26 | bw(QSharedPointer(new BevWrapper)), 27 | socket(-1), 28 | timeOfTyping(QDateTime::currentMSecsSinceEpoch()), 29 | connectTo(false), 30 | isAuthed(false) 31 | { 32 | // textColor = textColors.at(textColorsNext % textColors.length()); 33 | // textColorsNext++; 34 | } 35 | 36 | PeerData::PeerData(const PeerData& pd) 37 | : uuid(pd.uuid), 38 | addrRaw(pd.addrRaw), 39 | hostname(pd.hostname), 40 | // textColor(pd.textColor), 41 | progVersion(pd.progVersion), 42 | bw(pd.bw), 43 | socket(pd.socket), 44 | timeOfTyping(pd.timeOfTyping), 45 | connectTo(pd.connectTo), 46 | isAuthed(pd.isAuthed) 47 | { 48 | } 49 | 50 | PeerData::PeerData(PeerData&& pd) noexcept 51 | : uuid(pd.uuid), 52 | addrRaw(pd.addrRaw), 53 | hostname(pd.hostname), 54 | // textColor(pd.textColor), 55 | progVersion(pd.progVersion), 56 | bw(pd.bw), 57 | socket(pd.socket), 58 | timeOfTyping(pd.timeOfTyping), 59 | connectTo(pd.connectTo), 60 | isAuthed(pd.isAuthed) 61 | { 62 | pd.bw.clear(); 63 | } 64 | 65 | PeerData& PeerData::operator=(PeerData&& p) noexcept 66 | { 67 | if (this != &p) { 68 | bw = p.bw; 69 | p.bw.clear(); 70 | uuid = p.uuid; 71 | addrRaw = p.addrRaw; 72 | hostname = p.hostname; 73 | // textColor = p.textColor; 74 | progVersion = p.progVersion; 75 | socket = p.socket; 76 | timeOfTyping = p.timeOfTyping; 77 | connectTo = p.connectTo; 78 | isAuthed = p.isAuthed; 79 | } 80 | return *this; 81 | } 82 | 83 | PeerData& PeerData::operator=(const PeerData& p) 84 | { 85 | Peers::PeerData temp(p); 86 | *this = std::move(temp); 87 | return *this; 88 | } 89 | 90 | QString PeerData::toInfoString() 91 | { 92 | return QString( 93 | QStringLiteral("Hostname: ") % hostname % QStringLiteral("\nUUID: ") % uuid.toString() % 94 | QStringLiteral("\nProgram Version: ") % progVersion % QStringLiteral("\nIP Address: ") % 95 | QString::fromLocal8Bit(inet_ntoa(addrRaw.sin_addr)) % QStringLiteral(":") % 96 | QString::number(ntohs(addrRaw.sin_port)) % QStringLiteral("\nIsAuthenticated: ") % 97 | QString::fromLocal8Bit((isAuthed ? "true" : "false")) % QStringLiteral("\npreventAttemptConnection: ") % 98 | QString::fromLocal8Bit((connectTo ? "true" : "false")) % QStringLiteral("\nSocketDescriptor: ") % 99 | QString::number(socket) % QStringLiteral("\nBufferevent: ") % 100 | (bw->getBev() ? QString::asprintf("%8p", static_cast(bw->getBev())) : QStringLiteral("NULL")) % 101 | QStringLiteral("\n")); 102 | } 103 | 104 | BevWrapper::BevWrapper() : bev(nullptr), locker(new QMutex) 105 | { 106 | } 107 | 108 | BevWrapper::BevWrapper(bufferevent* buf) : bev(buf), locker(new QMutex) 109 | { 110 | } 111 | 112 | BevWrapper::~BevWrapper() 113 | { 114 | if (locker) { 115 | locker->tryLock(500); 116 | if (bev) { 117 | bufferevent_free(bev); 118 | bev = nullptr; 119 | } 120 | locker->unlock(); 121 | delete locker; 122 | locker = nullptr; 123 | } else if (bev) { 124 | bufferevent_free(bev); 125 | bev = nullptr; 126 | } 127 | } 128 | 129 | BevWrapper::BevWrapper(BevWrapper&& b) noexcept : bev(b.bev), locker(b.locker) 130 | { 131 | b.bev = nullptr; 132 | b.locker = nullptr; 133 | } 134 | 135 | BevWrapper& BevWrapper::operator=(BevWrapper&& b) noexcept 136 | { 137 | if (this != &b) { 138 | bev = b.bev; 139 | locker = b.locker; 140 | b.bev = nullptr; 141 | b.locker = nullptr; 142 | } 143 | return *this; 144 | } 145 | 146 | void BevWrapper::lockBev() 147 | { 148 | locker->lock(); 149 | } 150 | 151 | void BevWrapper::unlockBev() 152 | { 153 | locker->unlock(); 154 | } 155 | 156 | int BevWrapper::freeBev() 157 | { 158 | if (bev) { 159 | lockBev(); 160 | bufferevent_free(bev); 161 | bev = nullptr; 162 | unlockBev(); 163 | return 0; 164 | } else { 165 | return -1; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/include/QSimpleUpdater.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #ifndef _QSIMPLEUPDATER_MAIN_H 31 | #define _QSIMPLEUPDATER_MAIN_H 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #if defined (QSU_SHARED) 38 | #define QSU_DECL Q_DECL_EXPORT 39 | #elif defined (QSU_IMPORT) 40 | #define QSU_DECL Q_DECL_IMPORT 41 | #else 42 | #define QSU_DECL 43 | #endif 44 | 45 | class Updater; 46 | 47 | /** 48 | * \brief Manages the updater instances 49 | * 50 | * The \c QSimpleUpdater class manages the updater system and allows for 51 | * parallel application modules to check for updates and download them. 52 | * 53 | * The behavior of each updater can be regulated by specifying the update 54 | * definitions URL (from where we download the individual update definitions) 55 | * and defining the desired options by calling the individual "setter" 56 | * functions (e.g. \c setNotifyOnUpdate()). 57 | * 58 | * The \c QSimpleUpdater also implements an integrated downloader. 59 | * If you need to use a custom install procedure/code, just create a function 60 | * that is called when the \c downloadFinished() signal is emitted to 61 | * implement your own install procedures. 62 | * 63 | * By default, the downloader will try to open the file as if you opened it 64 | * from a file manager or a web browser (with the "file:*" url). 65 | */ 66 | class QSU_DECL QSimpleUpdater : public QObject 67 | { 68 | Q_OBJECT 69 | 70 | signals: 71 | void checkingFinished (const QString& url); 72 | void appcastDownloaded (const QString& url, const QByteArray& data); 73 | void downloadFinished (const QString& url, const QString& filepath); 74 | void installerOpened(); 75 | void updateDeclined(); 76 | 77 | public: 78 | static QSimpleUpdater* getInstance(); 79 | 80 | bool usesCustomAppcast (const QString& url) const; 81 | bool getNotifyOnUpdate (const QString& url) const; 82 | bool getNotifyOnFinish (const QString& url) const; 83 | bool getUpdateAvailable (const QString& url) const; 84 | bool getDownloaderEnabled (const QString& url) const; 85 | bool usesCustomInstallProcedures (const QString& url) const; 86 | 87 | QString getOpenUrl (const QString& url) const; 88 | QString getChangelog (const QString& url) const; 89 | QString getModuleName (const QString& url) const; 90 | QString getDownloadUrl (const QString& url) const; 91 | QString getPlatformKey (const QString& url) const; 92 | QString getLatestVersion (const QString& url) const; 93 | QString getModuleVersion (const QString& url) const; 94 | 95 | public slots: 96 | void checkForUpdates (const QString& url); 97 | void setModuleName (const QString& url, const QString& name); 98 | void setNotifyOnUpdate (const QString& url, const bool notify); 99 | void setNotifyOnFinish (const QString& url, const bool notify); 100 | void setPlatformKey (const QString& url, const QString& platform); 101 | void setModuleVersion (const QString& url, const QString& version); 102 | void setDownloaderEnabled (const QString& url, const bool enabled); 103 | void setUseCustomAppcast (const QString& url, const bool customAppcast); 104 | void setUseCustomInstallProcedures (const QString& url, const bool custom); 105 | 106 | protected: 107 | ~QSimpleUpdater(); 108 | 109 | private: 110 | Updater* getUpdater (const QString& url) const; 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/pxmconsole.cpp: -------------------------------------------------------------------------------- 1 | #include "pxmconsole.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace PXMConsole; 13 | struct PXMConsole::WindowPrivate { 14 | QWidget* centralwidget; 15 | QGridLayout* gridLayout; 16 | QGridLayout* gridLayout_2; 17 | QScrollBar* sb; 18 | QLabel* verbosity; 19 | bool atMaximum = false; 20 | }; 21 | 22 | QTextEdit* Window::textEdit = 0; 23 | int AppendTextEvent::type = QEvent::registerEventType(); 24 | Logger* Logger::loggerInstance = nullptr; 25 | 26 | Window::Window(QWidget* parent) : QMainWindow(parent), d_ptr(new PXMConsole::WindowPrivate()) 27 | { 28 | this->setObjectName("Debug Console"); 29 | this->setWindowTitle("Debug Console"); 30 | d_ptr->centralwidget = new QWidget(this); 31 | d_ptr->centralwidget->setObjectName(QStringLiteral("centralwidget")); 32 | d_ptr->gridLayout_2 = new QGridLayout(d_ptr->centralwidget); 33 | d_ptr->gridLayout_2->setObjectName(QStringLiteral("gridLayout_2")); 34 | d_ptr->gridLayout = new QGridLayout(); 35 | d_ptr->gridLayout->setObjectName(QStringLiteral("gridLayout")); 36 | textEdit = new QTextEdit(d_ptr->centralwidget); 37 | textEdit->setObjectName(QStringLiteral("plainTextEdit")); 38 | textEdit->setReadOnly(true); 39 | textEdit->document()->setMaximumBlockCount(HISTORY_LIMIT); 40 | QFont font("Monospaced"); 41 | font.setStyleHint(QFont::TypeWriter); 42 | textEdit->setFont(font); 43 | Logger::getInstance()->setTextEdit(textEdit); 44 | pushButton = new QPushButton(d_ptr->centralwidget); 45 | pushButton->setText("Print Info"); 46 | pushButton->setMaximumSize(QSize(250, 16777215)); 47 | pushButton1 = new QPushButton(d_ptr->centralwidget); 48 | pushButton1->setText("Clear"); 49 | pushButton1->setMaximumSize(QSize(250, 16777215)); 50 | 51 | d_ptr->verbosity = new QLabel(d_ptr->centralwidget); 52 | d_ptr->verbosity->setText("Debug Verbosity: " % QString::number(Logger::getInstance()->getVerbosityLevel())); 53 | d_ptr->gridLayout->addWidget(d_ptr->verbosity, 1, 3, 1, 1); 54 | 55 | d_ptr->gridLayout->addWidget(textEdit, 0, 0, 1, 4); 56 | d_ptr->gridLayout->addWidget(pushButton, 1, 0, 1, 1); 57 | d_ptr->gridLayout->addWidget(pushButton1, 1, 1, 1, 1); 58 | 59 | d_ptr->gridLayout_2->addLayout(d_ptr->gridLayout, 0, 0, 1, 1); 60 | 61 | d_ptr->sb = Window::textEdit->verticalScrollBar(); 62 | d_ptr->sb->setTracking(true); 63 | 64 | this->setCentralWidget(d_ptr->centralwidget); 65 | this->resize(1000, 300); 66 | d_ptr->sb->setValue(d_ptr->sb->maximum()); 67 | d_ptr->atMaximum = true; 68 | QObject::connect(d_ptr->sb, &QScrollBar::valueChanged, this, &Window::adjustScrollBar); 69 | QObject::connect(d_ptr->sb, &QScrollBar::rangeChanged, this, &Window::rangeChanged); 70 | QObject::connect(pushButton1, &QPushButton::clicked, textEdit, &QTextEdit::clear); 71 | 72 | if (Logger::getInstance()->getLogStatus()) { 73 | Logger::getInstance()->logFile->remove(); 74 | Logger::getInstance()->logFile->open(QIODevice::ReadWrite); 75 | } 76 | } 77 | 78 | Window::~Window() 79 | { 80 | } 81 | void Window::adjustScrollBar(int i) 82 | { 83 | if (i == d_ptr->sb->maximum()) { 84 | d_ptr->atMaximum = true; 85 | } else { 86 | d_ptr->atMaximum = false; 87 | } 88 | } 89 | void Window::rangeChanged(int, int i2) 90 | { 91 | if (d_ptr->atMaximum) { 92 | d_ptr->sb->setValue(i2); 93 | } 94 | } 95 | void Window::verbosityChanged() 96 | { 97 | d_ptr->verbosity->setText("Debug Verbosity: " % QString::number(Logger::getInstance()->getVerbosityLevel())); 98 | } 99 | 100 | void Logger::setLogStatus(bool stat) 101 | { 102 | if (logActive != stat && stat == true && logFile) { 103 | logFile->remove(); 104 | if (logFile->open(QIODevice::ReadWrite)) { 105 | logActive = true; 106 | } else { 107 | logActive = false; 108 | } 109 | } else if (logActive != stat && stat == false && logFile) { 110 | logActive = false; 111 | logFile->flush(); 112 | logFile->close(); 113 | } 114 | } 115 | 116 | void Logger::customEvent(QEvent* event) 117 | { 118 | AppendTextEvent* text = dynamic_cast(event); 119 | if (text) { 120 | logTextEdit->setTextColor(text->getColor()); 121 | /* 122 | QTextCursor curs = logTextEdit->textCursor(); 123 | QTextCursor insert = curs; 124 | insert.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); 125 | logTextEdit->setTextCursor(insert); 126 | */ 127 | logTextEdit->insertPlainText(text->getText()); 128 | // logTextEdit->setTextCursor(curs); 129 | event->accept(); 130 | } else { 131 | QObject::customEvent(event); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/Downloader.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Downloader 4 | 5 | 6 | 7 | 0 8 | 0 9 | 464 10 | 164 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Updater 21 | 22 | 23 | 24 | 0 25 | 26 | 27 | 12 28 | 29 | 30 | 12 31 | 32 | 33 | 12 34 | 35 | 36 | 12 37 | 38 | 39 | 40 | 41 | 42 | 0 43 | 44 | 45 | 0 46 | 47 | 48 | 0 49 | 50 | 51 | 0 52 | 53 | 54 | 0 55 | 56 | 57 | 58 | 59 | 60 | 96 61 | 96 62 | 63 | 64 | 65 | 66 | 67 | 68 | :/icons/update.png 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 78 | 79 | 80 | 81 | 82 | 75 83 | true 84 | 85 | 86 | 87 | Downloading updates 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 320 96 | 0 97 | 98 | 99 | 100 | 0 101 | 102 | 103 | false 104 | 105 | 106 | 107 | 108 | 109 | 110 | Time remaining: 0 minutes 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 12 125 | 126 | 127 | 12 128 | 129 | 130 | 12 131 | 132 | 133 | 12 134 | 135 | 136 | 137 | 138 | Qt::Horizontal 139 | 140 | 141 | 142 | 40 143 | 20 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | Open 152 | 153 | 154 | 155 | 156 | 157 | 158 | Stop 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/pxminireader.cpp: -------------------------------------------------------------------------------- 1 | #include "pxminireader.h" 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "pxmconsts.h" 16 | 17 | PXMIniReader::PXMIniReader() 18 | : iniFile(new QSettings(QSettings::IniFormat, QSettings::UserScope, "PXMessenger", "PXMessenger", NULL)) 19 | { 20 | } 21 | PXMIniReader::~PXMIniReader() 22 | { 23 | } 24 | bool PXMIniReader::checkAllowMoreThanOne() 25 | { 26 | if (iniFile->contains("config/AllowMoreThanOneInstance")) { 27 | return iniFile->value("config/AllowMoreThanOneInstance", false).toBool(); 28 | } 29 | iniFile->setValue("config/AllowMoreThanOneInstance", false); 30 | return false; 31 | } 32 | void PXMIniReader::setAllowMoreThanOne(bool value) 33 | { 34 | iniFile->setValue("config/AllowMoreThanOneInstance", value); 35 | } 36 | unsigned int PXMIniReader::getUUIDNumber() const 37 | { 38 | unsigned int i = 0; 39 | QString uuidStr = "uuid/"; 40 | while (iniFile->value(uuidStr + QString::number(i), QString()) == "INUSE") { 41 | i++; 42 | } 43 | if (iniFile->value(uuidStr + QString::number(i), QString()) == "") { 44 | iniFile->setValue(uuidStr + QString::number(i), QUuid::createUuid().toString()); 45 | } 46 | return i; 47 | } 48 | QUuid PXMIniReader::getUUID(unsigned int num, bool takeIt) const 49 | { 50 | QUuid uuid = iniFile->value("uuid/" + QString::number(num), QUuid()).toUuid(); 51 | if (uuid.isNull()) { 52 | uuid = QUuid::createUuid(); 53 | iniFile->setValue("uuid/" + QString::number(num), uuid); 54 | } 55 | if (takeIt) { 56 | iniFile->setValue("uuid/" + QString::number(num), "INUSE"); 57 | } 58 | return uuid; 59 | } 60 | int PXMIniReader::resetUUID(unsigned int num, QUuid uuid) 61 | { 62 | iniFile->setValue("uuid/" + QString::number(num), uuid.toString()); 63 | return 1; 64 | } 65 | void PXMIniReader::setPort(QString protocol, int portNumber) 66 | { 67 | iniFile->setValue("net/" + protocol, portNumber); 68 | } 69 | unsigned short PXMIniReader::getPort(QString protocol) 70 | { 71 | unsigned int portNumber = iniFile->value("net/" + protocol, 0).toUInt(); 72 | if (portNumber == 13649) { 73 | iniFile->setValue("net/" + protocol, 0); 74 | portNumber = 0; 75 | } else if (portNumber != 0 && protocol == QLatin1String("TCP")) { 76 | portNumber += getUUIDNumber(); 77 | } else if (portNumber >= 65535) { 78 | qWarning() << "Bad port number in UUID..." << protocol << portNumber; 79 | portNumber = 0; 80 | } 81 | return static_cast(portNumber); 82 | } 83 | void PXMIniReader::setHostname(QString hostname) 84 | { 85 | iniFile->setValue("hostname/hostname", hostname.left(PXMConsts::MAX_HOSTNAME_LENGTH)); 86 | } 87 | QString PXMIniReader::getHostname(QString defaultHostname) 88 | { 89 | QString hostname = iniFile->value("hostname/hostname", QString()).toString(); 90 | if (hostname.isEmpty()) { 91 | iniFile->setValue("hostname/hostname", defaultHostname); 92 | hostname = defaultHostname; 93 | } 94 | 95 | return hostname.left(PXMConsts::MAX_HOSTNAME_LENGTH).simplified(); 96 | } 97 | void PXMIniReader::setWindowSize(QSize windowSize) 98 | { 99 | if (windowSize.isValid()) 100 | iniFile->setValue("WindowSize/QSize", windowSize); 101 | } 102 | QSize PXMIniReader::getWindowSize(QSize defaultSize) const 103 | { 104 | QSize windowSize = iniFile->value("WindowSize/QSize", defaultSize).toSize(); 105 | if (windowSize.isValid()) { 106 | return windowSize; 107 | } else { 108 | windowSize = QSize(700, 500); 109 | iniFile->setValue("WindowSize/QSize", windowSize); 110 | return windowSize; 111 | } 112 | } 113 | void PXMIniReader::setMute(bool mute) 114 | { 115 | iniFile->setValue("config/Mute", mute); 116 | } 117 | bool PXMIniReader::getMute() const 118 | { 119 | return iniFile->value("config/Mute", false).toBool(); 120 | } 121 | void PXMIniReader::setFocus(bool focus) 122 | { 123 | iniFile->setValue("config/PreventFocus", focus); 124 | } 125 | bool PXMIniReader::getFocus() const 126 | { 127 | return iniFile->value("config/PreventFocus", false).toBool(); 128 | } 129 | QString PXMIniReader::getFont() 130 | { 131 | return iniFile->value("config/Font", "").toString(); 132 | } 133 | void PXMIniReader::setFont(QString font) 134 | { 135 | iniFile->setValue("config/Font", font); 136 | } 137 | QString PXMIniReader::getMulticastAddress() const 138 | { 139 | QString ipFull = iniFile->value("net/MulticastAddress", "").toString(); 140 | if (ipFull.isEmpty() || strlen(ipFull.toLatin1().constData()) > INET_ADDRSTRLEN) { 141 | return QString::fromLocal8Bit(PXMConsts::DEFAULT_MULTICAST_ADDRESS); 142 | } 143 | return ipFull; 144 | } 145 | int PXMIniReader::setMulticastAddress(QString ip) 146 | { 147 | iniFile->setValue("net/MulticastAddress", ip); 148 | 149 | return 0; 150 | } 151 | int PXMIniReader::getVerbosity() const 152 | { 153 | return iniFile->value("config/DebugVerbosity", 0).toInt(); 154 | } 155 | 156 | void PXMIniReader::setVerbosity(int level) const 157 | { 158 | iniFile->setValue("config/DebugVerbosity", level); 159 | } 160 | bool PXMIniReader::getLogActive() const 161 | { 162 | return iniFile->value("config/LogActive", false).toBool(); 163 | } 164 | 165 | void PXMIniReader::setLogActive(bool status) const 166 | { 167 | iniFile->setValue("config/LogActive", status); 168 | } 169 | 170 | bool PXMIniReader::getDarkColorScheme() const 171 | { 172 | return iniFile->value("config/DarkColorScheme", false).toBool(); 173 | } 174 | void PXMIniReader::setDarkColorScheme(bool status) const 175 | { 176 | iniFile->setValue("config/DarkColorScheme", status); 177 | } 178 | void PXMIniReader::setUpdates(bool status) const 179 | { 180 | #ifdef _WIN32 181 | iniFile->setValue("config/Autoupdate", status); 182 | #endif 183 | (void)status; 184 | } 185 | bool PXMIniReader::getUpdates() const 186 | { 187 | #ifdef _WIN32 188 | return iniFile->value("config/Autoupdate", true).toBool(); 189 | #endif 190 | return false; 191 | } 192 | -------------------------------------------------------------------------------- /src/pxmclient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #include 10 | #include 11 | #elif __unix__ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #else 17 | #error "include headers for socket implementation" 18 | #endif 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "pxmpeers.h" 26 | #include "netcompression.h" 27 | 28 | static_assert(sizeof(uint8_t) == 1, "uint8_t not defined as 1 byte"); 29 | static_assert(sizeof(uint16_t) == 2, "uint16_t not defined as 2 bytes"); 30 | static_assert(sizeof(uint32_t) == 4, "uint32_t not defined as 4 bytes"); 31 | 32 | struct PXMClientPrivate { 33 | PXMClientPrivate(PXMClient* q) : q_ptr(q) {} 34 | PXMClient* q_ptr; 35 | in_addr multicastAddress; 36 | unsigned char packedLocalUUID[NetCompression::PACKED_UUID_LENGTH]; 37 | size_t localUUIDLen; 38 | 39 | // Functions 40 | void sendMsg(const QSharedPointer bw, 41 | const char* msg, 42 | const size_t msgLen, 43 | const PXMConsts::MESSAGE_TYPE type, 44 | const QUuid uuidReceiver); 45 | }; 46 | PXMClient::PXMClient(QObject* parent, in_addr multicast, QUuid localUUID) 47 | : QObject(parent), d_ptr(new PXMClientPrivate(this)) 48 | { 49 | this->setObjectName("PXMClient"); 50 | 51 | d_ptr->multicastAddress = multicast; 52 | 53 | setLocalUUID(localUUID); 54 | d_ptr->localUUIDLen = sizeof(d_ptr->packedLocalUUID) / sizeof(d_ptr->packedLocalUUID[0]); 55 | } 56 | 57 | PXMClient::~PXMClient() 58 | { 59 | qDebug() << "Shutdown of PXMClient Successful"; 60 | } 61 | 62 | void PXMClient::setLocalUUID(const QUuid uuid) 63 | { 64 | NetCompression::packUUID(&d_ptr->packedLocalUUID[0], uuid); 65 | } 66 | int PXMClient::sendUDP(const char* msg, unsigned short port) 67 | { 68 | size_t len; 69 | struct sockaddr_in broadaddr; 70 | evutil_socket_t socketfd2; 71 | 72 | memset(&broadaddr, 0, sizeof(broadaddr)); 73 | broadaddr.sin_family = AF_INET; 74 | broadaddr.sin_addr = d_ptr->multicastAddress; 75 | broadaddr.sin_port = htons(port); 76 | 77 | if ((socketfd2 = (socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) < 0) { 78 | qCritical() << "socket: " + QString::fromUtf8(strerror(errno)); 79 | return -1; 80 | } 81 | 82 | len = strlen(msg); 83 | 84 | char loopback = 1; 85 | if (setsockopt(socketfd2, IPPROTO_IP, IP_MULTICAST_LOOP, &loopback, sizeof(loopback)) < 0) { 86 | qCritical() << "setsockopt: " + QString::fromUtf8(strerror(errno)); 87 | evutil_closesocket(socketfd2); 88 | return -1; 89 | } 90 | 91 | for (int i = 0; i < 1; i++) { 92 | ssize_t bytesSent = 93 | sendto(socketfd2, msg, len + 1, 0, reinterpret_cast(&broadaddr), sizeof(broadaddr)); 94 | if (bytesSent < 0) { 95 | qCritical() << "sendto: " + QString::fromUtf8(strerror(errno)); 96 | evutil_closesocket(socketfd2); 97 | return -1; 98 | } else if (static_cast(bytesSent) != len + 1) { 99 | qWarning().noquote() << "Partial UDP send on port" << port; 100 | return -1; 101 | } 102 | } 103 | evutil_closesocket(socketfd2); 104 | return 0; 105 | } 106 | 107 | void PXMClient::sendSingleType(QSharedPointer bw, PXMConsts::MESSAGE_TYPE type) 108 | { 109 | const unsigned int packetLen = d_ptr->localUUIDLen + sizeof(type); 110 | unsigned char packet[packetLen]; 111 | 112 | memcpy(&packet[0], d_ptr->packedLocalUUID, d_ptr->localUUIDLen); 113 | uint32_t typeNBO = htonl(type); 114 | memcpy(&packet[d_ptr->localUUIDLen], &typeNBO, sizeof(typeNBO)); 115 | 116 | uint16_t packetLenNBO = htons(static_cast(packetLen)); 117 | bw->lockBev(); 118 | 119 | if ((bw->getBev() != nullptr) && (bufferevent_get_enabled(bw->getBev()) & EV_WRITE)) { 120 | if (bufferevent_write(bw->getBev(), &packetLenNBO, sizeof(packetLenNBO)) == 0) { 121 | if (bufferevent_write(bw->getBev(), packet, packetLen) == 0) { 122 | qDebug() << "Successful Type Send"; 123 | } 124 | } 125 | } 126 | 127 | bw->unlockBev(); 128 | } 129 | 130 | void PXMClientPrivate::sendMsg(const QSharedPointer bw, 131 | const char* msg, 132 | const size_t msgLen, 133 | const PXMConsts::MESSAGE_TYPE type, 134 | const QUuid uuidReceiver) 135 | { 136 | int bytesSent = -1; 137 | size_t packetLen; 138 | uint16_t packetLenNBO; 139 | bool print = false; 140 | 141 | QUuid uuidLocal; 142 | NetCompression::unpackUUID(packedLocalUUID, uuidLocal); 143 | if (msgLen > 65400) { 144 | emit q_ptr->resultOfTCPSend(-1, uuidReceiver, uuidLocal, QString("Message too Long!"), print, bw); 145 | return; 146 | } 147 | packetLen = localUUIDLen + sizeof(type) + msgLen; 148 | 149 | if (type == PXMConsts::MSG_TEXT) 150 | print = true; 151 | 152 | QScopedArrayPointer full_mess(new char[packetLen + 1]); 153 | 154 | packetLenNBO = htons(static_cast(packetLen)); 155 | qDebug() << ntohs(packetLenNBO); 156 | uint32_t typeNBO = htonl(type); 157 | 158 | memcpy(&full_mess[0], packedLocalUUID, localUUIDLen); 159 | memcpy(&full_mess[localUUIDLen], &typeNBO, sizeof(typeNBO)); 160 | memcpy(&full_mess[localUUIDLen + sizeof(typeNBO)], msg, msgLen); 161 | full_mess[packetLen] = 0; 162 | 163 | bw->lockBev(); 164 | 165 | if ((bw->getBev() == nullptr) || !(bufferevent_get_enabled(bw->getBev()) & EV_WRITE)) { 166 | msg = "Peer is Disconnected, message not sent"; 167 | } else { 168 | if (bufferevent_write(bw->getBev(), &packetLenNBO, sizeof(packetLenNBO)) == 0) { 169 | if (bufferevent_write(bw->getBev(), full_mess.data(), packetLen) == 0) { 170 | qDebug() << "Successful Send"; 171 | bytesSent = 0; 172 | } else { 173 | msg = "Message send failure, not sent"; 174 | } 175 | } else { 176 | msg = "Message send failure, not sent"; 177 | } 178 | } 179 | 180 | bw->unlockBev(); 181 | 182 | if (!uuidReceiver.isNull()) { 183 | emit q_ptr->resultOfTCPSend(bytesSent, uuidReceiver, uuidLocal, QString::fromUtf8(msg), print, bw); 184 | } 185 | 186 | return; 187 | } 188 | void PXMClient::sendMsgSlot(QSharedPointer bw, 189 | QByteArray msg, 190 | size_t len, 191 | PXMConsts::MESSAGE_TYPE type, 192 | QUuid theiruuid) 193 | { 194 | QByteArray test = QByteArray::fromRawData(msg.data(), len); 195 | d_ptr->sendMsg(bw, test.constData(), len, type, theiruuid); 196 | } 197 | -------------------------------------------------------------------------------- /include/pxmmainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef PXMWINDOW_H 2 | #define PXMWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pxmconsts.h" 12 | //#include "ui_pxmmainwindow.h" 13 | #include "ui_pxmaboutdialog.h" 14 | #include "ui_pxmsettingsdialog.h" 15 | 16 | namespace PXMConsole { 17 | class Window; 18 | } 19 | namespace Ui { 20 | class PXMWindow; 21 | class PXMAboutDialog; 22 | class PXMSettingsDialog; 23 | class ManualConnect; 24 | } 25 | class QListWidgetItem; 26 | 27 | class PXMWindow : public QMainWindow { 28 | Q_OBJECT 29 | 30 | const QString selfColor = "#6495ED"; // Cornflower Blue 31 | const QString peerColor = "#FF0000"; // Red 32 | const QVector textColors = { 33 | "#808000", // Olive 34 | "#FFA500", // Orange 35 | "#FF00FF", // Fuschia 36 | "#DC143C", // Crimson 37 | "#FF69B4", // HotPink 38 | "#708090", // SlateGrey 39 | "#008000", // Green 40 | "#00FF00" // Lime 41 | }; 42 | unsigned int textColorsNext; 43 | QScopedPointer ui; 44 | QAction* messSystemTrayExitAction; 45 | QMenu* sysTrayMenu; 46 | QSystemTrayIcon* sysTray; 47 | QFrame* fsep; 48 | QString localHostname; 49 | QUuid localUuid; 50 | QUuid globalChatUuid; 51 | QScopedPointer debugWindow; 52 | QTimer* textEnteredTimer; 53 | QTimer* labelTest; 54 | bool ltBool = false; 55 | /*! 56 | * \brief focusWindow 57 | * 58 | * Brings the window to the foreground if allowed by the window manager. 59 | * Plays a sound when doing this. 60 | */ 61 | int focusWindow(); 62 | /*! 63 | * \brief changeListColor 64 | * 65 | * Changes the color of the background for an item in a given row of the 66 | * QListWidget. Changes to either red or default. 67 | * \param row Row of item in QListWidget to change 68 | * \param style Color to change to. 1 for red, 0 for default. 69 | */ 70 | int changeListItemColor(QUuid uuid, int style); 71 | /*! 72 | * \brief createTextBrowser 73 | * 74 | * Initializes the text browser where messages are displayed upon sending 75 | * or receiving. 76 | */ 77 | void setupLabels(); 78 | /*! 79 | * \brief createListWidget 80 | * 81 | * Initializes the QListWidget that holds the connected computers. Two 82 | * items are added, a seperator and a "Global Chat" 83 | */ 84 | void initListWidget(); 85 | /*! 86 | * \brief createSystemTray 87 | * 88 | * Initializes the icon and menu for a system tray if it is supported. 89 | */ 90 | void createSystemTray(); 91 | /*! 92 | * \brief connectGuiSignalsAndSlots 93 | * 94 | * All widget slots are connected to the respective signals here 95 | */ 96 | void connectGuiSignalsAndSlots(); 97 | void setupTooltips(); 98 | void setupMenuBar(); 99 | void setupGui(); 100 | int removeBodyFormatting(QByteArray& str); 101 | void redoFontStyling(); 102 | 103 | int formatMessage(QString &str, QString& hostname, QString color); 104 | public: 105 | PXMWindow(QString hostname, 106 | QSize windowSize, 107 | bool mute, 108 | bool focus, QUuid local, 109 | QUuid globalChat, 110 | QWidget* parent = nullptr); 111 | ~PXMWindow(); 112 | PXMWindow(PXMWindow const&) = delete; 113 | PXMWindow& operator=(PXMWindow const&) = delete; 114 | PXMWindow& operator=(PXMWindow&&) noexcept = delete; 115 | PXMWindow(PXMWindow&&) noexcept = delete; 116 | public slots: 117 | void bloomActionsSlot(); 118 | int printToTextBrowser(QSharedPointer str, QString hostname, QUuid uuid, QUuid sender, bool alert, bool fromServer, bool global); 119 | void setItalicsOnItem(QUuid uuid, bool italics); 120 | void updateListWidget(QUuid uuid, QString hostname); 121 | void typingAlert(QUuid); 122 | void textEnteredAlert(QUuid); 123 | void endOfTextEnteredAlert(QUuid); 124 | void alertFontColor(int index); 125 | 126 | protected: 127 | void closeEvent(QCloseEvent* event) Q_DECL_OVERRIDE; 128 | void changeEvent(QEvent* event) Q_DECL_OVERRIDE; 129 | private slots: 130 | int sendButtonClicked(); 131 | void quitButtonClicked(); 132 | void currentItemChanged(QListWidgetItem* item1, QListWidgetItem *item2); 133 | void textEditChanged(); 134 | void systemTrayAction(QSystemTrayIcon::ActivationReason reason); 135 | void aboutActionSlot(); 136 | void settingsActionsSlot(); 137 | void debugActionSlot(); 138 | void syncActionsSlot(); 139 | void manualConnect(); 140 | void aboutToClose(); 141 | void typingHandler(); 142 | void endOfTypingHandler(); 143 | void textEnteredCallback(); 144 | signals: 145 | void sendMsg(QByteArray, PXMConsts::MESSAGE_TYPE, QUuid); 146 | void sendUDP(const char*); 147 | void syncWithPeers(); 148 | void retryDiscover(); 149 | void addMessageToPeer(QString, QUuid, QUuid, bool, bool); 150 | void printInfoToDebug(); 151 | void manConnect(QString, int); 152 | void typing(QUuid uuid); 153 | void endOfTextEntered(QUuid uuid); 154 | void endOfTyping(QUuid uuid); 155 | void textEntered(QUuid uuid); 156 | void fontColorChange(QColor color, bool system); 157 | }; 158 | 159 | class PXMAboutDialog : public QDialog { 160 | Q_OBJECT 161 | 162 | QScopedPointer ui; 163 | QIcon icon; 164 | 165 | public: 166 | PXMAboutDialog(QWidget* parent = 0, QIcon icon = QIcon()); 167 | ~PXMAboutDialog() {} 168 | }; 169 | 170 | class PXMSettingsDialogPrivate; 171 | class PXMSettingsDialog : public QDialog { 172 | Q_OBJECT 173 | 174 | PXMSettingsDialogPrivate *d_ptr; 175 | QScopedPointer ui; 176 | 177 | public: 178 | PXMSettingsDialog(QWidget* parent = 0); 179 | void readIni(); 180 | ~PXMSettingsDialog(); 181 | private slots: 182 | void resetDefaults(QAbstractButton* button); 183 | void accept(); 184 | void currentFontChanged(QFont font); 185 | void valueChanged(int size); 186 | void logStateChange(int state); 187 | void colorSchemeChange(int index); 188 | signals: 189 | void nameChange(QString); 190 | void verbosityChanged(); 191 | void colorSchemeAlert(); 192 | }; 193 | 194 | class PXMTextEdit : public QTextEdit 195 | { 196 | Q_OBJECT 197 | public: 198 | explicit PXMTextEdit(QWidget* parent); 199 | void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; 200 | signals: 201 | void returnPressed(); 202 | void typing(); 203 | void endOfTyping(); 204 | void endOfTextEntered(); 205 | public slots: 206 | void toggleItalics(bool checked); 207 | void toggleBold(bool checked); 208 | void toggleUnderline(bool checked); 209 | 210 | void fontColorChange(QColor color, bool system); 211 | protected: 212 | void focusOutEvent(QFocusEvent* event) Q_DECL_OVERRIDE; 213 | void focusInEvent(QFocusEvent* event) Q_DECL_OVERRIDE; 214 | }; 215 | 216 | class ManualConnect : public QDialog 217 | { 218 | Q_OBJECT 219 | Ui::ManualConnect* ui; 220 | void accept(); 221 | public: 222 | ManualConnect(QWidget *parent); 223 | ~ManualConnect(); 224 | signals: 225 | void manConnect(QString, int); 226 | }; 227 | 228 | #endif 229 | -------------------------------------------------------------------------------- /src/pxmstackwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "pxmstackwidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace PXMMessageViewer; 12 | 13 | StackedWidget::StackedWidget(QWidget* parent) : QStackedWidget(parent) 14 | { 15 | LabelWidget* lw = new LabelWidget(this, QUuid::createUuid()); 16 | lw->setText("Select a friend on the right to begin chatting!"); 17 | lw->setAlignment(Qt::AlignCenter); 18 | this->addWidget(lw); 19 | typingTimer = new QTimer(); 20 | typingTimer->setInterval(typeTimerInterval); 21 | typingTimer->start(); 22 | QObject::connect(typingTimer, &QTimer::timeout, this, &StackedWidget::timerCallback); 23 | } 24 | 25 | void StackedWidget::timerCallback() 26 | { 27 | qint64 current = QDateTime::currentMSecsSinceEpoch(); 28 | for (int i = 0; i < this->count(); i++) { 29 | TextWidget* tw = dynamic_cast(this->widget(i)); 30 | if (tw) { 31 | if (tw->typingTime + 500 < current) { 32 | // QString temp = tw->info->text(); 33 | tw->timerCallback(); 34 | } else { 35 | continue; 36 | } 37 | } 38 | } 39 | } 40 | 41 | int StackedWidget::newHistory(QUuid& uuid) 42 | { 43 | if (this->count() < 10) { 44 | TextWidget* tw = new TextWidget(this, uuid); 45 | this->addWidget(tw); 46 | } else { 47 | History newHist(uuid); 48 | history.insert(uuid, newHist); 49 | } 50 | return 0; 51 | } 52 | 53 | TextWidget* StackedWidget::getItem(QUuid& uuid) 54 | { 55 | for (int i = 0; i < this->count(); i++) { 56 | TextWidget* mvb = dynamic_cast(this->widget(i)); 57 | if (mvb && mvb->getIdentifier() == uuid) { 58 | return mvb; 59 | } 60 | } 61 | return nullptr; 62 | } 63 | 64 | int StackedWidget::append(QString str, QUuid& uuid) 65 | { 66 | for (int i = 0; i < this->count(); i++) { 67 | MVBase* mvb = dynamic_cast(this->widget(i)); 68 | if (mvb && mvb->getIdentifier() == uuid) { 69 | TextWidget* tw = qobject_cast(this->widget(i)); 70 | if (tw) { 71 | tw->append(str); 72 | return 0; 73 | } else { 74 | return -1; 75 | } 76 | } 77 | } 78 | // not in stackwidget 79 | if (history.value(uuid).getUuid() == uuid) { 80 | history[uuid].append(str); 81 | } else { 82 | return -1; 83 | } 84 | return 0; 85 | } 86 | 87 | int StackedWidget::removeLastWidget() 88 | { 89 | if (this->count() < 10) { 90 | return 0; 91 | } 92 | QWidget* qw = this->widget(this->count() - 1); 93 | TextWidget* old = qobject_cast(qw); 94 | if (old) { 95 | History oldHist(old->getIdentifier(), old->toHtml()); 96 | history.insert(oldHist.getUuid(), oldHist); 97 | } else { 98 | qWarning() << "Removing non textedit widget"; 99 | } 100 | this->removeWidget(this->widget(this->count() - 1)); 101 | 102 | return 0; 103 | } 104 | 105 | int StackedWidget::switchToUuid(QUuid& uuid) 106 | { 107 | for (int i = 0; i < this->count(); i++) { 108 | MVBase* mvb = dynamic_cast(this->widget(i)); 109 | if (mvb->getIdentifier() == uuid) { 110 | QWidget* qw = this->widget(i); 111 | this->removeWidget(qw); 112 | this->insertWidget(0, qw); 113 | this->removeLastWidget(); 114 | this->setCurrentIndex(0); 115 | return 0; 116 | } 117 | } 118 | // isnt in the list 119 | if (history.value(uuid).getUuid() == uuid) { 120 | TextWidget* tw = new TextWidget(this, uuid); 121 | tw->setHtml(history[uuid].getText()); 122 | this->insertWidget(0, tw); 123 | this->setCurrentIndex(0); 124 | this->removeLastWidget(); 125 | history.remove(uuid); 126 | qDebug() << "Made a new textedit" << history.count() << this->count(); 127 | return 0; 128 | } 129 | return -1; 130 | } 131 | 132 | void StackedWidget::invert(QUuid uuid) 133 | { 134 | TextWidget* tw = getItem(uuid); 135 | if (tw) { 136 | tw->invert(); 137 | } 138 | } 139 | 140 | LabelWidget::LabelWidget(QWidget* parent, const QUuid& uuid) : QLabel(parent), MVBase(uuid) 141 | { 142 | this->setBackgroundRole(QPalette::Base); 143 | this->setAutoFillBackground(true); 144 | this->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); 145 | } 146 | 147 | void History::append(const QString& str) 148 | { 149 | text.append(str); 150 | } 151 | 152 | void TextWidget::rlabel() 153 | { 154 | int ax, ay, aw, ah; 155 | this->frameRect().getRect(&ax, &ay, &aw, &ah); 156 | info->setGeometry(this->lineWidth() + this->midLineWidth(), 157 | ah - info->height() - this->lineWidth() - this->midLineWidth(), 158 | aw - 2 * this->lineWidth() - 2 * this->midLineWidth(), info->height()); 159 | QString sheet = this->styleSheet(); 160 | QRegularExpression qre = QRegularExpression("(QTextBrowser { padding-bottom:)([0-9]*)(.*)"); 161 | if (info->isVisible()) { 162 | sheet.replace(qre, "\\1" + QString::number(info->height()) + "\\3"); 163 | 164 | this->setStyleSheet(sheet); 165 | } else { 166 | sheet.replace(qre, "\\10\\3"); 167 | this->setStyleSheet(sheet); 168 | } 169 | } 170 | 171 | void TextWidget::invert() 172 | { 173 | QString sheet = this->styleSheet(); 174 | QPalette pal = this->palette(); 175 | QColor col = pal.base().color(); 176 | int r, g, b; 177 | col.getRgb(&r, &g, &b); 178 | r = 255 - r; 179 | g = 255 - g; 180 | b = 255 - b; 181 | QRegularExpression qre = 182 | QRegularExpression("(QTextBrowser {)(.*)(background-color: rgb\\()([0-9]*),([0-9]*),([0-9]*)(.*)"); 183 | sheet.replace(qre, "\\1\\2\\3" + QString::number(r) + "," + QString::number(g) + "," + QString::number(b) + "\\7"); 184 | this->setStyleSheet(sheet); 185 | sheet = this->styleSheet(); 186 | } 187 | 188 | TextWidget::TextWidget(QWidget* parent, const QUuid& uuid) 189 | : QTextBrowser(parent), MVBase(uuid), typing(false), textEntered(false) 190 | { 191 | this->setOpenExternalLinks(true); 192 | this->setOpenLinks(true); 193 | this->setTextInteractionFlags(Qt::TextInteractionFlag::LinksAccessibleByMouse); 194 | info = new QLineEdit(this); 195 | info->setStyleSheet(infoBarStyle); 196 | info->setVisible(false); 197 | info->setReadOnly(true); 198 | info->setFocusPolicy(Qt::FocusPolicy::NoFocus); 199 | QPalette pal = this->palette(); 200 | QColor col = pal.base().color(); 201 | int r, g, b; 202 | col.getRgb(&r, &g, &b); 203 | if (r == 255 && g == 255 && b == 255) { 204 | pal.setColor(QPalette::Base, pal.alternateBase().color()); 205 | this->setPalette(pal); 206 | col.getRgb(&r, &g, &b); 207 | // r = 230; 208 | // g = 230; 209 | // b = 230; 210 | } 211 | setStyleSheet("QTextBrowser { padding-bottom:0; background-color: rgb(" + QString::number(r) + "," + 212 | QString::number(g) + "," + QString::number(b) + ") }"); 213 | } 214 | 215 | void TextWidget::showTyping(QString hostname) 216 | { 217 | QScrollBar* scroll = this->verticalScrollBar(); 218 | bool scrollMax = false; 219 | if (scroll->sliderPosition() == scroll->maximum()) { 220 | scrollMax = true; 221 | } 222 | info->setText(hostname % infoTyping); 223 | info->setVisible(true); 224 | QString sheet = this->styleSheet(); 225 | QRegularExpression qre = QRegularExpression("(QTextBrowser {)(.*)(padding-bottom:)([0-9]*)(.*)"); 226 | sheet.replace(qre, "\\1\\2\\3" + QString::number(info->height()) + "\\5"); 227 | 228 | this->setStyleSheet(sheet); 229 | if (scrollMax) { 230 | scroll->setSliderPosition(scroll->maximum()); 231 | } 232 | // typingTimer->start(); 233 | typing = true; 234 | typingTime = QDateTime::currentMSecsSinceEpoch(); 235 | } 236 | 237 | void TextWidget::showEntered(QString hostname) 238 | { 239 | this->textEntered = true; 240 | if (typing) { 241 | return; 242 | } 243 | QScrollBar* scroll = this->verticalScrollBar(); 244 | bool scrollMax = false; 245 | if (scroll->sliderPosition() == scroll->maximum()) { 246 | scrollMax = true; 247 | } 248 | info->setText(hostname % infoEntered); 249 | info->setVisible(true); 250 | QString sheet = this->styleSheet(); 251 | QRegularExpression qre = QRegularExpression("(QTextBrowser {)(.*)(padding-bottom:)([0-9]*)(.*)"); 252 | sheet.replace(qre, "\\1\\2\\3" + QString::number(info->height()) + "\\5"); 253 | 254 | this->setStyleSheet(sheet); 255 | if (scrollMax) { 256 | scroll->setSliderPosition(scroll->maximum()); 257 | } 258 | } 259 | 260 | void TextWidget::clearInfoLine() 261 | { 262 | // typingTimer->stop(); 263 | typing = false; 264 | textEntered = false; 265 | info->setVisible(false); 266 | QString sheet = this->styleSheet(); 267 | QRegularExpression qre = QRegularExpression("(QTextBrowser {)(.*)(padding-bottom:)([0-9]*)(.*)"); 268 | sheet.replace(qre, "\\1\\2\\30\\5"); 269 | 270 | this->setStyleSheet(sheet); 271 | } 272 | 273 | void TextWidget::timerCallback() 274 | { 275 | if (textEntered) { 276 | typing = false; 277 | QString temp = info->text(); 278 | if (temp.endsWith(infoTyping)) { 279 | this->showEntered(temp.left(temp.length() - infoTyping.length())); 280 | } 281 | } else { 282 | clearInfoLine(); 283 | } 284 | } 285 | 286 | int PXMMessageViewer::StackedWidget::showTyping(QUuid& uuid, QString hostname) 287 | { 288 | for (int i = 0; i < this->count(); i++) { 289 | TextWidget* tw = dynamic_cast(this->widget(i)); 290 | if (tw && tw->getIdentifier() == uuid) { 291 | tw->showTyping(hostname); 292 | return 0; 293 | } 294 | } 295 | return 1; 296 | } 297 | 298 | int StackedWidget::showEntered(QUuid& uuid, QString hostname) 299 | { 300 | for (int i = 0; i < this->count(); i++) { 301 | TextWidget* tw = dynamic_cast(this->widget(i)); 302 | if (tw && tw->getIdentifier() == uuid) { 303 | tw->showEntered(hostname); 304 | return 0; 305 | } 306 | } 307 | return 1; 308 | } 309 | int StackedWidget::clearInfoLine(QUuid& uuid) 310 | { 311 | for (int i = 0; i < this->count(); i++) { 312 | TextWidget* tw = dynamic_cast(this->widget(i)); 313 | if (tw && tw->getIdentifier() == uuid) { 314 | tw->clearInfoLine(); 315 | return 0; 316 | } 317 | } 318 | return 1; 319 | } 320 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/Downloader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include "Downloader.h" 41 | 42 | static const QString PARTIAL_DOWN(".part"); 43 | static const QDir DOWNLOAD_DIR(QDir::homePath() + "/Downloads/"); 44 | 45 | Downloader::Downloader(QWidget* parent) : QWidget(parent) 46 | { 47 | m_ui = new Ui::Downloader; 48 | m_ui->setupUi(this); 49 | 50 | /* Initialize private members */ 51 | m_manager = new QNetworkAccessManager(); 52 | 53 | /* Initialize internal values */ 54 | m_url = ""; 55 | m_fileName = ""; 56 | m_startTime = 0; 57 | m_useCustomProcedures = false; 58 | 59 | /* Make the window look like a modal dialog */ 60 | setWindowIcon(QIcon()); 61 | setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint); 62 | 63 | /* Configure the appearance and behavior of the buttons */ 64 | m_ui->openButton->setEnabled(false); 65 | m_ui->openButton->setVisible(false); 66 | connect(m_ui->stopButton, SIGNAL(clicked()), this, SLOT(cancelDownload())); 67 | connect(m_ui->openButton, SIGNAL(clicked()), this, SLOT(installUpdate())); 68 | 69 | /* Resize to fit */ 70 | setFixedSize(minimumSizeHint()); 71 | } 72 | 73 | Downloader::~Downloader() 74 | { 75 | delete m_ui; 76 | delete m_reply; 77 | delete m_manager; 78 | } 79 | 80 | /** 81 | * Returns \c true if the updater shall not intervene when the download has 82 | * finished (you can use the \c QSimpleUpdater signals to know when the 83 | * download is completed). 84 | */ 85 | bool Downloader::useCustomInstallProcedures() const 86 | { 87 | return m_useCustomProcedures; 88 | } 89 | 90 | /** 91 | * Changes the URL, which is used to indentify the downloader dialog 92 | * with an \c Updater instance 93 | * 94 | * \note the \a url parameter is not the download URL, it is the URL of 95 | * the AppCast file 96 | */ 97 | void Downloader::setUrlId(const QString& url) 98 | { 99 | m_url = url; 100 | } 101 | 102 | /** 103 | * Begins downloading the file at the given \a url 104 | */ 105 | void Downloader::startDownload(const QUrl& url) 106 | { 107 | /* Reset UI */ 108 | m_ui->progressBar->setValue(0); 109 | m_ui->stopButton->setText(tr("Stop")); 110 | m_ui->downloadLabel->setText(tr("Downloading updates")); 111 | m_ui->timeLabel->setText(tr("Time remaining") + ": " + tr("unknown")); 112 | 113 | /* Start download */ 114 | m_startTime = QDateTime::currentDateTime().toTime_t(); 115 | m_reply = m_manager->get(QNetworkRequest(url)); 116 | 117 | /* Ensure that downloads directory exists */ 118 | if (!DOWNLOAD_DIR.exists()) 119 | DOWNLOAD_DIR.mkpath("."); 120 | 121 | /* Remove old downloads */ 122 | QFile::remove(DOWNLOAD_DIR.filePath(m_fileName)); 123 | QFile::remove(DOWNLOAD_DIR.filePath(m_fileName + PARTIAL_DOWN)); 124 | 125 | /* Update UI when download progress changes or download finishes */ 126 | connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateProgress(qint64, qint64))); 127 | connect(m_reply, SIGNAL(redirected(QUrl)), this, SLOT(startDownload(QUrl))); 128 | 129 | showNormal(); 130 | } 131 | 132 | /** 133 | * Changes the name of the downloaded file 134 | */ 135 | void Downloader::setFileName(const QString& file) 136 | { 137 | m_fileName = file; 138 | 139 | if (m_fileName.isEmpty()) 140 | m_fileName = "QSU_Update.bin"; 141 | } 142 | 143 | /** 144 | * Opens the downloaded file. 145 | * \note If the downloaded file is not found, then the function will alert the 146 | * user about the error. 147 | */ 148 | void Downloader::openDownload() 149 | { 150 | if (!m_fileName.isEmpty()) { 151 | QDesktopServices::openUrl(QUrl::fromLocalFile(DOWNLOAD_DIR.filePath(m_fileName))); 152 | emit installerOpened(); 153 | 154 | } else { 155 | QMessageBox::critical(this, tr("Error"), tr("Cannot find downloaded update!"), QMessageBox::Close); 156 | } 157 | } 158 | 159 | /** 160 | * Instructs the OS to open the downloaded file. 161 | * 162 | * \note If \c useCustomInstallProcedures() returns \c true, the function will 163 | * not instruct the OS to open the downloaded file. You can use the 164 | * signals fired by the \c QSimpleUpdater to install the update with your 165 | * own implementations/code. 166 | */ 167 | void Downloader::installUpdate() 168 | { 169 | if (useCustomInstallProcedures()) 170 | return; 171 | 172 | /* Update labels */ 173 | m_ui->stopButton->setText(tr("Close")); 174 | m_ui->downloadLabel->setText(tr("Download complete!")); 175 | m_ui->timeLabel->setText(tr("The installer will open separately") + "..."); 176 | 177 | /* Ask the user to install the download */ 178 | QMessageBox box; 179 | box.setIcon(QMessageBox::Question); 180 | box.setDefaultButton(QMessageBox::Ok); 181 | box.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); 182 | box.setInformativeText(tr("Click \"OK\" to begin installing the update")); 183 | box.setText("

" + tr("In order to install the update, you may need to " 184 | "quit the application.") + 185 | "

"); 186 | 187 | /* User wants to install the download */ 188 | if (box.exec() == QMessageBox::Ok) { 189 | if (!useCustomInstallProcedures()) 190 | openDownload(); 191 | } 192 | 193 | /* Wait */ 194 | else { 195 | m_ui->openButton->setEnabled(true); 196 | m_ui->openButton->setVisible(true); 197 | m_ui->timeLabel->setText( 198 | tr("Click the \"Open\" button to " 199 | "apply the update")); 200 | } 201 | } 202 | 203 | /** 204 | * Prompts the user if he/she wants to cancel the download and cancels the 205 | * download if the user agrees to do that. 206 | */ 207 | void Downloader::cancelDownload() 208 | { 209 | if (!m_reply->isFinished()) { 210 | QMessageBox box; 211 | box.setWindowTitle(tr("Updater")); 212 | box.setIcon(QMessageBox::Question); 213 | box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); 214 | box.setText(tr("Are you sure you want to cancel the download?")); 215 | 216 | if (box.exec() == QMessageBox::Yes) { 217 | hide(); 218 | m_reply->abort(); 219 | emit downloadCanceled(); 220 | } 221 | } 222 | 223 | else 224 | hide(); 225 | } 226 | 227 | /** 228 | * Writes the downloaded data to the disk 229 | */ 230 | void Downloader::saveFile(qint64 received, qint64 total) 231 | { 232 | /* Check if we need to redirect */ 233 | QUrl url = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); 234 | if (!url.isEmpty()) { 235 | startDownload(url); 236 | return; 237 | } 238 | 239 | /* Save downloaded data to disk */ 240 | QFile file(DOWNLOAD_DIR.filePath(m_fileName + PARTIAL_DOWN)); 241 | if (file.open(QIODevice::WriteOnly | QIODevice::Append)) { 242 | file.write(m_reply->readAll()); 243 | file.close(); 244 | } 245 | 246 | /* Open downloaded update */ 247 | if (received >= total && total > 0) { 248 | /* Rename file */ 249 | QFile::rename(DOWNLOAD_DIR.filePath(m_fileName + PARTIAL_DOWN), DOWNLOAD_DIR.filePath(m_fileName)); 250 | 251 | /* Notify application */ 252 | emit downloadFinished(m_url, DOWNLOAD_DIR.filePath(m_fileName)); 253 | 254 | /* Install the update */ 255 | m_reply->close(); 256 | installUpdate(); 257 | } 258 | } 259 | 260 | /** 261 | * Calculates the appropiate size units (bytes, KB or MB) for the received 262 | * data and the total download size. Then, this function proceeds to update the 263 | * dialog controls/UI. 264 | */ 265 | void Downloader::calculateSizes(qint64 received, qint64 total) 266 | { 267 | QString totalSize; 268 | QString receivedSize; 269 | 270 | if (total < 1024) 271 | totalSize = tr("%1 bytes").arg(total); 272 | 273 | else if (total < 1048576) 274 | totalSize = tr("%1 KB").arg(round(total / 1024)); 275 | 276 | else 277 | totalSize = tr("%1 MB").arg(round(total / 1048576)); 278 | 279 | if (received < 1024) 280 | receivedSize = tr("%1 bytes").arg(received); 281 | 282 | else if (received < 1048576) 283 | receivedSize = tr("%1 KB").arg(received / 1024); 284 | 285 | else 286 | receivedSize = tr("%1 MB").arg(received / 1048576); 287 | 288 | m_ui->downloadLabel->setText(tr("Downloading updates") + " (" + receivedSize + " " + tr("of") + " " + totalSize + 289 | ")"); 290 | } 291 | 292 | /** 293 | * Uses the \a received and \a total parameters to get the download progress 294 | * and update the progressbar value on the dialog. 295 | */ 296 | void Downloader::updateProgress(qint64 received, qint64 total) 297 | { 298 | if (total > 0) { 299 | m_ui->progressBar->setMinimum(0); 300 | m_ui->progressBar->setMaximum(100); 301 | m_ui->progressBar->setValue((received * 100) / total); 302 | 303 | calculateSizes(received, total); 304 | calculateTimeRemaining(received, total); 305 | saveFile(received, total); 306 | } 307 | 308 | else { 309 | m_ui->progressBar->setMinimum(0); 310 | m_ui->progressBar->setMaximum(0); 311 | m_ui->progressBar->setValue(-1); 312 | m_ui->downloadLabel->setText(tr("Downloading Updates") + "..."); 313 | m_ui->timeLabel->setText(QString("%1: %2").arg(tr("Time Remaining")).arg(tr("Unknown"))); 314 | } 315 | } 316 | 317 | /** 318 | * Uses two time samples (from the current time and a previous sample) to 319 | * calculate how many bytes have been downloaded. 320 | * 321 | * Then, this function proceeds to calculate the appropiate units of time 322 | * (hours, minutes or seconds) and constructs a user-friendly string, which 323 | * is displayed in the dialog. 324 | */ 325 | void Downloader::calculateTimeRemaining(qint64 received, qint64 total) 326 | { 327 | uint difference = QDateTime::currentDateTime().toTime_t() - m_startTime; 328 | 329 | if (difference > 0) { 330 | QString timeString; 331 | qreal timeRemaining = total / (received / difference); 332 | 333 | if (timeRemaining > 7200) { 334 | timeRemaining /= 3600; 335 | int hours = int(timeRemaining + 0.5); 336 | 337 | if (hours > 1) 338 | timeString = tr("about %1 hours").arg(hours); 339 | else 340 | timeString = tr("about one hour"); 341 | } 342 | 343 | else if (timeRemaining > 60) { 344 | timeRemaining /= 60; 345 | int minutes = int(timeRemaining + 0.5); 346 | 347 | if (minutes > 1) 348 | timeString = tr("%1 minutes").arg(minutes); 349 | else 350 | timeString = tr("1 minute"); 351 | } 352 | 353 | else if (timeRemaining <= 60) { 354 | int seconds = int(timeRemaining + 0.5); 355 | 356 | if (seconds > 1) 357 | timeString = tr("%1 seconds").arg(seconds); 358 | else 359 | timeString = tr("1 second"); 360 | } 361 | 362 | m_ui->timeLabel->setText(tr("Time remaining") + ": " + timeString); 363 | } 364 | } 365 | 366 | /** 367 | * Rounds the given \a input to two decimal places 368 | */ 369 | qreal Downloader::round(const qreal& input) 370 | { 371 | return roundf(input * 100) / 100; 372 | } 373 | 374 | /** 375 | * If the \a custom parameter is set to \c true, then the \c Downloader will not 376 | * attempt to open the downloaded file. 377 | * 378 | * Use the signals fired by the \c QSimpleUpdater to implement your own install 379 | * procedures. 380 | */ 381 | void Downloader::setUseCustomInstallProcedures(const bool custom) 382 | { 383 | m_useCustomProcedures = custom; 384 | } 385 | -------------------------------------------------------------------------------- /src/pxmagent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _WIN32 4 | #include 5 | #include 6 | 7 | #ifdef _WIN64 8 | const char* modName = "windows64"; 9 | const char* arch = "x86_64"; 10 | #else 11 | const char* modName = "windows32"; 12 | const char* arch = "x86"; 13 | #endif 14 | 15 | #elif __unix__ 16 | #include 17 | #include 18 | #include 19 | #else 20 | #error "include headers for querying username" 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | Q_DECLARE_METATYPE(QSharedPointer) 45 | 46 | QPalette PXMAgent::defaultPalette; 47 | 48 | struct initialSettings { 49 | QUuid uuid; 50 | QSize windowSize; 51 | QString username; 52 | QString multicast; 53 | unsigned int uuidNum; 54 | unsigned short tcpPort; 55 | unsigned short udpPort; 56 | bool mute; 57 | bool preventFocus; 58 | bool darkColor; 59 | initialSettings() 60 | : uuid(QUuid()), 61 | windowSize(QSize(800, 600)), 62 | username(QString()), 63 | multicast(QString()), 64 | uuidNum(0), 65 | tcpPort(0), 66 | udpPort(0), 67 | mute(false), 68 | preventFocus(false), 69 | darkColor(false) 70 | { 71 | } 72 | }; 73 | 74 | class PXMAgentPrivate 75 | { 76 | public: 77 | PXMAgentPrivate() {} 78 | ~PXMAgentPrivate() 79 | { 80 | if (workerThread) { 81 | workerThread->quit(); 82 | workerThread->wait(3000); 83 | qInfo() << "Shutdown of WorkerThread"; 84 | } 85 | 86 | iniReader.resetUUID(presets.uuidNum, presets.uuid); 87 | } 88 | 89 | QScopedPointer window; 90 | PXMPeerWorker* peerWorker; 91 | PXMIniReader iniReader; 92 | PXMConsole::Logger* logger; 93 | QScopedPointer lockFile; 94 | QString address = "https://raw.githubusercontent.com/cbpeckles/PXMessenger/master/resources/updates.json"; 95 | 96 | initialSettings presets; 97 | 98 | #ifdef __WIN32 99 | QSimpleUpdater* qupdater; 100 | #endif 101 | QThread* workerThread = nullptr; 102 | // Functions 103 | QByteArray getUsername(); 104 | int setupHostname(const unsigned int uuidNum, QString& username); 105 | }; 106 | 107 | PXMAgent::PXMAgent(QObject* parent) : QObject(parent), d_ptr(new PXMAgentPrivate) 108 | { 109 | PXMAgent::defaultPalette = QGuiApplication::palette(); 110 | } 111 | 112 | PXMAgent::~PXMAgent() 113 | { 114 | } 115 | 116 | void PXMAgent::changeToDark() 117 | { 118 | qApp->setStyle(QStyleFactory::create("Fusion")); 119 | 120 | QPalette palette; 121 | palette.setColor(QPalette::Window, QColor(53, 53, 53)); 122 | palette.setColor(QPalette::WindowText, Qt::white); 123 | palette.setColor(QPalette::Base, QColor(35, 35, 35)); 124 | palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); 125 | palette.setColor(QPalette::ToolTipBase, Qt::white); 126 | palette.setColor(QPalette::ToolTipText, Qt::white); 127 | palette.setColor(QPalette::Text, Qt::white); 128 | palette.setColor(QPalette::Button, QColor(53, 53, 53)); 129 | palette.setColor(QPalette::ButtonText, Qt::white); 130 | palette.setColor(QPalette::BrightText, Qt::red); 131 | 132 | palette.setColor(QPalette::Highlight, QColor(142, 45, 197).lighter()); 133 | palette.setColor(QPalette::HighlightedText, Qt::black); 134 | qApp->setPalette(palette); 135 | } 136 | 137 | int PXMAgent::init() 138 | { 139 | QThread::currentThread()->setObjectName("MainThread"); 140 | #ifdef __WIN32 141 | if(d_ptr->iniReader.getUpdates()) 142 | { 143 | d_ptr->qupdater = QSimpleUpdater::getInstance(); 144 | connect(d_ptr->qupdater, SIGNAL(checkingFinished(QString)), this, SLOT(doneChkUpdt(QString))); 145 | connect(d_ptr->qupdater, &QSimpleUpdater::installerOpened, qApp, &QApplication::quit); 146 | connect(d_ptr->qupdater, &QSimpleUpdater::updateDeclined, this, &PXMAgent::postInit); 147 | d_ptr->qupdater->setModuleVersion(d_ptr->address, qApp->applicationVersion()); 148 | d_ptr->qupdater->setModuleName(d_ptr->address, modName); 149 | d_ptr->qupdater->setNotifyOnUpdate(d_ptr->address, true); 150 | d_ptr->qupdater->checkForUpdates(d_ptr->address); 151 | } 152 | else 153 | { 154 | postInit(); 155 | } 156 | #else 157 | this->postInit(); 158 | #endif 159 | return 0; 160 | } 161 | 162 | int PXMAgent::postInit() 163 | { 164 | #ifndef QT_DEBUG 165 | QImage splashImage(":/resources/PXMessenger_wBackground.png"); 166 | QSplashScreen splash(QPixmap::fromImage(splashImage)); 167 | splash.show(); 168 | QElapsedTimer startupTimer; 169 | startupTimer.start(); 170 | qApp->processEvents(); 171 | #endif 172 | 173 | #ifdef _WIN32 174 | try { 175 | WSADATA wsa; 176 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { 177 | qCritical() << "Failed WSAStartup error:" << WSAGetLastError(); 178 | throw(&"Failed WSAStartup error: "[WSAGetLastError()]); 179 | } 180 | } catch (const char* msg) { 181 | QMessageBox msgBox(QMessageBox::Critical, "WSAStartup Error", QString::fromUtf8(msg)); 182 | msgBox.exec(); 183 | emit alreadyRunning(); 184 | return -1; 185 | } 186 | 187 | qRegisterMetaType("intptr_t"); 188 | #endif 189 | qRegisterMetaType(); 190 | qRegisterMetaType("size_t"); 191 | qRegisterMetaType(); 192 | qRegisterMetaType(); 193 | qRegisterMetaType>(); 194 | qRegisterMetaType>(); 195 | 196 | QString username = d_ptr->getUsername(); 197 | QString localHostname = d_ptr->iniReader.getHostname(username); 198 | 199 | bool allowMoreThanOne = d_ptr->iniReader.checkAllowMoreThanOne(); 200 | QString tmpDir = QDir::tempPath(); 201 | d_ptr->lockFile.reset(new QLockFile(tmpDir + "/pxmessenger_" + username + ".lock")); 202 | if (!allowMoreThanOne) { 203 | if (!d_ptr->lockFile->tryLock(100)) { 204 | QMessageBox msgBox(QMessageBox::Warning, qApp->applicationName(), QString("PXMessenger is already " 205 | "running.\r\nOnly one instance " 206 | "is allowed")); 207 | msgBox.exec(); 208 | emit alreadyRunning(); 209 | return -1; 210 | } 211 | } 212 | 213 | d_ptr->logger = PXMConsole::Logger::getInstance(); 214 | d_ptr->logger->setVerbosityLevel(d_ptr->iniReader.getVerbosity()); 215 | d_ptr->logger->setLogStatus(d_ptr->iniReader.getLogActive()); 216 | 217 | d_ptr->presets.uuidNum = d_ptr->iniReader.getUUIDNumber(); 218 | d_ptr->presets.uuid = d_ptr->iniReader.getUUID(d_ptr->presets.uuidNum, allowMoreThanOne); 219 | d_ptr->presets.username = localHostname.left(PXMConsts::MAX_HOSTNAME_LENGTH); 220 | d_ptr->setupHostname(d_ptr->presets.uuidNum, d_ptr->presets.username); 221 | d_ptr->presets.tcpPort = d_ptr->iniReader.getPort("TCP"); 222 | d_ptr->presets.udpPort = d_ptr->iniReader.getPort("UDP"); 223 | d_ptr->presets.windowSize = d_ptr->iniReader.getWindowSize(QSize(700, 500)); 224 | d_ptr->presets.mute = d_ptr->iniReader.getMute(); 225 | d_ptr->presets.preventFocus = d_ptr->iniReader.getFocus(); 226 | d_ptr->presets.multicast = d_ptr->iniReader.getMulticastAddress(); 227 | d_ptr->presets.darkColor = d_ptr->iniReader.getDarkColorScheme(); 228 | 229 | if(d_ptr->presets.darkColor) 230 | { 231 | changeToDark(); 232 | } 233 | QUuid globalChat = QUuid::createUuid(); 234 | if (!(d_ptr->iniReader.getFont().isEmpty())) { 235 | QFont font; 236 | font.fromString(d_ptr->iniReader.getFont()); 237 | qApp->setFont(font); 238 | } 239 | 240 | d_ptr->workerThread = new QThread(this); 241 | d_ptr->workerThread->setObjectName("WorkerThread"); 242 | d_ptr->peerWorker = 243 | new PXMPeerWorker(nullptr, d_ptr->presets.username, d_ptr->presets.uuid, d_ptr->presets.multicast, 244 | d_ptr->presets.tcpPort, d_ptr->presets.udpPort, globalChat); 245 | d_ptr->peerWorker->moveToThread(d_ptr->workerThread); 246 | // QObject::connect(d_ptr->workerThread, &QThread::started, d_ptr->peerWorker, &PXMPeerWorker::init); 247 | QObject::connect(d_ptr->workerThread, &QThread::finished, d_ptr->peerWorker, &PXMPeerWorker::deleteLater); 248 | d_ptr->window.reset(new PXMWindow(d_ptr->presets.username, d_ptr->presets.windowSize, d_ptr->presets.mute, 249 | d_ptr->presets.preventFocus, d_ptr->presets.uuid, globalChat)); 250 | 251 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::msgRecieved, d_ptr->window.data(), 252 | &PXMWindow::printToTextBrowser, Qt::QueuedConnection); 253 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::connectionStatusChange, d_ptr->window.data(), 254 | &PXMWindow::setItalicsOnItem, Qt::QueuedConnection); 255 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::newAuthedPeer, d_ptr->window.data(), 256 | &PXMWindow::updateListWidget, Qt::QueuedConnection); 257 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::warnBox, [=](QString title, QString msg){QMessageBox::warning(d_ptr->window.data(), title, msg);}); 258 | QObject::connect(d_ptr->window.data(), SIGNAL(addMessageToPeer(QString, QUuid, QUuid, bool, bool)), 259 | d_ptr->peerWorker, SLOT(addMessageToPeer(QString, QUuid, QUuid, bool, bool)), 260 | Qt::QueuedConnection); 261 | QObject::connect(d_ptr->window.data(), &PXMWindow::sendMsg, d_ptr->peerWorker, &PXMPeerWorker::sendMsgAccessor, 262 | Qt::QueuedConnection); 263 | QObject::connect(d_ptr->window.data(), &PXMWindow::sendUDP, d_ptr->peerWorker, &PXMPeerWorker::sendUDPAccessor, 264 | Qt::QueuedConnection); 265 | QObject::connect(d_ptr->window.data(), &PXMWindow::syncWithPeers, d_ptr->peerWorker, &PXMPeerWorker::beginPeerSync, 266 | Qt::QueuedConnection); 267 | QObject::connect(d_ptr->window.data(), &PXMWindow::printInfoToDebug, d_ptr->peerWorker, 268 | &PXMPeerWorker::printInfoToDebug, Qt::QueuedConnection); 269 | QObject::connect(d_ptr->window.data(), &PXMWindow::typing, d_ptr->peerWorker, &PXMPeerWorker::typing, 270 | Qt::QueuedConnection); 271 | QObject::connect(d_ptr->window.data(), &PXMWindow::endOfTextEntered, d_ptr->peerWorker, 272 | &PXMPeerWorker::endOfTextEntered, Qt::QueuedConnection); 273 | QObject::connect(d_ptr->window.data(), &PXMWindow::textEntered, d_ptr->peerWorker, &PXMPeerWorker::textEntered, 274 | Qt::QueuedConnection); 275 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::typingAlert, d_ptr->window.data(), &PXMWindow::typingAlert); 276 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::textEnteredAlert, d_ptr->window.data(), 277 | &PXMWindow::textEnteredAlert); 278 | QObject::connect(d_ptr->peerWorker, &PXMPeerWorker::endOfTextEnteredAlert, d_ptr->window.data(), 279 | &PXMWindow::endOfTextEnteredAlert); 280 | 281 | #ifdef QT_DEBUG 282 | qInfo().noquote() << "Built in debug mode"; 283 | #else 284 | qInfo().noquote() << "Built in release mode"; 285 | splash.finish(d_ptr->window.data()); 286 | qApp->processEvents(); 287 | while (startupTimer.elapsed() < 1500) { 288 | qApp->processEvents(); 289 | QThread::currentThread()->msleep(100); 290 | } 291 | #endif 292 | #ifdef _WIN32 293 | qInfo().noquote() << "Architecture:" << arch; 294 | #endif 295 | qInfo().noquote() << "Our UUID:" << d_ptr->presets.uuid.toString(); 296 | 297 | d_ptr->workerThread->start(); 298 | 299 | d_ptr->window->show(); 300 | 301 | return 0; 302 | } 303 | void PXMAgent::doneChkUpdt(const QString&) 304 | { 305 | #ifdef __WIN32 306 | if (!d_ptr->qupdater->getUpdateAvailable(d_ptr->address)) { 307 | qDebug() << "No update Available"; 308 | this->postInit(); 309 | } else { 310 | qDebug() << "Update Available"; 311 | } 312 | 313 | #endif 314 | } 315 | 316 | QByteArray PXMAgentPrivate::getUsername() 317 | { 318 | #ifdef _WIN32 319 | const size_t len = UNLEN + 1; 320 | char localHostname[len]; 321 | TCHAR t_user[len]; 322 | DWORD user_size = len; 323 | if (GetUserName(t_user, &user_size)) { 324 | wcstombs(localHostname, t_user, len); 325 | return QByteArray(localHostname); 326 | } else 327 | return QByteArray("user"); 328 | #elif __unix__ 329 | struct passwd* user; 330 | user = getpwuid(getuid()); 331 | if (!user) 332 | return QByteArray("user"); 333 | else 334 | return QByteArray(user->pw_name); 335 | #else 336 | #error "implement username query" 337 | #endif 338 | } 339 | 340 | int PXMAgentPrivate::setupHostname(const unsigned int uuidNum, QString& username) 341 | { 342 | char computerHostname[256] = {}; 343 | 344 | gethostname(computerHostname, sizeof computerHostname); 345 | 346 | if (uuidNum > 0) { 347 | char temp[3]; 348 | sprintf(temp, "%d", uuidNum); 349 | username.append(QString::fromUtf8(temp)); 350 | } 351 | username.append("@"); 352 | username.append(QString::fromLocal8Bit(computerHostname).left(PXMConsts::MAX_COMPUTER_NAME_LENGTH)); 353 | return 0; 354 | } 355 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/Updater.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "Updater.h" 38 | #include "Downloader.h" 39 | 40 | Updater::Updater() 41 | { 42 | m_url = ""; 43 | m_openUrl = ""; 44 | m_changelog = ""; 45 | m_downloadUrl = ""; 46 | m_latestVersion = ""; 47 | m_customAppcast = false; 48 | m_notifyOnUpdate = true; 49 | m_notifyOnFinish = false; 50 | m_updateAvailable = false; 51 | m_downloaderEnabled = true; 52 | m_moduleName = qApp->applicationName(); 53 | m_moduleVersion = qApp->applicationVersion(); 54 | 55 | m_downloader = new Downloader(); 56 | m_manager = new QNetworkAccessManager(); 57 | 58 | #if defined Q_OS_WIN 59 | m_platform = "windows"; 60 | #elif defined Q_OS_MAC 61 | m_platform = "osx"; 62 | #elif defined Q_OS_LINUX 63 | m_platform = "linux"; 64 | #elif defined Q_OS_ANDROID 65 | m_platform = "android"; 66 | #elif defined Q_OS_IOS 67 | m_platform = "ios"; 68 | #endif 69 | 70 | connect(m_downloader, SIGNAL(downloadFinished(QString, QString)), this, SIGNAL(downloadFinished(QString, QString))); 71 | connect(m_downloader, SIGNAL(installerOpened()), this, SIGNAL(installerOpened())); 72 | connect(m_downloader, SIGNAL(downloadCanceled()), this, SIGNAL(downloadCanceled())); 73 | connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onReply(QNetworkReply*))); 74 | } 75 | 76 | Updater::~Updater() 77 | { 78 | delete m_downloader; 79 | } 80 | 81 | /** 82 | * Returns the URL of the update definitions file 83 | */ 84 | QString Updater::url() const 85 | { 86 | return m_url; 87 | } 88 | 89 | /** 90 | * Returns the URL that the update definitions file wants us to open in 91 | * a web browser. 92 | * 93 | * \warning You should call \c checkForUpdates() before using this functio 94 | */ 95 | QString Updater::openUrl() const 96 | { 97 | return m_openUrl; 98 | } 99 | 100 | /** 101 | * Returns the changelog defined by the update definitions file. 102 | * \warning You should call \c checkForUpdates() before using this function 103 | */ 104 | QString Updater::changelog() const 105 | { 106 | return m_changelog; 107 | } 108 | 109 | /** 110 | * Returns the name of the module (if defined) 111 | */ 112 | QString Updater::moduleName() const 113 | { 114 | return m_moduleName; 115 | } 116 | 117 | /** 118 | * Returns the platform key (be it system-set or user-set). 119 | * If you do not define a platform key, the system will assign the following 120 | * platform key: 121 | * - On iOS: \c ios 122 | * - On Mac OSX: \c osx 123 | * - On Android: \c android 124 | * - On GNU/Linux: \c linux 125 | * - On Microsoft Windows: \c windows 126 | */ 127 | QString Updater::platformKey() const 128 | { 129 | return m_platform; 130 | } 131 | 132 | /** 133 | * Returns the download URL defined by the update definitions file. 134 | * \warning You should call \c checkForUpdates() before using this function 135 | */ 136 | QString Updater::downloadUrl() const 137 | { 138 | return m_downloadUrl; 139 | } 140 | 141 | /** 142 | * Returns the latest version defined by the update definitions file. 143 | * \warning You should call \c checkForUpdates() before using this function 144 | */ 145 | QString Updater::latestVersion() const 146 | { 147 | return m_latestVersion; 148 | } 149 | 150 | /** 151 | * Returns the "local" version of the installed module 152 | */ 153 | QString Updater::moduleVersion() const 154 | { 155 | return m_moduleVersion; 156 | } 157 | 158 | /** 159 | * Returns \c true if the updater should NOT interpret the downloaded appcast. 160 | * This is useful if you need to store more variables (or information) in the 161 | * JSON file or use another appcast format (e.g. XML) 162 | */ 163 | bool Updater::customAppcast() const 164 | { 165 | return m_customAppcast; 166 | } 167 | 168 | /** 169 | * Returns \c true if the updater should notify the user when an update is 170 | * available. 171 | */ 172 | bool Updater::notifyOnUpdate() const 173 | { 174 | return m_notifyOnUpdate; 175 | } 176 | 177 | /** 178 | * Returns \c true if the updater should notify the user when it finishes 179 | * checking for updates. 180 | * 181 | * \note If set to \c true, the \c Updater will notify the user even when there 182 | * are no updates available (by congratulating him/her about being smart) 183 | */ 184 | bool Updater::notifyOnFinish() const 185 | { 186 | return m_notifyOnFinish; 187 | } 188 | 189 | /** 190 | * Returns \c true if there is an update available. 191 | * \warning You should call \c checkForUpdates() before using this function 192 | */ 193 | bool Updater::updateAvailable() const 194 | { 195 | return m_updateAvailable; 196 | } 197 | 198 | /** 199 | * Returns \c true if the integrated downloader is enabled. 200 | * \note If set to \c true, the \c Updater will open the downloader dialog if 201 | * the user agrees to download the update. 202 | */ 203 | bool Updater::downloaderEnabled() const 204 | { 205 | return m_downloaderEnabled; 206 | } 207 | 208 | /** 209 | * Returns \c true if the updater shall not intervene when the download has 210 | * finished (you can use the \c QSimpleUpdater signals to know when the 211 | * download is completed). 212 | */ 213 | bool Updater::useCustomInstallProcedures() const 214 | { 215 | return m_downloader->useCustomInstallProcedures(); 216 | } 217 | 218 | /** 219 | * Downloads and interpets the update definitions file referenced by the 220 | * \c url() function. 221 | */ 222 | void Updater::checkForUpdates() 223 | { 224 | m_manager->get(QNetworkRequest(url())); 225 | } 226 | 227 | /** 228 | * Changes the \c url in which the \c Updater can find the update definitions 229 | * file. 230 | */ 231 | void Updater::setUrl(const QString& url) 232 | { 233 | m_url = url; 234 | } 235 | 236 | /** 237 | * Changes the module \a name. 238 | * \note The module name is used on the user prompts. If the module name is 239 | * empty, then the prompts will show the name of the application. 240 | */ 241 | void Updater::setModuleName(const QString& name) 242 | { 243 | m_moduleName = name; 244 | } 245 | 246 | /** 247 | * If \a notify is set to \c true, then the \c Updater will notify the user 248 | * when an update is available. 249 | */ 250 | void Updater::setNotifyOnUpdate(const bool notify) 251 | { 252 | m_notifyOnUpdate = notify; 253 | } 254 | 255 | /** 256 | * If \a notify is set to \c true, then the \c Updater will notify the user 257 | * when it has finished interpreting the update definitions file. 258 | */ 259 | void Updater::setNotifyOnFinish(const bool notify) 260 | { 261 | m_notifyOnFinish = notify; 262 | } 263 | 264 | /** 265 | * Changes the module \a version 266 | * \note The module version is used to compare the local and remote versions. 267 | * If the \a version parameter is empty, then the \c Updater will use the 268 | * application version (referenced by \c qApp) 269 | */ 270 | void Updater::setModuleVersion(const QString& version) 271 | { 272 | m_moduleVersion = version; 273 | } 274 | 275 | /** 276 | * If the \a enabled parameter is set to \c true, the \c Updater will open the 277 | * integrated downloader if the user agrees to install the update (if any) 278 | */ 279 | void Updater::setDownloaderEnabled(const bool enabled) 280 | { 281 | m_downloaderEnabled = enabled; 282 | } 283 | 284 | /** 285 | * Changes the platform key. 286 | * If the platform key is empty, then the system will use the following keys: 287 | * - On iOS: \c ios 288 | * - On Mac OSX: \c osx 289 | * - On Android: \c android 290 | * - On GNU/Linux: \c linux 291 | * - On Microsoft Windows: \c windows 292 | */ 293 | void Updater::setPlatformKey(const QString& platformKey) 294 | { 295 | m_platform = platformKey; 296 | } 297 | 298 | /** 299 | * If the \a customAppcast parameter is set to \c true, then the \c Updater 300 | * will not try to read the network reply from the server, instead, it will 301 | * emit the \c appcastDownloaded() signal, which allows the application to 302 | * read and interpret the appcast file by itself 303 | */ 304 | void Updater::setUseCustomAppcast(const bool customAppcast) 305 | { 306 | m_customAppcast = customAppcast; 307 | } 308 | 309 | /** 310 | * If the \a custom parameter is set to \c true, the \c Updater will not try 311 | * to open the downloaded file. Use the signals fired by the \c QSimpleUpdater 312 | * to install the update from the downloaded file by yourself. 313 | */ 314 | void Updater::setUseCustomInstallProcedures(const bool custom) 315 | { 316 | m_downloader->setUseCustomInstallProcedures(custom); 317 | } 318 | 319 | /** 320 | * Called when the download of the update definitions file is finished. 321 | */ 322 | void Updater::onReply(QNetworkReply* reply) 323 | { 324 | /* Check if we need to redirect */ 325 | QUrl redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); 326 | if (!redirect.isEmpty()) { 327 | setUrl(redirect.toString()); 328 | checkForUpdates(); 329 | return; 330 | } 331 | 332 | /* There was a network error */ 333 | if (reply->error() != QNetworkReply::NoError) { 334 | setUpdateAvailable(false); 335 | emit checkingFinished(url()); 336 | return; 337 | } 338 | 339 | /* The application wants to interpret the appcast by itself */ 340 | if (customAppcast()) { 341 | emit appcastDownloaded(url(), reply->readAll()); 342 | emit checkingFinished(url()); 343 | return; 344 | } 345 | 346 | /* Try to create a JSON document from downloaded data */ 347 | QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); 348 | 349 | /* JSON is invalid */ 350 | if (document.isNull()) { 351 | setUpdateAvailable(false); 352 | emit checkingFinished(url()); 353 | return; 354 | } 355 | 356 | /* Get the platform information */ 357 | QJsonObject updates = document.object().value("updates").toObject(); 358 | QJsonObject platform = updates.value(platformKey()).toObject(); 359 | 360 | /* Get update information */ 361 | m_openUrl = platform.value("open-url").toString(); 362 | m_changelog = platform.value("changelog").toString(); 363 | m_downloadUrl = platform.value("download-url").toString(); 364 | m_latestVersion = platform.value("latest-version").toString(); 365 | 366 | /* Compare latest and current version */ 367 | setUpdateAvailable(compare(latestVersion(), moduleVersion())); 368 | emit checkingFinished(url()); 369 | } 370 | 371 | /** 372 | * Prompts the user based on the value of the \a available parameter and the 373 | * settings of this instance of the \c Updater class. 374 | */ 375 | void Updater::setUpdateAvailable(const bool available) 376 | { 377 | m_updateAvailable = available; 378 | 379 | QMessageBox box; 380 | box.setTextFormat(Qt::RichText); 381 | box.setIcon(QMessageBox::Information); 382 | 383 | if (updateAvailable() && (notifyOnUpdate() || notifyOnFinish())) { 384 | QString text = tr("Would you like to download the update now?"); 385 | QString title = 386 | "

" + tr("Version %1 of %2 has been released!").arg(latestVersion()).arg(moduleName()) + "

"; 387 | 388 | box.setText(title); 389 | box.setInformativeText(text); 390 | box.setStandardButtons(QMessageBox::No | QMessageBox::Yes); 391 | box.setDefaultButton(QMessageBox::Yes); 392 | 393 | if (box.exec() == QMessageBox::Yes) { 394 | if (!openUrl().isEmpty()) 395 | QDesktopServices::openUrl(QUrl(openUrl())); 396 | 397 | else if (downloaderEnabled()) { 398 | m_downloader->setUrlId(url()); 399 | m_downloader->setFileName(downloadUrl().split("/").last()); 400 | m_downloader->startDownload(QUrl(downloadUrl())); 401 | } 402 | 403 | else 404 | QDesktopServices::openUrl(QUrl(downloadUrl())); 405 | } else { 406 | emit updateDeclined(); 407 | } 408 | } 409 | 410 | else if (notifyOnFinish()) { 411 | box.setStandardButtons(QMessageBox::Close); 412 | box.setInformativeText(tr("No updates are available for the moment")); 413 | box.setText("

" + 414 | tr("Congratulations! You are running the " 415 | "latest version of %1") 416 | .arg(moduleName()) + 417 | "

"); 418 | 419 | box.exec(); 420 | } 421 | } 422 | 423 | /** 424 | * Compares the two version strings (\a x and \a y). 425 | * - If \a x is greater than \y, this function returns \c true. 426 | * - If \a y is greater than \x, this function returns \c false. 427 | * - If both versions are the same, this function returns \c false. 428 | */ 429 | bool Updater::compare(const QString& x, const QString& y) 430 | { 431 | QStringList versionsX = x.split("."); 432 | QStringList versionsY = y.split("."); 433 | 434 | int count = qMin(versionsX.count(), versionsY.count()); 435 | 436 | for (int i = 0; i < count; ++i) { 437 | int a = QString(versionsX.at(i)).toInt(); 438 | int b = QString(versionsY.at(i)).toInt(); 439 | 440 | if (a > b) 441 | return true; 442 | 443 | else if (b > a) 444 | return false; 445 | } 446 | 447 | return versionsY.count() < versionsX.count(); 448 | } 449 | -------------------------------------------------------------------------------- /include/QSimpleUpdater/src/QSimpleUpdater.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Alex Spataru 3 | * 4 | * This file is part of the QSimpleUpdater library, which is released under 5 | * the DBAD license, you can read a copy of it below: 6 | * 7 | * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 8 | * DISTRIBUTION AND MODIFICATION: 9 | * 10 | * Do whatever you like with the original work, just don't be a dick. 11 | * Being a dick includes - but is not limited to - the following instances: 12 | * 13 | * 1a. Outright copyright infringement - Don't just copy this and change the 14 | * name. 15 | * 1b. Selling the unmodified original with no work done what-so-ever, that's 16 | * REALLY being a dick. 17 | * 1c. Modifying the original work to contain hidden harmful content. 18 | * That would make you a PROPER dick. 19 | * 20 | * If you become rich through modifications, related works/services, or 21 | * supporting the original work, share the love. 22 | * Only a dick would make loads off this work and not buy the original works 23 | * creator(s) a pint. 24 | * 25 | * Code is provided with no warranty. Using somebody else's code and bitching 26 | * when it goes wrong makes you a DONKEY dick. 27 | * Fix the problem yourself. A non-dick would submit the fix back. 28 | */ 29 | 30 | #include "Updater.h" 31 | #include "QSimpleUpdater.h" 32 | 33 | static QList URLS; 34 | static QList UPDATERS; 35 | 36 | QSimpleUpdater::~QSimpleUpdater() 37 | { 38 | URLS.clear(); 39 | 40 | foreach (Updater* updater, UPDATERS) 41 | updater->deleteLater(); 42 | 43 | UPDATERS.clear(); 44 | } 45 | 46 | /** 47 | * Returns the only instance of the class 48 | */ 49 | QSimpleUpdater* QSimpleUpdater::getInstance() 50 | { 51 | static QSimpleUpdater updater; 52 | return &updater; 53 | } 54 | 55 | /** 56 | * Returns \c true if the \c Updater instance registered with the given \a url 57 | * uses a custom appcast format and/or allows the application to read and 58 | * interpret the downloaded appcast file 59 | * 60 | * \note If an \c Updater instance registered with the given \a url is not 61 | * found, that \c Updater instance will be initialized automatically 62 | */ 63 | bool QSimpleUpdater::usesCustomAppcast(const QString& url) const 64 | { 65 | return getUpdater(url)->customAppcast(); 66 | } 67 | 68 | /** 69 | * Returns \c true if the \c Updater instance registered with the given \a url 70 | * shall notify the user when an update is available. 71 | * 72 | * \note If an \c Updater instance registered with the given \a url is not 73 | * found, that \c Updater instance will be initialized automatically 74 | */ 75 | bool QSimpleUpdater::getNotifyOnUpdate(const QString& url) const 76 | { 77 | return getUpdater(url)->notifyOnUpdate(); 78 | } 79 | 80 | /** 81 | * Returns \c true if the \c Updater instance registered with the given \a url 82 | * shall notify the user when it finishes checking for updates. 83 | * 84 | * \note If an \c Updater instance registered with the given \a url is not 85 | * found, that \c Updater instance will be initialized automatically 86 | */ 87 | bool QSimpleUpdater::getNotifyOnFinish(const QString& url) const 88 | { 89 | return getUpdater(url)->notifyOnFinish(); 90 | } 91 | 92 | /** 93 | * Returns \c true if the \c Updater instance registered with the given \a url 94 | * has an update available. 95 | * 96 | * \warning You should call \c checkForUpdates() before using this function 97 | * \note If an \c Updater instance registered with the given \a url is not 98 | * found, that \c Updater instance will be initialized automatically 99 | */ 100 | bool QSimpleUpdater::getUpdateAvailable(const QString& url) const 101 | { 102 | return getUpdater(url)->updateAvailable(); 103 | } 104 | 105 | /** 106 | * Returns \c true if the \c Updater instance registered with the given \a url 107 | * has the integrated downloader enabled. 108 | * 109 | * \note If an \c Updater instance registered with the given \a url is not 110 | * found, that \c Updater instance will be initialized automatically 111 | */ 112 | bool QSimpleUpdater::getDownloaderEnabled(const QString& url) const 113 | { 114 | return getUpdater(url)->downloaderEnabled(); 115 | } 116 | 117 | /** 118 | * Returns \c true if the \c Updater instance registered with the given \a url 119 | * shall try to open the downloaded file. 120 | * 121 | * If you want to implement your own way to handle the downloaded file, just 122 | * bind to the \c downloadFinished() signal and disable the integrated 123 | * downloader with the \c setUseCustomInstallProcedures() function. 124 | * 125 | * \note If an \c Updater instance registered with the given \a url is not 126 | * found, that \c Updater instance will be initialized automatically 127 | */ 128 | bool QSimpleUpdater::usesCustomInstallProcedures(const QString& url) const 129 | { 130 | return getUpdater(url)->useCustomInstallProcedures(); 131 | } 132 | 133 | /** 134 | * Returns the URL to open in a web browser of the \c Updater instance 135 | * registered with the given \a url. 136 | * 137 | * \note If the module name is empty, then the \c Updater will use the 138 | * application name as its module name. 139 | * \note If an \c Updater instance registered with the given \a url is not 140 | * found, that \c Updater instance will be initialized automatically 141 | */ 142 | QString QSimpleUpdater::getOpenUrl(const QString& url) const 143 | { 144 | return getUpdater(url)->openUrl(); 145 | } 146 | 147 | /** 148 | * Returns the changelog of the \c Updater instance registered with the given 149 | * \a url. 150 | * 151 | * \warning You should call \c checkForUpdates() before using this function 152 | * \note If an \c Updater instance registered with the given \a url is not 153 | * found, that \c Updater instance will be initialized automatically 154 | */ 155 | QString QSimpleUpdater::getChangelog(const QString& url) const 156 | { 157 | return getUpdater(url)->changelog(); 158 | } 159 | 160 | /** 161 | * Returns the module name of the \c Updater instance registered with the given 162 | * \a url. 163 | * 164 | * \note If the module name is empty, then the \c Updater will use the 165 | * application name as its module name. 166 | * \note If an \c Updater instance registered with the given \a url is not 167 | * found, that \c Updater instance will be initialized automatically 168 | */ 169 | QString QSimpleUpdater::getModuleName(const QString& url) const 170 | { 171 | return getUpdater(url)->moduleName(); 172 | } 173 | 174 | /** 175 | * Returns the download URL of the \c Updater instance registered with the given 176 | * \a url. 177 | * 178 | * \warning You should call \c checkForUpdates() before using this function 179 | * \note If an \c Updater instance registered with the given \a url is not 180 | * found, that \c Updater instance will be initialized automatically 181 | */ 182 | QString QSimpleUpdater::getDownloadUrl(const QString& url) const 183 | { 184 | return getUpdater(url)->downloadUrl(); 185 | } 186 | 187 | /** 188 | * Returns the platform key of the \c Updater registered with the given \a url. 189 | * If you do not define a platform key, the system will assign the following 190 | * platform key: 191 | * - On iOS: \c ios 192 | * - On Mac OSX: \c osx 193 | * - On Android: \c android 194 | * - On GNU/Linux: \c linux 195 | * - On Microsoft Windows: \c windows 196 | * 197 | * \note If an \c Updater instance registered with the given \a url is not 198 | * found, that \c Updater instance will be initialized automatically 199 | */ 200 | QString QSimpleUpdater::getPlatformKey(const QString& url) const 201 | { 202 | return getUpdater(url)->platformKey(); 203 | } 204 | 205 | /** 206 | * Returns the remote module version of the \c Updater instance registered with 207 | * the given \a url. 208 | * 209 | * \warning You should call \c checkForUpdates() before using this function 210 | * \note If an \c Updater instance registered with the given \a url is not 211 | * found, that \c Updater instance will be initialized automatically 212 | */ 213 | QString QSimpleUpdater::getLatestVersion(const QString& url) const 214 | { 215 | return getUpdater(url)->latestVersion(); 216 | } 217 | 218 | /** 219 | * Returns the module version of the \c Updater instance registered with the 220 | * given \a url. 221 | * 222 | * \note If the module version is empty, then the \c Updater will use the 223 | * application version as its module version. 224 | * \note If an \c Updater instance registered with the given \a url is not 225 | * found, that \c Updater instance will be initialized automatically 226 | */ 227 | QString QSimpleUpdater::getModuleVersion(const QString& url) const 228 | { 229 | return getUpdater(url)->moduleVersion(); 230 | } 231 | 232 | /** 233 | * Instructs the \c Updater instance with the registered \c url to download and 234 | * interpret the update definitions file. 235 | * 236 | * \note If an \c Updater instance registered with the given \a url is not 237 | * found, that \c Updater instance will be initialized automatically 238 | */ 239 | void QSimpleUpdater::checkForUpdates(const QString& url) 240 | { 241 | getUpdater(url)->checkForUpdates(); 242 | } 243 | 244 | /** 245 | * Changes the module \a name of the \c Updater instance registered at the 246 | * given \a url. 247 | * 248 | * \note If an \c Updater instance registered with the given \a url is not 249 | * found, that \c Updater instance will be initialized automatically 250 | * \note The module name is used on the user prompts. If the module name is 251 | * empty, then the prompts will show the name of the application. 252 | */ 253 | void QSimpleUpdater::setModuleName(const QString& url, const QString& name) 254 | { 255 | getUpdater(url)->setModuleName(name); 256 | } 257 | 258 | /** 259 | * If \a notify is set to \c true, then the \c Updater instance registered with 260 | * the given \a url will notify the user when an update is available. 261 | * 262 | * \note If an \c Updater instance registered with the given \a url is not 263 | * found, that \c Updater instance will be initialized automatically 264 | */ 265 | void QSimpleUpdater::setNotifyOnUpdate(const QString& url, const bool notify) 266 | { 267 | getUpdater(url)->setNotifyOnUpdate(notify); 268 | } 269 | 270 | /** 271 | * If \a notify is set to \c true, then the \c Updater instance registered with 272 | * the given \a url will notify the user when it has finished interpreting the 273 | * update definitions file. 274 | * 275 | * \note If an \c Updater instance registered with the given \a url is not 276 | * found, that \c Updater instance will be initialized automatically 277 | */ 278 | void QSimpleUpdater::setNotifyOnFinish(const QString& url, const bool notify) 279 | { 280 | getUpdater(url)->setNotifyOnFinish(notify); 281 | } 282 | 283 | /** 284 | * Changes the platform key of the \c Updater isntance registered at the given 285 | * \a url. 286 | * 287 | * If the platform key is empty, then the system will use the following keys: 288 | * - On iOS: \c ios 289 | * - On Mac OSX: \c osx 290 | * - On Android: \c android 291 | * - On GNU/Linux: \c linux 292 | * - On Microsoft Windows: \c windows 293 | * 294 | * \note If an \c Updater instance registered with the given \a url is not 295 | * found, that \c Updater instance will be initialized automatically 296 | */ 297 | void QSimpleUpdater::setPlatformKey(const QString& url, const QString& platform) 298 | { 299 | getUpdater(url)->setPlatformKey(platform); 300 | } 301 | 302 | /** 303 | * Changes the module \version of the \c Updater instance registered at the 304 | * given \a url. 305 | * 306 | * \note The module version is used to compare it with the remove version. 307 | * If the module name is empty, then the \c Updater instance will use the 308 | * application version. 309 | */ 310 | void QSimpleUpdater::setModuleVersion(const QString& url, const QString& version) 311 | { 312 | getUpdater(url)->setModuleVersion(version); 313 | } 314 | 315 | /** 316 | * If the \a enabled parameter is set to \c true, the \c Updater instance 317 | * registered with the given \a url will open the integrated downloader 318 | * if the user agrees to install the update (if any). 319 | * 320 | * \note If an \c Updater instance registered with the given \a url is not 321 | * found, that \c Updater instance will be initialized automatically 322 | */ 323 | void QSimpleUpdater::setDownloaderEnabled(const QString& url, const bool enabled) 324 | { 325 | getUpdater(url)->setDownloaderEnabled(enabled); 326 | } 327 | 328 | /** 329 | * If the \a customAppcast parameter is set to \c true, then the \c Updater 330 | * will not try to read the network reply from the server, instead, it will 331 | * emit the \c appcastDownloaded() signal, which allows the application to 332 | * read and interpret the appcast file by itself. 333 | * 334 | * \note If an \c Updater instance registered with the given \a url is not 335 | * found, that \c Updater instance will be initialized automatically 336 | */ 337 | void QSimpleUpdater::setUseCustomAppcast(const QString& url, const bool customAppcast) 338 | { 339 | getUpdater(url)->setUseCustomAppcast(customAppcast); 340 | } 341 | 342 | /** 343 | * If the \a custom parameter is set to \c true, the \c Updater instance 344 | * registered with the given \a url will not try to open the downloaded file. 345 | * 346 | * If you want to implement your own way to handle the downloaded file, just 347 | * bind to the \c downloadFinished() signal and disable the integrated 348 | * downloader with the \c setUseCustomInstallProcedures() function. 349 | * 350 | * \note If an \c Updater instance registered with the given \a url is not 351 | * found, that \c Updater instance will be initialized automatically 352 | */ 353 | void QSimpleUpdater::setUseCustomInstallProcedures(const QString& url, const bool custom) 354 | { 355 | getUpdater(url)->setUseCustomInstallProcedures(custom); 356 | } 357 | 358 | /** 359 | * Returns the \c Updater instance registered with the given \a url. 360 | * 361 | * If an \c Updater instance registered with teh given \a url does not exist, 362 | * this function will create it and configure it automatically. 363 | */ 364 | Updater* QSimpleUpdater::getUpdater(const QString& url) const 365 | { 366 | if (!URLS.contains(url)) { 367 | Updater* updater = new Updater; 368 | updater->setUrl(url); 369 | 370 | URLS.append(url); 371 | UPDATERS.append(updater); 372 | 373 | connect(updater, SIGNAL(checkingFinished(QString)), this, SIGNAL(checkingFinished(QString))); 374 | connect(updater, SIGNAL(downloadFinished(QString, QString)), this, SIGNAL(downloadFinished(QString, QString))); 375 | connect(updater, SIGNAL(appcastDownloaded(QString, QByteArray)), this, 376 | SIGNAL(appcastDownloaded(QString, QByteArray))); 377 | connect(updater, SIGNAL(installerOpened()), this, SIGNAL(installerOpened())); 378 | connect(updater, SIGNAL(updateDeclined()), this, SIGNAL(updateDeclined())); 379 | connect(updater, SIGNAL(downloadCanceled()), this, SIGNAL(updateDeclined())); 380 | } 381 | 382 | return UPDATERS.at(URLS.indexOf(url)); 383 | } 384 | -------------------------------------------------------------------------------- /ui/pxmmainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PXMWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | 15 | 800 16 | 600 17 | 18 | 19 | 20 | PXMessenger 21 | 22 | 23 | 24 | :/resources/resources/PXM_Icon.ico:/resources/resources/PXM_Icon.ico 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 0 33 | 0 34 | 35 | 36 | 37 | 38 | 300 39 | 16777215 40 | 41 | 42 | 43 | <html><head/><body><p>Your Username</p></body></html> 44 | 45 | 46 | true 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 0 55 | 0 56 | 57 | 58 | 59 | 60 | 25 61 | 16777215 62 | 63 | 64 | 65 | B 66 | 67 | 68 | true 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 0 78 | 79 | 80 | 81 | Quit 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 0 90 | 0 91 | 92 | 93 | 94 | 95 | 25 96 | 16777215 97 | 98 | 99 | 100 | u 101 | 102 | 103 | true 104 | 105 | 106 | false 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 0 115 | 0 116 | 117 | 118 | 119 | 120 | 25 121 | 16777215 122 | 123 | 124 | 125 | i 126 | 127 | 128 | true 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 0 137 | 0 138 | 139 | 140 | 141 | Send 142 | 143 | 144 | 145 | 146 | 147 | 148 | Qt::Horizontal 149 | 150 | 151 | QSizePolicy::Maximum 152 | 153 | 154 | 155 | 60 156 | 20 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Qt::Horizontal 165 | 166 | 167 | QSizePolicy::Maximum 168 | 169 | 170 | 171 | 25 172 | 20 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | Qt::Horizontal 181 | 182 | 183 | QSizePolicy::Maximum 184 | 185 | 186 | 187 | 25 188 | 20 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | Qt::Horizontal 197 | 198 | 199 | QSizePolicy::Maximum 200 | 201 | 202 | 203 | 60 204 | 20 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | Qt::Horizontal 213 | 214 | 215 | QSizePolicy::Maximum 216 | 217 | 218 | 219 | 25 220 | 20 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 0 230 | 0 231 | 232 | 233 | 234 | 235 | 16777215 236 | 200 237 | 238 | 239 | 240 | <html><head/><body><p><img src=":/resources/PXMessenger_TightCrop_100px.png"/></p></body></html> 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 0 249 | 1 250 | 251 | 252 | 253 | 254 | 200 255 | 0 256 | 257 | 258 | 259 | 260 | 250 261 | 16777215 262 | 263 | 264 | 265 | Qt::LeftToRight 266 | 267 | 268 | 269 | 270 | 271 | 272 | Qt::RightToLeft 273 | 274 | 275 | Mute 276 | 277 | 278 | 279 | 280 | 281 | 282 | Qt::RightToLeft 283 | 284 | 285 | Prevent Pop-up 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 0 294 | 2 295 | 296 | 297 | 298 | 299 | 300 300 | 250 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 0 310 | 0 311 | 312 | 313 | 314 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 315 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 316 | p, li { white-space: pre-wrap; } 317 | </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> 318 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> 319 | 320 | 321 | Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 322 | 323 | 324 | Enter a message to send! 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 0 333 | 0 334 | 335 | 336 | 337 | Invert 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 0 346 | 0 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | Qt::Horizontal 355 | 356 | 357 | QSizePolicy::Maximum 358 | 359 | 360 | 361 | 25 362 | 20 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | Qt::Horizontal 371 | 372 | 373 | QSizePolicy::Expanding 374 | 375 | 376 | 377 | 40 378 | 20 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | Qt::Horizontal 387 | 388 | 389 | QSizePolicy::Maximum 390 | 391 | 392 | 393 | 25 394 | 20 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | Qt::Horizontal 403 | 404 | 405 | QSizePolicy::Maximum 406 | 407 | 408 | 409 | 25 410 | 20 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | Qt::Horizontal 419 | 420 | 421 | QSizePolicy::Expanding 422 | 423 | 424 | 425 | 50 426 | 20 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 0 437 | 0 438 | 800 439 | 21 440 | 441 | 442 | 443 | 444 | 445 | 446 | PXMTextEdit 447 | QTextEdit 448 |
pxmmainwindow.h
449 |
450 | 451 | PXMMessageViewer::StackedWidget 452 | QStackedWidget 453 |
pxmstackwidget.h
454 | 1 455 |
456 |
457 | 458 | 459 |
460 | -------------------------------------------------------------------------------- /ui/pxmsettingsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PXMSettingsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 546 10 | 186 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 16777215 22 | 16777215 23 | 24 | 25 | 26 | Settings 27 | 28 | 29 | 30 | 31 | 32 | 33 | 0 34 | 0 35 | 36 | 37 | 38 | 39 | 100 40 | 16777215 41 | 42 | 43 | 44 | 45 | General 46 | 47 | 48 | 49 | 50 | Network 51 | 52 | 53 | 54 | 55 | Advanced 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Qt::StrongFocus 64 | 65 | 66 | Qt::Horizontal 67 | 68 | 69 | QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults 70 | 71 | 72 | true 73 | 74 | 75 | 76 | 77 | 78 | 79 | 0 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Hostname 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 0 97 | 0 98 | 99 | 100 | 101 | Qt::RightToLeft 102 | 103 | 104 | 24 105 | 106 | 107 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 108 | 109 | 110 | 111 | 112 | 113 | 114 | Font 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 0 123 | 0 124 | 125 | 126 | 127 | Qt::LeftToRight 128 | 129 | 130 | 131 | 132 | 133 | 134 | Font Size 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 0 143 | 0 144 | 145 | 146 | 147 | 148 | 16777215 149 | 16777215 150 | 151 | 152 | 153 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 154 | 155 | 156 | 8 157 | 158 | 159 | 24 160 | 161 | 162 | 163 | 164 | 165 | 166 | Color Scheme 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | Default 175 | 176 | 177 | 178 | 179 | Dark 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | Auto Update 188 | 189 | 190 | 191 | 192 | 193 | 194 | Qt::RightToLeft 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | TCP Port number 213 | 214 | 215 | 216 | 217 | 218 | 219 | true 220 | 221 | 222 | 223 | 0 224 | 0 225 | 226 | 227 | 228 | 229 | 16777215 230 | 16777215 231 | 232 | 233 | 234 | Qt::LeftToRight 235 | 236 | 237 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 238 | 239 | 240 | true 241 | 242 | 243 | 65535 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 0 252 | 0 253 | 254 | 255 | 256 | 257 | 16777215 258 | 16777215 259 | 260 | 261 | 262 | Qt::LeftToRight 263 | 264 | 265 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 266 | 267 | 268 | true 269 | 270 | 271 | 65535 272 | 273 | 274 | 275 | 276 | 277 | 278 | UDP Port number 279 | 280 | 281 | 282 | 283 | 284 | 285 | Multicast Address 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 0 294 | 0 295 | 296 | 297 | 298 | Qt::RightToLeft 299 | 300 | 301 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | Logging Verbosity 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 0 325 | 0 326 | 327 | 328 | 329 | 330 | 16777215 331 | 16777215 332 | 333 | 334 | 335 | 336 | 100 337 | 10 338 | 339 | 340 | 341 | Qt::LeftToRight 342 | 343 | 344 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 345 | 346 | 347 | 2 348 | 349 | 350 | 351 | 352 | 353 | 354 | Enable Logging to File 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 0 363 | 0 364 | 365 | 366 | 367 | Qt::RightToLeft 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | Allow multiple instances 378 | 379 | 380 | 381 | 382 | 383 | 384 | true 385 | 386 | 387 | 388 | 0 389 | 0 390 | 391 | 392 | 393 | Qt::RightToLeft 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | buttonBox 412 | accepted() 413 | PXMSettingsDialog 414 | accept() 415 | 416 | 417 | 248 418 | 254 419 | 420 | 421 | 157 422 | 274 423 | 424 | 425 | 426 | 427 | buttonBox 428 | rejected() 429 | PXMSettingsDialog 430 | reject() 431 | 432 | 433 | 316 434 | 260 435 | 436 | 437 | 286 438 | 274 439 | 440 | 441 | 442 | 443 | listWidget 444 | currentRowChanged(int) 445 | stackedWidget 446 | setCurrentIndex(int) 447 | 448 | 449 | 58 450 | 163 451 | 452 | 453 | 326 454 | 163 455 | 456 | 457 | 458 | 459 | 460 | --------------------------------------------------------------------------------