Actually add old project files
This commit is contained in:
parent
c5ef6a6982
commit
210e40920c
18 changed files with 2047 additions and 0 deletions
70
qml/pages/ChoiceSetting.qml
Normal file
70
qml/pages/ChoiceSetting.qml
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
Setting {
|
||||||
|
property var choices
|
||||||
|
|
||||||
|
function prettifyChoice(name, value)
|
||||||
|
{
|
||||||
|
switch(name) {
|
||||||
|
case "print-quality":
|
||||||
|
switch(value) {
|
||||||
|
case 3:
|
||||||
|
return "draft";
|
||||||
|
case 4:
|
||||||
|
return "normal";
|
||||||
|
case 5:
|
||||||
|
return "high";
|
||||||
|
default:
|
||||||
|
return "unknown quality "+value
|
||||||
|
}
|
||||||
|
case "orientation-requested":
|
||||||
|
switch(value) {
|
||||||
|
case 3:
|
||||||
|
return "portrait";
|
||||||
|
case 4:
|
||||||
|
return "landscape";
|
||||||
|
case 5:
|
||||||
|
return "reverse landscape";
|
||||||
|
case 6:
|
||||||
|
return "reverse portrait";
|
||||||
|
default:
|
||||||
|
return "unknown orientation "+value
|
||||||
|
}
|
||||||
|
case "printer-resolution":
|
||||||
|
|
||||||
|
var units = "";
|
||||||
|
if(value.units==3) {
|
||||||
|
units="dpi";
|
||||||
|
} else if (units==4){
|
||||||
|
units="dots/cm"
|
||||||
|
}
|
||||||
|
return ""+value.x+"x"+value.y+units;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
ValueButton {
|
||||||
|
enabled: valid
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
label: prettyName
|
||||||
|
value: prettifyChoice(name, choice ? choice : default_choice)
|
||||||
|
onClicked: parent.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
property var menu: ContextMenu {
|
||||||
|
id: menu
|
||||||
|
Repeater {
|
||||||
|
model: choices
|
||||||
|
MenuItem {
|
||||||
|
text: prettifyChoice(name, choices[index])
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
choice = choices[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
52
qml/pages/InputDialog.qml
Normal file
52
qml/pages/InputDialog.qml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import seaprint.ippprinter 1.0
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: dialog
|
||||||
|
|
||||||
|
property string value
|
||||||
|
property string title
|
||||||
|
canAccept: label.text != label._default
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
DialogHeader { }
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: valueField
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: "192.168.1.1/ipp/print"
|
||||||
|
|
||||||
|
label: title
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: label
|
||||||
|
x: Theme.paddingLarge
|
||||||
|
property string _default: "No printer found"
|
||||||
|
text: _default
|
||||||
|
}
|
||||||
|
IppPrinter {
|
||||||
|
id: printer
|
||||||
|
url: valueField.text
|
||||||
|
onAttrsChanged: {
|
||||||
|
if(printer.attrs["printer-name"]) {
|
||||||
|
label.text = "Found: "+printer.attrs["printer-name"].value
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
label.text = label._default
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDone: {
|
||||||
|
if (result == DialogResult.Accepted) {
|
||||||
|
value = valueField.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
qml/pages/IntegerInputDialog.qml
Normal file
43
qml/pages/IntegerInputDialog.qml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
Dialog {
|
||||||
|
id: dialog
|
||||||
|
|
||||||
|
property int value
|
||||||
|
property int min;
|
||||||
|
property int max;
|
||||||
|
property string title
|
||||||
|
canAccept: valueField.acceptableInput
|
||||||
|
|
||||||
|
Component.onCompleted:
|
||||||
|
{
|
||||||
|
|
||||||
|
console.log("adasdsa", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
DialogHeader { }
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: valueField
|
||||||
|
validator: IntValidator{bottom: min; top: max;}
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: ""+min+"-"+max
|
||||||
|
label: title
|
||||||
|
focus: true
|
||||||
|
labelVisible: true
|
||||||
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDone: {
|
||||||
|
if (result == DialogResult.Accepted) {
|
||||||
|
value = valueField.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
qml/pages/IntegerSetting.qml
Normal file
50
qml/pages/IntegerSetting.qml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
Setting {
|
||||||
|
|
||||||
|
property int low
|
||||||
|
property int high
|
||||||
|
|
||||||
|
ValueButton {
|
||||||
|
enabled: valid
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
label: prettyName
|
||||||
|
value: choice ? choice : default_choice
|
||||||
|
onClicked: parent.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
property var menu: ContextMenu {
|
||||||
|
id: menu
|
||||||
|
MenuItem {
|
||||||
|
Slider
|
||||||
|
{
|
||||||
|
minimumValue: low
|
||||||
|
maximumValue: high < 100 ? high : 100
|
||||||
|
width: parent.width
|
||||||
|
stepSize: 1
|
||||||
|
value: choice
|
||||||
|
onValueChanged:
|
||||||
|
{
|
||||||
|
choice = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton
|
||||||
|
{
|
||||||
|
anchors.right: parent.right
|
||||||
|
icon.source: "image://theme/icon-s-edit"
|
||||||
|
onClicked: {var dialog = pageStack.push(Qt.resolvedUrl("IntegerInputDialog.qml"),
|
||||||
|
{value: choice, title: prettyName,
|
||||||
|
min: low, max: high});
|
||||||
|
dialog.accepted.connect(function() {
|
||||||
|
choice = dialog.value;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
130
qml/pages/PrinterPage.qml
Normal file
130
qml/pages/PrinterPage.qml
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import "utils.js" as Utils
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: page
|
||||||
|
property var printer
|
||||||
|
property var jobParams: new Object();
|
||||||
|
property string selectedFile
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log(JSON.stringify(printer.attrs))
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: printer
|
||||||
|
onJobAttrsChanged: {
|
||||||
|
notifier.notify(printer.jobAttrs["job-state-message"].value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To enable PullDownMenu, place our content in a SilicaFlickable
|
||||||
|
SilicaFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView
|
||||||
|
PullDownMenu {
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Print")
|
||||||
|
onClicked: {
|
||||||
|
console.log(JSON.stringify(jobParams))
|
||||||
|
printer.print(jobParams, page.selectedFile)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id:mod
|
||||||
|
ListElement {name: "sides"; prettyName: "Sides"; tag: 0x23}
|
||||||
|
ListElement {name: "copies"; prettyName: "Copies"; tag: 0x21}
|
||||||
|
// ListElement {name: "page-ranges"; prettyName: "Page range"; tag: 0x33}
|
||||||
|
ListElement {name: "print-color-mode"; prettyName: "Color mode"; tag: 0x23}
|
||||||
|
// ListElement {name: "orientation-requested"; prettyName: "Orientation"; tag: 0x23}
|
||||||
|
ListElement {name: "print-quality"; prettyName: "Quality"; tag: 0x23}
|
||||||
|
// Bleh, can't create the json object with another object as value, for whatever reason
|
||||||
|
ListElement {name: "printer-resolution"; prettyName: "Resolution"; tag: 0x32}
|
||||||
|
}
|
||||||
|
|
||||||
|
SilicaListView {
|
||||||
|
id: listView
|
||||||
|
model: mod
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
width: parent.width
|
||||||
|
header: PageHeader {
|
||||||
|
id: pageHeader
|
||||||
|
title: printer.attrs["printer-name"].value
|
||||||
|
description: selectedFile
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
delegate: ListItem {
|
||||||
|
id: delegate
|
||||||
|
property alias loaderItem: loader.item
|
||||||
|
|
||||||
|
openMenuOnPressAndHold: false
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: loader
|
||||||
|
anchors.fill: parent
|
||||||
|
property var menu
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("handling", tag, name, prettyName, JSON.stringify(printer.attrs[name+"-supported"]), JSON.stringify(printer.attrs[name+"-default"]))
|
||||||
|
switch(tag) {
|
||||||
|
case 0x21:
|
||||||
|
loader.setSource("IntegerSetting.qml",
|
||||||
|
{name: name,
|
||||||
|
prettyName: prettyName,
|
||||||
|
tag: tag,
|
||||||
|
low: printer.attrs[name+"-supported"].value.low,
|
||||||
|
high: printer.attrs[name+"-supported"].value.high,
|
||||||
|
default_choice: printer.attrs[name+"-default"].value
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 0x33:
|
||||||
|
loader.setSource("RangeSetting.qml",
|
||||||
|
{name: name,
|
||||||
|
prettyName: prettyName,
|
||||||
|
valid: false, //TODO
|
||||||
|
tag: 0x33 // integer-range
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 0x32:
|
||||||
|
case 0x23:
|
||||||
|
loader.setSource("ChoiceSetting.qml",
|
||||||
|
{name: name,
|
||||||
|
prettyName: prettyName,
|
||||||
|
tag: tag,
|
||||||
|
choices: printer.attrs[name+"-supported"].value,
|
||||||
|
default_choice: printer.attrs[name+"-default"].value
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoaderItemChanged: {
|
||||||
|
menu = loaderItem.menu
|
||||||
|
loaderItem.clicked.connect(function() {
|
||||||
|
openMenu()
|
||||||
|
})
|
||||||
|
loaderItem.choiceMade.connect(function(tag, choice) {
|
||||||
|
console.log("choice changed", tag, JSON.stringify(choice))
|
||||||
|
jobParams[name] = {tag: tag, value: choice};
|
||||||
|
console.log(JSON.stringify(jobParams));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
VerticalScrollDecorator {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
qml/pages/RangeSetting.qml
Normal file
30
qml/pages/RangeSetting.qml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
Setting {
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
property int low
|
||||||
|
property int high
|
||||||
|
property int choice_low
|
||||||
|
property int choice_high
|
||||||
|
|
||||||
|
ValueButton {
|
||||||
|
enabled: valid
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
label: prettyName
|
||||||
|
value: choice ? choice : default_choice
|
||||||
|
onClicked: parent.clicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
property var menu: ContextMenu {
|
||||||
|
id: menu
|
||||||
|
MenuItem {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
21
qml/pages/Setting.qml
Normal file
21
qml/pages/Setting.qml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
property string name
|
||||||
|
property string prettyName
|
||||||
|
property int tag
|
||||||
|
property bool valid: true
|
||||||
|
|
||||||
|
property var choice
|
||||||
|
property var default_choice
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
signal choiceMade(int tag, var choice)
|
||||||
|
onChoiceChanged: choiceMade(tag, choice)
|
||||||
|
|
||||||
|
property var menu
|
||||||
|
|
||||||
|
}
|
56
qml/pages/printer.svg
Normal file
56
qml/pages/printer.svg
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="487.381px" height="487.381px" viewBox="0 0 487.381 487.381" style="enable-background:new 0 0 487.381 487.381;"
|
||||||
|
xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M400.1,100.918h-9.871V11.646C390.229,5.215,385.014,0,378.582,0H108.804c-6.436,0-11.646,5.215-11.646,11.646v89.271
|
||||||
|
h-9.877c-38.523,0-69.871,31.344-69.871,69.871v144.258c0,38.528,31.348,69.87,69.871,69.87h9.877v90.818
|
||||||
|
c0,6.432,5.21,11.646,11.646,11.646h269.778c6.432,0,11.646-5.215,11.646-11.646v-90.818h9.871
|
||||||
|
c38.523,0,69.871-31.342,69.871-69.87V170.789C469.971,132.261,438.623,100.918,400.1,100.918z M120.449,23.291h246.489v77.627
|
||||||
|
H120.449V23.291z M366.938,464.092H120.449V320.465h246.489V464.092z M446.682,315.046c0,25.687-20.896,46.581-46.582,46.581
|
||||||
|
h-9.871v-41.162h25.186c6.436,0,11.645-5.214,11.645-11.646c0-6.43-5.209-11.645-11.645-11.645h-36.832H108.804H75.421
|
||||||
|
c-6.431,0-11.646,5.215-11.646,11.645c0,6.433,5.215,11.646,11.646,11.646h21.737v41.162h-9.877
|
||||||
|
c-25.685,0-46.581-20.896-46.581-46.581V170.789c0-25.685,20.896-46.58,46.581-46.58h21.522H378.58h21.52
|
||||||
|
c25.684,0,46.58,20.896,46.58,46.58v144.257H446.682z"/>
|
||||||
|
<path d="M378.582,170.646c-8.771,0-15.893,7.119-15.893,15.893s7.119,15.893,15.893,15.893c8.775,0,15.895-7.118,15.895-15.893
|
||||||
|
C394.475,177.765,387.357,170.646,378.582,170.646z"/>
|
||||||
|
<path d="M147.618,360.734h192.146c6.432,0,11.646-5.213,11.646-11.646c0-6.43-5.215-11.645-11.646-11.645H147.618
|
||||||
|
c-6.431,0-11.646,5.215-11.646,11.645C135.972,355.521,141.187,360.734,147.618,360.734z"/>
|
||||||
|
<path d="M147.618,403.926h192.146c6.432,0,11.646-5.217,11.646-11.646c0-6.432-5.215-11.645-11.646-11.645H147.618
|
||||||
|
c-6.431,0-11.646,5.213-11.646,11.645S141.187,403.926,147.618,403.926z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
45
qml/pages/utils.js
Normal file
45
qml/pages/utils.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
function supported_formats(printer)
|
||||||
|
{
|
||||||
|
return _formats(printer).supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_filters(printer)
|
||||||
|
{
|
||||||
|
return _formats(printer).filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _formats(printer)
|
||||||
|
{
|
||||||
|
var formats = printer.attrs["document-format-supported"].value;
|
||||||
|
var supported = [];
|
||||||
|
var filters = [];
|
||||||
|
if(has(formats, "application/pdf"))
|
||||||
|
{
|
||||||
|
supported.push("PDF");
|
||||||
|
filters.push("*.pdf");
|
||||||
|
}
|
||||||
|
if(has(formats, "image/jpeg"))
|
||||||
|
{
|
||||||
|
supported.push("JPEG");
|
||||||
|
filters.push("*.jpg");
|
||||||
|
filters.push("*.jpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {supported: supported.join(" "), filters: filters};
|
||||||
|
}
|
||||||
|
|
||||||
|
function has(arrayish, what)
|
||||||
|
{
|
||||||
|
for(var i in arrayish)
|
||||||
|
{
|
||||||
|
if(arrayish[i] == what)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function can_print(printer, fileName)
|
||||||
|
{
|
||||||
|
// Move to ippprinter?
|
||||||
|
return format_filters(printer).indexOf("*."+fileName.split('.').pop()) != -1;
|
||||||
|
}
|
67
rpm/harbour-seaprint.spec
Normal file
67
rpm/harbour-seaprint.spec
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#
|
||||||
|
# Do NOT Edit the Auto-generated Part!
|
||||||
|
# Generated by: spectacle version 0.27
|
||||||
|
#
|
||||||
|
|
||||||
|
Name: harbour-seaprint
|
||||||
|
|
||||||
|
# >> macros
|
||||||
|
# << macros
|
||||||
|
|
||||||
|
Summary: SeaPrint
|
||||||
|
Version: 0.1
|
||||||
|
Release: 1
|
||||||
|
Group: Qt/Qt
|
||||||
|
License: LICENSE
|
||||||
|
URL: http://example.org/
|
||||||
|
Source0: %{name}-%{version}.tar.bz2
|
||||||
|
Source100: harbour-seaprint.yaml
|
||||||
|
Requires: sailfishsilica-qt5 >= 0.10.9
|
||||||
|
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
|
||||||
|
BuildRequires: pkgconfig(Qt5Core)
|
||||||
|
BuildRequires: pkgconfig(Qt5Qml)
|
||||||
|
BuildRequires: pkgconfig(Qt5Quick)
|
||||||
|
BuildRequires: desktop-file-utils
|
||||||
|
|
||||||
|
%description
|
||||||
|
Short description of my Sailfish OS Application
|
||||||
|
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -n %{name}-%{version}
|
||||||
|
|
||||||
|
# >> setup
|
||||||
|
# << setup
|
||||||
|
|
||||||
|
%build
|
||||||
|
# >> build pre
|
||||||
|
# << build pre
|
||||||
|
|
||||||
|
%qmake5
|
||||||
|
|
||||||
|
make %{?_smp_mflags}
|
||||||
|
|
||||||
|
# >> build post
|
||||||
|
# << build post
|
||||||
|
|
||||||
|
%install
|
||||||
|
rm -rf %{buildroot}
|
||||||
|
# >> install pre
|
||||||
|
# << install pre
|
||||||
|
%qmake5_install
|
||||||
|
|
||||||
|
# >> install post
|
||||||
|
# << install post
|
||||||
|
|
||||||
|
desktop-file-install --delete-original \
|
||||||
|
--dir %{buildroot}%{_datadir}/applications \
|
||||||
|
%{buildroot}%{_datadir}/applications/*.desktop
|
||||||
|
|
||||||
|
%files
|
||||||
|
%defattr(-,root,root,-)
|
||||||
|
%{_bindir}
|
||||||
|
%{_datadir}/%{name}
|
||||||
|
%{_datadir}/applications/%{name}.desktop
|
||||||
|
%{_datadir}/icons/hicolor/*/apps/%{name}.png
|
||||||
|
# >> files
|
||||||
|
# << files
|
553
src/bytestream.cpp
Normal file
553
src/bytestream.cpp
Normal file
|
@ -0,0 +1,553 @@
|
||||||
|
#include "bytestream.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
Bytestream::Bytestream()
|
||||||
|
{
|
||||||
|
_size = 0;
|
||||||
|
_pos = 0;
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
_endianness = BigEndian;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream::Bytestream(size_t len)
|
||||||
|
{
|
||||||
|
_size = len;
|
||||||
|
_data = new uint8_t[_size];
|
||||||
|
memset(_data, 0, _size);
|
||||||
|
_pos = 0;
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
_endianness = BigEndian;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream::Bytestream(const void* data, size_t len)
|
||||||
|
{
|
||||||
|
_size = len;
|
||||||
|
_data = new uint8_t[_size];
|
||||||
|
memcpy(_data, data, _size);
|
||||||
|
_pos = 0;
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
_endianness = BigEndian;
|
||||||
|
}
|
||||||
|
Bytestream::Bytestream(const void* data, size_t len, Endianness e)
|
||||||
|
{
|
||||||
|
_size = len;
|
||||||
|
_data = new uint8_t[_size];
|
||||||
|
memcpy(_data, data, _size);
|
||||||
|
_pos = 0;
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
_endianness = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream::Bytestream(const Bytestream& rhs)
|
||||||
|
{
|
||||||
|
_size = rhs._size;
|
||||||
|
_data = new uint8_t[_size];
|
||||||
|
memcpy(_data, rhs._data, _size);
|
||||||
|
_pos = rhs._pos;
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
_endianness = rhs._endianness;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream::~Bytestream()
|
||||||
|
{
|
||||||
|
if(_size != 0)
|
||||||
|
{
|
||||||
|
delete _data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bytestream::operator==(const Bytestream& other) const
|
||||||
|
{
|
||||||
|
if(_size != other.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return memcmp(_data, other.raw(), _size) == 0;
|
||||||
|
}
|
||||||
|
bool Bytestream::operator!=(const Bytestream& other) const
|
||||||
|
{
|
||||||
|
if(_size != other.size())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return memcmp(_data, other.raw(), _size) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator=(const Bytestream& other)
|
||||||
|
{
|
||||||
|
if(_size != 0)
|
||||||
|
{
|
||||||
|
delete _data;
|
||||||
|
}
|
||||||
|
_pos = other.pos();
|
||||||
|
_size = other.size();
|
||||||
|
_data = new uint8_t[_size];
|
||||||
|
memcpy(_data, other.raw(), _size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T bswap(T u)
|
||||||
|
{
|
||||||
|
uint8_t* const p = reinterpret_cast<uint8_t*>(&u);
|
||||||
|
for (size_t i = 0; i < sizeof(T) / 2; i++)
|
||||||
|
{
|
||||||
|
std::swap(p[i], p[sizeof(T) - i - 1]);
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET(type, shorthand, len) GET_(type##len##_t, shorthand##len, len)
|
||||||
|
#define GET_(type, shortType, len) \
|
||||||
|
type Bytestream::get##shortType() \
|
||||||
|
{type tmp; getBytes(&tmp, sizeof(type));\
|
||||||
|
if(needsSwap()){tmp=bswap(tmp);} return tmp;}
|
||||||
|
|
||||||
|
GET(uint, U, 8)
|
||||||
|
GET(uint, U, 16)
|
||||||
|
GET(uint, U, 32)
|
||||||
|
GET(uint, U, 64)
|
||||||
|
GET(int, S, 8)
|
||||||
|
GET(int, S, 16)
|
||||||
|
GET(int, S, 32)
|
||||||
|
GET(int, S, 64)
|
||||||
|
GET(float, F, 32)
|
||||||
|
GET(float, F, 64)
|
||||||
|
|
||||||
|
std::string Bytestream::getString()
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
throw invalid_argument("No length given");
|
||||||
|
}
|
||||||
|
char* cs = new char[_noOfNextBytes+1];
|
||||||
|
cs[_noOfNextBytes] = 0;
|
||||||
|
getBytes(cs, _noOfNextBytes);
|
||||||
|
string s = std::string(cs, _noOfNextBytes);
|
||||||
|
delete cs;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
Bytestream Bytestream::getBytestream()
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
throw invalid_argument("No length given");
|
||||||
|
}
|
||||||
|
uint8_t* cs = new uint8_t[_noOfNextBytes];
|
||||||
|
getBytes(cs, _noOfNextBytes);
|
||||||
|
Bytestream other = Bytestream(cs, _noOfNextBytes);
|
||||||
|
delete cs;
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
std::string Bytestream::getString(size_t len)
|
||||||
|
{
|
||||||
|
if(_noOfNextBytesValid && len != _noOfNextBytes)
|
||||||
|
{
|
||||||
|
throw logic_error("Desired lengths does not match");
|
||||||
|
}
|
||||||
|
else if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(len);
|
||||||
|
}
|
||||||
|
return getString();
|
||||||
|
}
|
||||||
|
Bytestream Bytestream::getBytestream(size_t len)
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid && len != _noOfNextBytes)
|
||||||
|
{
|
||||||
|
throw logic_error("Desired lengths does not match");
|
||||||
|
}
|
||||||
|
setNoOfNextBytes(len);
|
||||||
|
return getBytestream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytestream::getBytes(void* cs, size_t len)
|
||||||
|
{
|
||||||
|
_before(len);
|
||||||
|
memcpy(cs, &(_data[_pos]), len);
|
||||||
|
_after(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PEEK(type, shorthand, len) PEEK_(type##len##_t, shorthand##len, len)
|
||||||
|
#define PEEK_(type, shortType, len) \
|
||||||
|
type Bytestream::peek##shortType() \
|
||||||
|
{type tmp; getBytes(&tmp, sizeof(type));\
|
||||||
|
if(needsSwap()){tmp=bswap(tmp);} (*this) -= sizeof(type); return tmp;}
|
||||||
|
|
||||||
|
PEEK(uint, U, 8)
|
||||||
|
PEEK(uint, U, 16)
|
||||||
|
PEEK(uint, U, 32)
|
||||||
|
PEEK(uint, U, 64)
|
||||||
|
PEEK(int, S, 8)
|
||||||
|
PEEK(int, S, 16)
|
||||||
|
PEEK(int, S, 32)
|
||||||
|
PEEK(int, S, 64)
|
||||||
|
PEEK(float, F, 32)
|
||||||
|
PEEK(float, F, 64)
|
||||||
|
|
||||||
|
std::string Bytestream::peekString()
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
throw invalid_argument("No length given");
|
||||||
|
}
|
||||||
|
char* cs = new char[_noOfNextBytes+1];
|
||||||
|
cs[_noOfNextBytes] = 0;
|
||||||
|
getBytes(cs, _noOfNextBytes);
|
||||||
|
string s = std::string(cs, _noOfNextBytes);
|
||||||
|
delete cs;
|
||||||
|
(*this) -= _noOfNextBytes;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
Bytestream Bytestream::peekBytestream()
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
throw invalid_argument("No length given");
|
||||||
|
}
|
||||||
|
uint8_t* cs = new uint8_t[_noOfNextBytes];
|
||||||
|
getBytes(cs, _noOfNextBytes);
|
||||||
|
Bytestream other = Bytestream(cs, _noOfNextBytes);
|
||||||
|
delete cs;
|
||||||
|
(*this) -= _noOfNextBytes;
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
std::string Bytestream::peekString(size_t len)
|
||||||
|
{
|
||||||
|
if(_noOfNextBytesValid && len != _noOfNextBytes)
|
||||||
|
{
|
||||||
|
throw logic_error("Desired lengths does not match");
|
||||||
|
}
|
||||||
|
else if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(len);
|
||||||
|
}
|
||||||
|
return peekString();
|
||||||
|
}
|
||||||
|
Bytestream Bytestream::peekBytestream(size_t len)
|
||||||
|
{
|
||||||
|
if(!_noOfNextBytesValid && len != _noOfNextBytes)
|
||||||
|
{
|
||||||
|
throw logic_error("Desired lengths does not match");
|
||||||
|
}
|
||||||
|
setNoOfNextBytes(len);
|
||||||
|
return peekBytestream();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NEXT(type, shorthand, len) NEXT_(type##len##_t, shorthand##len)
|
||||||
|
#define NEXT_(type, shortType) \
|
||||||
|
bool Bytestream::next##shortType(type u) \
|
||||||
|
{if(u == get##shortType())\
|
||||||
|
{return true;} \
|
||||||
|
else\
|
||||||
|
{(*this) -= sizeof(type);\
|
||||||
|
return false;}}
|
||||||
|
|
||||||
|
NEXT(uint, U, 8)
|
||||||
|
NEXT(uint, U, 16)
|
||||||
|
NEXT(uint, U, 32)
|
||||||
|
NEXT(uint, U, 64)
|
||||||
|
NEXT(int, S, 8)
|
||||||
|
NEXT(int, S, 16)
|
||||||
|
NEXT(int, S, 32)
|
||||||
|
NEXT(int, S, 64)
|
||||||
|
NEXT(float, F, 32)
|
||||||
|
NEXT(float, F, 64)
|
||||||
|
|
||||||
|
bool Bytestream::nextString(const std::string& s)
|
||||||
|
{
|
||||||
|
if(_noOfNextBytesValid && getNoOfNextBytes() != s.length())
|
||||||
|
{
|
||||||
|
throw logic_error("Desired length does not match const length");
|
||||||
|
}
|
||||||
|
else if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t noOfNextBytes = getNoOfNextBytes();
|
||||||
|
|
||||||
|
if(noOfNextBytes > remaining())
|
||||||
|
{
|
||||||
|
invalidateNoOfNextBytes();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getString() == s)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*this) -= noOfNextBytes;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Bytestream::nextBytestream(const Bytestream& other)
|
||||||
|
{
|
||||||
|
if(_noOfNextBytesValid && getNoOfNextBytes() != other.size())
|
||||||
|
{
|
||||||
|
throw logic_error("Desired length does not match const length");
|
||||||
|
}
|
||||||
|
else if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(other.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t noOfNextBytes = getNoOfNextBytes();
|
||||||
|
|
||||||
|
if(noOfNextBytes > remaining())
|
||||||
|
{
|
||||||
|
invalidateNoOfNextBytes();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getBytestream() == other)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*this) -= noOfNextBytes;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PUT(type, shorthand, len) PUT_(type##len##_t, shorthand##len, len)
|
||||||
|
#define PUT_(type, shortType, len) \
|
||||||
|
void Bytestream::put##shortType(type u) \
|
||||||
|
{if(needsSwap()){u=bswap(u);} \
|
||||||
|
putBytes(&u, sizeof(u));}
|
||||||
|
|
||||||
|
PUT(uint, U, 8)
|
||||||
|
PUT(uint, U, 16)
|
||||||
|
PUT(uint, U, 32)
|
||||||
|
PUT(uint, U, 64)
|
||||||
|
PUT(int, S, 8)
|
||||||
|
PUT(int, S, 16)
|
||||||
|
PUT(int, S, 32)
|
||||||
|
PUT(int, S, 64)
|
||||||
|
PUT(float, F, 32)
|
||||||
|
PUT(float, F, 64)
|
||||||
|
|
||||||
|
void Bytestream::putString(const std::string& s)
|
||||||
|
{
|
||||||
|
putBytes(s.c_str(), s.length());
|
||||||
|
}
|
||||||
|
void Bytestream::putBytestream(const Bytestream& other)
|
||||||
|
{
|
||||||
|
putBytes(other.raw(), other.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytestream::putBytes(const void* c, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t* old = _data;
|
||||||
|
_data = new uint8_t[_size+len];
|
||||||
|
|
||||||
|
if (_size != 0)
|
||||||
|
{
|
||||||
|
memcpy(_data, old, _size);
|
||||||
|
delete old;
|
||||||
|
}
|
||||||
|
memcpy((_data+_size), c, len);
|
||||||
|
_size += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytestream::setNoOfNextBytes(size_t n)
|
||||||
|
{
|
||||||
|
_noOfNextBytes = n;
|
||||||
|
_noOfNextBytesValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytestream::invalidateNoOfNextBytes()
|
||||||
|
{
|
||||||
|
_noOfNextBytes = 0;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bytestream::_before(size_t bytesToRead)
|
||||||
|
{
|
||||||
|
if(bytesToRead > remaining())
|
||||||
|
{
|
||||||
|
invalidateNoOfNextBytes();
|
||||||
|
throw out_of_range("Tried to read past end");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytestream::_after(size_t bytesRead)
|
||||||
|
{
|
||||||
|
_pos += bytesRead;
|
||||||
|
_noOfNextBytesValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream Bytestream::operator[](size_t i)
|
||||||
|
{
|
||||||
|
Bytestream tmp(_data+i, _size-i);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator+=(size_t i)
|
||||||
|
{
|
||||||
|
if((_pos+i) > _size)
|
||||||
|
{
|
||||||
|
invalidateNoOfNextBytes();
|
||||||
|
throw out_of_range("Tried to address data past end");
|
||||||
|
}
|
||||||
|
_pos += i;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator-=(size_t i)
|
||||||
|
{
|
||||||
|
_pos -= i;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator/(int i)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(i);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PUTOP(type, shorthand, len) PUTOP_(type##len##_t, shorthand##len)
|
||||||
|
#define PUTOP_(type, shortType) \
|
||||||
|
Bytestream& Bytestream::operator<<(const type& u) \
|
||||||
|
{put##shortType(u); return *this;}
|
||||||
|
|
||||||
|
PUTOP(uint, U, 8)
|
||||||
|
PUTOP(uint, U, 16)
|
||||||
|
PUTOP(uint, U, 32)
|
||||||
|
PUTOP(uint, U, 64)
|
||||||
|
PUTOP(int, S, 8)
|
||||||
|
PUTOP(int, S, 16)
|
||||||
|
PUTOP(int, S, 32)
|
||||||
|
PUTOP(int, S, 64)
|
||||||
|
PUTOP(float, F, 32)
|
||||||
|
PUTOP(float, F, 64)
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator<<(const std::string& s)
|
||||||
|
{
|
||||||
|
putString(s);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Bytestream& Bytestream::operator<<(const Bytestream& other)
|
||||||
|
{
|
||||||
|
putBytestream(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GETOP(type, shorthand, len) GETOP_(type##len##_t, shorthand##len)
|
||||||
|
#define GETOP_(type, shortType) \
|
||||||
|
Bytestream& Bytestream::operator>>(type& u) \
|
||||||
|
{u = get##shortType(); return *this;}
|
||||||
|
|
||||||
|
GETOP(uint, U, 8)
|
||||||
|
GETOP(uint, U, 16)
|
||||||
|
GETOP(uint, U, 32)
|
||||||
|
GETOP(uint, U, 64)
|
||||||
|
GETOP(int, S, 8)
|
||||||
|
GETOP(int, S, 16)
|
||||||
|
GETOP(int, S, 32)
|
||||||
|
GETOP(int, S, 64)
|
||||||
|
GETOP(float, F, 32)
|
||||||
|
GETOP(float, F, 64)
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator>>(std::string& s)
|
||||||
|
{
|
||||||
|
s = getString();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
Bytestream& Bytestream::operator>>(Bytestream& other)
|
||||||
|
{
|
||||||
|
other = getBytestream();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GETOP_CONST(type, shorthand, len) \
|
||||||
|
GETOP_CONST_(type##len##_t, shorthand##len)
|
||||||
|
#define GETOP_CONST_(type, shortType) \
|
||||||
|
Bytestream& Bytestream::operator>>(const type& u) \
|
||||||
|
{type v = get##shortType();\
|
||||||
|
if(u!=v) {(*this) -= sizeof(type);\
|
||||||
|
throw Badmatch("Does not match const", v, u);}\
|
||||||
|
else{return *this;}}
|
||||||
|
|
||||||
|
GETOP_CONST(uint, U, 8)
|
||||||
|
GETOP_CONST(uint, U, 16)
|
||||||
|
GETOP_CONST(uint, U, 32)
|
||||||
|
GETOP_CONST(uint, U, 64)
|
||||||
|
GETOP_CONST(int, S, 8)
|
||||||
|
GETOP_CONST(int, S, 16)
|
||||||
|
GETOP_CONST(int, S, 32)
|
||||||
|
GETOP_CONST(int, S, 64)
|
||||||
|
GETOP_CONST(float, F, 32)
|
||||||
|
GETOP_CONST(float, F, 64)
|
||||||
|
|
||||||
|
Bytestream& Bytestream::operator>>(const std::string& s)
|
||||||
|
{
|
||||||
|
if (_noOfNextBytesValid && getNoOfNextBytes() != s.length())
|
||||||
|
{
|
||||||
|
throw logic_error("Desired length does not match const length");
|
||||||
|
}
|
||||||
|
else if(!_noOfNextBytesValid)
|
||||||
|
{
|
||||||
|
setNoOfNextBytes(s.length());
|
||||||
|
}
|
||||||
|
std::string sv = getString();
|
||||||
|
if(sv != s)
|
||||||
|
{
|
||||||
|
(*this) -= s.length();
|
||||||
|
throw Badmatch("Does not match const", sv, s);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NEXTOP(type, shorthand, len) NEXTOP_(type##len##_t, shorthand##len)
|
||||||
|
#define NEXTOP_(type, shortType) \
|
||||||
|
bool Bytestream::operator>>=(const type& u) \
|
||||||
|
{return next##shortType(u);}
|
||||||
|
|
||||||
|
NEXTOP(uint, U, 8)
|
||||||
|
NEXTOP(uint, U, 16)
|
||||||
|
NEXTOP(uint, U, 32)
|
||||||
|
NEXTOP(uint, U, 64)
|
||||||
|
NEXTOP(int, S, 8)
|
||||||
|
NEXTOP(int, S, 16)
|
||||||
|
NEXTOP(int, S, 32)
|
||||||
|
NEXTOP(int, S, 64)
|
||||||
|
NEXTOP(float, F, 32)
|
||||||
|
NEXTOP(float, F, 64)
|
||||||
|
bool Bytestream::operator>>=(const std::string& s)
|
||||||
|
{
|
||||||
|
return nextString(s);
|
||||||
|
}
|
||||||
|
bool Bytestream::operator>>=(const Bytestream& other)
|
||||||
|
{
|
||||||
|
return nextBytestream(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
bool Bytestream::needsSwap()
|
||||||
|
{
|
||||||
|
return _endianness != Endianness::NativeEndian
|
||||||
|
&& _endianness != Endianness::LittleEndian;
|
||||||
|
}
|
||||||
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
bool Bytestream::needsSwap()
|
||||||
|
{
|
||||||
|
return _endianness != Endianness::NativeEndian
|
||||||
|
&& _endianness != Endianness::BigEndian;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error
|
||||||
|
#endif
|
189
src/bytestream.h
Normal file
189
src/bytestream.h
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#ifndef BYTESTREAM_H
|
||||||
|
#define BYTESTREAM_H
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#ifndef __STDC_IEC_559__
|
||||||
|
#error "Double must be IEEE 754"
|
||||||
|
#endif
|
||||||
|
#define float32_t float
|
||||||
|
#define float64_t double
|
||||||
|
class Bytestream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Badmatch : public std::invalid_argument::invalid_argument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Badmatch(std::string s, std::string v, std::string u) :
|
||||||
|
invalid_argument(s+": "+v+" != "+u) {}
|
||||||
|
template<typename T>
|
||||||
|
Badmatch(std::string s, T v, T u) :
|
||||||
|
invalid_argument(s+": "+std::to_string(v)+" != "+std::to_string(u)) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Endianness {
|
||||||
|
NativeEndian,
|
||||||
|
BigEndian,
|
||||||
|
LittleEndian
|
||||||
|
};
|
||||||
|
|
||||||
|
Bytestream();
|
||||||
|
Bytestream(size_t len);
|
||||||
|
Bytestream(const void* data, size_t len);
|
||||||
|
Bytestream(const void* data, size_t len, Endianness e);
|
||||||
|
Bytestream(const Bytestream& rhs);
|
||||||
|
~Bytestream();
|
||||||
|
|
||||||
|
bool operator==(const Bytestream& other) const;
|
||||||
|
bool operator!=(const Bytestream& other) const;
|
||||||
|
|
||||||
|
Bytestream& operator=(const Bytestream& other);
|
||||||
|
|
||||||
|
uint8_t* raw() const {return _data;}
|
||||||
|
size_t size() const {return _size;}
|
||||||
|
size_t pos() const {return _pos;}
|
||||||
|
size_t remaining() const {return _size - _pos;}
|
||||||
|
bool atEnd() const {return _pos >= _size;}
|
||||||
|
void setPos(size_t pos) {_pos = pos;}
|
||||||
|
Endianness getEndianness() {return _endianness;}
|
||||||
|
void setEndianness(Endianness e) {_endianness = e;}
|
||||||
|
|
||||||
|
uint8_t getU8();
|
||||||
|
uint16_t getU16();
|
||||||
|
uint32_t getU32();
|
||||||
|
uint64_t getU64();
|
||||||
|
int8_t getS8();
|
||||||
|
int16_t getS16();
|
||||||
|
int32_t getS32();
|
||||||
|
int64_t getS64();
|
||||||
|
float32_t getF32();
|
||||||
|
float64_t getF64();
|
||||||
|
std::string getString();
|
||||||
|
Bytestream getBytestream();
|
||||||
|
std::string getString(size_t len);
|
||||||
|
Bytestream getBytestream(size_t len);
|
||||||
|
void getBytes(void* cs, size_t len);
|
||||||
|
|
||||||
|
uint8_t peekU8();
|
||||||
|
uint16_t peekU16();
|
||||||
|
uint32_t peekU32();
|
||||||
|
uint64_t peekU64();
|
||||||
|
int8_t peekS8();
|
||||||
|
int16_t peekS16();
|
||||||
|
int32_t peekS32();
|
||||||
|
int64_t peekS64();
|
||||||
|
float32_t peekF32();
|
||||||
|
float64_t peekF64();
|
||||||
|
std::string peekString();
|
||||||
|
Bytestream peekBytestream();
|
||||||
|
std::string peekString(size_t len);
|
||||||
|
Bytestream peekBytestream(size_t len);
|
||||||
|
|
||||||
|
bool nextU8(uint8_t);
|
||||||
|
bool nextU16(uint16_t);
|
||||||
|
bool nextU32(uint32_t);
|
||||||
|
bool nextU64(uint64_t);
|
||||||
|
bool nextS8(int8_t);
|
||||||
|
bool nextS16(int16_t);
|
||||||
|
bool nextS32(int32_t);
|
||||||
|
bool nextS64(int64_t);
|
||||||
|
bool nextF32(float32_t);
|
||||||
|
bool nextF64(float64_t);
|
||||||
|
bool nextString(const std::string& bts);
|
||||||
|
bool nextBytestream(const Bytestream& bts);
|
||||||
|
|
||||||
|
void putU8(uint8_t);
|
||||||
|
void putU16(uint16_t);
|
||||||
|
void putU32(uint32_t);
|
||||||
|
void putU64(uint64_t);
|
||||||
|
void putS8(int8_t);
|
||||||
|
void putS16(int16_t);
|
||||||
|
void putS32(int32_t);
|
||||||
|
void putS64(int64_t);
|
||||||
|
void putF32(float32_t);
|
||||||
|
void putF64(float64_t);
|
||||||
|
void putString(const std::string&);
|
||||||
|
void putBytestream(const Bytestream&);
|
||||||
|
void putBytes(const void* c, size_t len);
|
||||||
|
|
||||||
|
void setNoOfNextBytes(size_t n);
|
||||||
|
void invalidateNoOfNextBytes();
|
||||||
|
size_t getNoOfNextBytes() {return _noOfNextBytes;}
|
||||||
|
bool noOfNextBytesValid() const {return _noOfNextBytesValid;}
|
||||||
|
|
||||||
|
Bytestream operator[](size_t i);
|
||||||
|
Bytestream& operator+=(size_t i);
|
||||||
|
Bytestream& operator-=(size_t i);
|
||||||
|
|
||||||
|
Bytestream& operator/(int i);
|
||||||
|
|
||||||
|
Bytestream& operator<<(const uint8_t& u);
|
||||||
|
Bytestream& operator<<(const uint16_t& u);
|
||||||
|
Bytestream& operator<<(const uint32_t& u);
|
||||||
|
Bytestream& operator<<(const uint64_t& u);
|
||||||
|
Bytestream& operator<<(const int8_t& u);
|
||||||
|
Bytestream& operator<<(const int16_t& u);
|
||||||
|
Bytestream& operator<<(const int32_t& u);
|
||||||
|
Bytestream& operator<<(const int64_t& u);
|
||||||
|
Bytestream& operator<<(const float32_t& u);
|
||||||
|
Bytestream& operator<<(const float64_t& u);
|
||||||
|
Bytestream& operator<<(const std::string& s);
|
||||||
|
Bytestream& operator<<(const Bytestream& other);
|
||||||
|
|
||||||
|
Bytestream& operator>>(uint8_t& u);
|
||||||
|
Bytestream& operator>>(uint16_t& u);
|
||||||
|
Bytestream& operator>>(uint32_t& u);
|
||||||
|
Bytestream& operator>>(uint64_t& u);
|
||||||
|
Bytestream& operator>>(int8_t& u);
|
||||||
|
Bytestream& operator>>(int16_t& u);
|
||||||
|
Bytestream& operator>>(int32_t& u);
|
||||||
|
Bytestream& operator>>(int64_t& u);
|
||||||
|
Bytestream& operator>>(float32_t& u);
|
||||||
|
Bytestream& operator>>(float64_t& u);
|
||||||
|
Bytestream& operator>>(std::string& s);
|
||||||
|
Bytestream& operator>>(Bytestream& other);
|
||||||
|
|
||||||
|
Bytestream& operator>>(const uint8_t& u);
|
||||||
|
Bytestream& operator>>(const uint16_t& u);
|
||||||
|
Bytestream& operator>>(const uint32_t& u);
|
||||||
|
Bytestream& operator>>(const uint64_t& u);
|
||||||
|
Bytestream& operator>>(const int8_t& u);
|
||||||
|
Bytestream& operator>>(const int16_t& u);
|
||||||
|
Bytestream& operator>>(const int32_t& u);
|
||||||
|
Bytestream& operator>>(const int64_t& u);
|
||||||
|
Bytestream& operator>>(const float32_t& u);
|
||||||
|
Bytestream& operator>>(const float64_t& u);
|
||||||
|
Bytestream& operator>>(const std::string& s);
|
||||||
|
|
||||||
|
bool operator>>=(const uint8_t& u);
|
||||||
|
bool operator>>=(const uint16_t& u);
|
||||||
|
bool operator>>=(const uint32_t& u);
|
||||||
|
bool operator>>=(const uint64_t& u);
|
||||||
|
bool operator>>=(const int8_t& u);
|
||||||
|
bool operator>>=(const int16_t& u);
|
||||||
|
bool operator>>=(const int32_t& u);
|
||||||
|
bool operator>>=(const int64_t& u);
|
||||||
|
bool operator>>=(const float32_t& u);
|
||||||
|
bool operator>>=(const float64_t& u);
|
||||||
|
bool operator>>=(const std::string& s);
|
||||||
|
bool operator>>=(const Bytestream& other);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* _data;
|
||||||
|
size_t _size;
|
||||||
|
size_t _pos;
|
||||||
|
size_t _noOfNextBytes;
|
||||||
|
bool _noOfNextBytesValid;
|
||||||
|
|
||||||
|
Endianness _endianness;
|
||||||
|
|
||||||
|
bool needsSwap();
|
||||||
|
|
||||||
|
void _after(size_t bytesRead);
|
||||||
|
void _before(size_t bytesToRead);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
184
src/ippdiscovery.cpp
Normal file
184
src/ippdiscovery.cpp
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
#include "ippdiscovery.h"
|
||||||
|
#define A 1
|
||||||
|
#define PTR 12
|
||||||
|
#define TXT 16
|
||||||
|
#define AAAA 28
|
||||||
|
#define SRV 33
|
||||||
|
|
||||||
|
void put_addr(Bytestream& bts, QStringList addr)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < addr.length(); i++)
|
||||||
|
{
|
||||||
|
QString elem = addr[i];
|
||||||
|
bts << (quint8)elem.size() << elem.toStdString();
|
||||||
|
}
|
||||||
|
bts << (quint8)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList get_addr(Bytestream& bts)
|
||||||
|
{
|
||||||
|
QStringList addr;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
if(bts.nextU8(0))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((bts.peekU8()&0xc0)==0xc0)
|
||||||
|
{
|
||||||
|
quint16 ref = bts.getU16() & 0x0fff;
|
||||||
|
Bytestream tmp = bts;
|
||||||
|
tmp.setPos(ref);
|
||||||
|
addr += get_addr(tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string elem;
|
||||||
|
bts/bts.getU8() >> elem;
|
||||||
|
addr.append(QString(elem.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IppDiscovery::IppDiscovery() : QStringListModel()
|
||||||
|
{
|
||||||
|
socket = new QUdpSocket(this);
|
||||||
|
connect(socket, SIGNAL(readyRead()),
|
||||||
|
this, SLOT(readPendingDatagrams()));
|
||||||
|
connect(this, SIGNAL(favouritesChanged()),
|
||||||
|
this, SLOT(update()));
|
||||||
|
}
|
||||||
|
|
||||||
|
IppDiscovery::~IppDiscovery() {
|
||||||
|
delete socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IppDiscovery::discover() {
|
||||||
|
|
||||||
|
Bytestream query;
|
||||||
|
quint16 transactionid = 0;
|
||||||
|
quint16 flags = 0;
|
||||||
|
quint16 questions = 1;
|
||||||
|
|
||||||
|
query << transactionid << flags << questions << (quint16)0 << (quint16)0 << (quint16)0;
|
||||||
|
put_addr(query, {"_ipp","_tcp","local"});
|
||||||
|
query << (quint16)0x000C << (quint16)0x0001;
|
||||||
|
|
||||||
|
QByteArray bytes((char*)(query.raw()), query.size());
|
||||||
|
socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppDiscovery::update()
|
||||||
|
{
|
||||||
|
qDebug() << _favourites << _found;
|
||||||
|
this->setStringList(_favourites+_found);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppDiscovery::readPendingDatagrams()
|
||||||
|
{
|
||||||
|
while (socket->hasPendingDatagrams()) {
|
||||||
|
|
||||||
|
size_t size = socket->pendingDatagramSize();
|
||||||
|
Bytestream resp(size);
|
||||||
|
QHostAddress sender;
|
||||||
|
quint16 senderPort;
|
||||||
|
|
||||||
|
QMap<QString,QString> ptrs;
|
||||||
|
QMap<QString,QString> rps;
|
||||||
|
QMap<QString,quint16> ports;
|
||||||
|
QMap<QString,QString> targets;
|
||||||
|
QMultiMap<QString,QString> AAs;
|
||||||
|
QMultiMap<QString,QString> AAAAs;
|
||||||
|
|
||||||
|
socket->readDatagram((char*)(resp.raw()), size, &sender, &senderPort);
|
||||||
|
sender = QHostAddress(sender.toIPv4Address());
|
||||||
|
|
||||||
|
quint16 transactionid, flags, questions, answerRRs, authRRs, addRRs;
|
||||||
|
resp >> transactionid >> flags >> questions >> answerRRs >> authRRs >> addRRs;
|
||||||
|
|
||||||
|
for(quint16 i = 0; i < questions; i++)
|
||||||
|
{
|
||||||
|
quint16 qtype, qflags;
|
||||||
|
QString qaddr = get_addr(resp).join('.');
|
||||||
|
resp >> qtype >> qflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(quint16 i = 0; i < answerRRs; i++)
|
||||||
|
{
|
||||||
|
quint16 atype, aflags, len;
|
||||||
|
quint32 ttl;
|
||||||
|
|
||||||
|
QString aaddr = get_addr(resp).join('.');
|
||||||
|
resp >> atype >> aflags >> ttl >> len;
|
||||||
|
|
||||||
|
quint16 pos_before = resp.pos();
|
||||||
|
if (atype == PTR)
|
||||||
|
{
|
||||||
|
QString tmpname = get_addr(resp).join(".");
|
||||||
|
ptrs[aaddr] = tmpname;
|
||||||
|
}
|
||||||
|
else if(atype == TXT)
|
||||||
|
{
|
||||||
|
Bytestream tmp;
|
||||||
|
while(resp.pos() < pos_before+len)
|
||||||
|
{
|
||||||
|
resp/resp.getU8() >> tmp;
|
||||||
|
if(tmp >>= "rp=")
|
||||||
|
{
|
||||||
|
std::string tmprp;
|
||||||
|
tmp/tmp.remaining() >> tmprp;
|
||||||
|
rps[aaddr] = tmprp.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (atype == SRV)
|
||||||
|
{
|
||||||
|
quint16 prio, w, port;
|
||||||
|
resp >> prio >> w >> port;
|
||||||
|
QString target = get_addr(resp).join(".");
|
||||||
|
ports[aaddr] = port;
|
||||||
|
targets[aaddr] = target;
|
||||||
|
}
|
||||||
|
else if(atype == A)
|
||||||
|
{
|
||||||
|
quint32 addr;
|
||||||
|
resp >> addr;
|
||||||
|
QHostAddress haddr(addr);
|
||||||
|
AAs.insert(aaddr, haddr.toString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resp += len;
|
||||||
|
}
|
||||||
|
Q_ASSERT(resp.pos() == pos_before+len);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QMap<QString,QString>::Iterator it = ptrs.begin(); it != ptrs.end(); it++)
|
||||||
|
{
|
||||||
|
quint16 port = ports[it.value()];
|
||||||
|
QString target = targets[it.value()];
|
||||||
|
QString rp = rps[it.value()];
|
||||||
|
|
||||||
|
for(QMultiMap<QString,QString>::Iterator ait = AAs.begin(); ait != AAs.end(); ait++)
|
||||||
|
{
|
||||||
|
if(ait.key() == target)
|
||||||
|
{
|
||||||
|
QString ip = ait.value();
|
||||||
|
QString addr = ip+":"+QString::number(port)+"/"+rp;
|
||||||
|
if(!_found.contains(addr))
|
||||||
|
{
|
||||||
|
_found.append(addr);
|
||||||
|
_found.sort(Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->update();
|
||||||
|
|
||||||
|
}
|
29
src/ippdiscovery.h
Normal file
29
src/ippdiscovery.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef IPPDISCOVERY_H
|
||||||
|
#define IPPDISCOVERY_H
|
||||||
|
#include <QStringListModel>
|
||||||
|
#include <QUdpSocket>
|
||||||
|
#include "bytestream.h"
|
||||||
|
|
||||||
|
class IppDiscovery : public QStringListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
IppDiscovery();
|
||||||
|
~IppDiscovery();
|
||||||
|
Q_PROPERTY(QStringList favourites MEMBER _favourites NOTIFY favouritesChanged)
|
||||||
|
Q_INVOKABLE void discover();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void favouritesChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void readPendingDatagrams();
|
||||||
|
void update();
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
QStringList _favourites;
|
||||||
|
QStringList _found;
|
||||||
|
QUdpSocket* socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPPDISCOVERY_H
|
278
src/ippmsg.cpp
Normal file
278
src/ippmsg.cpp
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
#include "ippmsg.h"
|
||||||
|
|
||||||
|
#define MAJ_VSN 1
|
||||||
|
#define MIN_VSN 1
|
||||||
|
|
||||||
|
quint32 IppMsg::_reqid=1;
|
||||||
|
|
||||||
|
IppMsg::IppMsg()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IppMsg::IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs)
|
||||||
|
{
|
||||||
|
_opAttrs = opAttrs;
|
||||||
|
_jobAttrs = jobAttrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IppMsg::~IppMsg()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IppMsg::IppMsg(QNetworkReply* resp)
|
||||||
|
{
|
||||||
|
QByteArray tmp = resp->readAll();
|
||||||
|
Bytestream bts(tmp.constData(), tmp.length());
|
||||||
|
|
||||||
|
quint8 majVsn;
|
||||||
|
quint8 minVsn;
|
||||||
|
quint16 status;
|
||||||
|
quint32 reqId;
|
||||||
|
|
||||||
|
bts >> majVsn >> minVsn >> status >> reqId;
|
||||||
|
|
||||||
|
QJsonObject* attrs = 0;
|
||||||
|
|
||||||
|
QString last_name;
|
||||||
|
|
||||||
|
while(!bts.atEnd())
|
||||||
|
{
|
||||||
|
if(bts>>=(quint8)IppTag::OpAttrs)
|
||||||
|
{
|
||||||
|
attrs = &_opAttrs;
|
||||||
|
}
|
||||||
|
else if (bts>>=(quint8)IppTag::JobAttrs) {
|
||||||
|
attrs = &_jobAttrs;
|
||||||
|
}
|
||||||
|
else if (bts>>=(quint8)IppTag::EndAttrs) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (bts>>=(quint8)IppTag::PrinterAttrs) {
|
||||||
|
attrs = &_printerAttrs;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
last_name = consume_attribute(attrs, bts, last_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString IppMsg::consume_attribute(QJsonObject* attrs, Bytestream& data, QString lastName)
|
||||||
|
{
|
||||||
|
quint8 tag;
|
||||||
|
quint16 tmp_len;
|
||||||
|
QString name;
|
||||||
|
QJsonValue value;
|
||||||
|
std::string tmp_str = "";
|
||||||
|
bool noList = false;
|
||||||
|
|
||||||
|
data >> tag >> tmp_len;
|
||||||
|
|
||||||
|
data/tmp_len >> tmp_str;
|
||||||
|
name = tmp_str!="" ? tmp_str.c_str() : lastName;
|
||||||
|
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case OpAttrs:
|
||||||
|
case JobAttrs:
|
||||||
|
case EndAttrs:
|
||||||
|
case PrinterAttrs:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
case Integer:
|
||||||
|
case Enum:
|
||||||
|
quint32 tmp_u32;
|
||||||
|
data >> tmp_len >> tmp_u32;
|
||||||
|
value = (int)tmp_u32;
|
||||||
|
break;
|
||||||
|
case Boolean:
|
||||||
|
quint8 tmp_bool;
|
||||||
|
data >> tmp_len >> tmp_bool;
|
||||||
|
value = (bool)tmp_bool;
|
||||||
|
noList = true;
|
||||||
|
break;
|
||||||
|
case DateTime:
|
||||||
|
{
|
||||||
|
quint16 year;
|
||||||
|
quint8 month, day, hour, minutes, seconds, deci_seconds,
|
||||||
|
plus_minus, utc_h_offset, utc_m_offset;
|
||||||
|
data >> tmp_len >> year >> month >> day >> hour >> minutes >> seconds >> deci_seconds
|
||||||
|
>> plus_minus >> utc_h_offset >> utc_m_offset;
|
||||||
|
QDate date(year, month, day);
|
||||||
|
QTime time(hour, minutes, seconds, deci_seconds*100);
|
||||||
|
int offset_seconds = (plus_minus == '+' ? 1 : -1)*(utc_h_offset*60*60+utc_m_offset*60);
|
||||||
|
value = QDateTime(date, time, Qt::OffsetFromUTC, offset_seconds).toString(Qt::ISODate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Resolution:
|
||||||
|
{
|
||||||
|
qint32 x, y;
|
||||||
|
qint8 units;
|
||||||
|
QJsonObject tmp_res;
|
||||||
|
data >> tmp_len >> x >> y >> units;
|
||||||
|
tmp_res.insert("x", x);
|
||||||
|
tmp_res.insert("y", y);
|
||||||
|
tmp_res.insert("units", units);
|
||||||
|
value = tmp_res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IntegerRange:
|
||||||
|
{
|
||||||
|
qint32 low, high;
|
||||||
|
data >> tmp_len >> low >> high;
|
||||||
|
QJsonObject tmp_range;
|
||||||
|
tmp_range.insert("low", low);
|
||||||
|
tmp_range.insert("high", high);
|
||||||
|
value = tmp_range;
|
||||||
|
noList = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OctetStringUnknown:
|
||||||
|
case TextWithLanguage:
|
||||||
|
case NameWithLanguage:
|
||||||
|
case TextWithoutLanguage:
|
||||||
|
case NameWithoutLanguage:
|
||||||
|
case Keyword:
|
||||||
|
case Uri:
|
||||||
|
case UriScheme:
|
||||||
|
case Charset:
|
||||||
|
case NaturalLanguage:
|
||||||
|
case MimeMediaType:
|
||||||
|
default:
|
||||||
|
data >> tmp_len;
|
||||||
|
data/tmp_len >> tmp_str;
|
||||||
|
value = tmp_str.c_str();
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if(attrs->contains(name))
|
||||||
|
{
|
||||||
|
QJsonObject tmp = (*attrs)[name].toObject();
|
||||||
|
QJsonArray tmpa;
|
||||||
|
if(tmp["value"].isArray())
|
||||||
|
{
|
||||||
|
tmpa = tmp["value"].toArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmpa = QJsonArray {tmp["value"]};
|
||||||
|
}
|
||||||
|
tmpa.append(value);
|
||||||
|
tmp["value"] = tmpa;
|
||||||
|
attrs->insert(name, tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if((name.endsWith("-supported") || name == "printer-icons") && !noList)
|
||||||
|
{
|
||||||
|
value = QJsonArray {value};
|
||||||
|
}
|
||||||
|
attrs->insert(name, QJsonObject {{"tag", tag}, {"value", value}});
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray IppMsg::encode(Operation op)
|
||||||
|
{
|
||||||
|
Bytestream ipp;
|
||||||
|
|
||||||
|
ipp << quint8(MAJ_VSN) << quint8(MIN_VSN);
|
||||||
|
|
||||||
|
ipp << quint16(op);
|
||||||
|
ipp << _reqid++;
|
||||||
|
|
||||||
|
if(!_opAttrs.empty())
|
||||||
|
{
|
||||||
|
ipp << quint8(1);
|
||||||
|
for(QJsonObject::iterator it = _opAttrs.begin(); it != _opAttrs.end(); it++)
|
||||||
|
{
|
||||||
|
QJsonObject val = it.value().toObject();
|
||||||
|
ipp << encode_attr(val["tag"].toInt(), it.key(), val["value"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!_jobAttrs.empty())
|
||||||
|
{
|
||||||
|
ipp << quint8(2);
|
||||||
|
for(QJsonObject::iterator it = _jobAttrs.begin(); it != _jobAttrs.end(); it++)
|
||||||
|
{
|
||||||
|
QJsonObject val = it.value().toObject();
|
||||||
|
ipp << encode_attr(val["tag"].toInt(), it.key(), val["value"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipp << quint8(3);
|
||||||
|
|
||||||
|
return QByteArray((char*)(ipp.raw()), ipp.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream IppMsg::encode_attr(quint8 tag, QString name, QJsonValueRef value)
|
||||||
|
{
|
||||||
|
Bytestream req;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case OpAttrs:
|
||||||
|
case JobAttrs:
|
||||||
|
case EndAttrs:
|
||||||
|
case PrinterAttrs:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
case Integer:
|
||||||
|
case Enum:
|
||||||
|
{
|
||||||
|
quint32 tmp_u32 = value.toInt();
|
||||||
|
req << (quint16)4 << tmp_u32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Boolean:
|
||||||
|
{
|
||||||
|
quint32 tmp_u8 = value.toBool();
|
||||||
|
req << (quint16)1 << tmp_u8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DateTime:
|
||||||
|
{
|
||||||
|
Q_ASSERT("fixme");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Resolution:
|
||||||
|
{
|
||||||
|
qDebug() << value << value.toObject();
|
||||||
|
qint32 x = value.toObject()["x"].toInt();
|
||||||
|
qint32 y = value.toObject()["y"].toInt();
|
||||||
|
qint8 units = value.toObject()["units"].toInt();
|
||||||
|
req << (quint16)9 << x << y << units;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IntegerRange:
|
||||||
|
{
|
||||||
|
qint32 low = value.toObject()["low"].toInt();
|
||||||
|
qint32 high = value.toObject()["high"].toInt();
|
||||||
|
req << (quint16)8 << low << high;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OctetStringUnknown:
|
||||||
|
case TextWithLanguage:
|
||||||
|
case NameWithLanguage:
|
||||||
|
case TextWithoutLanguage:
|
||||||
|
case NameWithoutLanguage:
|
||||||
|
case Keyword:
|
||||||
|
case Uri:
|
||||||
|
case UriScheme:
|
||||||
|
case Charset:
|
||||||
|
case NaturalLanguage:
|
||||||
|
case MimeMediaType:
|
||||||
|
req << quint16(value.toString().length()) << value.toString().toStdString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug() << "uncaught tag" << tag;
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytestream actual;
|
||||||
|
if(req.size() != 0)
|
||||||
|
{
|
||||||
|
actual << tag << quint16(name.length()) << name.toStdString() << req;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual;
|
||||||
|
}
|
84
src/ippmsg.h
Normal file
84
src/ippmsg.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef IPP_PROTO_H
|
||||||
|
#define IPP_PROTO_H
|
||||||
|
|
||||||
|
#include "bytestream.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QtNetwork>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QIODevice>
|
||||||
|
|
||||||
|
class IppMsg
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum IppTag : quint8 {
|
||||||
|
OpAttrs = 0x01,
|
||||||
|
JobAttrs = 0x02,
|
||||||
|
EndAttrs = 0x03,
|
||||||
|
PrinterAttrs = 0x04,
|
||||||
|
Integer = 0x21,
|
||||||
|
Boolean = 0x22,
|
||||||
|
Enum = 0x23,
|
||||||
|
OctetStringUnknown = 0x30,
|
||||||
|
DateTime = 0x31,
|
||||||
|
Resolution = 0x32,
|
||||||
|
IntegerRange = 0x33,
|
||||||
|
TextWithLanguage = 0x35,
|
||||||
|
NameWithLanguage = 0x36,
|
||||||
|
TextWithoutLanguage = 0x41,
|
||||||
|
NameWithoutLanguage = 0x42,
|
||||||
|
Keyword = 0x44,
|
||||||
|
Uri = 0x45,
|
||||||
|
UriScheme = 0x46,
|
||||||
|
Charset = 0x47,
|
||||||
|
NaturalLanguage = 0x48,
|
||||||
|
MimeMediaType = 0x49
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Operation : quint16 {
|
||||||
|
PrintJob = 0x0002,
|
||||||
|
PrintUri = 0x0003,
|
||||||
|
ValidateJob = 0x0004,
|
||||||
|
CreateJob = 0x0005,
|
||||||
|
SendDocument = 0x0006,
|
||||||
|
SendUri = 0x0007,
|
||||||
|
CancelJob = 0x0008,
|
||||||
|
GetJobAttrs = 0x0009,
|
||||||
|
GetJobs = 0x000A,
|
||||||
|
GetPrinterAttrs = 0x000B,
|
||||||
|
HoldJob = 0x000C,
|
||||||
|
ReleaseJob = 0x000D,
|
||||||
|
RestartJob = 0x000E,
|
||||||
|
PausePrinter = 0x0010,
|
||||||
|
ResumePrinter = 0x0011,
|
||||||
|
PurgeJobs = 0x0012
|
||||||
|
};
|
||||||
|
|
||||||
|
IppMsg();
|
||||||
|
IppMsg(QNetworkReply* resp);
|
||||||
|
IppMsg(QJsonObject opAttrs, QJsonObject jobAttrs = QJsonObject());
|
||||||
|
IppMsg(const IppMsg& other) = default;
|
||||||
|
~IppMsg();
|
||||||
|
|
||||||
|
QByteArray encode(Operation op);
|
||||||
|
QJsonObject getPrinterAttrs() {return _printerAttrs;}
|
||||||
|
QJsonObject getJobAttrs() {return _jobAttrs;}
|
||||||
|
QJsonObject getOpAttrs() {return _opAttrs;}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
QString consume_attribute(QJsonObject* attrs, Bytestream& data, QString lastName);
|
||||||
|
Bytestream encode_attr(quint8 tag, QString name, QJsonValueRef value);
|
||||||
|
|
||||||
|
QJsonObject _opAttrs;
|
||||||
|
QJsonObject _jobAttrs;
|
||||||
|
QJsonObject _printerAttrs;
|
||||||
|
|
||||||
|
static quint32 _reqid;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPP_PROTO_H
|
120
src/ippprinter.cpp
Normal file
120
src/ippprinter.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include "ippprinter.h"
|
||||||
|
|
||||||
|
IppPrinter::IppPrinter()
|
||||||
|
{
|
||||||
|
_nam = new QNetworkAccessManager(this);
|
||||||
|
_jnam = new QNetworkAccessManager(this);
|
||||||
|
connect(_nam, SIGNAL(finished(QNetworkReply*)),this, SLOT(getPrinterAttributesFinished(QNetworkReply*)));
|
||||||
|
connect(_jnam, SIGNAL(finished(QNetworkReply*)),this, SLOT(jobRequestFinished(QNetworkReply*)));
|
||||||
|
QObject::connect(this, &IppPrinter::urlChanged, this, &IppPrinter::onUrlChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
IppPrinter::~IppPrinter() {
|
||||||
|
delete _nam;
|
||||||
|
delete _jnam;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppPrinter::setUrl(QString url)
|
||||||
|
{
|
||||||
|
if(url != _url)
|
||||||
|
{
|
||||||
|
_url = url;
|
||||||
|
emit urlChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppPrinter::onUrlChanged()
|
||||||
|
{
|
||||||
|
_attrs = QJsonObject();
|
||||||
|
emit attrsChanged();
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
QUrl url("http://"+_url);
|
||||||
|
qDebug() << _url << url.port();
|
||||||
|
if(url.port() == -1) {
|
||||||
|
url.setPort(631);
|
||||||
|
}
|
||||||
|
request.setUrl(url);
|
||||||
|
// request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/ipp");
|
||||||
|
|
||||||
|
QJsonObject o
|
||||||
|
{
|
||||||
|
{"attributes-charset", QJsonObject {{"tag", IppMsg::Charset}, {"value", "utf-8"}}},
|
||||||
|
{"attributes-natural-language", QJsonObject {{"tag", IppMsg::NaturalLanguage}, {"value", "en-us"}}},
|
||||||
|
{"printer-uri", QJsonObject {{"tag", IppMsg::Uri}, {"value", "ipp://"+_url}}},
|
||||||
|
{"requesting-user-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", "nemo"}}}
|
||||||
|
};
|
||||||
|
IppMsg msg = IppMsg(o);
|
||||||
|
_nam->post(request, msg.encode(IppMsg::GetPrinterAttrs));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppPrinter::getPrinterAttributesFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if(reply->error() == QNetworkReply::NoError)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
IppMsg resp(reply);
|
||||||
|
_attrs = resp.getPrinterAttrs();
|
||||||
|
emit attrsChanged();
|
||||||
|
}
|
||||||
|
catch(std::exception e)
|
||||||
|
{
|
||||||
|
qDebug() << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IppPrinter::jobRequestFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if(reply->error() == QNetworkReply::NoError)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
IppMsg resp(reply);
|
||||||
|
qDebug() << resp.getOpAttrs() << resp.getJobAttrs();
|
||||||
|
_jobAttrs = resp.getJobAttrs();
|
||||||
|
emit jobAttrsChanged();
|
||||||
|
}
|
||||||
|
catch(std::exception e)
|
||||||
|
{
|
||||||
|
qDebug() << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool IppPrinter::print(QJsonObject attrs, QString filename){
|
||||||
|
qDebug() << "printing" << filename << attrs;
|
||||||
|
|
||||||
|
QFile file(filename);
|
||||||
|
bool file_ok = file.open(QIODevice::ReadOnly);
|
||||||
|
if(!file_ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QFileInfo fileinfo(file);
|
||||||
|
QNetworkRequest request;
|
||||||
|
QUrl url("http://"+_url);
|
||||||
|
request.setUrl(url);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/ipp");
|
||||||
|
|
||||||
|
QJsonObject o
|
||||||
|
{
|
||||||
|
{"attributes-charset", QJsonObject {{"tag", IppMsg::Charset}, {"value", "utf-8"}}},
|
||||||
|
{"attributes-natural-language", QJsonObject {{"tag", IppMsg::NaturalLanguage}, {"value", "en-us"}}},
|
||||||
|
{"printer-uri", QJsonObject {{"tag", IppMsg::Uri}, {"value", "ipp://"+_url}}},
|
||||||
|
{"requesting-user-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", "nemo"}}},
|
||||||
|
{"job-name", QJsonObject {{"tag", IppMsg::NameWithoutLanguage}, {"value", fileinfo.fileName()}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
IppMsg job = IppMsg(o, attrs);
|
||||||
|
|
||||||
|
QByteArray contents = job.encode(IppMsg::PrintJob);
|
||||||
|
QByteArray filedata = file.readAll();
|
||||||
|
contents = contents.append(filedata);
|
||||||
|
|
||||||
|
_jnam->post(request, contents);
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
46
src/ippprinter.h
Normal file
46
src/ippprinter.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef IPPPRINTER_H
|
||||||
|
#define IPPPRINTER_H
|
||||||
|
|
||||||
|
#include <QtNetwork>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include "ippmsg.h"
|
||||||
|
|
||||||
|
class IppPrinter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString url READ getUrl WRITE setUrl NOTIFY urlChanged)
|
||||||
|
Q_PROPERTY(QJsonObject attrs MEMBER _attrs NOTIFY attrsChanged)
|
||||||
|
Q_PROPERTY(QJsonObject jobAttrs MEMBER _jobAttrs NOTIFY jobAttrsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
IppPrinter();
|
||||||
|
~IppPrinter();
|
||||||
|
|
||||||
|
|
||||||
|
QString getUrl() {return _url;}
|
||||||
|
void setUrl(QString url);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool print(QJsonObject attrs, QString file);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void urlChanged();
|
||||||
|
void attrsChanged();
|
||||||
|
void jobAttrsChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onUrlChanged();
|
||||||
|
void getPrinterAttributesFinished(QNetworkReply* reply);
|
||||||
|
void jobRequestFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _url;
|
||||||
|
|
||||||
|
QNetworkAccessManager* _nam;
|
||||||
|
QNetworkAccessManager* _jnam;
|
||||||
|
|
||||||
|
QJsonObject _attrs;
|
||||||
|
QJsonObject _jobAttrs;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IPPPRINTER_H
|
Loading…
Reference in a new issue