diff options
| author | Ed Tanous <ed.tanous@intel.com> | 2018-09-05 08:30:59 -0700 |
|---|---|---|
| committer | Ed Tanous <ed.tanous@intel.com> | 2018-09-05 08:44:12 -0700 |
| commit | 1abe55ef9844afcddcab9d862ae06118f3a2390c (patch) | |
| tree | d0b5fcfd0b1cd679a8995af3eb0b6d31b368380e /include | |
| parent | a38b0b206300c792979b900f506b85e535f5708a (diff) | |
| download | bmcweb-1abe55ef9844afcddcab9d862ae06118f3a2390c.tar.gz bmcweb-1abe55ef9844afcddcab9d862ae06118f3a2390c.zip | |
Move to clang-format-6.0
This commit moves the codebase to the lastest clang-format file from
upstream, as well as clang-format-6.0.
Change-Id: Ice8313468097c0c42317fbb9e10ddf036e8cff4c
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Diffstat (limited to 'include')
| -rw-r--r-- | include/ast_jpeg_decoder.hpp | 2685 | ||||
| -rw-r--r-- | include/ast_video_puller.hpp | 318 | ||||
| -rw-r--r-- | include/ast_video_types.hpp | 32 | ||||
| -rw-r--r-- | include/dbus_monitor.hpp | 370 | ||||
| -rw-r--r-- | include/dbus_singleton.hpp | 33 | ||||
| -rw-r--r-- | include/gzip_helper.hpp | 85 | ||||
| -rw-r--r-- | include/http_utility.hpp | 38 | ||||
| -rw-r--r-- | include/image_upload.hpp | 170 | ||||
| -rw-r--r-- | include/openbmc_dbus_rest.hpp | 2407 | ||||
| -rw-r--r-- | include/pam_authenticate.hpp | 112 | ||||
| -rw-r--r-- | include/persistent_data_middleware.hpp | 234 | ||||
| -rw-r--r-- | include/redfish_v1.hpp | 241 | ||||
| -rw-r--r-- | include/security_headers_middleware.hpp | 63 | ||||
| -rw-r--r-- | include/sessions.hpp | 444 | ||||
| -rw-r--r-- | include/ssl_key_handler.hpp | 567 | ||||
| -rw-r--r-- | include/token_authorization_middleware.hpp | 711 | ||||
| -rw-r--r-- | include/web_kvm.hpp | 665 | ||||
| -rw-r--r-- | include/webassets.hpp | 250 |
18 files changed, 5209 insertions, 4216 deletions
diff --git a/include/ast_jpeg_decoder.hpp b/include/ast_jpeg_decoder.hpp index e6e0f08..e8bdddb 100644 --- a/include/ast_jpeg_decoder.hpp +++ b/include/ast_jpeg_decoder.hpp @@ -1,345 +1,390 @@ #pragma once #include <aspeed/JTABLES.H> -#include <ast_video_types.hpp> + #include <array> +#include <ast_video_types.hpp> #include <cassert> #include <cstdint> #include <cstring> #include <iostream> #include <vector> -namespace ast_video { +namespace ast_video +{ -struct ColorCache { - ColorCache() - : color{0x008080, 0xFF8080, 0x808080, 0xC08080}, index{0, 1, 2, 3} {} +struct ColorCache +{ + ColorCache() : + color{0x008080, 0xFF8080, 0x808080, 0xC08080}, index{0, 1, 2, 3} + { + } - unsigned long color[4]; - unsigned char index[4]; - unsigned char bitMapBits{}; + unsigned long color[4]; + unsigned char index[4]; + unsigned char bitMapBits{}; }; -struct RGB { - unsigned char b; - unsigned char g; - unsigned char r; - unsigned char reserved; +struct RGB +{ + unsigned char b; + unsigned char g; + unsigned char r; + unsigned char reserved; }; -enum class JpgBlock { - JPEG_NO_SKIP_CODE = 0x00, - JPEG_SKIP_CODE = 0x08, +enum class JpgBlock +{ + JPEG_NO_SKIP_CODE = 0x00, + JPEG_SKIP_CODE = 0x08, - JPEG_PASS2_CODE = 0x02, - JPEG_SKIP_PASS2_CODE = 0x0A, + JPEG_PASS2_CODE = 0x02, + JPEG_SKIP_PASS2_CODE = 0x0A, - LOW_JPEG_NO_SKIP_CODE = 0x04, - LOW_JPEG_SKIP_CODE = 0x0C, + LOW_JPEG_NO_SKIP_CODE = 0x04, + LOW_JPEG_SKIP_CODE = 0x0C, - VQ_NO_SKIP_1_COLOR_CODE = 0x05, - VQ_SKIP_1_COLOR_CODE = 0x0D, + VQ_NO_SKIP_1_COLOR_CODE = 0x05, + VQ_SKIP_1_COLOR_CODE = 0x0D, - VQ_NO_SKIP_2_COLOR_CODE = 0x06, - VQ_SKIP_2_COLOR_CODE = 0x0E, + VQ_NO_SKIP_2_COLOR_CODE = 0x06, + VQ_SKIP_2_COLOR_CODE = 0x0E, - VQ_NO_SKIP_4_COLOR_CODE = 0x07, - VQ_SKIP_4_COLOR_CODE = 0x0F, + VQ_NO_SKIP_4_COLOR_CODE = 0x07, + VQ_SKIP_4_COLOR_CODE = 0x0F, - FRAME_END_CODE = 0x09, + FRAME_END_CODE = 0x09, }; -class AstJpegDecoder { - public: - AstJpegDecoder() { - // TODO(ed) figure out how to init this in the constructor - yuvBuffer.resize(1920 * 1200); - outBuffer.resize(1920 * 1200); - for (auto &r : outBuffer) { - r.r = 0x00; - r.g = 0x00; - r.b = 0x00; - r.reserved = 0xAA; - } +class AstJpegDecoder +{ + public: + AstJpegDecoder() + { + // TODO(ed) figure out how to init this in the constructor + yuvBuffer.resize(1920 * 1200); + outBuffer.resize(1920 * 1200); + for (auto &r : outBuffer) + { + r.r = 0x00; + r.g = 0x00; + r.b = 0x00; + r.reserved = 0xAA; + } - int qfactor = 16; - - scalefactor = qfactor; - scalefactoruv = qfactor; - advancescalefactor = 16; - advancescalefactoruv = 16; - initJpgTable(); - } - - void loadQuantTable(std::array<long, 64> &quant_table) { - float scalefactorF[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - switch (ySelector) { - case 0: - stdLuminanceQt = tbl000Y; - break; - case 1: - stdLuminanceQt = tbl014Y; - break; - case 2: - stdLuminanceQt = tbl029Y; - break; - case 3: - stdLuminanceQt = tbl043Y; - break; - case 4: - stdLuminanceQt = tbl057Y; - break; - case 5: - stdLuminanceQt = tbl071Y; - break; - case 6: - stdLuminanceQt = tbl086Y; - break; - case 7: - stdLuminanceQt = tbl100Y; - break; - } - setQuantTable(stdLuminanceQt, static_cast<uint8_t>(scalefactor), tempQT); + int qfactor = 16; - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactorF[row] * scalefactorF[col]) * 65536); - j++; - } - } - bytePos += 64; - } - - void loadQuantTableCb(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder from zig-zag order - if (mapping == 0) { - switch (uvSelector) { - case 0: - stdChrominanceQt = tbl000Y; - break; - case 1: - stdChrominanceQt = tbl014Y; - break; - case 2: - stdChrominanceQt = tbl029Y; - break; - case 3: - stdChrominanceQt = tbl043Y; - break; - case 4: - stdChrominanceQt = tbl057Y; - break; - case 5: - stdChrominanceQt = tbl071Y; - break; - case 6: - stdChrominanceQt = tbl086Y; - break; - case 7: - stdChrominanceQt = tbl100Y; - break; - } - } else { - switch (uvSelector) { - case 0: - stdChrominanceQt = tbl000Uv; - break; - case 1: - stdChrominanceQt = tbl014Uv; - break; - case 2: - stdChrominanceQt = tbl029Uv; - break; - case 3: - stdChrominanceQt = tbl043Uv; - break; - case 4: - stdChrominanceQt = tbl057Uv; - break; - case 5: - stdChrominanceQt = tbl071Uv; - break; - case 6: - stdChrominanceQt = tbl086Uv; - break; - case 7: - stdChrominanceQt = tbl100Uv; - break; - } + scalefactor = qfactor; + scalefactoruv = qfactor; + advancescalefactor = 16; + advancescalefactoruv = 16; + initJpgTable(); } - setQuantTable(stdChrominanceQt, static_cast<uint8_t>(scalefactoruv), - tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } - } - bytePos += 64; - } - // Note: Added for Dual_JPEG - void loadAdvanceQuantTable(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - switch (advanceSelector) { - case 0: - stdLuminanceQt = tbl000Y; - break; - case 1: - stdLuminanceQt = tbl014Y; - break; - case 2: - stdLuminanceQt = tbl029Y; - break; - case 3: - stdLuminanceQt = tbl043Y; - break; - case 4: - stdLuminanceQt = tbl057Y; - break; - case 5: - stdLuminanceQt = tbl071Y; - break; - case 6: - stdLuminanceQt = tbl086Y; - break; - case 7: - stdLuminanceQt = tbl100Y; - break; - } - // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG - setQuantTable(stdLuminanceQt, static_cast<uint8_t>(advancescalefactor), - tempQT); + void loadQuantTable(std::array<long, 64> &quant_table) + { + float scalefactorF[8] = {1.0f, 1.387039845f, 1.306562965f, + 1.175875602f, 1.0f, 0.785694958f, + 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + switch (ySelector) + { + case 0: + stdLuminanceQt = tbl000Y; + break; + case 1: + stdLuminanceQt = tbl014Y; + break; + case 2: + stdLuminanceQt = tbl029Y; + break; + case 3: + stdLuminanceQt = tbl043Y; + break; + case 4: + stdLuminanceQt = tbl057Y; + break; + case 5: + stdLuminanceQt = tbl071Y; + break; + case 6: + stdLuminanceQt = tbl086Y; + break; + case 7: + stdLuminanceQt = tbl100Y; + break; + } + setQuantTable(stdLuminanceQt, static_cast<uint8_t>(scalefactor), + tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; - } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactorF[row] * scalefactorF[col]) * + 65536); + j++; + } + } + bytePos += 64; } - bytePos += 64; - } - - // Note: Added for Dual-JPEG - void loadAdvanceQuantTableCb(std::array<long, 64> &quant_table) { - float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, - 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; - uint8_t j, row, col; - std::array<uint8_t, 64> tempQT{}; - - // Load quantization coefficients from JPG file, scale them for DCT and - // reorder - // from zig-zag order - if (mapping == 1) { - switch (advanceSelector) { - case 0: - stdChrominanceQt = tbl000Y; - break; - case 1: - stdChrominanceQt = tbl014Y; - break; - case 2: - stdChrominanceQt = tbl029Y; - break; - case 3: - stdChrominanceQt = tbl043Y; - break; - case 4: - stdChrominanceQt = tbl057Y; - break; - case 5: - stdChrominanceQt = tbl071Y; - break; - case 6: - stdChrominanceQt = tbl086Y; - break; - case 7: - stdChrominanceQt = tbl100Y; - break; - } - } else { - switch (advanceSelector) { - case 0: - stdChrominanceQt = tbl000Uv; - break; - case 1: - stdChrominanceQt = tbl014Uv; - break; - case 2: - stdChrominanceQt = tbl029Uv; - break; - case 3: - stdChrominanceQt = tbl043Uv; - break; - case 4: - stdChrominanceQt = tbl057Uv; - break; - case 5: - stdChrominanceQt = tbl071Uv; - break; - case 6: - stdChrominanceQt = tbl086Uv; - break; - case 7: - stdChrominanceQt = tbl100Uv; - break; - } + + void loadQuantTableCb(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder from zig-zag order + if (mapping == 0) + { + switch (uvSelector) + { + case 0: + stdChrominanceQt = tbl000Y; + break; + case 1: + stdChrominanceQt = tbl014Y; + break; + case 2: + stdChrominanceQt = tbl029Y; + break; + case 3: + stdChrominanceQt = tbl043Y; + break; + case 4: + stdChrominanceQt = tbl057Y; + break; + case 5: + stdChrominanceQt = tbl071Y; + break; + case 6: + stdChrominanceQt = tbl086Y; + break; + case 7: + stdChrominanceQt = tbl100Y; + break; + } + } + else + { + switch (uvSelector) + { + case 0: + stdChrominanceQt = tbl000Uv; + break; + case 1: + stdChrominanceQt = tbl014Uv; + break; + case 2: + stdChrominanceQt = tbl029Uv; + break; + case 3: + stdChrominanceQt = tbl043Uv; + break; + case 4: + stdChrominanceQt = tbl057Uv; + break; + case 5: + stdChrominanceQt = tbl071Uv; + break; + case 6: + stdChrominanceQt = tbl086Uv; + break; + case 7: + stdChrominanceQt = tbl100Uv; + break; + } + } + setQuantTable(stdChrominanceQt, static_cast<uint8_t>(scalefactoruv), + tempQT); + + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG - setQuantTable(stdChrominanceQt, static_cast<uint8_t>(advancescalefactoruv), - tempQT); + // Note: Added for Dual_JPEG + void loadAdvanceQuantTable(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + switch (advanceSelector) + { + case 0: + stdLuminanceQt = tbl000Y; + break; + case 1: + stdLuminanceQt = tbl014Y; + break; + case 2: + stdLuminanceQt = tbl029Y; + break; + case 3: + stdLuminanceQt = tbl043Y; + break; + case 4: + stdLuminanceQt = tbl057Y; + break; + case 5: + stdLuminanceQt = tbl071Y; + break; + case 6: + stdLuminanceQt = tbl086Y; + break; + case 7: + stdLuminanceQt = tbl100Y; + break; + } + // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG + setQuantTable(stdLuminanceQt, static_cast<uint8_t>(advancescalefactor), + tempQT); - for (j = 0; j <= 63; j++) { - quant_table[j] = tempQT[zigzag[j]]; + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - j = 0; - for (row = 0; row <= 7; row++) { - for (col = 0; col <= 7; col++) { - quant_table[j] = static_cast<long>( - (quant_table[j] * scalefactor[row] * scalefactor[col]) * 65536); - j++; - } + + // Note: Added for Dual-JPEG + void loadAdvanceQuantTableCb(std::array<long, 64> &quant_table) + { + float scalefactor[8] = {1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f}; + uint8_t j, row, col; + std::array<uint8_t, 64> tempQT{}; + + // Load quantization coefficients from JPG file, scale them for DCT and + // reorder + // from zig-zag order + if (mapping == 1) + { + switch (advanceSelector) + { + case 0: + stdChrominanceQt = tbl000Y; + break; + case 1: + stdChrominanceQt = tbl014Y; + break; + case 2: + stdChrominanceQt = tbl029Y; + break; + case 3: + stdChrominanceQt = tbl043Y; + break; + case 4: + stdChrominanceQt = tbl057Y; + break; + case 5: + stdChrominanceQt = tbl071Y; + break; + case 6: + stdChrominanceQt = tbl086Y; + break; + case 7: + stdChrominanceQt = tbl100Y; + break; + } + } + else + { + switch (advanceSelector) + { + case 0: + stdChrominanceQt = tbl000Uv; + break; + case 1: + stdChrominanceQt = tbl014Uv; + break; + case 2: + stdChrominanceQt = tbl029Uv; + break; + case 3: + stdChrominanceQt = tbl043Uv; + break; + case 4: + stdChrominanceQt = tbl057Uv; + break; + case 5: + stdChrominanceQt = tbl071Uv; + break; + case 6: + stdChrominanceQt = tbl086Uv; + break; + case 7: + stdChrominanceQt = tbl100Uv; + break; + } + } + // Note: pass ADVANCE SCALE FACTOR to sub-function in Dual-JPEG + setQuantTable(stdChrominanceQt, + static_cast<uint8_t>(advancescalefactoruv), tempQT); + + for (j = 0; j <= 63; j++) + { + quant_table[j] = tempQT[zigzag[j]]; + } + j = 0; + for (row = 0; row <= 7; row++) + { + for (col = 0; col <= 7; col++) + { + quant_table[j] = static_cast<long>( + (quant_table[j] * scalefactor[row] * scalefactor[col]) * + 65536); + j++; + } + } + bytePos += 64; } - bytePos += 64; - } - void idctTransform(short *coef, uint8_t *data, uint8_t nBlock) { + void idctTransform(short *coef, uint8_t *data, uint8_t nBlock) + { #define FIX_1_082392200 ((int)277) /* FIX(1.082392200) */ #define FIX_1_414213562 ((int)362) /* FIX(1.414213562) */ #define FIX_1_847759065 ((int)473) /* FIX(1.847759065) */ @@ -347,1008 +392,1156 @@ class AstJpegDecoder { #define MULTIPLY(var, cons) ((int)((var) * (cons)) >> 8) - int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - int tmp10, tmp11, tmp12, tmp13; - int z5, z10, z11, z12, z13; - int workspace[64]; /* buffers data between passes */ - - short *inptr = coef; - long *quantptr; - int *wsptr = workspace; - unsigned char *outptr; - unsigned char *rLimit = rlimitTable + 128; - int ctr, dcval, dctsize = 8; - - quantptr = &qt[nBlock][0]; - - // Pass 1: process columns from input (inptr), store into work array(wsptr) - - for (ctr = 8; ctr > 0; ctr--) { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if ((inptr[dctsize * 1] | inptr[dctsize * 2] | inptr[dctsize * 3] | - inptr[dctsize * 4] | inptr[dctsize * 5] | inptr[dctsize * 6] | - inptr[dctsize * 7]) == 0) { - /* AC terms all zero */ - dcval = static_cast<int>((inptr[dctsize * 0] * quantptr[dctsize * 0]) >> - 16); - - wsptr[dctsize * 0] = dcval; - wsptr[dctsize * 1] = dcval; - wsptr[dctsize * 2] = dcval; - wsptr[dctsize * 3] = dcval; - wsptr[dctsize * 4] = dcval; - wsptr[dctsize * 5] = dcval; - wsptr[dctsize * 6] = dcval; - wsptr[dctsize * 7] = dcval; - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - continue; - } - - /* Even part */ - - tmp0 = (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16; - tmp1 = (inptr[dctsize * 2] * quantptr[dctsize * 2]) >> 16; - tmp2 = (inptr[dctsize * 4] * quantptr[dctsize * 4]) >> 16; - tmp3 = (inptr[dctsize * 6] * quantptr[dctsize * 6]) >> 16; - - tmp10 = tmp0 + tmp2; /* phase 3 */ - tmp11 = tmp0 - tmp2; - - tmp13 = tmp1 + tmp3; /* phases 5-3 */ - tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ - - tmp0 = tmp10 + tmp13; /* phase 2 */ - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - tmp4 = (inptr[dctsize * 1] * quantptr[dctsize * 1]) >> 16; - tmp5 = (inptr[dctsize * 3] * quantptr[dctsize * 3]) >> 16; - tmp6 = (inptr[dctsize * 5] * quantptr[dctsize * 5]) >> 16; - tmp7 = (inptr[dctsize * 7] * quantptr[dctsize * 7]) >> 16; - - z13 = tmp6 + tmp5; /* phase 6 */ - z10 = tmp6 - tmp5; - z11 = tmp4 + tmp7; - z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ - - z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ - tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; - - wsptr[dctsize * 0] = (tmp0 + tmp7); - wsptr[dctsize * 7] = (tmp0 - tmp7); - wsptr[dctsize * 1] = (tmp1 + tmp6); - wsptr[dctsize * 6] = (tmp1 - tmp6); - wsptr[dctsize * 2] = (tmp2 + tmp5); - wsptr[dctsize * 5] = (tmp2 - tmp5); - wsptr[dctsize * 4] = (tmp3 + tmp4); - wsptr[dctsize * 3] = (tmp3 - tmp4); - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - } + int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + int tmp10, tmp11, tmp12, tmp13; + int z5, z10, z11, z12, z13; + int workspace[64]; /* buffers data between passes */ + + short *inptr = coef; + long *quantptr; + int *wsptr = workspace; + unsigned char *outptr; + unsigned char *rLimit = rlimitTable + 128; + int ctr, dcval, dctsize = 8; + + quantptr = &qt[nBlock][0]; + + // Pass 1: process columns from input (inptr), store into work + // array(wsptr) + + for (ctr = 8; ctr > 0; ctr--) + { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit + * this by short-circuiting the IDCT calculation for any column in + * which all the AC terms are zero. In that case each output is + * equal to the DC coefficient (with scale factor as needed). With + * typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if ((inptr[dctsize * 1] | inptr[dctsize * 2] | inptr[dctsize * 3] | + inptr[dctsize * 4] | inptr[dctsize * 5] | inptr[dctsize * 6] | + inptr[dctsize * 7]) == 0) + { + /* AC terms all zero */ + dcval = static_cast<int>( + (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16); + + wsptr[dctsize * 0] = dcval; + wsptr[dctsize * 1] = dcval; + wsptr[dctsize * 2] = dcval; + wsptr[dctsize * 3] = dcval; + wsptr[dctsize * 4] = dcval; + wsptr[dctsize * 5] = dcval; + wsptr[dctsize * 6] = dcval; + wsptr[dctsize * 7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } -/* Pass 2: process rows from work array, store into output array. */ -/* Note that we must descale the results by a factor of 8 == 2**3, */ -/* and also undo the PASS1_BITS scaling. */ + /* Even part */ -//#define RANGE_MASK 1023; //2 bits wider than legal samples -#define PASS1_BITS 0 -#define IDESCALE(x, n) ((int)((x) >> (n))) + tmp0 = (inptr[dctsize * 0] * quantptr[dctsize * 0]) >> 16; + tmp1 = (inptr[dctsize * 2] * quantptr[dctsize * 2]) >> 16; + tmp2 = (inptr[dctsize * 4] * quantptr[dctsize * 4]) >> 16; + tmp3 = (inptr[dctsize * 6] * quantptr[dctsize * 6]) >> 16; - wsptr = workspace; - for (ctr = 0; ctr < dctsize; ctr++) { - outptr = data + ctr * 8; + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the - * time). On machines with very fast multiplication, it's possible that - * the test takes more time than it's worth. In that case this section - * may be commented out. - */ - /* Even part */ + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ - tmp10 = (wsptr[0] + wsptr[4]); - tmp11 = (wsptr[0] - wsptr[4]); + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; - tmp13 = (wsptr[2] + wsptr[6]); - tmp12 = MULTIPLY((int)wsptr[2] - (int)wsptr[6], FIX_1_414213562) - tmp13; + /* Odd part */ - tmp0 = tmp10 + tmp13; - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; + tmp4 = (inptr[dctsize * 1] * quantptr[dctsize * 1]) >> 16; + tmp5 = (inptr[dctsize * 3] * quantptr[dctsize * 3]) >> 16; + tmp6 = (inptr[dctsize * 5] * quantptr[dctsize * 5]) >> 16; + tmp7 = (inptr[dctsize * 7] * quantptr[dctsize * 7]) >> 16; - /* Odd part */ + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; - z13 = wsptr[5] + wsptr[3]; - z10 = wsptr[5] - wsptr[3]; - z11 = wsptr[1] + wsptr[7]; - z12 = wsptr[1] - wsptr[7]; + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ - z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ - tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; + wsptr[dctsize * 0] = (tmp0 + tmp7); + wsptr[dctsize * 7] = (tmp0 - tmp7); + wsptr[dctsize * 1] = (tmp1 + tmp6); + wsptr[dctsize * 6] = (tmp1 - tmp6); + wsptr[dctsize * 2] = (tmp2 + tmp5); + wsptr[dctsize * 5] = (tmp2 - tmp5); + wsptr[dctsize * 4] = (tmp3 + tmp4); + wsptr[dctsize * 3] = (tmp3 - tmp4); - /* Final output stage: scale down by a factor of 8 and range-limit */ + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } - outptr[0] = rLimit[IDESCALE((tmp0 + tmp7), (PASS1_BITS + 3)) & 1023L]; - outptr[7] = rLimit[IDESCALE((tmp0 - tmp7), (PASS1_BITS + 3)) & 1023L]; - outptr[1] = rLimit[IDESCALE((tmp1 + tmp6), (PASS1_BITS + 3)) & 1023L]; - outptr[6] = rLimit[IDESCALE((tmp1 - tmp6), (PASS1_BITS + 3)) & 1023L]; - outptr[2] = rLimit[IDESCALE((tmp2 + tmp5), (PASS1_BITS + 3)) & 1023L]; - outptr[5] = rLimit[IDESCALE((tmp2 - tmp5), (PASS1_BITS + 3)) & 1023L]; - outptr[4] = rLimit[IDESCALE((tmp3 + tmp4), (PASS1_BITS + 3)) & 1023L]; - outptr[3] = rLimit[IDESCALE((tmp3 - tmp4), (PASS1_BITS + 3)) & 1023L]; +/* Pass 2: process rows from work array, store into output array. */ +/* Note that we must descale the results by a factor of 8 == 2**3, */ +/* and also undo the PASS1_BITS scaling. */ + +//#define RANGE_MASK 1023; //2 bits wider than legal samples +#define PASS1_BITS 0 +#define IDESCALE(x, n) ((int)((x) >> (n))) - wsptr += dctsize; /* advance pointer to next row */ + wsptr = workspace; + for (ctr = 0; ctr < dctsize; ctr++) + { + outptr = data + ctr * 8; + + /* Rows of zeroes can be exploited in the same way as we did with + * columns. However, the column calculation has created many nonzero + * AC terms, so the simplification applies less often (typically 5% + * to 10% of the time). On machines with very fast multiplication, + * it's possible that the test takes more time than it's worth. In + * that case this section may be commented out. + */ + /* Even part */ + + tmp10 = (wsptr[0] + wsptr[4]); + tmp11 = (wsptr[0] - wsptr[4]); + + tmp13 = (wsptr[2] + wsptr[6]); + tmp12 = MULTIPLY((int)wsptr[2] - (int)wsptr[6], FIX_1_414213562) - + tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, -FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit + */ + + outptr[0] = + rLimit[IDESCALE((tmp0 + tmp7), (PASS1_BITS + 3)) & 1023L]; + outptr[7] = + rLimit[IDESCALE((tmp0 - tmp7), (PASS1_BITS + 3)) & 1023L]; + outptr[1] = + rLimit[IDESCALE((tmp1 + tmp6), (PASS1_BITS + 3)) & 1023L]; + outptr[6] = + rLimit[IDESCALE((tmp1 - tmp6), (PASS1_BITS + 3)) & 1023L]; + outptr[2] = + rLimit[IDESCALE((tmp2 + tmp5), (PASS1_BITS + 3)) & 1023L]; + outptr[5] = + rLimit[IDESCALE((tmp2 - tmp5), (PASS1_BITS + 3)) & 1023L]; + outptr[4] = + rLimit[IDESCALE((tmp3 + tmp4), (PASS1_BITS + 3)) & 1023L]; + outptr[3] = + rLimit[IDESCALE((tmp3 - tmp4), (PASS1_BITS + 3)) & 1023L]; + + wsptr += dctsize; /* advance pointer to next row */ + } } - } - void yuvToRgb( - int txb, int tyb, - unsigned char - *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - struct RGB *pYUV, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - unsigned char - *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - ) { - int i, j, pos, m, n; - unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; - int y; - struct RGB *pByte; - int nBlocksInMcu = 6; - unsigned int pixelX, pixelY; - - pByte = reinterpret_cast<struct RGB *>(pBgr); - if (yuvmode == YuvMode::YUV444) { - py = pYCbCr; - pcb = pYCbCr + 64; - pcr = pcb + 64; - - pixelX = txb * 8; - pixelY = tyb * 8; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 8; j++) { - for (i = 0; i < 8; i++) { - m = ((j << 3) + i); - y = py[m]; - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - // For 2Pass. Save the YUV value - pYUV[n].b = cb; - pYUV[n].g = y; - pYUV[n].r = cr; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + void yuvToRgb( + int txb, int tyb, + unsigned char + *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + struct RGB *pYUV, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + unsigned char + *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + ) + { + int i, j, pos, m, n; + unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; + int y; + struct RGB *pByte; + int nBlocksInMcu = 6; + unsigned int pixelX, pixelY; + + pByte = reinterpret_cast<struct RGB *>(pBgr); + if (yuvmode == YuvMode::YUV444) + { + py = pYCbCr; + pcb = pYCbCr + 64; + pcr = pcb + 64; + + pixelX = txb * 8; + pixelY = tyb * 8; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + m = ((j << 3) + i); + y = py[m]; + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + // For 2Pass. Save the YUV value + pYUV[n].b = cb; + pYUV[n].g = y; + pYUV[n].r = cr; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } - } else { - for (i = 0; i < nBlocksInMcu - 2; i++) { - py420[i] = pYCbCr + i * 64; - } - pcb = pYCbCr + (nBlocksInMcu - 2) * 64; - pcr = pcb + 64; - - pixelX = txb * 16; - pixelY = tyb * 16; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 16; j++) { - for (i = 0; i < 16; i++) { - // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} - y = *(py420[(j >> 3) * 2 + (i >> 3)]++); - m = ((j >> 1) << 3) + (i >> 1); - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + else + { + for (i = 0; i < nBlocksInMcu - 2; i++) + { + py420[i] = pYCbCr + i * 64; + } + pcb = pYCbCr + (nBlocksInMcu - 2) * 64; + pcr = pcb + 64; + + pixelX = txb * 16; + pixelY = tyb * 16; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} + y = *(py420[(j >> 3) * 2 + (i >> 3)]++); + m = ((j >> 1) << 3) + (i >> 1); + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } } - } - void yuvToBuffer( - int txb, int tyb, - unsigned char - *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes - struct RGB - *pYUV, // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - unsigned char - *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes - ) { - int i, j, pos, m, n; - unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; - int y; - struct RGB *pByte; - int nBlocksInMcu = 6; - unsigned int pixelX, pixelY; - - pByte = reinterpret_cast<struct RGB *>(pBgr); - if (yuvmode == YuvMode::YUV444) { - py = pYCbCr; - pcb = pYCbCr + 64; - pcr = pcb + 64; - - pixelX = txb * 8; - pixelY = tyb * 8; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 8; j++) { - for (i = 0; i < 8; i++) { - m = ((j << 3) + i); - n = pos + i; - y = pYUV[n].g + (py[m] - 128); - cb = pYUV[n].b + (pcb[m] - 128); - cr = pYUV[n].r + (pcr[m] - 128); - pYUV[n].b = cb; - pYUV[n].g = y; - pYUV[n].r = cr; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + void yuvToBuffer( + int txb, int tyb, + unsigned char + *pYCbCr, // in, Y: 256 or 64 bytes; Cb: 64 bytes; Cr: 64 bytes + struct RGB + *pYUV, // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + unsigned char + *pBgr // out, BGR format, 16*16*3 = 768 bytes; or 8*8*3=192 bytes + ) + { + int i, j, pos, m, n; + unsigned char cb, cr, *py, *pcb, *pcr, *py420[4]; + int y; + struct RGB *pByte; + int nBlocksInMcu = 6; + unsigned int pixelX, pixelY; + + pByte = reinterpret_cast<struct RGB *>(pBgr); + if (yuvmode == YuvMode::YUV444) + { + py = pYCbCr; + pcb = pYCbCr + 64; + pcr = pcb + 64; + + pixelX = txb * 8; + pixelY = tyb * 8; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 8; j++) + { + for (i = 0; i < 8; i++) + { + m = ((j << 3) + i); + n = pos + i; + y = pYUV[n].g + (py[m] - 128); + cb = pYUV[n].b + (pcb[m] - 128); + cr = pYUV[n].r + (pcr[m] - 128); + pYUV[n].b = cb; + pYUV[n].g = y; + pYUV[n].r = cr; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } - } else { - for (i = 0; i < nBlocksInMcu - 2; i++) { - py420[i] = pYCbCr + i * 64; - } - pcb = pYCbCr + (nBlocksInMcu - 2) * 64; - pcr = pcb + 64; - - pixelX = txb * 16; - pixelY = tyb * 16; - pos = (pixelY * width) + pixelX; - - for (j = 0; j < 16; j++) { - for (i = 0; i < 16; i++) { - // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} - y = *(py420[(j >> 3) * 2 + (i >> 3)]++); - m = ((j >> 1) << 3) + (i >> 1); - cb = pcb[m]; - cr = pcr[m]; - n = pos + i; - pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; - pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; - pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + else + { + for (i = 0; i < nBlocksInMcu - 2; i++) + { + py420[i] = pYCbCr + i * 64; + } + pcb = pYCbCr + (nBlocksInMcu - 2) * 64; + pcr = pcb + 64; + + pixelX = txb * 16; + pixelY = tyb * 16; + pos = (pixelY * width) + pixelX; + + for (j = 0; j < 16; j++) + { + for (i = 0; i < 16; i++) + { + // block number is ((j/8) * 2 + i/8)={0, 1, 2, 3} + y = *(py420[(j >> 3) * 2 + (i >> 3)]++); + m = ((j >> 1) << 3) + (i >> 1); + cb = pcb[m]; + cr = pcr[m]; + n = pos + i; + pByte[n].b = rlimitTable[mY[y] + mCbToB[cb]]; + pByte[n].g = rlimitTable[mY[y] + mCbToG[cb] + mCrToG[cr]]; + pByte[n].r = rlimitTable[mY[y] + mCrToR[cr]]; + } + pos += width; + } } - pos += width; - } } - } - void decompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection) { - unsigned char *ptr; - unsigned char byTileYuv[768] = {}; - - memset(dctCoeff, 0, 384 * 2); - ptr = byTileYuv; - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); - idctTransform(dctCoeff, ptr, QT_TableSelection); - ptr += 64; - - if (yuvmode == YuvMode::YUV420) { - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 192); - idctTransform(dctCoeff + 192, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 256); - idctTransform(dctCoeff + 256, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 320); - idctTransform(dctCoeff + 320, ptr, QT_TableSelection + 1); - } else { - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); - } - - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - // yuvBuffer for YUV record - yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - } - - void decompress2Pass(int txb, int tyb, char *outBuf, - uint8_t QT_TableSelection) { - unsigned char *ptr; - unsigned char byTileYuv[768]; - memset(dctCoeff, 0, 384 * 2); - - ptr = byTileYuv; - processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); - idctTransform(dctCoeff, ptr, QT_TableSelection); - ptr += 64; - - processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); - idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); - ptr += 64; - - processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); - idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); - - yuvToBuffer(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - } - - void vqDecompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection, - struct ColorCache *VQ) { - unsigned char *ptr, i; - unsigned char byTileYuv[192]; - int data; - - ptr = byTileYuv; - if (VQ->bitMapBits == 0) { - for (i = 0; i < 64; i++) { - ptr[0] = (VQ->color[VQ->index[0]] & 0xFF0000) >> 16; - ptr[64] = (VQ->color[VQ->index[0]] & 0x00FF00) >> 8; - ptr[128] = VQ->color[VQ->index[0]] & 0x0000FF; - ptr += 1; - } - } else { - for (i = 0; i < 64; i++) { - data = static_cast<int>(lookKbits(VQ->bitMapBits)); - ptr[0] = (VQ->color[VQ->index[data]] & 0xFF0000) >> 16; - ptr[64] = (VQ->color[VQ->index[data]] & 0x00FF00) >> 8; - ptr[128] = VQ->color[VQ->index[data]] & 0x0000FF; - ptr += 1; - skipKbits(VQ->bitMapBits); - } - } - // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); - yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), - reinterpret_cast<unsigned char *>(outBuf)); - } - - void moveBlockIndex() { - if (yuvmode == YuvMode::YUV444) { - txb++; - if (txb >= static_cast<int>(width / 8)) { - tyb++; - if (tyb >= static_cast<int>(height / 8)) { - tyb = 0; + void decompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection) + { + unsigned char *ptr; + unsigned char byTileYuv[768] = {}; + + memset(dctCoeff, 0, 384 * 2); + ptr = byTileYuv; + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); + idctTransform(dctCoeff, ptr, QT_TableSelection); + ptr += 64; + + if (yuvmode == YuvMode::YUV420) + { + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 192); + idctTransform(dctCoeff + 192, ptr, QT_TableSelection); + ptr += 64; + + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 256); + idctTransform(dctCoeff + 256, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 320); + idctTransform(dctCoeff + 320, ptr, QT_TableSelection + 1); } - txb = 0; - } - } else { - txb++; - if (txb >= static_cast<int>(width / 16)) { - tyb++; - if (tyb >= static_cast<int>(height / 16)) { - tyb = 0; + else + { + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); } - txb = 0; - } + + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); + // yuvBuffer for YUV record + yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); } - } - void initColorTable() { - int i, x; - int nScale = 1L << 16; // equal to power(2,16) - int nHalf = nScale >> 1; + void decompress2Pass(int txb, int tyb, char *outBuf, + uint8_t QT_TableSelection) + { + unsigned char *ptr; + unsigned char byTileYuv[768]; + memset(dctCoeff, 0, 384 * 2); -#define FIX(x) ((int)((x)*nScale + 0.5)) + ptr = byTileYuv; + processHuffmanDataUnit(ydcNr, yacNr, &dcy, 0); + idctTransform(dctCoeff, ptr, QT_TableSelection); + ptr += 64; - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>r value is nearest int to 1.597656 * x */ - /* Cb=>b value is nearest int to 2.015625 * x */ - /* Cr=>g value is scaled-up -0.8125 * x */ - /* Cb=>g value is scaled-up -0.390625 * x */ - for (i = 0, x = -128; i < 256; i++, x++) { - mCrToR[i] = (FIX(1.597656) * x + nHalf) >> 16; - mCbToB[i] = (FIX(2.015625) * x + nHalf) >> 16; - mCrToG[i] = (-FIX(0.8125) * x + nHalf) >> 16; - mCbToG[i] = (-FIX(0.390625) * x + nHalf) >> 16; - } - for (i = 0, x = -16; i < 256; i++, x++) { - mY[i] = (FIX(1.164) * x + nHalf) >> 16; + processHuffmanDataUnit(cbDcNr, cbAcNr, &dcCb, 64); + idctTransform(dctCoeff + 64, ptr, QT_TableSelection + 1); + ptr += 64; + + processHuffmanDataUnit(crDcNr, crAcNr, &dcCr, 128); + idctTransform(dctCoeff + 128, ptr, QT_TableSelection + 1); + + yuvToBuffer(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); } - // For color Text Enchance Y Re-map. Recommend to disable in default - /* - for (i = 0; i < (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); - i++) { - temp = (double)i / - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate; - temp1 = 1.0 / VideoEngineInfo->INFData.Gamma1Parameter; - mY[i] = - (BYTE)(VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate * pow (temp, - temp1)); - if (mY[i] > 255) mY[i] = 255; + + void vqDecompress(int txb, int tyb, char *outBuf, uint8_t QT_TableSelection, + struct ColorCache *VQ) + { + unsigned char *ptr, i; + unsigned char byTileYuv[192]; + int data; + + ptr = byTileYuv; + if (VQ->bitMapBits == 0) + { + for (i = 0; i < 64; i++) + { + ptr[0] = (VQ->color[VQ->index[0]] & 0xFF0000) >> 16; + ptr[64] = (VQ->color[VQ->index[0]] & 0x00FF00) >> 8; + ptr[128] = VQ->color[VQ->index[0]] & 0x0000FF; + ptr += 1; } - for (i = (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i < 256; - i++) { - mY[i] = - (BYTE)((VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) + (256 - - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) * ( pow((double)((i - - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) / (256 - - (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate))), (1.0 / - VideoEngineInfo->INFData.Gamma2Parameter)) )); - if (mY[i] > 255) mY[i] = 255; + } + else + { + for (i = 0; i < 64; i++) + { + data = static_cast<int>(lookKbits(VQ->bitMapBits)); + ptr[0] = (VQ->color[VQ->index[data]] & 0xFF0000) >> 16; + ptr[64] = (VQ->color[VQ->index[data]] & 0x00FF00) >> 8; + ptr[128] = VQ->color[VQ->index[data]] & 0x0000FF; + ptr += 1; + skipKbits(VQ->bitMapBits); } - */ - } - void loadHuffmanTable(HuffmanTable *HT, const unsigned char *nrcode, - const unsigned char *value, - const unsigned short int *Huff_code) { - unsigned char k, j, i; - unsigned int code, codeIndex; - - for (j = 1; j <= 16; j++) { - HT->length[j] = nrcode[j]; - } - for (i = 0, k = 1; k <= 16; k++) { - for (j = 0; j < HT->length[k]; j++) { - HT->v[wordHiLo(k, j)] = value[i]; - i++; - } + } + // yuvToRgb (txb, tyb, byTileYuv, (unsigned char *)outBuf); + yuvToRgb(txb, tyb, byTileYuv, yuvBuffer.data(), + reinterpret_cast<unsigned char *>(outBuf)); } - code = 0; - for (k = 1; k <= 16; k++) { - HT->minorCode[k] = static_cast<unsigned short int>(code); - for (j = 1; j <= HT->length[k]; j++) { - code++; - } - HT->majorCode[k] = static_cast<unsigned short int>(code - 1); - code *= 2; - if (HT->length[k] == 0) { - HT->minorCode[k] = 0xFFFF; - HT->majorCode[k] = 0; - } + void moveBlockIndex() + { + if (yuvmode == YuvMode::YUV444) + { + txb++; + if (txb >= static_cast<int>(width / 8)) + { + tyb++; + if (tyb >= static_cast<int>(height / 8)) + { + tyb = 0; + } + txb = 0; + } + } + else + { + txb++; + if (txb >= static_cast<int>(width / 16)) + { + tyb++; + if (tyb >= static_cast<int>(height / 16)) + { + tyb = 0; + } + txb = 0; + } + } } - HT->len[0] = 2; - i = 2; + void initColorTable() + { + int i, x; + int nScale = 1L << 16; // equal to power(2,16) + int nHalf = nScale >> 1; - for (codeIndex = 1; codeIndex < 65535; codeIndex++) { - if (codeIndex < Huff_code[i]) { - HT->len[codeIndex] = static_cast<unsigned char>(Huff_code[i + 1]); - } else { - i = i + 2; - HT->len[codeIndex] = static_cast<unsigned char>(Huff_code[i + 1]); - } - } - } - void initJpgTable() { - initColorTable(); - prepareRangeLimitTable(); - loadHuffmanTable(&htdc[0], stdDcLuminanceNrcodes, stdDcLuminanceValues, - dcLuminanceHuffmancode); - loadHuffmanTable(&htac[0], stdAcLuminanceNrcodes, stdAcLuminanceValues, - acLuminanceHuffmancode); - loadHuffmanTable(&htdc[1], stdDcChrominanceNrcodes, stdDcChrominanceValues, - dcChrominanceHuffmancode); - loadHuffmanTable(&htac[1], stdAcChrominanceNrcodes, stdAcChrominanceValues, - acChrominanceHuffmancode); - } - - void prepareRangeLimitTable() - /* Allocate and fill in the sample_range_limit table */ - { - int j; - rlimitTable = reinterpret_cast<unsigned char *>(malloc(5 * 256L + 128)); - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - memset((void *)rlimitTable, 0, 256); - rlimitTable += 256; /* allow negative subscripts of simple table */ - /* Main part of "simple" table: limit[x] = x */ - for (j = 0; j < 256; j++) { - rlimitTable[j] = j; - } - /* End of simple table, rest of first half of post-IDCT table */ - for (j = 256; j < 640; j++) { - rlimitTable[j] = 255; +#define FIX(x) ((int)((x)*nScale + 0.5)) + + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>r value is nearest int to 1.597656 * x */ + /* Cb=>b value is nearest int to 2.015625 * x */ + /* Cr=>g value is scaled-up -0.8125 * x */ + /* Cb=>g value is scaled-up -0.390625 * x */ + for (i = 0, x = -128; i < 256; i++, x++) + { + mCrToR[i] = (FIX(1.597656) * x + nHalf) >> 16; + mCbToB[i] = (FIX(2.015625) * x + nHalf) >> 16; + mCrToG[i] = (-FIX(0.8125) * x + nHalf) >> 16; + mCbToG[i] = (-FIX(0.390625) * x + nHalf) >> 16; + } + for (i = 0, x = -16; i < 256; i++, x++) + { + mY[i] = (FIX(1.164) * x + nHalf) >> 16; + } + // For color Text Enchance Y Re-map. Recommend to disable in default + /* + for (i = 0; i < + (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i++) { temp = + (double)i / VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate; temp1 + = 1.0 / VideoEngineInfo->INFData.Gamma1Parameter; mY[i] = + (BYTE)(VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate * pow (temp, + temp1)); + if (mY[i] > 255) mY[i] = 255; + } + for (i = (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate); i < + 256; i++) { mY[i] = + (BYTE)((VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) + (256 - + VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) * ( pow((double)((i + - VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate) / (256 - + (VideoEngineInfo->INFData.Gamma1_Gamma2_Seperate))), (1.0 / + VideoEngineInfo->INFData.Gamma2Parameter)) )); + if (mY[i] > 255) mY[i] = 255; + } + */ } + void loadHuffmanTable(HuffmanTable *HT, const unsigned char *nrcode, + const unsigned char *value, + const unsigned short int *Huff_code) + { + unsigned char k, j, i; + unsigned int code, codeIndex; + + for (j = 1; j <= 16; j++) + { + HT->length[j] = nrcode[j]; + } + for (i = 0, k = 1; k <= 16; k++) + { + for (j = 0; j < HT->length[k]; j++) + { + HT->v[wordHiLo(k, j)] = value[i]; + i++; + } + } + + code = 0; + for (k = 1; k <= 16; k++) + { + HT->minorCode[k] = static_cast<unsigned short int>(code); + for (j = 1; j <= HT->length[k]; j++) + { + code++; + } + HT->majorCode[k] = static_cast<unsigned short int>(code - 1); + code *= 2; + if (HT->length[k] == 0) + { + HT->minorCode[k] = 0xFFFF; + HT->majorCode[k] = 0; + } + } + + HT->len[0] = 2; + i = 2; - /* Second half of post-IDCT table */ - memset((void *)(rlimitTable + 640), 0, 384); - for (j = 0; j < 128; j++) { - rlimitTable[j + 1024] = j; + for (codeIndex = 1; codeIndex < 65535; codeIndex++) + { + if (codeIndex < Huff_code[i]) + { + HT->len[codeIndex] = + static_cast<unsigned char>(Huff_code[i + 1]); + } + else + { + i = i + 2; + HT->len[codeIndex] = + static_cast<unsigned char>(Huff_code[i + 1]); + } + } } - } - - inline unsigned short int wordHiLo(uint8_t byte_high, uint8_t byte_low) { - return (byte_high << 8) + byte_low; - } - - // river - void processHuffmanDataUnit(uint8_t DC_nr, uint8_t AC_nr, - signed short int *previous_DC, - unsigned short int position) { - uint8_t nr = 0; - uint8_t k; - unsigned short int tmpHcode; - uint8_t sizeVal, count0; - unsigned short int *minCode; - uint8_t *huffValues; - uint8_t byteTemp; - - minCode = htdc[DC_nr].minorCode; - // maj_code=htdc[DC_nr].majorCode; - huffValues = htdc[DC_nr].v; - - // DC - k = htdc[DC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; - // river - // tmp_Hcode=lookKbits(k); - tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - skipKbits(k); - sizeVal = - huffValues[wordHiLo(k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; - if (sizeVal == 0) { - dctCoeff[position + 0] = *previous_DC; - } else { - dctCoeff[position + 0] = *previous_DC + getKbits(sizeVal); - *previous_DC = dctCoeff[position + 0]; + void initJpgTable() + { + initColorTable(); + prepareRangeLimitTable(); + loadHuffmanTable(&htdc[0], stdDcLuminanceNrcodes, stdDcLuminanceValues, + dcLuminanceHuffmancode); + loadHuffmanTable(&htac[0], stdAcLuminanceNrcodes, stdAcLuminanceValues, + acLuminanceHuffmancode); + loadHuffmanTable(&htdc[1], stdDcChrominanceNrcodes, + stdDcChrominanceValues, dcChrominanceHuffmancode); + loadHuffmanTable(&htac[1], stdAcChrominanceNrcodes, + stdAcChrominanceValues, acChrominanceHuffmancode); } - // Second, AC coefficient decoding - minCode = htac[AC_nr].minorCode; - // maj_code=htac[AC_nr].majorCode; - huffValues = htac[AC_nr].v; - - nr = 1; // AC coefficient - do { - k = htac[AC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; - tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - skipKbits(k); - - byteTemp = - huffValues[wordHiLo(k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; - sizeVal = byteTemp & 0xF; - count0 = byteTemp >> 4; - if (sizeVal == 0) { - if (count0 != 0xF) { - break; + void prepareRangeLimitTable() + /* Allocate and fill in the sample_range_limit table */ + { + int j; + rlimitTable = reinterpret_cast<unsigned char *>(malloc(5 * 256L + 128)); + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + memset((void *)rlimitTable, 0, 256); + rlimitTable += 256; /* allow negative subscripts of simple table */ + /* Main part of "simple" table: limit[x] = x */ + for (j = 0; j < 256; j++) + { + rlimitTable[j] = j; + } + /* End of simple table, rest of first half of post-IDCT table */ + for (j = 256; j < 640; j++) + { + rlimitTable[j] = 255; + } + + /* Second half of post-IDCT table */ + memset((void *)(rlimitTable + 640), 0, 384); + for (j = 0; j < 128; j++) + { + rlimitTable[j + 1024] = j; } - nr += 16; - } else { - nr += count0; // skip count_0 zeroes - dctCoeff[position + dezigzag[nr++]] = getKbits(sizeVal); - } - } while (nr < 64); - } - - unsigned short int lookKbits(uint8_t k) { - unsigned short int revcode; - - revcode = static_cast<unsigned short int>(codebuf >> (32 - k)); - - return (revcode); - } - - void skipKbits(uint8_t k) { - unsigned long readbuf; - - if ((newbits - k) <= 0) { - readbuf = buffer[bufferIndex]; - bufferIndex++; - codebuf = - (codebuf << k) | ((newbuf | (readbuf >> (newbits))) >> (32 - k)); - newbuf = readbuf << (k - newbits); - newbits = 32 + newbits - k; - } else { - codebuf = (codebuf << k) | (newbuf >> (32 - k)); - newbuf = newbuf << k; - newbits -= k; } - } - signed short int getKbits(uint8_t k) { - signed short int signedWordvalue; + inline unsigned short int wordHiLo(uint8_t byte_high, uint8_t byte_low) + { + return (byte_high << 8) + byte_low; + } // river - // signed_wordvalue=lookKbits(k); - signedWordvalue = static_cast<unsigned short int>(codebuf >> (32 - k)); - if (((1L << (k - 1)) & signedWordvalue) == 0) { - // neg_pow2 was previously defined as the below. It seemed silly to keep - // a table of values around for something - // THat's relatively easy to compute, so it was replaced with the - // appropriate math - // signed_wordvalue = signed_wordvalue - (0xFFFF >> (16 - k)); - std::array<signed short int, 17> negPow2 = { - 0, -1, -3, -7, -15, -31, -63, -127, - -255, -511, -1023, -2047, -4095, -8191, -16383, -32767}; - - signedWordvalue = signedWordvalue + negPow2[k]; - } - skipKbits(k); - return signedWordvalue; - } - int initJpgDecoding() { - bytePos = 0; - loadQuantTable(qt[0]); - loadQuantTableCb(qt[1]); - // Note: Added for Dual-JPEG - loadAdvanceQuantTable(qt[2]); - loadAdvanceQuantTableCb(qt[3]); - return 1; - } - - void setQuantTable(const uint8_t *basic_table, uint8_t scale_factor, - std::array<uint8_t, 64> &newtable) - // Set quantization table and zigzag reorder it - { - uint8_t i; - long temp; - for (i = 0; i < 64; i++) { - temp = (static_cast<long>(basic_table[i] * 16) / scale_factor); - /* limit the values to the valid range */ - if (temp <= 0L) { - temp = 1L; - } - if (temp > 255L) { - temp = 255L; /* limit to baseline range if requested */ - } - newtable[zigzag[i]] = static_cast<uint8_t>(temp); - } - } - - void updatereadbuf(uint32_t *codebuf, uint32_t *newbuf, int walks, - int *newbits, std::vector<uint32_t> &buffer) { - unsigned long readbuf; - - if ((*newbits - walks) <= 0) { - readbuf = buffer[bufferIndex]; - bufferIndex++; - *codebuf = (*codebuf << walks) | - ((*newbuf | (readbuf >> (*newbits))) >> (32 - walks)); - *newbuf = readbuf << (walks - *newbits); - *newbits = 32 + *newbits - walks; - } else { - *codebuf = (*codebuf << walks) | (*newbuf >> (32 - walks)); - *newbuf = *newbuf << walks; - *newbits -= walks; - } - } - - uint32_t decode(std::vector<uint32_t> &bufferVector, unsigned long width, - unsigned long height, YuvMode yuvmode_in, int ySelector, - int uvSelector) { - ColorCache decodeColor; - if (width != userWidth || height != userHeight || yuvmode_in != yuvmode || - ySelector != ySelector || uvSelector != uvSelector) { - yuvmode = yuvmode_in; - ySelector = ySelector; // 0-7 - uvSelector = uvSelector; // 0-7 - userHeight = height; - userWidth = width; - width = width; - height = height; - - // TODO(ed) Magic number section. Document appropriately - advanceSelector = 0; // 0-7 - mapping = 0; // 0 or 1 - - if (yuvmode == YuvMode::YUV420) { - if ((width % 16) != 0u) { - width = width + 16 - (width % 16); + void processHuffmanDataUnit(uint8_t DC_nr, uint8_t AC_nr, + signed short int *previous_DC, + unsigned short int position) + { + uint8_t nr = 0; + uint8_t k; + unsigned short int tmpHcode; + uint8_t sizeVal, count0; + unsigned short int *minCode; + uint8_t *huffValues; + uint8_t byteTemp; + + minCode = htdc[DC_nr].minorCode; + // maj_code=htdc[DC_nr].majorCode; + huffValues = htdc[DC_nr].v; + + // DC + k = htdc[DC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; + // river + // tmp_Hcode=lookKbits(k); + tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + skipKbits(k); + sizeVal = huffValues[wordHiLo( + k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; + if (sizeVal == 0) + { + dctCoeff[position + 0] = *previous_DC; } - if ((height % 16) != 0u) { - height = height + 16 - (height % 16); + else + { + dctCoeff[position + 0] = *previous_DC + getKbits(sizeVal); + *previous_DC = dctCoeff[position + 0]; } - } else { - if ((width % 8) != 0u) { - width = width + 8 - (width % 8); + + // Second, AC coefficient decoding + minCode = htac[AC_nr].minorCode; + // maj_code=htac[AC_nr].majorCode; + huffValues = htac[AC_nr].v; + + nr = 1; // AC coefficient + do + { + k = htac[AC_nr].len[static_cast<unsigned short int>(codebuf >> 16)]; + tmpHcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + skipKbits(k); + + byteTemp = huffValues[wordHiLo( + k, static_cast<uint8_t>(tmpHcode - minCode[k]))]; + sizeVal = byteTemp & 0xF; + count0 = byteTemp >> 4; + if (sizeVal == 0) + { + if (count0 != 0xF) + { + break; + } + nr += 16; + } + else + { + nr += count0; // skip count_0 zeroes + dctCoeff[position + dezigzag[nr++]] = getKbits(sizeVal); + } + } while (nr < 64); + } + + unsigned short int lookKbits(uint8_t k) + { + unsigned short int revcode; + + revcode = static_cast<unsigned short int>(codebuf >> (32 - k)); + + return (revcode); + } + + void skipKbits(uint8_t k) + { + unsigned long readbuf; + + if ((newbits - k) <= 0) + { + readbuf = buffer[bufferIndex]; + bufferIndex++; + codebuf = (codebuf << k) | + ((newbuf | (readbuf >> (newbits))) >> (32 - k)); + newbuf = readbuf << (k - newbits); + newbits = 32 + newbits - k; } - if ((height % 8) != 0u) { - height = height + 8 - (height % 8); + else + { + codebuf = (codebuf << k) | (newbuf >> (32 - k)); + newbuf = newbuf << k; + newbits -= k; } - } + } - initJpgDecoding(); + signed short int getKbits(uint8_t k) + { + signed short int signedWordvalue; + + // river + // signed_wordvalue=lookKbits(k); + signedWordvalue = static_cast<unsigned short int>(codebuf >> (32 - k)); + if (((1L << (k - 1)) & signedWordvalue) == 0) + { + // neg_pow2 was previously defined as the below. It seemed silly to + // keep a table of values around for something THat's relatively + // easy to compute, so it was replaced with the appropriate math + // signed_wordvalue = signed_wordvalue - (0xFFFF >> (16 - k)); + std::array<signed short int, 17> negPow2 = { + 0, -1, -3, -7, -15, -31, -63, -127, + -255, -511, -1023, -2047, -4095, -8191, -16383, -32767}; + + signedWordvalue = signedWordvalue + negPow2[k]; + } + skipKbits(k); + return signedWordvalue; } - // TODO(ed) cleanup cruft - buffer = bufferVector.data(); - - codebuf = bufferVector[0]; - newbuf = bufferVector[1]; - bufferIndex = 2; - - txb = tyb = 0; - newbits = 32; - dcy = dcCb = dcCr = 0; - - static const uint32_t vqHeaderMask = 0x01; - static const uint32_t vqNoUpdateHeader = 0x00; - static const uint32_t vqUpdateHeader = 0x01; - static const int vqNoUpdateLength = 0x03; - static const int vqUpdateLength = 0x1B; - static const uint32_t vqIndexMask = 0x03; - static const uint32_t vqColorMask = 0xFFFFFF; - - static const int blockAsT2100StartLength = 0x04; - static const int blockAsT2100SkipLength = 20; // S:1 H:3 X:8 Y:8 - - do { - auto blockHeader = static_cast<JpgBlock>((codebuf >> 28) & 0xFF); - switch (blockHeader) { - case JpgBlock::JPEG_NO_SKIP_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0); - break; - case JpgBlock::FRAME_END_CODE: - return 0; - break; - case JpgBlock::JPEG_SKIP_CODE: - - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0); - break; - case JpgBlock::VQ_NO_SKIP_1_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 0; - - for (int i = 0; i < 1; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - case JpgBlock::VQ_SKIP_1_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 0; - - for (int i = 0; i < 1; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - - case JpgBlock::VQ_NO_SKIP_2_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 1; - - for (int i = 0; i < 2; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - break; - case JpgBlock::VQ_SKIP_2_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 1; - - for (int i = 0; i < 2; i++) { - decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[decodeColor.index[i]] = - ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); - } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - - break; - case JpgBlock::VQ_NO_SKIP_4_COLOR_CODE: - updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 2; - - for (unsigned char &i : decodeColor.index) { - i = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[i] = ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); + int initJpgDecoding() + { + bytePos = 0; + loadQuantTable(qt[0]); + loadQuantTableCb(qt[1]); + // Note: Added for Dual-JPEG + loadAdvanceQuantTable(qt[2]); + loadAdvanceQuantTableCb(qt[3]); + return 1; + } + + void setQuantTable(const uint8_t *basic_table, uint8_t scale_factor, + std::array<uint8_t, 64> &newtable) + // Set quantization table and zigzag reorder it + { + uint8_t i; + long temp; + for (i = 0; i < 64; i++) + { + temp = (static_cast<long>(basic_table[i] * 16) / scale_factor); + /* limit the values to the valid range */ + if (temp <= 0L) + { + temp = 1L; } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); - - break; - - case JpgBlock::VQ_SKIP_4_COLOR_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; - - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decodeColor.bitMapBits = 2; - - for (unsigned char &i : decodeColor.index) { - i = ((codebuf >> 29) & vqIndexMask); - if (((codebuf >> 31) & vqHeaderMask) == vqNoUpdateHeader) { - updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, &newbits, - bufferVector); - } else { - decodeColor.color[i] = ((codebuf >> 5) & vqColorMask); - updatereadbuf(&codebuf, &newbuf, vqUpdateLength, &newbits, - bufferVector); + if (temp > 255L) + { + temp = 255L; /* limit to baseline range if requested */ } - } - vqDecompress(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), 0, - &decodeColor); + newtable[zigzag[i]] = static_cast<uint8_t>(temp); + } + } - break; - case JpgBlock::JPEG_SKIP_PASS2_CODE: - txb = (codebuf & 0x0FF00000) >> 20; - tyb = (codebuf & 0x0FF000) >> 12; + void updatereadbuf(uint32_t *codebuf, uint32_t *newbuf, int walks, + int *newbits, std::vector<uint32_t> &buffer) + { + unsigned long readbuf; + + if ((*newbits - walks) <= 0) + { + readbuf = buffer[bufferIndex]; + bufferIndex++; + *codebuf = (*codebuf << walks) | + ((*newbuf | (readbuf >> (*newbits))) >> (32 - walks)); + *newbuf = readbuf << (walks - *newbits); + *newbits = 32 + *newbits - walks; + } + else + { + *codebuf = (*codebuf << walks) | (*newbuf >> (32 - walks)); + *newbuf = *newbuf << walks; + *newbits -= walks; + } + } - updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, &newbits, - bufferVector); - decompress2Pass(txb, tyb, reinterpret_cast<char *>(outBuffer.data()), - 2); + uint32_t decode(std::vector<uint32_t> &bufferVector, unsigned long width, + unsigned long height, YuvMode yuvmode_in, int ySelector, + int uvSelector) + { + ColorCache decodeColor; + if (width != userWidth || height != userHeight || + yuvmode_in != yuvmode || ySelector != ySelector || + uvSelector != uvSelector) + { + yuvmode = yuvmode_in; + ySelector = ySelector; // 0-7 + uvSelector = uvSelector; // 0-7 + userHeight = height; + userWidth = width; + width = width; + height = height; + + // TODO(ed) Magic number section. Document appropriately + advanceSelector = 0; // 0-7 + mapping = 0; // 0 or 1 + + if (yuvmode == YuvMode::YUV420) + { + if ((width % 16) != 0u) + { + width = width + 16 - (width % 16); + } + if ((height % 16) != 0u) + { + height = height + 16 - (height % 16); + } + } + else + { + if ((width % 8) != 0u) + { + width = width + 8 - (width % 8); + } + if ((height % 8) != 0u) + { + height = height + 8 - (height % 8); + } + } - break; - default: - // TODO(ed) propogate errors upstream - return -1; - break; - } - moveBlockIndex(); + initJpgDecoding(); + } + // TODO(ed) cleanup cruft + buffer = bufferVector.data(); + + codebuf = bufferVector[0]; + newbuf = bufferVector[1]; + bufferIndex = 2; + + txb = tyb = 0; + newbits = 32; + dcy = dcCb = dcCr = 0; + + static const uint32_t vqHeaderMask = 0x01; + static const uint32_t vqNoUpdateHeader = 0x00; + static const uint32_t vqUpdateHeader = 0x01; + static const int vqNoUpdateLength = 0x03; + static const int vqUpdateLength = 0x1B; + static const uint32_t vqIndexMask = 0x03; + static const uint32_t vqColorMask = 0xFFFFFF; + + static const int blockAsT2100StartLength = 0x04; + static const int blockAsT2100SkipLength = 20; // S:1 H:3 X:8 Y:8 + + do + { + auto blockHeader = static_cast<JpgBlock>((codebuf >> 28) & 0xFF); + switch (blockHeader) + { + case JpgBlock::JPEG_NO_SKIP_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0); + break; + case JpgBlock::FRAME_END_CODE: + return 0; + break; + case JpgBlock::JPEG_SKIP_CODE: + + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0); + break; + case JpgBlock::VQ_NO_SKIP_1_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 0; + + for (int i = 0; i < 1; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + case JpgBlock::VQ_SKIP_1_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 0; + + for (int i = 0; i < 1; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + + case JpgBlock::VQ_NO_SKIP_2_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 1; + + for (int i = 0; i < 2; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + break; + case JpgBlock::VQ_SKIP_2_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 1; + + for (int i = 0; i < 2; i++) + { + decodeColor.index[i] = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[decodeColor.index[i]] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + case JpgBlock::VQ_NO_SKIP_4_COLOR_CODE: + updatereadbuf(&codebuf, &newbuf, blockAsT2100StartLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 2; + + for (unsigned char &i : decodeColor.index) + { + i = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[i] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + + case JpgBlock::VQ_SKIP_4_COLOR_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decodeColor.bitMapBits = 2; + + for (unsigned char &i : decodeColor.index) + { + i = ((codebuf >> 29) & vqIndexMask); + if (((codebuf >> 31) & vqHeaderMask) == + vqNoUpdateHeader) + { + updatereadbuf(&codebuf, &newbuf, vqNoUpdateLength, + &newbits, bufferVector); + } + else + { + decodeColor.color[i] = + ((codebuf >> 5) & vqColorMask); + updatereadbuf(&codebuf, &newbuf, vqUpdateLength, + &newbits, bufferVector); + } + } + vqDecompress(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), 0, + &decodeColor); + + break; + case JpgBlock::JPEG_SKIP_PASS2_CODE: + txb = (codebuf & 0x0FF00000) >> 20; + tyb = (codebuf & 0x0FF000) >> 12; + + updatereadbuf(&codebuf, &newbuf, blockAsT2100SkipLength, + &newbits, bufferVector); + decompress2Pass(txb, tyb, + reinterpret_cast<char *>(outBuffer.data()), + 2); + + break; + default: + // TODO(ed) propogate errors upstream + return -1; + break; + } + moveBlockIndex(); - } while (bufferIndex <= bufferVector.size()); + } while (bufferIndex <= bufferVector.size()); - return -1; - } + return -1; + } #ifdef cimg_version - void dump_to_bitmap_file() { - cimg_library::CImg<unsigned char> image(width, height, 1, 3); - for (int y = 0; y < width; y++) { - for (int x = 0; x < height; x++) { - auto pixel = outBuffer[x + (y * width)]; - image(x, y, 0) = pixel.r; - image(x, y, 1) = pixel.g; - image(x, y, 2) = pixel.b; - } + void dump_to_bitmap_file() + { + cimg_library::CImg<unsigned char> image(width, height, 1, 3); + for (int y = 0; y < width; y++) + { + for (int x = 0; x < height; x++) + { + auto pixel = outBuffer[x + (y * width)]; + image(x, y, 0) = pixel.r; + image(x, y, 1) = pixel.g; + image(x, y, 2) = pixel.b; + } + } + image.save("/tmp/file2.bmp"); } - image.save("/tmp/file2.bmp"); - } #endif - private: - YuvMode yuvmode{}; - // width and height are the modes your display used - unsigned long width{}; - unsigned long height{}; - unsigned long userWidth{}; - unsigned long userHeight{}; - unsigned char ySelector{}; - int scalefactor; - int scalefactoruv; - int advancescalefactor; - int advancescalefactoruv; - int mapping{}; - unsigned char uvSelector{}; - unsigned char advanceSelector{}; - int bytePos{}; // current byte position - - // quantization tables, no more than 4 quantization tables - std::array<std::array<long, 64>, 4> qt{}; - - // DC huffman tables , no more than 4 (0..3) - std::array<HuffmanTable, 4> htdc{}; - // AC huffman tables (0..3) - std::array<HuffmanTable, 4> htac{}; - std::array<int, 256> mCrToR{}; - std::array<int, 256> mCbToB{}; - std::array<int, 256> mCrToG{}; - std::array<int, 256> mCbToG{}; - std::array<int, 256> mY{}; - unsigned long bufferIndex{}; - uint32_t codebuf{}, newbuf{}, readbuf{}; - const unsigned char *stdLuminanceQt{}; - const uint8_t *stdChrominanceQt{}; - - signed short int dcy{}, dcCb{}, dcCr{}; // Coeficientii DC pentru Y,Cb,Cr - signed short int dctCoeff[384]{}; - // std::vector<signed short int> dctCoeff; // Current DCT_coefficients - // quantization table number for Y, Cb, Cr - uint8_t yqNr = 0, cbQNr = 1, crQNr = 1; - // DC Huffman table number for Y,Cb, Cr - uint8_t ydcNr = 0, cbDcNr = 1, crDcNr = 1; - // AC Huffman table number for Y,Cb, Cr - uint8_t yacNr = 0, cbAcNr = 1, crAcNr = 1; - int txb = 0; - int tyb = 0; - int newbits{}; - uint8_t *rlimitTable{}; - std::vector<RGB> yuvBuffer; - // TODO(ed) this shouldn't exist. It is cruft that needs cleaning up - uint32_t *buffer{}; - - public: - std::vector<RGB> outBuffer; + private: + YuvMode yuvmode{}; + // width and height are the modes your display used + unsigned long width{}; + unsigned long height{}; + unsigned long userWidth{}; + unsigned long userHeight{}; + unsigned char ySelector{}; + int scalefactor; + int scalefactoruv; + int advancescalefactor; + int advancescalefactoruv; + int mapping{}; + unsigned char uvSelector{}; + unsigned char advanceSelector{}; + int bytePos{}; // current byte position + + // quantization tables, no more than 4 quantization tables + std::array<std::array<long, 64>, 4> qt{}; + + // DC huffman tables , no more than 4 (0..3) + std::array<HuffmanTable, 4> htdc{}; + // AC huffman tables (0..3) + std::array<HuffmanTable, 4> htac{}; + std::array<int, 256> mCrToR{}; + std::array<int, 256> mCbToB{}; + std::array<int, 256> mCrToG{}; + std::array<int, 256> mCbToG{}; + std::array<int, 256> mY{}; + unsigned long bufferIndex{}; + uint32_t codebuf{}, newbuf{}, readbuf{}; + const unsigned char *stdLuminanceQt{}; + const uint8_t *stdChrominanceQt{}; + + signed short int dcy{}, dcCb{}, dcCr{}; // Coeficientii DC pentru Y,Cb,Cr + signed short int dctCoeff[384]{}; + // std::vector<signed short int> dctCoeff; // Current DCT_coefficients + // quantization table number for Y, Cb, Cr + uint8_t yqNr = 0, cbQNr = 1, crQNr = 1; + // DC Huffman table number for Y,Cb, Cr + uint8_t ydcNr = 0, cbDcNr = 1, crDcNr = 1; + // AC Huffman table number for Y,Cb, Cr + uint8_t yacNr = 0, cbAcNr = 1, crAcNr = 1; + int txb = 0; + int tyb = 0; + int newbits{}; + uint8_t *rlimitTable{}; + std::vector<RGB> yuvBuffer; + // TODO(ed) this shouldn't exist. It is cruft that needs cleaning up + uint32_t *buffer{}; + + public: + std::vector<RGB> outBuffer; }; -} // namespace ast_video
\ No newline at end of file +} // namespace ast_video
\ No newline at end of file diff --git a/include/ast_video_puller.hpp b/include/ast_video_puller.hpp index c2ccea2..520fc68 100644 --- a/include/ast_video_puller.hpp +++ b/include/ast_video_puller.hpp @@ -1,186 +1,212 @@ #pragma once #include <ast_video_types.hpp> +#include <boost/asio.hpp> #include <cassert> #include <iostream> #include <mutex> #include <vector> -#include <boost/asio.hpp> -namespace ast_video { +namespace ast_video +{ // // Cursor struct is used in User Mode // -struct AstCurAttributionTag { - unsigned int posX; - unsigned int posY; - unsigned int curWidth; - unsigned int curHeight; - unsigned int curType; // 0:mono 1:color 2:disappear cursor - unsigned int curChangeFlag; +struct AstCurAttributionTag +{ + unsigned int posX; + unsigned int posY; + unsigned int curWidth; + unsigned int curHeight; + unsigned int curType; // 0:mono 1:color 2:disappear cursor + unsigned int curChangeFlag; }; // // For storing Cursor Information // -struct AstCursorTag { - AstCurAttributionTag attr; - // unsigned char icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2]; - unsigned char *icon; //[64*64*2]; +struct AstCursorTag +{ + AstCurAttributionTag attr; + // unsigned char icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2]; + unsigned char *icon; //[64*64*2]; }; // // For select image format, i.e. 422 JPG420, 444 JPG444, lumin/chrom table, 0 // ~ 11, low to high // -struct FeaturesTag { - short jpgFmt; // 422:JPG420, 444:JPG444 - short luminTbl; - short chromTbl; - short toleranceNoise; - int w; - int h; - unsigned char *buf; +struct FeaturesTag +{ + short jpgFmt; // 422:JPG420, 444:JPG444 + short luminTbl; + short chromTbl; + short toleranceNoise; + int w; + int h; + unsigned char *buf; }; // // For configure video engine control registers // -struct ImageInfo { - short doImageRefresh; // Action 0:motion 1:fullframe 2:quick cursor - char qcValid; // quick cursor enable/disable - unsigned int len; - int crypttype; - char cryptkey[16]; - union { - FeaturesTag features; - AstCursorTag cursorInfo; - } parameter; +struct ImageInfo +{ + short doImageRefresh; // Action 0:motion 1:fullframe 2:quick cursor + char qcValid; // quick cursor enable/disable + unsigned int len; + int crypttype; + char cryptkey[16]; + union + { + FeaturesTag features; + AstCursorTag cursorInfo; + } parameter; }; -class SimpleVideoPuller { - public: - SimpleVideoPuller() : imageInfo(){}; - - void initialize() { - std::cout << "Opening /dev/video\n"; - videoFd = open("/dev/video", O_RDWR); - if (videoFd == 0) { - std::cout << "Failed to open /dev/video\n"; - throw std::runtime_error("Failed to open /dev/video"); - } - std::cout << "Opened successfully\n"; - } - - RawVideoBuffer readVideo() { - assert(videoFd != 0); - RawVideoBuffer raw; - - imageInfo.doImageRefresh = 1; // full frame refresh - imageInfo.qcValid = 0; // quick cursor disabled - imageInfo.parameter.features.buf = - reinterpret_cast<unsigned char *>(raw.buffer.data()); - imageInfo.crypttype = -1; - std::cout << "Writing\n"; - - int status; - /* - status = write(videoFd, reinterpret_cast<char*>(&imageInfo), - sizeof(imageInfo)); - if (status != sizeof(imageInfo)) { - std::cout << "Write failed. Return: " << status << "\n"; - perror("perror output:"); - } - - std::cout << "Write done\n"; - */ - std::cout << "Reading\n"; - status = - read(videoFd, reinterpret_cast<char *>(&imageInfo), sizeof(imageInfo)); - std::cout << "Done reading\n"; - - if (status != 0) { - std::cerr << "Read failed with status " << status << "\n"; +class SimpleVideoPuller +{ + public: + SimpleVideoPuller() : imageInfo(){}; + + void initialize() + { + std::cout << "Opening /dev/video\n"; + videoFd = open("/dev/video", O_RDWR); + if (videoFd == 0) + { + std::cout << "Failed to open /dev/video\n"; + throw std::runtime_error("Failed to open /dev/video"); + } + std::cout << "Opened successfully\n"; } - raw.buffer.resize(imageInfo.len); - - raw.height = imageInfo.parameter.features.h; - raw.width = imageInfo.parameter.features.w; - if (imageInfo.parameter.features.jpgFmt == 422) { - raw.mode = YuvMode::YUV420; - } else { - raw.mode = YuvMode::YUV444; + RawVideoBuffer readVideo() + { + assert(videoFd != 0); + RawVideoBuffer raw; + + imageInfo.doImageRefresh = 1; // full frame refresh + imageInfo.qcValid = 0; // quick cursor disabled + imageInfo.parameter.features.buf = + reinterpret_cast<unsigned char *>(raw.buffer.data()); + imageInfo.crypttype = -1; + std::cout << "Writing\n"; + + int status; + /* + status = write(videoFd, reinterpret_cast<char*>(&imageInfo), + sizeof(imageInfo)); + if (status != sizeof(imageInfo)) { + std::cout << "Write failed. Return: " << status << "\n"; + perror("perror output:"); + } + + std::cout << "Write done\n"; + */ + std::cout << "Reading\n"; + status = read(videoFd, reinterpret_cast<char *>(&imageInfo), + sizeof(imageInfo)); + std::cout << "Done reading\n"; + + if (status != 0) + { + std::cerr << "Read failed with status " << status << "\n"; + } + + raw.buffer.resize(imageInfo.len); + + raw.height = imageInfo.parameter.features.h; + raw.width = imageInfo.parameter.features.w; + if (imageInfo.parameter.features.jpgFmt == 422) + { + raw.mode = YuvMode::YUV420; + } + else + { + raw.mode = YuvMode::YUV444; + } + return raw; } - return raw; - } - private: - int videoFd{}; - ImageInfo imageInfo; + private: + int videoFd{}; + ImageInfo imageInfo; }; #if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) -class AsyncVideoPuller { - public: - using video_callback = std::function<void(RawVideoBuffer &)>; - - explicit AsyncVideoPuller(boost::asio::io_service &ioService) - : imageInfo(), devVideo(ioService, open("/dev/video", O_RDWR)) { - videobuf = std::make_shared<RawVideoBuffer>(); - - imageInfo.doImageRefresh = 1; // full frame refresh - imageInfo.qcValid = 0; // quick cursor disabled - imageInfo.parameter.features.buf = - reinterpret_cast<unsigned char *>(videobuf->buffer.data()); - imageInfo.crypttype = -1; - }; - - void registerCallback(video_callback &callback) { - std::lock_guard<std::mutex> lock(callbackMutex); - callbacks.push_back(callback); - startRead(); - } - - void startRead() { - auto mutableBuffer = boost::asio::buffer(&imageInfo, sizeof(imageInfo)); - boost::asio::async_read(devVideo, mutableBuffer, - [this](const boost::system::error_code &ec, - std::size_t bytes_transferred) { - if (ec) { - std::cerr << "Read failed with status " << ec - << "\n"; - } else { - this->readDone(); - } - }); - } - - void readDone() { - std::cout << "Done reading\n"; - videobuf->buffer.resize(imageInfo.len); - - videobuf->height = imageInfo.parameter.features.h; - videobuf->width = imageInfo.parameter.features.w; - if (imageInfo.parameter.features.jpgFmt == 422) { - videobuf->mode = YuvMode::YUV420; - } else { - videobuf->mode = YuvMode::YUV444; +class AsyncVideoPuller +{ + public: + using video_callback = std::function<void(RawVideoBuffer &)>; + + explicit AsyncVideoPuller(boost::asio::io_service &ioService) : + imageInfo(), devVideo(ioService, open("/dev/video", O_RDWR)) + { + videobuf = std::make_shared<RawVideoBuffer>(); + + imageInfo.doImageRefresh = 1; // full frame refresh + imageInfo.qcValid = 0; // quick cursor disabled + imageInfo.parameter.features.buf = + reinterpret_cast<unsigned char *>(videobuf->buffer.data()); + imageInfo.crypttype = -1; + }; + + void registerCallback(video_callback &callback) + { + std::lock_guard<std::mutex> lock(callbackMutex); + callbacks.push_back(callback); + startRead(); + } + + void startRead() + { + auto mutableBuffer = boost::asio::buffer(&imageInfo, sizeof(imageInfo)); + boost::asio::async_read(devVideo, mutableBuffer, + [this](const boost::system::error_code &ec, + std::size_t bytes_transferred) { + if (ec) + { + std::cerr << "Read failed with status " + << ec << "\n"; + } + else + { + this->readDone(); + } + }); } - std::lock_guard<std::mutex> lock(callbackMutex); - for (auto &callback : callbacks) { - // TODO(ed) call callbacks async and double buffer frames - callback(*videobuf); + + void readDone() + { + std::cout << "Done reading\n"; + videobuf->buffer.resize(imageInfo.len); + + videobuf->height = imageInfo.parameter.features.h; + videobuf->width = imageInfo.parameter.features.w; + if (imageInfo.parameter.features.jpgFmt == 422) + { + videobuf->mode = YuvMode::YUV420; + } + else + { + videobuf->mode = YuvMode::YUV444; + } + std::lock_guard<std::mutex> lock(callbackMutex); + for (auto &callback : callbacks) + { + // TODO(ed) call callbacks async and double buffer frames + callback(*videobuf); + } } - } - - private: - std::shared_ptr<RawVideoBuffer> videobuf; - boost::asio::posix::stream_descriptor devVideo; - ImageInfo imageInfo; - std::mutex callbackMutex; - std::vector<video_callback> callbacks; + + private: + std::shared_ptr<RawVideoBuffer> videobuf; + boost::asio::posix::stream_descriptor devVideo; + ImageInfo imageInfo; + std::mutex callbackMutex; + std::vector<video_callback> callbacks; }; -#endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) -} // namespace ast_video +#endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +} // namespace ast_video diff --git a/include/ast_video_types.hpp b/include/ast_video_types.hpp index f980146..aeef1d8 100644 --- a/include/ast_video_types.hpp +++ b/include/ast_video_types.hpp @@ -2,18 +2,24 @@ #include <cstdint> #include <vector> -namespace ast_video { -enum class YuvMode { YUV444 = 0, YUV420 = 1 }; +namespace ast_video +{ +enum class YuvMode +{ + YUV444 = 0, + YUV420 = 1 +}; -class RawVideoBuffer { - public: - RawVideoBuffer() : buffer(1024 * 1024 * 10, 0){}; - unsigned long height{}; - unsigned long width{}; - int ySelector{}; - int uvSelector{}; - YuvMode mode; - // TODO(ed) determine a more appropriate buffer size - std::vector<uint32_t> buffer; +class RawVideoBuffer +{ + public: + RawVideoBuffer() : buffer(1024 * 1024 * 10, 0){}; + unsigned long height{}; + unsigned long width{}; + int ySelector{}; + int uvSelector{}; + YuvMode mode; + // TODO(ed) determine a more appropriate buffer size + std::vector<uint32_t> buffer; }; -} // namespace ast_video
\ No newline at end of file +} // namespace ast_video
\ No newline at end of file diff --git a/include/dbus_monitor.hpp b/include/dbus_monitor.hpp index e8b1a32..5dcd5ca 100644 --- a/include/dbus_monitor.hpp +++ b/include/dbus_monitor.hpp @@ -1,26 +1,33 @@ #pragma once -#include <dbus_singleton.hpp> -#include <sdbusplus/bus/match.hpp> #include <crow/app.h> #include <crow/websocket.h> + #include <boost/container/flat_map.hpp> #include <boost/container/flat_set.hpp> +#include <dbus_singleton.hpp> +#include <sdbusplus/bus/match.hpp> -namespace nlohmann { +namespace nlohmann +{ template <typename... Args> -struct adl_serializer<sdbusplus::message::variant<Args...>> { - static void to_json(json& j, const sdbusplus::message::variant<Args...>& v) { - mapbox::util::apply_visitor([&](auto&& val) { j = val; }, v); - } +struct adl_serializer<sdbusplus::message::variant<Args...>> +{ + static void to_json(json& j, const sdbusplus::message::variant<Args...>& v) + { + mapbox::util::apply_visitor([&](auto&& val) { j = val; }, v); + } }; -} // namespace nlohmann +} // namespace nlohmann -namespace crow { -namespace dbus_monitor { +namespace crow +{ +namespace dbus_monitor +{ -struct DbusWebsocketSession { - std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; - boost::container::flat_set<std::string> interfaces; +struct DbusWebsocketSession +{ + std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; + boost::container::flat_set<std::string> interfaces; }; static boost::container::flat_map<crow::websocket::Connection*, @@ -28,165 +35,198 @@ static boost::container::flat_map<crow::websocket::Connection*, sessions; inline int onPropertyUpdate(sd_bus_message* m, void* userdata, - sd_bus_error* ret_error) { - if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) { - BMCWEB_LOG_ERROR << "Got sdbus error on match"; - return 0; - } - crow::websocket::Connection* connection = - static_cast<crow::websocket::Connection*>(userdata); - auto thisSession = sessions.find(connection); - if (thisSession == sessions.end()) { - BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection; - return 0; - } - sdbusplus::message::message message(m); - using VariantType = - sdbusplus::message::variant<std::string, bool, int64_t, uint64_t, double>; - nlohmann::json j{{"event", message.get_member()}, - {"path", message.get_path()}}; - if (strcmp(message.get_member(), "PropertiesChanged") == 0) { - std::string interface_name; - boost::container::flat_map<std::string, VariantType> values; - message.read(interface_name, values); - j["properties"] = values; - j["interface"] = std::move(interface_name); - - } else if (strcmp(message.get_member(), "InterfacesAdded") == 0) { - std::string object_name; - boost::container::flat_map< - std::string, boost::container::flat_map<std::string, VariantType>> - values; - message.read(object_name, values); - for (const std::pair<std::string, - boost::container::flat_map<std::string, VariantType>>& - paths : values) { - auto it = thisSession->second.interfaces.find(paths.first); - if (it != thisSession->second.interfaces.end()) { - j["interfaces"][paths.first] = paths.second; - } + sd_bus_error* ret_error) +{ + if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) + { + BMCWEB_LOG_ERROR << "Got sdbus error on match"; + return 0; + } + crow::websocket::Connection* connection = + static_cast<crow::websocket::Connection*>(userdata); + auto thisSession = sessions.find(connection); + if (thisSession == sessions.end()) + { + BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection; + return 0; + } + sdbusplus::message::message message(m); + using VariantType = sdbusplus::message::variant<std::string, bool, int64_t, + uint64_t, double>; + nlohmann::json j{{"event", message.get_member()}, + {"path", message.get_path()}}; + if (strcmp(message.get_member(), "PropertiesChanged") == 0) + { + std::string interface_name; + boost::container::flat_map<std::string, VariantType> values; + message.read(interface_name, values); + j["properties"] = values; + j["interface"] = std::move(interface_name); + } + else if (strcmp(message.get_member(), "InterfacesAdded") == 0) + { + std::string object_name; + boost::container::flat_map< + std::string, boost::container::flat_map<std::string, VariantType>> + values; + message.read(object_name, values); + for (const std::pair< + std::string, + boost::container::flat_map<std::string, VariantType>>& paths : + values) + { + auto it = thisSession->second.interfaces.find(paths.first); + if (it != thisSession->second.interfaces.end()) + { + j["interfaces"][paths.first] = paths.second; + } + } + } + else + { + BMCWEB_LOG_CRITICAL << "message " << message.get_member() + << " was unexpected"; + return 0; } - } else { - BMCWEB_LOG_CRITICAL << "message " << message.get_member() - << " was unexpected"; - return 0; - } - connection->sendText(j.dump()); - return 0; + connection->sendText(j.dump()); + return 0; }; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/subscribe") - .websocket() - .onopen([&](crow::websocket::Connection& conn) { - BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; - sessions[&conn] = DbusWebsocketSession(); - }) - .onclose([&](crow::websocket::Connection& conn, - const std::string& reason) { sessions.erase(&conn); }) - .onmessage([&](crow::websocket::Connection& conn, const std::string& data, - bool is_binary) { - DbusWebsocketSession& thisSession = sessions[&conn]; - BMCWEB_LOG_DEBUG << "Connection " << &conn << " recevied " << data; - nlohmann::json j = nlohmann::json::parse(data, nullptr, false); - if (j.is_discarded()) { - BMCWEB_LOG_ERROR << "Unable to parse json data for monitor"; - conn.close("Unable to parse json request"); - return; - } - nlohmann::json::iterator interfaces = j.find("interfaces"); - if (interfaces != j.end()) { - thisSession.interfaces.reserve(interfaces->size()); - for (auto& interface : *interfaces) { - const std::string* str = interface.get_ptr<const std::string*>(); - if (str != nullptr) { - thisSession.interfaces.insert(*str); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/subscribe") + .websocket() + .onopen([&](crow::websocket::Connection& conn) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; + sessions[&conn] = DbusWebsocketSession(); + }) + .onclose([&](crow::websocket::Connection& conn, + const std::string& reason) { sessions.erase(&conn); }) + .onmessage([&](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + DbusWebsocketSession& thisSession = sessions[&conn]; + BMCWEB_LOG_DEBUG << "Connection " << &conn << " recevied " << data; + nlohmann::json j = nlohmann::json::parse(data, nullptr, false); + if (j.is_discarded()) + { + BMCWEB_LOG_ERROR << "Unable to parse json data for monitor"; + conn.close("Unable to parse json request"); + return; + } + nlohmann::json::iterator interfaces = j.find("interfaces"); + if (interfaces != j.end()) + { + thisSession.interfaces.reserve(interfaces->size()); + for (auto& interface : *interfaces) + { + const std::string* str = + interface.get_ptr<const std::string*>(); + if (str != nullptr) + { + thisSession.interfaces.insert(*str); + } + } } - } - } - nlohmann::json::iterator paths = j.find("paths"); - if (paths != j.end()) { - int interfaceCount = thisSession.interfaces.size(); - if (interfaceCount == 0) { - interfaceCount = 1; - } - // Reserve our matches upfront. For each path there is 1 for - // interfacesAdded, and InterfaceCount number for PropertiesChanged - thisSession.matches.reserve(thisSession.matches.size() + - paths->size() * (1 + interfaceCount)); - } - std::string object_manager_match_string; - std::string properties_match_string; - std::string object_manager_interfaces_match_string; - // These regexes derived on the rules here: - // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names - std::regex validPath("^/([A-Za-z0-9_]+/?)*$"); - std::regex validInterface( - "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"); + nlohmann::json::iterator paths = j.find("paths"); + if (paths != j.end()) + { + int interfaceCount = thisSession.interfaces.size(); + if (interfaceCount == 0) + { + interfaceCount = 1; + } + // Reserve our matches upfront. For each path there is 1 for + // interfacesAdded, and InterfaceCount number for + // PropertiesChanged + thisSession.matches.reserve(thisSession.matches.size() + + paths->size() * + (1 + interfaceCount)); + } + std::string object_manager_match_string; + std::string properties_match_string; + std::string object_manager_interfaces_match_string; + // These regexes derived on the rules here: + // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names + std::regex validPath("^/([A-Za-z0-9_]+/?)*$"); + std::regex validInterface( + "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"); - for (const auto& thisPath : *paths) { - const std::string* thisPathString = - thisPath.get_ptr<const std::string*>(); - if (thisPathString == nullptr) { - BMCWEB_LOG_ERROR << "subscribe path isn't a string?"; - conn.close(); - return; - } - if (!std::regex_match(*thisPathString, validPath)) { - BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString; - conn.close(); - return; - } - properties_match_string = - ("type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "path_namespace='" + - *thisPathString + - "'," - "member='PropertiesChanged'"); - // If interfaces weren't specified, add a single match for all - // interfaces - if (thisSession.interfaces.size() == 0) { - BMCWEB_LOG_DEBUG << "Creating match " << properties_match_string; + for (const auto& thisPath : *paths) + { + const std::string* thisPathString = + thisPath.get_ptr<const std::string*>(); + if (thisPathString == nullptr) + { + BMCWEB_LOG_ERROR << "subscribe path isn't a string?"; + conn.close(); + return; + } + if (!std::regex_match(*thisPathString, validPath)) + { + BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString; + conn.close(); + return; + } + properties_match_string = + ("type='signal'," + "interface='org.freedesktop.DBus.Properties'," + "path_namespace='" + + *thisPathString + + "'," + "member='PropertiesChanged'"); + // If interfaces weren't specified, add a single match for all + // interfaces + if (thisSession.interfaces.size() == 0) + { + BMCWEB_LOG_DEBUG << "Creating match " + << properties_match_string; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, properties_match_string, - onPropertyUpdate, &conn)); - } else { - // If interfaces were specified, add a match for each interface - for (const std::string& interface : thisSession.interfaces) { - if (!std::regex_match(interface, validInterface)) { - BMCWEB_LOG_ERROR << "Invalid interface name " << interface; - conn.close(); - return; - } - std::string ifaceMatchString = - properties_match_string + ",arg0='" + interface + "'"; - BMCWEB_LOG_DEBUG << "Creating match " << ifaceMatchString; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, ifaceMatchString, - onPropertyUpdate, &conn)); + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + properties_match_string, onPropertyUpdate, &conn)); + } + else + { + // If interfaces were specified, add a match for each + // interface + for (const std::string& interface : thisSession.interfaces) + { + if (!std::regex_match(interface, validInterface)) + { + BMCWEB_LOG_ERROR << "Invalid interface name " + << interface; + conn.close(); + return; + } + std::string ifaceMatchString = properties_match_string + + ",arg0='" + interface + + "'"; + BMCWEB_LOG_DEBUG << "Creating match " + << ifaceMatchString; + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, ifaceMatchString, + onPropertyUpdate, &conn)); + } + } + object_manager_match_string = + ("type='signal'," + "interface='org.freedesktop.DBus.ObjectManager'," + "path_namespace='" + + *thisPathString + + "'," + "member='InterfacesAdded'"); + BMCWEB_LOG_DEBUG << "Creating match " + << object_manager_match_string; + thisSession.matches.emplace_back( + std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + object_manager_match_string, onPropertyUpdate, &conn)); } - } - object_manager_match_string = - ("type='signal'," - "interface='org.freedesktop.DBus.ObjectManager'," - "path_namespace='" + - *thisPathString + - "'," - "member='InterfacesAdded'"); - BMCWEB_LOG_DEBUG << "Creating match " << object_manager_match_string; - thisSession.matches.emplace_back( - std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, object_manager_match_string, - onPropertyUpdate, &conn)); - } - }); + }); } -} // namespace dbus_monitor -} // namespace crow +} // namespace dbus_monitor +} // namespace crow diff --git a/include/dbus_singleton.hpp b/include/dbus_singleton.hpp index a4a16bb..2438152 100644 --- a/include/dbus_singleton.hpp +++ b/include/dbus_singleton.hpp @@ -1,21 +1,28 @@ #pragma once -#include <sdbusplus/asio/connection.hpp> #include <iostream> +#include <sdbusplus/asio/connection.hpp> -namespace mapbox { +namespace mapbox +{ template <typename T, typename... Types> -const T* getPtr(const mapbox::util::variant<Types...>& v) { - if (v.template is<std::remove_const_t<T>>()) { - return &v.template get_unchecked<std::remove_const_t<T>>(); - } else { - return nullptr; - } +const T* getPtr(const mapbox::util::variant<Types...>& v) +{ + if (v.template is<std::remove_const_t<T>>()) + { + return &v.template get_unchecked<std::remove_const_t<T>>(); + } + else + { + return nullptr; + } } -} // namespace mapbox +} // namespace mapbox -namespace crow { -namespace connections { +namespace crow +{ +namespace connections +{ static std::shared_ptr<sdbusplus::asio::connection> systemBus; -} // namespace connections -} // namespace crow +} // namespace connections +} // namespace crow diff --git a/include/gzip_helper.hpp b/include/gzip_helper.hpp index e14fc1b..be13809 100644 --- a/include/gzip_helper.hpp +++ b/include/gzip_helper.hpp @@ -1,56 +1,65 @@ #pragma once #include <zlib.h> + #include <cstring> #include <string> inline bool gzipInflate(const std::string& compressedBytes, - std::string& uncompressedBytes) { - if (compressedBytes.empty()) { - uncompressedBytes = compressedBytes; - return true; - } - - uncompressedBytes.clear(); + std::string& uncompressedBytes) +{ + if (compressedBytes.empty()) + { + uncompressedBytes = compressedBytes; + return true; + } - unsigned half_length = compressedBytes.size() / 2; + uncompressedBytes.clear(); - z_stream strm{}; + unsigned half_length = compressedBytes.size() / 2; - // The following line is nolint because we're declaring away constness. - // It's not clear why the input buffers on zlib aren't const, so this is a - // bit of a cheat for the moment - strm.next_in = (Bytef*)compressedBytes.data(); // NOLINT - strm.avail_in = compressedBytes.size(); - strm.total_out = 0; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; + z_stream strm{}; - bool done = false; + // The following line is nolint because we're declaring away constness. + // It's not clear why the input buffers on zlib aren't const, so this is a + // bit of a cheat for the moment + strm.next_in = (Bytef*)compressedBytes.data(); // NOLINT + strm.avail_in = compressedBytes.size(); + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; - if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { - return false; - } + bool done = false; - while (!done) { - // If our output buffer is too small - if (strm.total_out >= uncompressedBytes.size()) { - uncompressedBytes.resize(uncompressedBytes.size() + half_length); + if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) + { + return false; } - strm.next_out = - (Bytef*)(uncompressedBytes.data() + strm.total_out); // NOLINT - strm.avail_out = - ((uLong)uncompressedBytes.size() - strm.total_out); // NOLINT - - // Inflate another chunk. - int err = inflate(&strm, Z_SYNC_FLUSH); - if (err == Z_STREAM_END) { - done = true; - } else if (err != Z_OK) { - break; + while (!done) + { + // If our output buffer is too small + if (strm.total_out >= uncompressedBytes.size()) + { + uncompressedBytes.resize(uncompressedBytes.size() + half_length); + } + + strm.next_out = + (Bytef*)(uncompressedBytes.data() + strm.total_out); // NOLINT + strm.avail_out = + ((uLong)uncompressedBytes.size() - strm.total_out); // NOLINT + + // Inflate another chunk. + int err = inflate(&strm, Z_SYNC_FLUSH); + if (err == Z_STREAM_END) + { + done = true; + } + else if (err != Z_OK) + { + break; + } } - } - return inflateEnd(&strm) == Z_OK; + return inflateEnd(&strm) == Z_OK; }
\ No newline at end of file diff --git a/include/http_utility.hpp b/include/http_utility.hpp index f2d3172..e13dfc0 100644 --- a/include/http_utility.hpp +++ b/include/http_utility.hpp @@ -1,21 +1,27 @@ #pragma once #include <boost/algorithm/string.hpp> -namespace http_helpers { -inline bool requestPrefersHtml(const crow::Request& req) { - boost::string_view header = req.getHeaderValue("accept"); - std::vector<std::string> encodings; - // chrome currently sends 6 accepts headers, firefox sends 4. - encodings.reserve(6); - boost::split(encodings, header, boost::is_any_of(", "), - boost::token_compress_on); - for (const std::string& encoding : encodings) { - if (encoding == "text/html") { - return true; - } else if (encoding == "application/json") { - return false; +namespace http_helpers +{ +inline bool requestPrefersHtml(const crow::Request& req) +{ + boost::string_view header = req.getHeaderValue("accept"); + std::vector<std::string> encodings; + // chrome currently sends 6 accepts headers, firefox sends 4. + encodings.reserve(6); + boost::split(encodings, header, boost::is_any_of(", "), + boost::token_compress_on); + for (const std::string& encoding : encodings) + { + if (encoding == "text/html") + { + return true; + } + else if (encoding == "application/json") + { + return false; + } } - } - return false; + return false; } -} // namespace http_helpers
\ No newline at end of file +} // namespace http_helpers
\ No newline at end of file diff --git a/include/image_upload.hpp b/include/image_upload.hpp index df5c1ae..2b84db8 100644 --- a/include/image_upload.hpp +++ b/include/image_upload.hpp @@ -1,103 +1,113 @@ #pragma once -#include <dbus_singleton.hpp> -#include <cstdio> -#include <fstream> -#include <memory> #include <crow/app.h> + #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <cstdio> +#include <dbus_singleton.hpp> +#include <fstream> +#include <memory> -namespace crow { -namespace image_upload { +namespace crow +{ +namespace image_upload +{ std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; inline void uploadImageHandler(const crow::Request& req, crow::Response& res, - const std::string& filename) { - // Only allow one FW update at a time - if (fwUpdateMatcher != nullptr) { - res.addHeader("Retry-After", "30"); - res.result(boost::beast::http::status::service_unavailable); - res.end(); - return; - } - // Make this const static so it survives outside this method - static boost::asio::deadline_timer timeout(*req.ioService, - boost::posix_time::seconds(5)); - - timeout.expires_from_now(boost::posix_time::seconds(5)); - - timeout.async_wait([&res](const boost::system::error_code& ec) { - fwUpdateMatcher = nullptr; - if (ec == asio::error::operation_aborted) { - // expected, we were canceled before the timer completed. - return; - } - BMCWEB_LOG_ERROR << "Timed out waiting for log event"; - - if (ec) { - BMCWEB_LOG_ERROR << "Async_wait failed " << ec; - return; + const std::string& filename) +{ + // Only allow one FW update at a time + if (fwUpdateMatcher != nullptr) + { + res.addHeader("Retry-After", "30"); + res.result(boost::beast::http::status::service_unavailable); + res.end(); + return; } + // Make this const static so it survives outside this method + static boost::asio::deadline_timer timeout(*req.ioService, + boost::posix_time::seconds(5)); - res.result(boost::beast::http::status::internal_server_error); - res.end(); - }); + timeout.expires_from_now(boost::posix_time::seconds(5)); - std::function<void(sdbusplus::message::message&)> callback = - [&res](sdbusplus::message::message& m) { - BMCWEB_LOG_DEBUG << "Match fired"; - boost::system::error_code ec; - timeout.cancel(ec); - if (ec) { - BMCWEB_LOG_ERROR << "error canceling timer " << ec; + timeout.async_wait([&res](const boost::system::error_code& ec) { + fwUpdateMatcher = nullptr; + if (ec == asio::error::operation_aborted) + { + // expected, we were canceled before the timer completed. + return; } - std::string versionInfo; - m.read(versionInfo); // Read in the object path that was just created + BMCWEB_LOG_ERROR << "Timed out waiting for log event"; - std::size_t index = versionInfo.rfind('/'); - if (index != std::string::npos) { - versionInfo.erase(0, index); + if (ec) + { + BMCWEB_LOG_ERROR << "Async_wait failed " << ec; + return; } - res.jsonValue = {{"data", std::move(versionInfo)}, - {"message", "200 OK"}, - {"status", "ok"}}; - BMCWEB_LOG_DEBUG << "ending response"; + + res.result(boost::beast::http::status::internal_server_error); res.end(); - fwUpdateMatcher = nullptr; - }; - fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( - *crow::connections::systemBus, - "interface='org.freedesktop.DBus.ObjectManager',type='signal'," - "member='InterfacesAdded',path='/xyz/openbmc_project/logging'", - callback); + }); + + std::function<void(sdbusplus::message::message&)> callback = + [&res](sdbusplus::message::message& m) { + BMCWEB_LOG_DEBUG << "Match fired"; + boost::system::error_code ec; + timeout.cancel(ec); + if (ec) + { + BMCWEB_LOG_ERROR << "error canceling timer " << ec; + } + std::string versionInfo; + m.read( + versionInfo); // Read in the object path that was just created + + std::size_t index = versionInfo.rfind('/'); + if (index != std::string::npos) + { + versionInfo.erase(0, index); + } + res.jsonValue = {{"data", std::move(versionInfo)}, + {"message", "200 OK"}, + {"status", "ok"}}; + BMCWEB_LOG_DEBUG << "ending response"; + res.end(); + fwUpdateMatcher = nullptr; + }; + fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( + *crow::connections::systemBus, + "interface='org.freedesktop.DBus.ObjectManager',type='signal'," + "member='InterfacesAdded',path='/xyz/openbmc_project/logging'", + callback); - std::string filepath( - "/tmp/images/" + - boost::uuids::to_string(boost::uuids::random_generator()())); - BMCWEB_LOG_DEBUG << "Writing file to " << filepath; - std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | - std::ofstream::trunc); - out << req.body; - out.close(); + std::string filepath( + "/tmp/images/" + + boost::uuids::to_string(boost::uuids::random_generator()())); + BMCWEB_LOG_DEBUG << "Writing file to " << filepath; + std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | + std::ofstream::trunc); + out << req.body; + out.close(); } -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/upload/image/<str>") - .methods("POST"_method, - "PUT"_method)([](const crow::Request& req, crow::Response& res, - const std::string& filename) { - uploadImageHandler(req, res, filename); - }); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/upload/image/<str>") + .methods("POST"_method, + "PUT"_method)([](const crow::Request& req, crow::Response& res, + const std::string& filename) { + uploadImageHandler(req, res, filename); + }); - BMCWEB_ROUTE(app, "/upload/image") - .methods("POST"_method, - "PUT"_method)([](const crow::Request& req, crow::Response& res) { - uploadImageHandler(req, res, ""); - }); + BMCWEB_ROUTE(app, "/upload/image") + .methods("POST"_method, "PUT"_method)( + [](const crow::Request& req, crow::Response& res) { + uploadImageHandler(req, res, ""); + }); } -} // namespace image_upload -} // namespace crow +} // namespace image_upload +} // namespace crow diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp index 3e6443d..4f6c233 100644 --- a/include/openbmc_dbus_rest.hpp +++ b/include/openbmc_dbus_rest.hpp @@ -1,64 +1,78 @@ #include <crow/app.h> - #include <tinyxml2.h> + +#include <boost/algorithm/string.hpp> +#include <boost/container/flat_set.hpp> #include <dbus_singleton.hpp> #include <experimental/filesystem> #include <fstream> -#include <boost/algorithm/string.hpp> -#include <boost/container/flat_set.hpp> -namespace crow { -namespace openbmc_mapper { +namespace crow +{ +namespace openbmc_mapper +{ void introspectObjects(crow::Response &res, std::string process_name, std::string path, - std::shared_ptr<nlohmann::json> transaction) { - crow::connections::systemBus->async_method_call( - [ - &res, transaction, processName{std::move(process_name)}, - objectPath{std::move(path)} - ](const boost::system::error_code ec, const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR << "Introspect call failed with error: " - << ec.message() << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - transaction->push_back({{"path", objectPath}}); - - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " << processName - << " " << objectPath << "\n"; - - } else { - tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); - while (node != nullptr) { - std::string childPath = node->Attribute("name"); - std::string newpath; - if (objectPath != "/") { - newpath += objectPath; - } - newpath += "/" + childPath; - // introspect the subobjects as well - introspectObjects(res, processName, newpath, transaction); - - node = node->NextSiblingElement("node"); + std::shared_ptr<nlohmann::json> transaction) +{ + crow::connections::systemBus->async_method_call( + [&res, transaction, processName{std::move(process_name)}, + objectPath{std::move(path)}](const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " << ec.message() + << " on process: " << processName << " path: " << objectPath + << "\n"; } - } - } - // if we're the last outstanding caller, finish the request - if (transaction.use_count() == 1) { - res.jsonValue = {{"status", "ok"}, - {"bus_name", processName}, - {"objects", std::move(*transaction)}}; - res.end(); - } - }, - process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect"); + else + { + transaction->push_back({{"path", objectPath}}); + + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse " + << processName << " " << objectPath + << "\n"; + } + else + { + tinyxml2::XMLElement *node = + pRoot->FirstChildElement("node"); + while (node != nullptr) + { + std::string childPath = node->Attribute("name"); + std::string newpath; + if (objectPath != "/") + { + newpath += objectPath; + } + newpath += "/" + childPath; + // introspect the subobjects as well + introspectObjects(res, processName, newpath, + transaction); + + node = node->NextSiblingElement("node"); + } + } + } + // if we're the last outstanding caller, finish the request + if (transaction.use_count() == 1) + { + res.jsonValue = {{"status", "ok"}, + {"bus_name", processName}, + {"objects", std::move(*transaction)}}; + res.end(); + } + }, + process_name, path, "org.freedesktop.DBus.Introspectable", + "Introspect"); } // A smattering of common types to unpack. TODO(ed) this should really iterate @@ -74,46 +88,60 @@ using ManagedObjectType = std::vector<std::pair< std::string, boost::container::flat_map<std::string, DbusRestVariantType>>>>; -void getManagedObjectsForEnumerate( - const std::string &object_name, const std::string &connection_name, - crow::Response &res, std::shared_ptr<nlohmann::json> transaction) { - crow::connections::systemBus->async_method_call( - [&res, transaction](const boost::system::error_code ec, - const ManagedObjectType &objects) { - if (ec) { - BMCWEB_LOG_ERROR << ec; - } else { - nlohmann::json &dataJson = *transaction; - - for (auto &objectPath : objects) { - BMCWEB_LOG_DEBUG - << "Reading object " - << static_cast<const std::string &>(objectPath.first); - nlohmann::json &objectJson = - dataJson[static_cast<const std::string &>(objectPath.first)]; - if (objectJson.is_null()) { - objectJson = nlohmann::json::object(); +void getManagedObjectsForEnumerate(const std::string &object_name, + const std::string &connection_name, + crow::Response &res, + std::shared_ptr<nlohmann::json> transaction) +{ + crow::connections::systemBus->async_method_call( + [&res, transaction](const boost::system::error_code ec, + const ManagedObjectType &objects) { + if (ec) + { + BMCWEB_LOG_ERROR << ec; } - for (const auto &interface : objectPath.second) { - for (const auto &property : interface.second) { - nlohmann::json &propertyJson = objectJson[property.first]; - mapbox::util::apply_visitor( - [&propertyJson](auto &&val) { propertyJson = val; }, - property.second); - } + else + { + nlohmann::json &dataJson = *transaction; + + for (auto &objectPath : objects) + { + BMCWEB_LOG_DEBUG + << "Reading object " + << static_cast<const std::string &>(objectPath.first); + nlohmann::json &objectJson = + dataJson[static_cast<const std::string &>( + objectPath.first)]; + if (objectJson.is_null()) + { + objectJson = nlohmann::json::object(); + } + for (const auto &interface : objectPath.second) + { + for (const auto &property : interface.second) + { + nlohmann::json &propertyJson = + objectJson[property.first]; + mapbox::util::apply_visitor( + [&propertyJson](auto &&val) { + propertyJson = val; + }, + property.second); + } + } + } } - } - } - if (transaction.use_count() == 1) { - res.jsonValue = {{"message", "200 OK"}, - {"status", "ok"}, - {"data", std::move(*transaction)}}; - res.end(); - } - }, - connection_name, object_name, "org.freedesktop.DBus.ObjectManager", - "GetManagedObjects"); + if (transaction.use_count() == 1) + { + res.jsonValue = {{"message", "200 OK"}, + {"status", "ok"}, + {"data", std::move(*transaction)}}; + res.end(); + } + }, + connection_name, object_name, "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects"); } using GetSubTreeType = std::vector< @@ -121,1025 +149,1320 @@ using GetSubTreeType = std::vector< std::vector<std::pair<std::string, std::vector<std::string>>>>>; // Structure for storing data on an in progress action -struct InProgressActionData { - InProgressActionData(crow::Response &res) : res(res){}; - ~InProgressActionData() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +struct InProgressActionData +{ + InProgressActionData(crow::Response &res) : res(res){}; + ~InProgressActionData() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } + res.end(); + } + + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); } - res.end(); - } - - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } - crow::Response &res; - std::string path; - std::string methodName; - nlohmann::json arguments; + crow::Response &res; + std::string path; + std::string methodName; + nlohmann::json arguments; }; -std::vector<std::string> dbusArgSplit(const std::string &string) { - std::vector<std::string> ret; - if (string.empty()) { - return ret; - } - ret.push_back(""); - int containerDepth = 0; - - for (std::string::const_iterator character = string.begin(); - character != string.end(); character++) { - ret.back() += *character; - switch (*character) { - case ('a'): - break; - case ('('): - case ('{'): - containerDepth++; - break; - case ('}'): - case (')'): - containerDepth--; - if (containerDepth == 0) { - if (character + 1 != string.end()) { - ret.push_back(""); - } - } - break; - default: - if (containerDepth == 0) { - if (character + 1 != string.end()) { - ret.push_back(""); - } +std::vector<std::string> dbusArgSplit(const std::string &string) +{ + std::vector<std::string> ret; + if (string.empty()) + { + return ret; + } + ret.push_back(""); + int containerDepth = 0; + + for (std::string::const_iterator character = string.begin(); + character != string.end(); character++) + { + ret.back() += *character; + switch (*character) + { + case ('a'): + break; + case ('('): + case ('{'): + containerDepth++; + break; + case ('}'): + case (')'): + containerDepth--; + if (containerDepth == 0) + { + if (character + 1 != string.end()) + { + ret.push_back(""); + } + } + break; + default: + if (containerDepth == 0) + { + if (character + 1 != string.end()) + { + ret.push_back(""); + } + } + break; } - break; } - } } int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, - const nlohmann::json &input_json) { - int r = 0; - BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() - << " to type: " << arg_type; - const std::vector<std::string> argTypes = dbusArgSplit(arg_type); - - // Assume a single object for now. - const nlohmann::json *j = &input_json; - nlohmann::json::const_iterator jIt = input_json.begin(); - - for (const std::string &arg_code : argTypes) { - // If we are decoding multiple objects, grab the pointer to the iterator, - // and increment it for the next loop - if (argTypes.size() > 1) { - if (jIt == input_json.end()) { - return -2; - } - j = &*jIt; - jIt++; - } - const int64_t *int_value = j->get_ptr<const int64_t *>(); - const uint64_t *uint_value = j->get_ptr<const uint64_t *>(); - const std::string *string_value = j->get_ptr<const std::string *>(); - const double *double_value = j->get_ptr<const double *>(); - const bool *b = j->get_ptr<const bool *>(); - int64_t v = 0; - double d = 0.0; - - // Do some basic type conversions that make sense. uint can be converted to - // int. int and uint can be converted to double - if (uint_value != nullptr && int_value == nullptr) { - v = static_cast<int64_t>(*uint_value); - int_value = &v; - } - if (uint_value != nullptr && double_value == nullptr) { - d = static_cast<double>(*uint_value); - double_value = &d; - } - if (int_value != nullptr && double_value == nullptr) { - d = static_cast<double>(*int_value); - double_value = &d; - } + const nlohmann::json &input_json) +{ + int r = 0; + BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() + << " to type: " << arg_type; + const std::vector<std::string> argTypes = dbusArgSplit(arg_type); + + // Assume a single object for now. + const nlohmann::json *j = &input_json; + nlohmann::json::const_iterator jIt = input_json.begin(); + + for (const std::string &arg_code : argTypes) + { + // If we are decoding multiple objects, grab the pointer to the + // iterator, and increment it for the next loop + if (argTypes.size() > 1) + { + if (jIt == input_json.end()) + { + return -2; + } + j = &*jIt; + jIt++; + } + const int64_t *int_value = j->get_ptr<const int64_t *>(); + const uint64_t *uint_value = j->get_ptr<const uint64_t *>(); + const std::string *string_value = j->get_ptr<const std::string *>(); + const double *double_value = j->get_ptr<const double *>(); + const bool *b = j->get_ptr<const bool *>(); + int64_t v = 0; + double d = 0.0; + + // Do some basic type conversions that make sense. uint can be + // converted to int. int and uint can be converted to double + if (uint_value != nullptr && int_value == nullptr) + { + v = static_cast<int64_t>(*uint_value); + int_value = &v; + } + if (uint_value != nullptr && double_value == nullptr) + { + d = static_cast<double>(*uint_value); + double_value = &d; + } + if (int_value != nullptr && double_value == nullptr) + { + d = static_cast<double>(*int_value); + double_value = &d; + } - if (arg_code == "s") { - if (string_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], - (void *)string_value->c_str()); - if (r < 0) { - return r; - } - } else if (arg_code == "i") { - if (int_value == nullptr) { - return -1; - } - int32_t i = static_cast<int32_t>(*int_value); - r = sd_bus_message_append_basic(m, arg_code[0], &i); - if (r < 0) { - return r; - } - } else if (arg_code == "b") { - // lots of ways bool could be represented here. Try them all - int bool_int = false; - if (int_value != nullptr) { - bool_int = *int_value > 0 ? 1 : 0; - } else if (b != nullptr) { - bool_int = b ? 1 : 0; - } else if (string_value != nullptr) { - bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0; - } else { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], &bool_int); - if (r < 0) { - return r; - } - } else if (arg_code == "n") { - if (int_value == nullptr) { - return -1; - } - int16_t n = static_cast<int16_t>(*int_value); - r = sd_bus_message_append_basic(m, arg_code[0], &n); - if (r < 0) { - return r; - } - } else if (arg_code == "x") { - if (int_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], int_value); - if (r < 0) { - return r; - } - } else if (arg_code == "y") { - if (uint_value == nullptr) { - return -1; - } - uint8_t y = static_cast<uint8_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &y); - } else if (arg_code == "q") { - if (uint_value == nullptr) { - return -1; - } - uint16_t q = static_cast<uint16_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &q); - } else if (arg_code == "u") { - if (uint_value == nullptr) { - return -1; - } - uint32_t u = static_cast<uint32_t>(*uint_value); - r = sd_bus_message_append_basic(m, arg_code[0], &u); - } else if (arg_code == "t") { - if (uint_value == nullptr) { - return -1; - } - r = sd_bus_message_append_basic(m, arg_code[0], uint_value); - } else if (arg_code == "d") { - sd_bus_message_append_basic(m, arg_code[0], double_value); - } else if (boost::starts_with(arg_code, "a")) { - std::string contained_type = arg_code.substr(1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, - contained_type.c_str()); - if (r < 0) { - return r; - } - - for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); - ++it) { - r = convertJsonToDbus(m, contained_type, *it); - if (r < 0) { - return r; + if (arg_code == "s") + { + if (string_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], + (void *)string_value->c_str()); + if (r < 0) + { + return r; + } } + else if (arg_code == "i") + { + if (int_value == nullptr) + { + return -1; + } + int32_t i = static_cast<int32_t>(*int_value); + r = sd_bus_message_append_basic(m, arg_code[0], &i); + if (r < 0) + { + return r; + } + } + else if (arg_code == "b") + { + // lots of ways bool could be represented here. Try them all + int bool_int = false; + if (int_value != nullptr) + { + bool_int = *int_value > 0 ? 1 : 0; + } + else if (b != nullptr) + { + bool_int = b ? 1 : 0; + } + else if (string_value != nullptr) + { + bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0; + } + else + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], &bool_int); + if (r < 0) + { + return r; + } + } + else if (arg_code == "n") + { + if (int_value == nullptr) + { + return -1; + } + int16_t n = static_cast<int16_t>(*int_value); + r = sd_bus_message_append_basic(m, arg_code[0], &n); + if (r < 0) + { + return r; + } + } + else if (arg_code == "x") + { + if (int_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], int_value); + if (r < 0) + { + return r; + } + } + else if (arg_code == "y") + { + if (uint_value == nullptr) + { + return -1; + } + uint8_t y = static_cast<uint8_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &y); + } + else if (arg_code == "q") + { + if (uint_value == nullptr) + { + return -1; + } + uint16_t q = static_cast<uint16_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &q); + } + else if (arg_code == "u") + { + if (uint_value == nullptr) + { + return -1; + } + uint32_t u = static_cast<uint32_t>(*uint_value); + r = sd_bus_message_append_basic(m, arg_code[0], &u); + } + else if (arg_code == "t") + { + if (uint_value == nullptr) + { + return -1; + } + r = sd_bus_message_append_basic(m, arg_code[0], uint_value); + } + else if (arg_code == "d") + { + sd_bus_message_append_basic(m, arg_code[0], double_value); + } + else if (boost::starts_with(arg_code, "a")) + { + std::string contained_type = arg_code.substr(1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, + contained_type.c_str()); + if (r < 0) + { + return r; + } + + for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); + ++it) + { + r = convertJsonToDbus(m, contained_type, *it); + if (r < 0) + { + return r; + } - it++; - } - sd_bus_message_close_container(m); - } else if (boost::starts_with(arg_code, "v")) { - std::string contained_type = arg_code.substr(1); - BMCWEB_LOG_DEBUG << "variant type: " << arg_code - << " appending variant of type: " << contained_type; - r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, - contained_type.c_str()); - if (r < 0) { - return r; - } - - r = convertJsonToDbus(m, contained_type, input_json); - if (r < 0) { - return r; - } - - r = sd_bus_message_close_container(m); - if (r < 0) { - return r; - } - } else if (boost::starts_with(arg_code, "(") && - boost::ends_with(arg_code, ")")) { - std::string contained_type = arg_code.substr(1, arg_code.size() - 1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, - contained_type.c_str()); - nlohmann::json::const_iterator it = j->begin(); - for (const std::string &arg_code : dbusArgSplit(arg_type)) { - if (it == j->end()) { - return -1; + it++; + } + sd_bus_message_close_container(m); } - r = convertJsonToDbus(m, arg_code, *it); - if (r < 0) { - return r; + else if (boost::starts_with(arg_code, "v")) + { + std::string contained_type = arg_code.substr(1); + BMCWEB_LOG_DEBUG + << "variant type: " << arg_code + << " appending variant of type: " << contained_type; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, + contained_type.c_str()); + if (r < 0) + { + return r; + } + + r = convertJsonToDbus(m, contained_type, input_json); + if (r < 0) + { + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + { + return r; + } } - it++; - } - r = sd_bus_message_close_container(m); - } else if (boost::starts_with(arg_code, "{") && - boost::ends_with(arg_code, "}")) { - std::string contained_type = arg_code.substr(1, arg_code.size() - 1); - r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, - contained_type.c_str()); - std::vector<std::string> codes = dbusArgSplit(contained_type); - if (codes.size() != 2) { - return -1; - } - const std::string &key_type = codes[0]; - const std::string &value_type = codes[1]; - for (auto it : j->items()) { - r = convertJsonToDbus(m, key_type, it.key()); - if (r < 0) { - return r; + else if (boost::starts_with(arg_code, "(") && + boost::ends_with(arg_code, ")")) + { + std::string contained_type = + arg_code.substr(1, arg_code.size() - 1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, + contained_type.c_str()); + nlohmann::json::const_iterator it = j->begin(); + for (const std::string &arg_code : dbusArgSplit(arg_type)) + { + if (it == j->end()) + { + return -1; + } + r = convertJsonToDbus(m, arg_code, *it); + if (r < 0) + { + return r; + } + it++; + } + r = sd_bus_message_close_container(m); } + else if (boost::starts_with(arg_code, "{") && + boost::ends_with(arg_code, "}")) + { + std::string contained_type = + arg_code.substr(1, arg_code.size() - 1); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, + contained_type.c_str()); + std::vector<std::string> codes = dbusArgSplit(contained_type); + if (codes.size() != 2) + { + return -1; + } + const std::string &key_type = codes[0]; + const std::string &value_type = codes[1]; + for (auto it : j->items()) + { + r = convertJsonToDbus(m, key_type, it.key()); + if (r < 0) + { + return r; + } - r = convertJsonToDbus(m, value_type, it.value()); - if (r < 0) { - return r; + r = convertJsonToDbus(m, value_type, it.value()); + if (r < 0) + { + return r; + } + } + r = sd_bus_message_close_container(m); + } + else + { + return -2; + } + if (r < 0) + { + return r; } - } - r = sd_bus_message_close_container(m); - } else { - return -2; - } - if (r < 0) { - return r; - } - if (argTypes.size() > 1) { - jIt++; + if (argTypes.size() > 1) + { + jIt++; + } } - } } void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, - const std::string &connectionName) { - BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " << connectionName; - crow::connections::systemBus->async_method_call( - [ - transaction, connectionName{std::string(connectionName)} - ](const boost::system::error_code ec, const std::string &introspect_xml) { - BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; - if (ec) { - BMCWEB_LOG_ERROR << "Introspect call failed with error: " - << ec.message() << " on process: " << connectionName - << "\n"; - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << connectionName << "\n"; - - } else { - tinyxml2::XMLElement *interface_node = - pRoot->FirstChildElement("interface"); - while (interface_node != nullptr) { - std::string this_interface_name = - interface_node->Attribute("name"); - tinyxml2::XMLElement *method_node = - interface_node->FirstChildElement("method"); - while (method_node != nullptr) { - std::string this_methodName = method_node->Attribute("name"); - BMCWEB_LOG_DEBUG << "Found method: " << this_methodName; - if (this_methodName == transaction->methodName) { - sdbusplus::message::message m = - crow::connections::systemBus->new_method_call( - connectionName.c_str(), transaction->path.c_str(), - this_interface_name.c_str(), - transaction->methodName.c_str()); - - tinyxml2::XMLElement *argument_node = - method_node->FirstChildElement("arg"); - - nlohmann::json::const_iterator arg_it = - transaction->arguments.begin(); - - while (argument_node != nullptr) { - std::string arg_direction = - argument_node->Attribute("direction"); - if (arg_direction == "in") { - std::string arg_type = argument_node->Attribute("type"); - if (arg_it == transaction->arguments.end()) { - transaction->setErrorStatus(); - return; - } - if (convertJsonToDbus(m.get(), arg_type, *arg_it) < 0) { - transaction->setErrorStatus(); - return; - } - - arg_it++; - } - argument_node = method_node->NextSiblingElement("arg"); - } - crow::connections::systemBus->async_send( - m, [transaction](boost::system::error_code ec, - sdbusplus::message::message &m) { - if (ec) { - transaction->setErrorStatus(); - return; + const std::string &connectionName) +{ + BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " + << connectionName; + crow::connections::systemBus->async_method_call( + [transaction, connectionName{std::string(connectionName)}]( + const boost::system::error_code ec, + const std::string &introspect_xml) { + BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " << ec.message() + << " on process: " << connectionName << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse " + << connectionName << "\n"; + } + else + { + tinyxml2::XMLElement *interface_node = + pRoot->FirstChildElement("interface"); + while (interface_node != nullptr) + { + std::string this_interface_name = + interface_node->Attribute("name"); + tinyxml2::XMLElement *method_node = + interface_node->FirstChildElement("method"); + while (method_node != nullptr) + { + std::string this_methodName = + method_node->Attribute("name"); + BMCWEB_LOG_DEBUG << "Found method: " + << this_methodName; + if (this_methodName == transaction->methodName) + { + sdbusplus::message::message m = + crow::connections::systemBus + ->new_method_call( + connectionName.c_str(), + transaction->path.c_str(), + this_interface_name.c_str(), + transaction->methodName.c_str()); + + tinyxml2::XMLElement *argument_node = + method_node->FirstChildElement("arg"); + + nlohmann::json::const_iterator arg_it = + transaction->arguments.begin(); + + while (argument_node != nullptr) + { + std::string arg_direction = + argument_node->Attribute("direction"); + if (arg_direction == "in") + { + std::string arg_type = + argument_node->Attribute("type"); + if (arg_it == + transaction->arguments.end()) + { + transaction->setErrorStatus(); + return; + } + if (convertJsonToDbus(m.get(), arg_type, + *arg_it) < 0) + { + transaction->setErrorStatus(); + return; + } + + arg_it++; + } + argument_node = + method_node->NextSiblingElement("arg"); + } + crow::connections::systemBus->async_send( + m, [transaction]( + boost::system::error_code ec, + sdbusplus::message::message &m) { + if (ec) + { + transaction->setErrorStatus(); + return; + } + transaction->res.jsonValue = { + {"status", "ok"}, + {"message", "200 OK"}, + {"data", nullptr}}; + }); + break; + } + method_node = + method_node->NextSiblingElement("method"); } - transaction->res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", nullptr}}; - }); - break; + interface_node = + interface_node->NextSiblingElement("interface"); + } } - method_node = method_node->NextSiblingElement("method"); - } - interface_node = interface_node->NextSiblingElement("interface"); } - } - } - }, - connectionName, transaction->path, "org.freedesktop.DBus.Introspectable", - "Introspect"); + }, + connectionName, transaction->path, + "org.freedesktop.DBus.Introspectable", "Introspect"); } void handle_action(const crow::Request &req, crow::Response &res, - const std::string &objectPath, - const std::string &methodName) { - nlohmann::json requestDbusData = - nlohmann::json::parse(req.body, nullptr, false); - - if (requestDbusData.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - if (!requestDbusData.is_array()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - auto transaction = std::make_shared<InProgressActionData>(res); - - transaction->path = objectPath; - transaction->methodName = methodName; - transaction->arguments = std::move(requestDbusData); - crow::connections::systemBus->async_method_call( - [transaction]( - const boost::system::error_code ec, - const std::vector<std::pair<std::string, std::vector<std::string>>> - &interface_names) { - if (ec || interface_names.size() <= 0) { - transaction->setErrorStatus(); - return; - } + const std::string &objectPath, const std::string &methodName) +{ + nlohmann::json requestDbusData = + nlohmann::json::parse(req.body, nullptr, false); + + if (requestDbusData.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + if (!requestDbusData.is_array()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + auto transaction = std::make_shared<InProgressActionData>(res); + + transaction->path = objectPath; + transaction->methodName = methodName; + transaction->arguments = std::move(requestDbusData); + crow::connections::systemBus->async_method_call( + [transaction]( + const boost::system::error_code ec, + const std::vector<std::pair<std::string, std::vector<std::string>>> + &interface_names) { + if (ec || interface_names.size() <= 0) + { + transaction->setErrorStatus(); + return; + } - BMCWEB_LOG_DEBUG << "GetObject returned objects " - << interface_names.size(); + BMCWEB_LOG_DEBUG << "GetObject returned objects " + << interface_names.size(); - for (const std::pair<std::string, std::vector<std::string>> &object : - interface_names) { - findActionOnInterface(transaction, object.first); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, - std::array<std::string, 0>()); + for (const std::pair<std::string, std::vector<std::string>> + &object : interface_names) + { + findActionOnInterface(transaction, object.first); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, + std::array<std::string, 0>()); } -void handle_list(crow::Response &res, const std::string &objectPath) { - crow::connections::systemBus->async_method_call( - [&res](const boost::system::error_code ec, - std::vector<std::string> &objectPaths) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", std::move(objectPaths)}}; - } - res.end(); - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, - static_cast<int32_t>(99), std::array<std::string, 0>()); +void handle_list(crow::Response &res, const std::string &objectPath) +{ + crow::connections::systemBus->async_method_call( + [&res](const boost::system::error_code ec, + std::vector<std::string> &objectPaths) { + if (ec) + { + res.result(boost::beast::http::status::internal_server_error); + } + else + { + res.jsonValue = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", std::move(objectPaths)}}; + } + res.end(); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, + static_cast<int32_t>(99), std::array<std::string, 0>()); } -void handle_enumerate(crow::Response &res, const std::string &objectPath) { - crow::connections::systemBus->async_method_call( - [&res, objectPath{std::string(objectPath)} ]( - const boost::system::error_code ec, - const GetSubTreeType &object_names) { - if (ec) { - res.jsonValue = {{"message", "200 OK"}, - {"status", "ok"}, - {"data", nlohmann::json::object()}}; - - res.end(); - return; - } +void handle_enumerate(crow::Response &res, const std::string &objectPath) +{ + crow::connections::systemBus->async_method_call( + [&res, objectPath{std::string(objectPath)}]( + const boost::system::error_code ec, + const GetSubTreeType &object_names) { + if (ec) + { + res.jsonValue = {{"message", "200 OK"}, + {"status", "ok"}, + {"data", nlohmann::json::object()}}; - boost::container::flat_set<std::string> connections; + res.end(); + return; + } - for (const auto &object : object_names) { - for (const auto &Connection : object.second) { - connections.insert(Connection.first); - } - } + boost::container::flat_set<std::string> connections; - if (connections.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - auto transaction = - std::make_shared<nlohmann::json>(nlohmann::json::object()); - for (const std::string &Connection : connections) { - getManagedObjectsForEnumerate(objectPath, Connection, res, - transaction); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, (int32_t)0, - std::array<std::string, 0>()); + for (const auto &object : object_names) + { + for (const auto &Connection : object.second) + { + connections.insert(Connection.first); + } + } + + if (connections.size() <= 0) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + auto transaction = + std::make_shared<nlohmann::json>(nlohmann::json::object()); + for (const std::string &Connection : connections) + { + getManagedObjectsForEnumerate(objectPath, Connection, res, + transaction); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, + (int32_t)0, std::array<std::string, 0>()); } void handle_get(crow::Response &res, std::string &objectPath, - std::string &destProperty) { - BMCWEB_LOG_DEBUG << "handle_get: " << objectPath << " prop:" << destProperty; - std::shared_ptr<std::string> property_name = - std::make_shared<std::string>(std::move(destProperty)); - - std::shared_ptr<std::string> path = - std::make_shared<std::string>(std::move(objectPath)); - - using GetObjectType = - std::vector<std::pair<std::string, std::vector<std::string>>>; - crow::connections::systemBus->async_method_call( - [&res, path, property_name](const boost::system::error_code ec, - const GetObjectType &object_names) { - if (ec || object_names.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::shared_ptr<nlohmann::json> response = - std::make_shared<nlohmann::json>(nlohmann::json::object()); - // The mapper should never give us an empty interface names list, but - // check anyway - for (const std::pair<std::string, std::vector<std::string>> connection : - object_names) { - const std::vector<std::string> &interfaceNames = connection.second; - - if (interfaceNames.size() <= 0) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - - for (const std::string &interface : interfaceNames) { - crow::connections::systemBus->async_method_call( - [&res, response, property_name]( - const boost::system::error_code ec, - const std::vector<std::pair< - std::string, DbusRestVariantType>> &properties) { - if (ec) { - BMCWEB_LOG_ERROR << "Bad dbus request error: " << ec; - } else { - for (const std::pair<std::string, DbusRestVariantType> - &property : properties) { - // if property name is empty, or matches our search query, - // add it to the response json - - if (property_name->empty()) { - mapbox::util::apply_visitor( - [&response, &property](auto &&val) { - (*response)[property.first] = val; - }, - property.second); - } else if (property.first == *property_name) { - mapbox::util::apply_visitor( - [&response](auto &&val) { (*response) = val; }, - property.second); - } - } - } - if (response.use_count() == 1) { - res.jsonValue = {{"status", "ok"}, - {"message", "200 OK"}, - {"data", *response}}; - + std::string &destProperty) +{ + BMCWEB_LOG_DEBUG << "handle_get: " << objectPath + << " prop:" << destProperty; + std::shared_ptr<std::string> property_name = + std::make_shared<std::string>(std::move(destProperty)); + + std::shared_ptr<std::string> path = + std::make_shared<std::string>(std::move(objectPath)); + + using GetObjectType = + std::vector<std::pair<std::string, std::vector<std::string>>>; + crow::connections::systemBus->async_method_call( + [&res, path, property_name](const boost::system::error_code ec, + const GetObjectType &object_names) { + if (ec || object_names.size() <= 0) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::shared_ptr<nlohmann::json> response = + std::make_shared<nlohmann::json>(nlohmann::json::object()); + // The mapper should never give us an empty interface names list, + // but check anyway + for (const std::pair<std::string, std::vector<std::string>> + connection : object_names) + { + const std::vector<std::string> &interfaceNames = + connection.second; + + if (interfaceNames.size() <= 0) + { + res.result(boost::beast::http::status::not_found); res.end(); - } - }, - connection.first, *path, "org.freedesktop.DBus.Properties", - "GetAll", interface); - } - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", *path, - std::array<std::string, 0>()); + return; + } + + for (const std::string &interface : interfaceNames) + { + crow::connections::systemBus->async_method_call( + [&res, response, property_name]( + const boost::system::error_code ec, + const std::vector< + std::pair<std::string, DbusRestVariantType>> + &properties) { + if (ec) + { + BMCWEB_LOG_ERROR << "Bad dbus request error: " + << ec; + } + else + { + for (const std::pair<std::string, + DbusRestVariantType> + &property : properties) + { + // if property name is empty, or matches our + // search query, add it to the response json + + if (property_name->empty()) + { + mapbox::util::apply_visitor( + [&response, &property](auto &&val) { + (*response)[property.first] = + val; + }, + property.second); + } + else if (property.first == *property_name) + { + mapbox::util::apply_visitor( + [&response](auto &&val) { + (*response) = val; + }, + property.second); + } + } + } + if (response.use_count() == 1) + { + res.jsonValue = {{"status", "ok"}, + {"message", "200 OK"}, + {"data", *response}}; + + res.end(); + } + }, + connection.first, *path, + "org.freedesktop.DBus.Properties", "GetAll", interface); + } + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", *path, + std::array<std::string, 0>()); } -struct AsyncPutRequest { - AsyncPutRequest(crow::Response &res) : res(res) { - res.jsonValue = { - {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; - } - ~AsyncPutRequest() { - if (res.result() == boost::beast::http::status::internal_server_error) { - // Reset the json object to clear out any data that made it in before the - // error happened - // todo(ed) handle error condition with proper code - res.jsonValue = nlohmann::json::object(); +struct AsyncPutRequest +{ + AsyncPutRequest(crow::Response &res) : res(res) + { + res.jsonValue = { + {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; } + ~AsyncPutRequest() + { + if (res.result() == boost::beast::http::status::internal_server_error) + { + // Reset the json object to clear out any data that made it in + // before the error happened todo(ed) handle error condition with + // proper code + res.jsonValue = nlohmann::json::object(); + } - if (res.jsonValue.empty()) { - res.result(boost::beast::http::status::forbidden); - res.jsonValue = { - {"status", "error"}, - {"message", "403 Forbidden"}, - {"data", - {{"message", - "The specified property cannot be created: " + propertyName}}}}; - } + if (res.jsonValue.empty()) + { + res.result(boost::beast::http::status::forbidden); + res.jsonValue = { + {"status", "error"}, + {"message", "403 Forbidden"}, + {"data", + {{"message", "The specified property cannot be created: " + + propertyName}}}}; + } - res.end(); - } + res.end(); + } - void setErrorStatus() { - res.result(boost::beast::http::status::internal_server_error); - } + void setErrorStatus() + { + res.result(boost::beast::http::status::internal_server_error); + } - crow::Response &res; - std::string objectPath; - std::string propertyName; - nlohmann::json propertyValue; + crow::Response &res; + std::string objectPath; + std::string propertyName; + nlohmann::json propertyValue; }; void handlePut(const crow::Request &req, crow::Response &res, - const std::string &objectPath, const std::string &destProperty) { - nlohmann::json requestDbusData = - nlohmann::json::parse(req.body, nullptr, false); - - if (requestDbusData.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - - nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); - if (propertyIt == requestDbusData.end()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - const nlohmann::json &propertySetValue = *propertyIt; - auto transaction = std::make_shared<AsyncPutRequest>(res); - transaction->objectPath = objectPath; - transaction->propertyName = destProperty; - transaction->propertyValue = propertySetValue; - - using GetObjectType = - std::vector<std::pair<std::string, std::vector<std::string>>>; - - crow::connections::systemBus->async_method_call( - [transaction](const boost::system::error_code ec, - const GetObjectType &object_names) { - if (!ec && object_names.size() <= 0) { - transaction->res.result(boost::beast::http::status::not_found); - return; - } + const std::string &objectPath, const std::string &destProperty) +{ + nlohmann::json requestDbusData = + nlohmann::json::parse(req.body, nullptr, false); + + if (requestDbusData.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } - for (const std::pair<std::string, std::vector<std::string>> connection : - object_names) { - const std::string &connectionName = connection.first; - - crow::connections::systemBus->async_method_call( - [ connectionName{std::string(connectionName)}, transaction ]( - const boost::system::error_code ec, - const std::string &introspectXml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << connectionName; - transaction->setErrorStatus(); - return; - } - tinyxml2::XMLDocument doc; + nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); + if (propertyIt == requestDbusData.end()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + const nlohmann::json &propertySetValue = *propertyIt; + auto transaction = std::make_shared<AsyncPutRequest>(res); + transaction->objectPath = objectPath; + transaction->propertyName = destProperty; + transaction->propertyValue = propertySetValue; + + using GetObjectType = + std::vector<std::pair<std::string, std::vector<std::string>>>; + + crow::connections::systemBus->async_method_call( + [transaction](const boost::system::error_code ec, + const GetObjectType &object_names) { + if (!ec && object_names.size() <= 0) + { + transaction->res.result(boost::beast::http::status::not_found); + return; + } - doc.Parse(introspectXml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse: " - << introspectXml; - transaction->setErrorStatus(); - return; - } - tinyxml2::XMLElement *ifaceNode = - pRoot->FirstChildElement("interface"); - while (ifaceNode != nullptr) { - const char *interfaceName = ifaceNode->Attribute("name"); - BMCWEB_LOG_DEBUG << "found interface " << interfaceName; - tinyxml2::XMLElement *propNode = - ifaceNode->FirstChildElement("property"); - while (propNode != nullptr) { - const char *propertyName = propNode->Attribute("name"); - BMCWEB_LOG_DEBUG << "Found property " << propertyName; - if (propertyName == transaction->propertyName) { - const char *argType = propNode->Attribute("type"); - if (argType != nullptr) { - sdbusplus::message::message m = - crow::connections::systemBus->new_method_call( - connectionName.c_str(), - transaction->objectPath.c_str(), - "org.freedesktop.DBus.Properties", "Set"); - m.append(interfaceName, transaction->propertyName); - int r = sd_bus_message_open_container( - m.get(), SD_BUS_TYPE_VARIANT, argType); - if (r < 0) { - transaction->setErrorStatus(); - return; + for (const std::pair<std::string, std::vector<std::string>> + connection : object_names) + { + const std::string &connectionName = connection.first; + + crow::connections::systemBus->async_method_call( + [connectionName{std::string(connectionName)}, + transaction](const boost::system::error_code ec, + const std::string &introspectXml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << connectionName; + transaction->setErrorStatus(); + return; } - r = convertJsonToDbus(m.get(), argType, - transaction->propertyValue); - if (r < 0) { - transaction->setErrorStatus(); - return; + tinyxml2::XMLDocument doc; + + doc.Parse(introspectXml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR << "XML document failed to parse: " + << introspectXml; + transaction->setErrorStatus(); + return; } - r = sd_bus_message_close_container(m.get()); - if (r < 0) { - transaction->setErrorStatus(); - return; + tinyxml2::XMLElement *ifaceNode = + pRoot->FirstChildElement("interface"); + while (ifaceNode != nullptr) + { + const char *interfaceName = + ifaceNode->Attribute("name"); + BMCWEB_LOG_DEBUG << "found interface " + << interfaceName; + tinyxml2::XMLElement *propNode = + ifaceNode->FirstChildElement("property"); + while (propNode != nullptr) + { + const char *propertyName = + propNode->Attribute("name"); + BMCWEB_LOG_DEBUG << "Found property " + << propertyName; + if (propertyName == transaction->propertyName) + { + const char *argType = + propNode->Attribute("type"); + if (argType != nullptr) + { + sdbusplus::message::message m = + crow::connections::systemBus + ->new_method_call( + connectionName.c_str(), + transaction->objectPath + .c_str(), + "org.freedesktop.DBus." + "Properties", + "Set"); + m.append(interfaceName, + transaction->propertyName); + int r = sd_bus_message_open_container( + m.get(), SD_BUS_TYPE_VARIANT, + argType); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + r = convertJsonToDbus( + m.get(), argType, + transaction->propertyValue); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + r = sd_bus_message_close_container( + m.get()); + if (r < 0) + { + transaction->setErrorStatus(); + return; + } + + crow::connections::systemBus + ->async_send( + m, + [transaction]( + boost::system::error_code + ec, + sdbusplus::message::message + &m) { + BMCWEB_LOG_DEBUG << "sent"; + if (ec) + { + transaction->res + .jsonValue + ["status"] = + "error"; + transaction->res + .jsonValue + ["message"] = + ec.message(); + } + }); + } + } + propNode = + propNode->NextSiblingElement("property"); + } + ifaceNode = + ifaceNode->NextSiblingElement("interface"); } - - crow::connections::systemBus->async_send( - m, [transaction](boost::system::error_code ec, - sdbusplus::message::message &m) { - BMCWEB_LOG_DEBUG << "sent"; - if (ec) { - transaction->res.jsonValue["status"] = "error"; - transaction->res.jsonValue["message"] = - ec.message(); - } - }); - } - } - propNode = propNode->NextSiblingElement("property"); - } - ifaceNode = ifaceNode->NextSiblingElement("interface"); - } - }, - connectionName, transaction->objectPath, - "org.freedesktop.DBus.Introspectable", "Introspect"); - } - }, - "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", - "xyz.openbmc_project.ObjectMapper", "GetObject", transaction->objectPath, - std::array<std::string, 0>()); + }, + connectionName, transaction->objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetObject", + transaction->objectPath, std::array<std::string, 0>()); } -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...> &app) { - BMCWEB_ROUTE(app, "/bus/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - res.jsonValue = {{"busses", {{{"name", "system"}}}}, {"status", "ok"}}; - }); - - BMCWEB_ROUTE(app, "/bus/system/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - - auto myCallback = [&res](const boost::system::error_code ec, - std::vector<std::string> &names) { - if (ec) { - BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; - res.result(boost::beast::http::status::internal_server_error); - } else { - std::sort(names.begin(), names.end()); - nlohmann::json j{{"status", "ok"}}; - auto &objectsSub = j["objects"]; - for (auto &name : names) { - objectsSub.push_back({{"name", name}}); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) +{ + BMCWEB_ROUTE(app, "/bus/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + res.jsonValue = {{"busses", {{{"name", "system"}}}}, + {"status", "ok"}}; + }); + + BMCWEB_ROUTE(app, "/bus/system/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + auto myCallback = [&res](const boost::system::error_code ec, + std::vector<std::string> &names) { + if (ec) + { + BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + std::sort(names.begin(), names.end()); + nlohmann::json j{{"status", "ok"}}; + auto &objectsSub = j["objects"]; + for (auto &name : names) + { + objectsSub.push_back({{"name", name}}); + } + res.jsonValue = std::move(j); + } + res.end(); + }; + crow::connections::systemBus->async_method_call( + std::move(myCallback), "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "ListNames"); + }); + + BMCWEB_ROUTE(app, "/list/") + .methods("GET"_method)( + [](const crow::Request &req, crow::Response &res) { + handle_list(res, "/"); + }); + + BMCWEB_ROUTE(app, "/xyz/<path>") + .methods("GET"_method, "PUT"_method, + "POST"_method)([](const crow::Request &req, + crow::Response &res, + const std::string &path) { + std::string objectPath = "/xyz/" + path; + + // Trim any trailing "/" at the end + if (boost::ends_with(objectPath, "/")) + { + objectPath.pop_back(); } - res.jsonValue = std::move(j); - } - res.end(); - }; - crow::connections::systemBus->async_method_call( - std::move(myCallback), "org.freedesktop.DBus", "/", - "org.freedesktop.DBus", "ListNames"); - }); - - BMCWEB_ROUTE(app, "/list/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res) { - handle_list(res, "/"); - }); - - BMCWEB_ROUTE(app, "/xyz/<path>") - .methods("GET"_method, "PUT"_method, - "POST"_method)([](const crow::Request &req, crow::Response &res, - const std::string &path) { - std::string objectPath = "/xyz/" + path; - - // Trim any trailing "/" at the end - if (boost::ends_with(objectPath, "/")) { - objectPath.pop_back(); - } - - // If accessing a single attribute, fill in and update objectPath, - // otherwise leave destProperty blank - std::string destProperty = ""; - const char *attrSeperator = "/attr/"; - size_t attrPosition = path.find(attrSeperator); - if (attrPosition != path.npos) { - objectPath = "/xyz/" + path.substr(0, attrPosition); - destProperty = - path.substr(attrPosition + strlen(attrSeperator), path.length()); - } - - if (req.method() == "POST"_method) { - constexpr const char *action_seperator = "/action/"; - size_t action_position = path.find(action_seperator); - if (action_position != path.npos) { - objectPath = "/xyz/" + path.substr(0, action_position); - std::string post_property = path.substr( - (action_position + strlen(action_seperator)), path.length()); - handle_action(req, res, objectPath, post_property); - return; - } - } else if (req.method() == "GET"_method) { - if (boost::ends_with(objectPath, "/enumerate")) { - objectPath.erase(objectPath.end() - 10, objectPath.end()); - handle_enumerate(res, objectPath); - } else if (boost::ends_with(objectPath, "/list")) { - objectPath.erase(objectPath.end() - 5, objectPath.end()); - handle_list(res, objectPath); - } else { - handle_get(res, objectPath, destProperty); - } - return; - } else if (req.method() == "PUT"_method) { - handlePut(req, res, objectPath, destProperty); - return; - } - res.result(boost::beast::http::status::method_not_allowed); - res.end(); - }); - - BMCWEB_ROUTE(app, "/bus/system/<str>/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &Connection) { - std::shared_ptr<nlohmann::json> transaction; - introspectObjects(res, Connection, "/", transaction); - }); - - BMCWEB_ROUTE(app, "/download/dump/<str>/") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &dumpId) { - std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); - if (!std::regex_match(dumpId, validFilename)) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::experimental::filesystem::path loc( - "/var/lib/phosphor-debug-collector/dumps"); + // If accessing a single attribute, fill in and update objectPath, + // otherwise leave destProperty blank + std::string destProperty = ""; + const char *attrSeperator = "/attr/"; + size_t attrPosition = path.find(attrSeperator); + if (attrPosition != path.npos) + { + objectPath = "/xyz/" + path.substr(0, attrPosition); + destProperty = path.substr(attrPosition + strlen(attrSeperator), + path.length()); + } - loc += dumpId; + if (req.method() == "POST"_method) + { + constexpr const char *action_seperator = "/action/"; + size_t action_position = path.find(action_seperator); + if (action_position != path.npos) + { + objectPath = "/xyz/" + path.substr(0, action_position); + std::string post_property = path.substr( + (action_position + strlen(action_seperator)), + path.length()); + handle_action(req, res, objectPath, post_property); + return; + } + } + else if (req.method() == "GET"_method) + { + if (boost::ends_with(objectPath, "/enumerate")) + { + objectPath.erase(objectPath.end() - 10, objectPath.end()); + handle_enumerate(res, objectPath); + } + else if (boost::ends_with(objectPath, "/list")) + { + objectPath.erase(objectPath.end() - 5, objectPath.end()); + handle_list(res, objectPath); + } + else + { + handle_get(res, objectPath, destProperty); + } + return; + } + else if (req.method() == "PUT"_method) + { + handlePut(req, res, objectPath, destProperty); + return; + } - if (!std::experimental::filesystem::exists(loc) || - !std::experimental::filesystem::is_directory(loc)) { - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - std::experimental::filesystem::directory_iterator files(loc); - for (auto &file : files) { - std::ifstream readFile(file.path()); - if (readFile.good()) { - continue; - } - res.addHeader("Content-Type", "application/octet-stream"); - res.body() = {std::istreambuf_iterator<char>(readFile), - std::istreambuf_iterator<char>()}; - res.end(); - } - res.result(boost::beast::http::status::not_found); - res.end(); - return; - }); - - BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") - .methods("GET"_method)([](const crow::Request &req, crow::Response &res, - const std::string &processName, - const std::string &requestedPath) { - std::vector<std::string> strs; - boost::split(strs, requestedPath, boost::is_any_of("/")); - std::string objectPath; - std::string interfaceName; - std::string methodName; - auto it = strs.begin(); - if (it == strs.end()) { - objectPath = "/"; - } - while (it != strs.end()) { - // Check if segment contains ".". If it does, it must be an - // interface - if (it->find(".") != std::string::npos) { - break; - // THis check is neccesary as the trailing slash gets parsed as - // part of our <path> specifier above, which causes the normal - // trailing backslash redirector to fail. - } else if (!it->empty()) { - objectPath += "/" + *it; - } - it++; - } - if (it != strs.end()) { - interfaceName = *it; - it++; - - // after interface, we might have a method name - if (it != strs.end()) { - methodName = *it; - it++; - } - } - if (it != strs.end()) { - // if there is more levels past the method name, something went - // wrong, return not found - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - if (interfaceName.empty()) { - crow::connections::systemBus->async_method_call( - [&, processName, objectPath](const boost::system::error_code ec, - const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << processName << " " << objectPath - << "\n"; - res.jsonValue = {{"status", "XML parse error"}}; - res.result( - boost::beast::http::status::internal_server_error); - } else { - nlohmann::json interfacesArray = nlohmann::json::array(); - tinyxml2::XMLElement *interface = - pRoot->FirstChildElement("interface"); + res.result(boost::beast::http::status::method_not_allowed); + res.end(); + }); + + BMCWEB_ROUTE(app, "/bus/system/<str>/") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &Connection) { + std::shared_ptr<nlohmann::json> transaction; + introspectObjects(res, Connection, "/", transaction); + }); + + BMCWEB_ROUTE(app, "/download/dump/<str>/") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &dumpId) { + std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); + if (!std::regex_match(dumpId, validFilename)) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::experimental::filesystem::path loc( + "/var/lib/phosphor-debug-collector/dumps"); - while (interface != nullptr) { - std::string ifaceName = interface->Attribute("name"); - interfacesArray.push_back({{"name", ifaceName}}); + loc += dumpId; - interface = interface->NextSiblingElement("interface"); - } - res.jsonValue = {{"status", "ok"}, - {"bus_name", processName}, - {"interfaces", interfacesArray}, - {"objectPath", objectPath}}; - } + if (!std::experimental::filesystem::exists(loc) || + !std::experimental::filesystem::is_directory(loc)) + { + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + std::experimental::filesystem::directory_iterator files(loc); + for (auto &file : files) + { + std::ifstream readFile(file.path()); + if (readFile.good()) + { + continue; } + res.addHeader("Content-Type", "application/octet-stream"); + res.body() = {std::istreambuf_iterator<char>(readFile), + std::istreambuf_iterator<char>()}; res.end(); - }, - processName, objectPath, "org.freedesktop.DBus.Introspectable", - "Introspect"); - } else { - crow::connections::systemBus->async_method_call( - [ - &, processName, objectPath, - interface_name{std::move(interfaceName)} - ](const boost::system::error_code ec, - const std::string &introspect_xml) { - if (ec) { - BMCWEB_LOG_ERROR - << "Introspect call failed with error: " << ec.message() - << " on process: " << processName - << " path: " << objectPath << "\n"; - - } else { - tinyxml2::XMLDocument doc; - - doc.Parse(introspect_xml.c_str()); - tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); - if (pRoot == nullptr) { - BMCWEB_LOG_ERROR << "XML document failed to parse " - << processName << " " << objectPath - << "\n"; - res.result( - boost::beast::http::status::internal_server_error); - - } else { - tinyxml2::XMLElement *node = - pRoot->FirstChildElement("node"); - - // if we know we're the only call, build the json directly - nlohmann::json methodsArray = nlohmann::json::array(); - nlohmann::json signalsArray = nlohmann::json::array(); - tinyxml2::XMLElement *interface = - pRoot->FirstChildElement("interface"); - - while (interface != nullptr) { - std::string ifaceName = interface->Attribute("name"); - - if (ifaceName == interfaceName) { - tinyxml2::XMLElement *methods = - interface->FirstChildElement("method"); - while (methods != nullptr) { - nlohmann::json argsArray = nlohmann::json::array(); - tinyxml2::XMLElement *arg = - methods->FirstChildElement("arg"); - while (arg != nullptr) { - argsArray.push_back( - {{"name", arg->Attribute("name")}, - {"type", arg->Attribute("type")}, - {"direction", arg->Attribute("direction")}}); - arg = arg->NextSiblingElement("arg"); - } - methodsArray.push_back( - {{"name", methods->Attribute("name")}, - {"uri", "/bus/system/" + processName + - objectPath + "/" + interfaceName + - "/" + methods->Attribute("name")}, - {"args", argsArray}}); - methods = methods->NextSiblingElement("method"); - } - tinyxml2::XMLElement *signals = - interface->FirstChildElement("signal"); - while (signals != nullptr) { - nlohmann::json argsArray = nlohmann::json::array(); - - tinyxml2::XMLElement *arg = - signals->FirstChildElement("arg"); - while (arg != nullptr) { - std::string name = arg->Attribute("name"); - std::string type = arg->Attribute("type"); - argsArray.push_back({ - {"name", name}, - {"type", type}, - }); - arg = arg->NextSiblingElement("arg"); - } - signalsArray.push_back( - {{"name", signals->Attribute("name")}, - {"args", argsArray}}); - signals = signals->NextSiblingElement("signal"); - } - - res.jsonValue = { - {"status", "ok"}, - {"bus_name", processName}, - {"interface", interfaceName}, - {"methods", methodsArray}, - {"objectPath", objectPath}, - {"properties", nlohmann::json::object()}, - {"signals", signalsArray}}; - - break; - } - - interface = interface->NextSiblingElement("interface"); - } - if (interface == nullptr) { - // if we got to the end of the list and never found a - // match, throw 404 - res.result(boost::beast::http::status::not_found); - } - } + } + res.result(boost::beast::http::status::not_found); + res.end(); + return; + }); + + BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") + .methods("GET"_method)([](const crow::Request &req, crow::Response &res, + const std::string &processName, + const std::string &requestedPath) { + std::vector<std::string> strs; + boost::split(strs, requestedPath, boost::is_any_of("/")); + std::string objectPath; + std::string interfaceName; + std::string methodName; + auto it = strs.begin(); + if (it == strs.end()) + { + objectPath = "/"; + } + while (it != strs.end()) + { + // Check if segment contains ".". If it does, it must be an + // interface + if (it->find(".") != std::string::npos) + { + break; + // THis check is neccesary as the trailing slash gets parsed + // as part of our <path> specifier above, which causes the + // normal trailing backslash redirector to fail. + } + else if (!it->empty()) + { + objectPath += "/" + *it; } + it++; + } + if (it != strs.end()) + { + interfaceName = *it; + it++; + + // after interface, we might have a method name + if (it != strs.end()) + { + methodName = *it; + it++; + } + } + if (it != strs.end()) + { + // if there is more levels past the method name, something went + // wrong, return not found + res.result(boost::beast::http::status::not_found); res.end(); - }, - processName, objectPath, "org.freedesktop.DBus.Introspectable", - "Introspect"); - } - }); + return; + } + if (interfaceName.empty()) + { + crow::connections::systemBus->async_method_call( + [&, processName, + objectPath](const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << processName + << " path: " << objectPath << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR + << "XML document failed to parse " + << processName << " " << objectPath << "\n"; + res.jsonValue = {{"status", "XML parse error"}}; + res.result(boost::beast::http::status:: + internal_server_error); + } + else + { + nlohmann::json interfacesArray = + nlohmann::json::array(); + tinyxml2::XMLElement *interface = + pRoot->FirstChildElement("interface"); + + while (interface != nullptr) + { + std::string ifaceName = + interface->Attribute("name"); + interfacesArray.push_back( + {{"name", ifaceName}}); + + interface = interface->NextSiblingElement( + "interface"); + } + res.jsonValue = { + {"status", "ok"}, + {"bus_name", processName}, + {"interfaces", interfacesArray}, + {"objectPath", objectPath}}; + } + } + res.end(); + }, + processName, objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + else + { + crow::connections::systemBus->async_method_call( + [&, processName, objectPath, + interface_name{std::move(interfaceName)}]( + const boost::system::error_code ec, + const std::string &introspect_xml) { + if (ec) + { + BMCWEB_LOG_ERROR + << "Introspect call failed with error: " + << ec.message() + << " on process: " << processName + << " path: " << objectPath << "\n"; + } + else + { + tinyxml2::XMLDocument doc; + + doc.Parse(introspect_xml.c_str()); + tinyxml2::XMLNode *pRoot = + doc.FirstChildElement("node"); + if (pRoot == nullptr) + { + BMCWEB_LOG_ERROR + << "XML document failed to parse " + << processName << " " << objectPath << "\n"; + res.result(boost::beast::http::status:: + internal_server_error); + } + else + { + tinyxml2::XMLElement *node = + pRoot->FirstChildElement("node"); + + // if we know we're the only call, build the + // json directly + nlohmann::json methodsArray = + nlohmann::json::array(); + nlohmann::json signalsArray = + nlohmann::json::array(); + tinyxml2::XMLElement *interface = + pRoot->FirstChildElement("interface"); + + while (interface != nullptr) + { + std::string ifaceName = + interface->Attribute("name"); + + if (ifaceName == interfaceName) + { + tinyxml2::XMLElement *methods = + interface->FirstChildElement( + "method"); + while (methods != nullptr) + { + nlohmann::json argsArray = + nlohmann::json::array(); + tinyxml2::XMLElement *arg = + methods->FirstChildElement( + "arg"); + while (arg != nullptr) + { + argsArray.push_back( + {{"name", + arg->Attribute("name")}, + {"type", + arg->Attribute("type")}, + {"direction", + arg->Attribute( + "direction")}}); + arg = arg->NextSiblingElement( + "arg"); + } + methodsArray.push_back( + {{"name", + methods->Attribute("name")}, + {"uri", + "/bus/system/" + processName + + objectPath + "/" + + interfaceName + "/" + + methods->Attribute( + "name")}, + {"args", argsArray}}); + methods = + methods->NextSiblingElement( + "method"); + } + tinyxml2::XMLElement *signals = + interface->FirstChildElement( + "signal"); + while (signals != nullptr) + { + nlohmann::json argsArray = + nlohmann::json::array(); + + tinyxml2::XMLElement *arg = + signals->FirstChildElement( + "arg"); + while (arg != nullptr) + { + std::string name = + arg->Attribute("name"); + std::string type = + arg->Attribute("type"); + argsArray.push_back({ + {"name", name}, + {"type", type}, + }); + arg = arg->NextSiblingElement( + "arg"); + } + signalsArray.push_back( + {{"name", + signals->Attribute("name")}, + {"args", argsArray}}); + signals = + signals->NextSiblingElement( + "signal"); + } + + res.jsonValue = { + {"status", "ok"}, + {"bus_name", processName}, + {"interface", interfaceName}, + {"methods", methodsArray}, + {"objectPath", objectPath}, + {"properties", + nlohmann::json::object()}, + {"signals", signalsArray}}; + + break; + } + + interface = interface->NextSiblingElement( + "interface"); + } + if (interface == nullptr) + { + // if we got to the end of the list and + // never found a match, throw 404 + res.result( + boost::beast::http::status::not_found); + } + } + } + res.end(); + }, + processName, objectPath, + "org.freedesktop.DBus.Introspectable", "Introspect"); + } + }); } -} // namespace openbmc_mapper -} // namespace crow +} // namespace openbmc_mapper +} // namespace crow diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp index 65e4740..f51f9aa 100644 --- a/include/pam_authenticate.hpp +++ b/include/pam_authenticate.hpp @@ -1,72 +1,84 @@ #pragma once #include <security/pam_appl.h> + +#include <boost/utility/string_view.hpp> #include <cstring> #include <memory> -#include <boost/utility/string_view.hpp> // function used to get user input inline int pamFunctionConversation(int numMsg, const struct pam_message** msg, - struct pam_response** resp, - void* appdataPtr) { - if (appdataPtr == nullptr) { - return PAM_AUTH_ERR; - } - auto* pass = reinterpret_cast<char*>( - malloc(std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1)); - std::strcpy(pass, reinterpret_cast<char*>(appdataPtr)); + struct pam_response** resp, void* appdataPtr) +{ + if (appdataPtr == nullptr) + { + return PAM_AUTH_ERR; + } + auto* pass = reinterpret_cast<char*>( + malloc(std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1)); + std::strcpy(pass, reinterpret_cast<char*>(appdataPtr)); - *resp = reinterpret_cast<pam_response*>( - calloc(numMsg, sizeof(struct pam_response))); + *resp = reinterpret_cast<pam_response*>( + calloc(numMsg, sizeof(struct pam_response))); - for (int i = 0; i < numMsg; ++i) { - /* Ignore all PAM messages except prompting for hidden input */ - if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) { - continue; - } + for (int i = 0; i < numMsg; ++i) + { + /* Ignore all PAM messages except prompting for hidden input */ + if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) + { + continue; + } - /* Assume PAM is only prompting for the password as hidden input */ - resp[i]->resp = pass; - } + /* Assume PAM is only prompting for the password as hidden input */ + resp[i]->resp = pass; + } - return PAM_SUCCESS; + return PAM_SUCCESS; } inline bool pamAuthenticateUser(const boost::string_view username, - const boost::string_view password) { - std::string userStr(username); - std::string passStr(password); - const struct pam_conv localConversation = { - pamFunctionConversation, const_cast<char*>(passStr.c_str())}; - pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start + const boost::string_view password) +{ + std::string userStr(username); + std::string passStr(password); + const struct pam_conv localConversation = { + pamFunctionConversation, const_cast<char*>(passStr.c_str())}; + pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start - if (pam_start("webserver", userStr.c_str(), &localConversation, - &localAuthHandle) != PAM_SUCCESS) { - return false; - } - int retval = - pam_authenticate(localAuthHandle, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); + if (pam_start("webserver", userStr.c_str(), &localConversation, + &localAuthHandle) != PAM_SUCCESS) + { + return false; + } + int retval = pam_authenticate(localAuthHandle, + PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK); - if (retval != PAM_SUCCESS) { - if (retval == PAM_AUTH_ERR) { - // printf("Authentication failure.\n"); - } else { - // printf("pam_authenticate returned %d\n", retval); + if (retval != PAM_SUCCESS) + { + if (retval == PAM_AUTH_ERR) + { + // printf("Authentication failure.\n"); + } + else + { + // printf("pam_authenticate returned %d\n", retval); + } + pam_end(localAuthHandle, PAM_SUCCESS); + return false; } - pam_end(localAuthHandle, PAM_SUCCESS); - return false; - } - /* check that the account is healthy */ - if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) != - PAM_SUCCESS) { - pam_end(localAuthHandle, PAM_SUCCESS); - return false; - } + /* check that the account is healthy */ + if (pam_acct_mgmt(localAuthHandle, PAM_DISALLOW_NULL_AUTHTOK) != + PAM_SUCCESS) + { + pam_end(localAuthHandle, PAM_SUCCESS); + return false; + } - if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) { - return false; - } + if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) + { + return false; + } - return true; + return true; } diff --git a/include/persistent_data_middleware.hpp b/include/persistent_data_middleware.hpp index 706f6f4..b384f02 100644 --- a/include/persistent_data_middleware.hpp +++ b/include/persistent_data_middleware.hpp @@ -1,121 +1,165 @@ #pragma once -#include <nlohmann/json.hpp> -#include <pam_authenticate.hpp> -#include <sessions.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_map.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <nlohmann/json.hpp> +#include <pam_authenticate.hpp> +#include <random> +#include <sessions.hpp> +#include <webassets.hpp> -namespace crow { - -namespace persistent_data { +namespace crow +{ -class Middleware { - // todo(ed) should read this from a fixed location somewhere, not CWD - static constexpr const char* filename = "bmcweb_persistent_data.json"; - int jsonRevision = 1; +namespace persistent_data +{ - public: - struct Context {}; +class Middleware +{ + // todo(ed) should read this from a fixed location somewhere, not CWD + static constexpr const char* filename = "bmcweb_persistent_data.json"; + int jsonRevision = 1; - Middleware() { readData(); } + public: + struct Context + { + }; - ~Middleware() { - if (persistent_data::SessionStore::getInstance().needsWrite()) { - writeData(); + Middleware() + { + readData(); } - } - - void beforeHandle(crow::Request& req, Response& res, Context& ctx) {} - - void afterHandle(Request& req, Response& res, Context& ctx) {} - - // TODO(ed) this should really use protobuf, or some other serialization - // library, but adding another dependency is somewhat outside the scope of - // this application for the moment - void readData() { - std::ifstream persistentFile(filename); - int fileRevision = 0; - if (persistentFile.is_open()) { - // call with exceptions disabled - auto data = nlohmann::json::parse(persistentFile, nullptr, false); - if (data.is_discarded()) { - BMCWEB_LOG_ERROR << "Error parsing persistent data in json file."; - } else { - for (const auto& item : data.items()) { - if (item.key() == "revision") { - fileRevision = 0; - - const uint64_t* uintPtr = item.value().get_ptr<const uint64_t*>(); - if (uintPtr == nullptr) { - BMCWEB_LOG_ERROR << "Failed to read revision flag"; - } else { - fileRevision = *uintPtr; - } - } else if (item.key() == "system_uuid") { - const std::string* jSystemUuid = - item.value().get_ptr<const std::string*>(); - if (jSystemUuid != nullptr) { - systemUuid = *jSystemUuid; - } - } else if (item.key() == "sessions") { - for (const auto& elem : item.value()) { - std::shared_ptr<UserSession> newSession = - UserSession::fromJson(elem); - if (newSession == nullptr) { - BMCWEB_LOG_ERROR - << "Problem reading session from persistent store"; - continue; - } - - BMCWEB_LOG_DEBUG << "Restored session: " << newSession->csrfToken - << " " << newSession->uniqueId << " " - << newSession->sessionToken; - SessionStore::getInstance().authTokens.emplace( - newSession->sessionToken, newSession); - } - } else { - // Do nothing in the case of extra fields. We may have cases where - // fields are added in the future, and we want to at least attempt - // to gracefully support downgrades in that case, even if we don't - // officially support it - } + ~Middleware() + { + if (persistent_data::SessionStore::getInstance().needsWrite()) + { + writeData(); } - } } - bool needWrite = false; - if (systemUuid.empty()) { - systemUuid = boost::uuids::to_string(boost::uuids::random_generator()()); - needWrite = true; + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { } - if (fileRevision < jsonRevision) { - needWrite = true; + + void afterHandle(Request& req, Response& res, Context& ctx) + { } - // write revision changes or system uuid changes immediately - if (needWrite) { - writeData(); + + // TODO(ed) this should really use protobuf, or some other serialization + // library, but adding another dependency is somewhat outside the scope of + // this application for the moment + void readData() + { + std::ifstream persistentFile(filename); + int fileRevision = 0; + if (persistentFile.is_open()) + { + // call with exceptions disabled + auto data = nlohmann::json::parse(persistentFile, nullptr, false); + if (data.is_discarded()) + { + BMCWEB_LOG_ERROR + << "Error parsing persistent data in json file."; + } + else + { + for (const auto& item : data.items()) + { + if (item.key() == "revision") + { + fileRevision = 0; + + const uint64_t* uintPtr = + item.value().get_ptr<const uint64_t*>(); + if (uintPtr == nullptr) + { + BMCWEB_LOG_ERROR << "Failed to read revision flag"; + } + else + { + fileRevision = *uintPtr; + } + } + else if (item.key() == "system_uuid") + { + const std::string* jSystemUuid = + item.value().get_ptr<const std::string*>(); + if (jSystemUuid != nullptr) + { + systemUuid = *jSystemUuid; + } + } + else if (item.key() == "sessions") + { + for (const auto& elem : item.value()) + { + std::shared_ptr<UserSession> newSession = + UserSession::fromJson(elem); + + if (newSession == nullptr) + { + BMCWEB_LOG_ERROR << "Problem reading session " + "from persistent store"; + continue; + } + + BMCWEB_LOG_DEBUG + << "Restored session: " << newSession->csrfToken + << " " << newSession->uniqueId << " " + << newSession->sessionToken; + SessionStore::getInstance().authTokens.emplace( + newSession->sessionToken, newSession); + } + } + else + { + // Do nothing in the case of extra fields. We may have + // cases where fields are added in the future, and we + // want to at least attempt to gracefully support + // downgrades in that case, even if we don't officially + // support it + } + } + } + } + bool needWrite = false; + + if (systemUuid.empty()) + { + systemUuid = + boost::uuids::to_string(boost::uuids::random_generator()()); + needWrite = true; + } + if (fileRevision < jsonRevision) + { + needWrite = true; + } + // write revision changes or system uuid changes immediately + if (needWrite) + { + writeData(); + } } - } - void writeData() { - std::ofstream persistentFile(filename); - nlohmann::json data{{"sessions", SessionStore::getInstance().authTokens}, - {"system_uuid", systemUuid}, - {"revision", jsonRevision}}; - persistentFile << data; - } + void writeData() + { + std::ofstream persistentFile(filename); + nlohmann::json data{ + {"sessions", SessionStore::getInstance().authTokens}, + {"system_uuid", systemUuid}, + {"revision", jsonRevision}}; + persistentFile << data; + } - std::string systemUuid{""}; + std::string systemUuid{""}; }; -} // namespace persistent_data -} // namespace crow +} // namespace persistent_data +} // namespace crow diff --git a/include/redfish_v1.hpp b/include/redfish_v1.hpp index b81aa54..13e1838 100644 --- a/include/redfish_v1.hpp +++ b/include/redfish_v1.hpp @@ -1,15 +1,18 @@ #pragma once +#include <crow/app.h> + +#include <boost/algorithm/string.hpp> #include <dbus_singleton.hpp> -#include <persistent_data_middleware.hpp> -#include <token_authorization_middleware.hpp> #include <fstream> +#include <persistent_data_middleware.hpp> #include <streambuf> #include <string> -#include <crow/app.h> -#include <boost/algorithm/string.hpp> -namespace crow { -namespace redfish { +#include <token_authorization_middleware.hpp> +namespace crow +{ +namespace redfish +{ using ManagedObjectType = std::vector<std::pair< sdbusplus::message::object_path, @@ -17,108 +20,130 @@ using ManagedObjectType = std::vector<std::pair< std::string, boost::container::flat_map< std::string, sdbusplus::message::variant<bool>>>>>; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/redfish/") - .methods("GET"_method)([](const crow::Request& req, crow::Response& res) { - res.jsonValue = {{"v1", "/redfish/v1/"}}; - res.end(); - }); - - BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/") - .methods( - "GET"_method)([&](const crow::Request& req, crow::Response& res) { - crow::connections::systemBus->async_method_call( - [&](const boost::system::error_code ec, - const ManagedObjectType& users) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - res.jsonValue = { - {"@odata.context", - "/redfish/v1/" - "$metadata#ManagerAccountCollection." - "ManagerAccountCollection"}, - {"@odata.id", "/redfish/v1/AccountService/Accounts"}, - {"@odata.type", - "#ManagerAccountCollection.ManagerAccountCollection"}, - {"Name", "Accounts Collection"}, - {"Description", "BMC User Accounts"}, - {"Members@odata.count", users.size()}}; - nlohmann::json memberArray = nlohmann::json::array(); - int userIndex = 0; - for (auto& user : users) { - const std::string& path = - static_cast<const std::string&>(user.first); - std::size_t lastIndex = path.rfind("/"); - if (lastIndex == std::string::npos) { - lastIndex = 0; - } else { - lastIndex += 1; - } - memberArray.push_back( - {{"@odata.id", "/redfish/v1/AccountService/Accounts/" + - path.substr(lastIndex)}}); - } - res.jsonValue["Members"] = memberArray; - } - res.end(); - }, - "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/redfish/") + .methods("GET"_method)( + [](const crow::Request& req, crow::Response& res) { + res.jsonValue = {{"v1", "/redfish/v1/"}}; + res.end(); + }); - BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/") - .methods("GET"_method)([](const crow::Request& req, crow::Response& res, - const std::string& account_name) { + BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/") + .methods( + "GET"_method)([&](const crow::Request& req, crow::Response& res) { + crow::connections::systemBus->async_method_call( + [&](const boost::system::error_code ec, + const ManagedObjectType& users) { + if (ec) + { + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + res.jsonValue = { + {"@odata.context", + "/redfish/v1/" + "$metadata#ManagerAccountCollection." + "ManagerAccountCollection"}, + {"@odata.id", + "/redfish/v1/AccountService/Accounts"}, + {"@odata.type", "#ManagerAccountCollection." + "ManagerAccountCollection"}, + {"Name", "Accounts Collection"}, + {"Description", "BMC User Accounts"}, + {"Members@odata.count", users.size()}}; + nlohmann::json memberArray = nlohmann::json::array(); + int userIndex = 0; + for (auto& user : users) + { + const std::string& path = + static_cast<const std::string&>(user.first); + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + lastIndex = 0; + } + else + { + lastIndex += 1; + } + memberArray.push_back( + {{"@odata.id", + "/redfish/v1/AccountService/Accounts/" + + path.substr(lastIndex)}}); + } + res.jsonValue["Members"] = memberArray; + } + res.end(); + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); - crow::connections::systemBus->async_method_call( - [&, accountName{std::move(account_name)} ]( - const boost::system::error_code ec, - const ManagedObjectType& users) { - if (ec) { - res.result(boost::beast::http::status::internal_server_error); - } else { - for (auto& user : users) { - const std::string& path = - static_cast<const std::string&>(user.first); - std::size_t lastIndex = path.rfind("/"); - if (lastIndex == std::string::npos) { - lastIndex = 0; - } else { - lastIndex += 1; - } - if (path.substr(lastIndex) == accountName) { - res.jsonValue = { - {"@odata.context", - "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"}, - {"@odata.id", "/redfish/v1/AccountService/Accounts/1"}, - {"@odata.type", - "#ManagerAccount.v1_0_3.ManagerAccount"}, - {"Id", "1"}, - {"Name", "User Account"}, - {"Description", "User Account"}, - {"Enabled", false}, - {"Password", nullptr}, - {"UserName", accountName}, - {"RoleId", "Administrator"}, - {"Links", - {{"Role", - {{"@odata.id", - "/redfish/v1/AccountService/Roles/" - "Administrator"}}}}}}; - break; - } - } - if (res.jsonValue.is_null()) { - res.result(boost::beast::http::status::not_found); - } - } - res.end(); - }, - "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", - "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); - }); + BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/") + .methods("GET"_method)([](const crow::Request& req, crow::Response& res, + const std::string& account_name) { + crow::connections::systemBus->async_method_call( + [&, accountName{std::move(account_name)}]( + const boost::system::error_code ec, + const ManagedObjectType& users) { + if (ec) + { + res.result( + boost::beast::http::status::internal_server_error); + } + else + { + for (auto& user : users) + { + const std::string& path = + static_cast<const std::string&>(user.first); + std::size_t lastIndex = path.rfind("/"); + if (lastIndex == std::string::npos) + { + lastIndex = 0; + } + else + { + lastIndex += 1; + } + if (path.substr(lastIndex) == accountName) + { + res.jsonValue = { + {"@odata.context", + "/redfish/v1/" + "$metadata#ManagerAccount.ManagerAccount"}, + {"@odata.id", + "/redfish/v1/AccountService/Accounts/1"}, + {"@odata.type", + "#ManagerAccount.v1_0_3.ManagerAccount"}, + {"Id", "1"}, + {"Name", "User Account"}, + {"Description", "User Account"}, + {"Enabled", false}, + {"Password", nullptr}, + {"UserName", accountName}, + {"RoleId", "Administrator"}, + {"Links", + {{"Role", + {{"@odata.id", + "/redfish/v1/AccountService/Roles/" + "Administrator"}}}}}}; + break; + } + } + if (res.jsonValue.is_null()) + { + res.result(boost::beast::http::status::not_found); + } + } + res.end(); + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); + }); } -} // namespace redfish -} // namespace crow +} // namespace redfish +} // namespace crow diff --git a/include/security_headers_middleware.hpp b/include/security_headers_middleware.hpp index 750f87b..561fd81 100644 --- a/include/security_headers_middleware.hpp +++ b/include/security_headers_middleware.hpp @@ -3,7 +3,8 @@ #include <crow/http_request.h> #include <crow/http_response.h> -namespace crow { +namespace crow +{ static const char* strictTransportSecurityKey = "Strict-Transport-Security"; static const char* strictTransportSecurityValue = "max-age=31536000; includeSubdomains; preload"; @@ -26,40 +27,46 @@ static const char* pragmaValue = "no-cache"; static const char* cacheControlKey = "Cache-Control"; static const char* cacheControlValue = "no-Store,no-Cache"; -struct SecurityHeadersMiddleware { - struct Context {}; +struct SecurityHeadersMiddleware +{ + struct Context + { + }; - void beforeHandle(crow::Request& req, Response& res, Context& ctx) { + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { #ifdef BMCWEB_INSECURE_DISABLE_XSS_PREVENTION - if ("OPTIONS"_method == req.method()) { - res.end(); - } + if ("OPTIONS"_method == req.method()) + { + res.end(); + } #endif - } + } - void afterHandle(Request& req, Response& res, Context& ctx) { - /* - TODO(ed) these should really check content types. for example, - X-UA-Compatible header doesn't make sense when retrieving a JSON or - javascript file. It doesn't hurt anything, it's just ugly. - */ - res.addHeader(strictTransportSecurityKey, strictTransportSecurityValue); - res.addHeader(uaCompatabilityKey, uaCompatabilityValue); - res.addHeader(xframeKey, xframeValue); - res.addHeader(xssKey, xssValue); - res.addHeader(contentSecurityKey, contentSecurityValue); - res.addHeader(pragmaKey, pragmaValue); - res.addHeader(cacheControlKey, cacheControlValue); + void afterHandle(Request& req, Response& res, Context& ctx) + { + /* + TODO(ed) these should really check content types. for example, + X-UA-Compatible header doesn't make sense when retrieving a JSON or + javascript file. It doesn't hurt anything, it's just ugly. + */ + res.addHeader(strictTransportSecurityKey, strictTransportSecurityValue); + res.addHeader(uaCompatabilityKey, uaCompatabilityValue); + res.addHeader(xframeKey, xframeValue); + res.addHeader(xssKey, xssValue); + res.addHeader(contentSecurityKey, contentSecurityValue); + res.addHeader(pragmaKey, pragmaValue); + res.addHeader(cacheControlKey, cacheControlValue); #ifdef BMCWEB_INSECURE_DISABLE_XSS_PREVENTION - res.addHeader("Access-Control-Allow-Origin", "http://localhost:8080"); - res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH"); - res.addHeader("Access-Control-Allow-Credentials", "true"); - res.addHeader("Access-Control-Allow-Headers", - "Origin, Content-Type, Accept, Cookie, X-XSRF-TOKEN"); + res.addHeader("Access-Control-Allow-Origin", "http://localhost:8080"); + res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH"); + res.addHeader("Access-Control-Allow-Credentials", "true"); + res.addHeader("Access-Control-Allow-Headers", + "Origin, Content-Type, Accept, Cookie, X-XSRF-TOKEN"); #endif - } + } }; -} // namespace crow +} // namespace crow diff --git a/include/sessions.hpp b/include/sessions.hpp index f549fde..510f566 100644 --- a/include/sessions.hpp +++ b/include/sessions.hpp @@ -1,232 +1,284 @@ #pragma once -#include <nlohmann/json.hpp> -#include <pam_authenticate.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_map.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_io.hpp> +#include <nlohmann/json.hpp> +#include <pam_authenticate.hpp> +#include <random> +#include <webassets.hpp> -namespace crow { +namespace crow +{ -namespace persistent_data { +namespace persistent_data +{ -enum class PersistenceType { - TIMEOUT, // User session times out after a predetermined amount of time - SINGLE_REQUEST // User times out once this request is completed. +enum class PersistenceType +{ + TIMEOUT, // User session times out after a predetermined amount of time + SINGLE_REQUEST // User times out once this request is completed. }; -struct UserSession { - std::string uniqueId; - std::string sessionToken; - std::string username; - std::string csrfToken; - std::chrono::time_point<std::chrono::steady_clock> lastUpdated; - PersistenceType persistence; - - /** - * @brief Fills object with data from UserSession's JSON representation - * - * This replaces nlohmann's from_json to ensure no-throw approach - * - * @param[in] j JSON object from which data should be loaded - * - * @return a shared pointer if data has been loaded properly, nullptr - * otherwise - */ - static std::shared_ptr<UserSession> fromJson(const nlohmann::json& j) { - std::shared_ptr<UserSession> userSession = std::make_shared<UserSession>(); - for (const auto& element : j.items()) { - const std::string* thisValue = - element.value().get_ptr<const std::string*>(); - if (thisValue == nullptr) { - BMCWEB_LOG_ERROR << "Error reading persistent store. Property " - << element.key() << " was not of type string"; - return nullptr; - } - if (element.key() == "unique_id") { - userSession->uniqueId = *thisValue; - } else if (element.key() == "session_token") { - userSession->sessionToken = *thisValue; - } else if (element.key() == "csrf_token") { - userSession->csrfToken = *thisValue; - } else if (element.key() == "username") { - userSession->username = *thisValue; - } else { - BMCWEB_LOG_ERROR << "Got unexpected property reading persistent file: " - << element.key(); - return nullptr; - } - } +struct UserSession +{ + std::string uniqueId; + std::string sessionToken; + std::string username; + std::string csrfToken; + std::chrono::time_point<std::chrono::steady_clock> lastUpdated; + PersistenceType persistence; + + /** + * @brief Fills object with data from UserSession's JSON representation + * + * This replaces nlohmann's from_json to ensure no-throw approach + * + * @param[in] j JSON object from which data should be loaded + * + * @return a shared pointer if data has been loaded properly, nullptr + * otherwise + */ + static std::shared_ptr<UserSession> fromJson(const nlohmann::json& j) + { + std::shared_ptr<UserSession> userSession = + std::make_shared<UserSession>(); + for (const auto& element : j.items()) + { + const std::string* thisValue = + element.value().get_ptr<const std::string*>(); + if (thisValue == nullptr) + { + BMCWEB_LOG_ERROR << "Error reading persistent store. Property " + << element.key() << " was not of type string"; + return nullptr; + } + if (element.key() == "unique_id") + { + userSession->uniqueId = *thisValue; + } + else if (element.key() == "session_token") + { + userSession->sessionToken = *thisValue; + } + else if (element.key() == "csrf_token") + { + userSession->csrfToken = *thisValue; + } + else if (element.key() == "username") + { + userSession->username = *thisValue; + } + else + { + BMCWEB_LOG_ERROR + << "Got unexpected property reading persistent file: " + << element.key(); + return nullptr; + } + } - // For now, sessions that were persisted through a reboot get their idle - // timer reset. This could probably be overcome with a better understanding - // of wall clock time and steady timer time, possibly persisting values with - // wall clock time instead of steady timer, but the tradeoffs of all the - // corner cases involved are non-trivial, so this is done temporarily - userSession->lastUpdated = std::chrono::steady_clock::now(); - userSession->persistence = PersistenceType::TIMEOUT; + // For now, sessions that were persisted through a reboot get their idle + // timer reset. This could probably be overcome with a better + // understanding of wall clock time and steady timer time, possibly + // persisting values with wall clock time instead of steady timer, but + // the tradeoffs of all the corner cases involved are non-trivial, so + // this is done temporarily + userSession->lastUpdated = std::chrono::steady_clock::now(); + userSession->persistence = PersistenceType::TIMEOUT; - return userSession; - } + return userSession; + } }; class Middleware; -class SessionStore { - public: - std::shared_ptr<UserSession> generateUserSession( - const boost::string_view username, - PersistenceType persistence = PersistenceType::TIMEOUT) { - // TODO(ed) find a secure way to not generate session identifiers if - // persistence is set to SINGLE_REQUEST - static constexpr std::array<char, 62> alphanum = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'b', 'C', - 'D', 'E', 'F', 'g', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'r', 'S', 'T', 'U', 'v', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', - 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - - // entropy: 30 characters, 62 possibilities. log2(62^30) = 178 bits of - // entropy. OWASP recommends at least 60 - // https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Entropy - std::string sessionToken; - sessionToken.resize(20, '0'); - std::uniform_int_distribution<int> dist(0, alphanum.size() - 1); - for (int i = 0; i < sessionToken.size(); ++i) { - sessionToken[i] = alphanum[dist(rd)]; +class SessionStore +{ + public: + std::shared_ptr<UserSession> generateUserSession( + const boost::string_view username, + PersistenceType persistence = PersistenceType::TIMEOUT) + { + // TODO(ed) find a secure way to not generate session identifiers if + // persistence is set to SINGLE_REQUEST + static constexpr std::array<char, 62> alphanum = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'b', 'C', + 'D', 'E', 'F', 'g', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'r', 'S', 'T', 'U', 'v', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', + 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + // entropy: 30 characters, 62 possibilities. log2(62^30) = 178 bits of + // entropy. OWASP recommends at least 60 + // https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Session_ID_Entropy + std::string sessionToken; + sessionToken.resize(20, '0'); + std::uniform_int_distribution<int> dist(0, alphanum.size() - 1); + for (int i = 0; i < sessionToken.size(); ++i) + { + sessionToken[i] = alphanum[dist(rd)]; + } + // Only need csrf tokens for cookie based auth, token doesn't matter + std::string csrfToken; + csrfToken.resize(20, '0'); + for (int i = 0; i < csrfToken.size(); ++i) + { + csrfToken[i] = alphanum[dist(rd)]; + } + + std::string uniqueId; + uniqueId.resize(10, '0'); + for (int i = 0; i < uniqueId.size(); ++i) + { + uniqueId[i] = alphanum[dist(rd)]; + } + auto session = std::make_shared<UserSession>(UserSession{ + uniqueId, sessionToken, std::string(username), csrfToken, + std::chrono::steady_clock::now(), persistence}); + auto it = authTokens.emplace(std::make_pair(sessionToken, session)); + // Only need to write to disk if session isn't about to be destroyed. + needWrite = persistence == PersistenceType::TIMEOUT; + return it.first->second; } - // Only need csrf tokens for cookie based auth, token doesn't matter - std::string csrfToken; - csrfToken.resize(20, '0'); - for (int i = 0; i < csrfToken.size(); ++i) { - csrfToken[i] = alphanum[dist(rd)]; + + std::shared_ptr<UserSession> + loginSessionByToken(const boost::string_view token) + { + applySessionTimeouts(); + auto sessionIt = authTokens.find(std::string(token)); + if (sessionIt == authTokens.end()) + { + return nullptr; + } + std::shared_ptr<UserSession> userSession = sessionIt->second; + userSession->lastUpdated = std::chrono::steady_clock::now(); + return userSession; } - std::string uniqueId; - uniqueId.resize(10, '0'); - for (int i = 0; i < uniqueId.size(); ++i) { - uniqueId[i] = alphanum[dist(rd)]; + std::shared_ptr<UserSession> getSessionByUid(const boost::string_view uid) + { + applySessionTimeouts(); + // TODO(Ed) this is inefficient + auto sessionIt = authTokens.begin(); + while (sessionIt != authTokens.end()) + { + if (sessionIt->second->uniqueId == uid) + { + return sessionIt->second; + } + sessionIt++; + } + return nullptr; } - auto session = std::make_shared<UserSession>( - UserSession{uniqueId, sessionToken, std::string(username), csrfToken, - std::chrono::steady_clock::now(), persistence}); - auto it = authTokens.emplace(std::make_pair(sessionToken, session)); - // Only need to write to disk if session isn't about to be destroyed. - needWrite = persistence == PersistenceType::TIMEOUT; - return it.first->second; - } - - std::shared_ptr<UserSession> loginSessionByToken( - const boost::string_view token) { - applySessionTimeouts(); - auto sessionIt = authTokens.find(std::string(token)); - if (sessionIt == authTokens.end()) { - return nullptr; + + void removeSession(std::shared_ptr<UserSession> session) + { + authTokens.erase(session->sessionToken); + needWrite = true; } - std::shared_ptr<UserSession> userSession = sessionIt->second; - userSession->lastUpdated = std::chrono::steady_clock::now(); - return userSession; - } - - std::shared_ptr<UserSession> getSessionByUid(const boost::string_view uid) { - applySessionTimeouts(); - // TODO(Ed) this is inefficient - auto sessionIt = authTokens.begin(); - while (sessionIt != authTokens.end()) { - if (sessionIt->second->uniqueId == uid) { - return sessionIt->second; - } - sessionIt++; + + std::vector<const std::string*> getUniqueIds( + bool getAll = true, + const PersistenceType& type = PersistenceType::SINGLE_REQUEST) + { + applySessionTimeouts(); + + std::vector<const std::string*> ret; + ret.reserve(authTokens.size()); + for (auto& session : authTokens) + { + if (getAll || type == session.second->persistence) + { + ret.push_back(&session.second->uniqueId); + } + } + return ret; + } + + bool needsWrite() + { + return needWrite; + } + int getTimeoutInSeconds() const + { + return std::chrono::seconds(timeoutInMinutes).count(); + }; + + // Persistent data middleware needs to be able to serialize our authTokens + // structure, which is private + friend Middleware; + + static SessionStore& getInstance() + { + static SessionStore sessionStore; + return sessionStore; } - return nullptr; - } - - void removeSession(std::shared_ptr<UserSession> session) { - authTokens.erase(session->sessionToken); - needWrite = true; - } - - std::vector<const std::string*> getUniqueIds( - bool getAll = true, - const PersistenceType& type = PersistenceType::SINGLE_REQUEST) { - applySessionTimeouts(); - - std::vector<const std::string*> ret; - ret.reserve(authTokens.size()); - for (auto& session : authTokens) { - if (getAll || type == session.second->persistence) { - ret.push_back(&session.second->uniqueId); - } + + SessionStore(const SessionStore&) = delete; + SessionStore& operator=(const SessionStore&) = delete; + + private: + SessionStore() : timeoutInMinutes(60) + { } - return ret; - } - - bool needsWrite() { return needWrite; } - int getTimeoutInSeconds() const { - return std::chrono::seconds(timeoutInMinutes).count(); - }; - - // Persistent data middleware needs to be able to serialize our authTokens - // structure, which is private - friend Middleware; - - static SessionStore& getInstance() { - static SessionStore sessionStore; - return sessionStore; - } - - SessionStore(const SessionStore&) = delete; - SessionStore& operator=(const SessionStore&) = delete; - - private: - SessionStore() : timeoutInMinutes(60) {} - - void applySessionTimeouts() { - auto timeNow = std::chrono::steady_clock::now(); - if (timeNow - lastTimeoutUpdate > std::chrono::minutes(1)) { - lastTimeoutUpdate = timeNow; - auto authTokensIt = authTokens.begin(); - while (authTokensIt != authTokens.end()) { - if (timeNow - authTokensIt->second->lastUpdated >= timeoutInMinutes) { - authTokensIt = authTokens.erase(authTokensIt); - needWrite = true; - } else { - authTokensIt++; + + void applySessionTimeouts() + { + auto timeNow = std::chrono::steady_clock::now(); + if (timeNow - lastTimeoutUpdate > std::chrono::minutes(1)) + { + lastTimeoutUpdate = timeNow; + auto authTokensIt = authTokens.begin(); + while (authTokensIt != authTokens.end()) + { + if (timeNow - authTokensIt->second->lastUpdated >= + timeoutInMinutes) + { + authTokensIt = authTokens.erase(authTokensIt); + needWrite = true; + } + else + { + authTokensIt++; + } + } } - } } - } - std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate; - boost::container::flat_map<std::string, std::shared_ptr<UserSession>> - authTokens; - std::random_device rd; - bool needWrite{false}; - std::chrono::minutes timeoutInMinutes; + std::chrono::time_point<std::chrono::steady_clock> lastTimeoutUpdate; + boost::container::flat_map<std::string, std::shared_ptr<UserSession>> + authTokens; + std::random_device rd; + bool needWrite{false}; + std::chrono::minutes timeoutInMinutes; }; -} // namespace persistent_data -} // namespace crow +} // namespace persistent_data +} // namespace crow // to_json(...) definition for objects of UserSession type -namespace nlohmann { +namespace nlohmann +{ template <> -struct adl_serializer<std::shared_ptr<crow::persistent_data::UserSession>> { - static void to_json( - nlohmann::json& j, - const std::shared_ptr<crow::persistent_data::UserSession>& p) { - if (p->persistence != - crow::persistent_data::PersistenceType::SINGLE_REQUEST) { - j = nlohmann::json{{"unique_id", p->uniqueId}, - {"session_token", p->sessionToken}, - {"username", p->username}, - {"csrf_token", p->csrfToken}}; +struct adl_serializer<std::shared_ptr<crow::persistent_data::UserSession>> +{ + static void + to_json(nlohmann::json& j, + const std::shared_ptr<crow::persistent_data::UserSession>& p) + { + if (p->persistence != + crow::persistent_data::PersistenceType::SINGLE_REQUEST) + { + j = nlohmann::json{{"unique_id", p->uniqueId}, + {"session_token", p->sessionToken}, + {"username", p->username}, + {"csrf_token", p->csrfToken}}; + } } - } }; -} // namespace nlohmann +} // namespace nlohmann diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp index 4eac803..47893bf 100644 --- a/include/ssl_key_handler.hpp +++ b/include/ssl_key_handler.hpp @@ -10,304 +10,355 @@ #include <openssl/rand.h> #include <openssl/rsa.h> #include <openssl/ssl.h> -#include <random> + #include <boost/asio.hpp> +#include <random> -namespace ensuressl { +namespace ensuressl +{ static void initOpenssl(); static void cleanupOpenssl(); static EVP_PKEY *createRsaKey(); static EVP_PKEY *createEcKey(); static void handleOpensslError(); -inline bool verifyOpensslKeyCert(const std::string &filepath) { - bool privateKeyValid = false; - bool certValid = false; - - std::cout << "Checking certs in file " << filepath << "\n"; - - FILE *file = fopen(filepath.c_str(), "r"); - if (file != NULL) { - EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); - int rc; - if (pkey != nullptr) { - RSA *rsa = EVP_PKEY_get1_RSA(pkey); - if (rsa != nullptr) { - std::cout << "Found an RSA key\n"; - if (RSA_check_key(rsa) == 1) { - // private_key_valid = true; - } else { - std::cerr << "Key not valid error number " << ERR_get_error() << "\n"; - } - RSA_free(rsa); - } else { - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); - if (ec != nullptr) { - std::cout << "Found an EC key\n"; - if (EC_KEY_check_key(ec) == 1) { - privateKeyValid = true; - } else { - std::cerr << "Key not valid error number " << ERR_get_error() - << "\n"; - } - EC_KEY_free(ec); - } - } - - if (privateKeyValid) { - X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); - if (x509 == nullptr) { - std::cout << "error getting x509 cert " << ERR_get_error() << "\n"; - } else { - rc = X509_verify(x509, pkey); - if (rc == 1) { - certValid = true; - } else { - std::cerr << "Error in verifying private key signature " - << ERR_get_error() << "\n"; - } +inline bool verifyOpensslKeyCert(const std::string &filepath) +{ + bool privateKeyValid = false; + bool certValid = false; + + std::cout << "Checking certs in file " << filepath << "\n"; + + FILE *file = fopen(filepath.c_str(), "r"); + if (file != NULL) + { + EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); + int rc; + if (pkey != nullptr) + { + RSA *rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa != nullptr) + { + std::cout << "Found an RSA key\n"; + if (RSA_check_key(rsa) == 1) + { + // private_key_valid = true; + } + else + { + std::cerr << "Key not valid error number " + << ERR_get_error() << "\n"; + } + RSA_free(rsa); + } + else + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey); + if (ec != nullptr) + { + std::cout << "Found an EC key\n"; + if (EC_KEY_check_key(ec) == 1) + { + privateKeyValid = true; + } + else + { + std::cerr << "Key not valid error number " + << ERR_get_error() << "\n"; + } + EC_KEY_free(ec); + } + } + + if (privateKeyValid) + { + X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL); + if (x509 == nullptr) + { + std::cout << "error getting x509 cert " << ERR_get_error() + << "\n"; + } + else + { + rc = X509_verify(x509, pkey); + if (rc == 1) + { + certValid = true; + } + else + { + std::cerr << "Error in verifying private key signature " + << ERR_get_error() << "\n"; + } + } + } + + EVP_PKEY_free(pkey); } - } - - EVP_PKEY_free(pkey); + fclose(file); } - fclose(file); - } - return certValid; + return certValid; } -inline void generateSslCertificate(const std::string &filepath) { - FILE *pFile = NULL; - std::cout << "Generating new keys\n"; - initOpenssl(); - - // std::cerr << "Generating RSA key"; - // EVP_PKEY *pRsaPrivKey = create_rsa_key(); - - std::cerr << "Generating EC key\n"; - EVP_PKEY *pRsaPrivKey = createEcKey(); - if (pRsaPrivKey != nullptr) { - std::cerr << "Generating x509 Certificate\n"; - // Use this code to directly generate a certificate - X509 *x509; - x509 = X509_new(); - if (x509 != nullptr) { - // get a random number from the RNG for the certificate serial number - // If this is not random, regenerating certs throws broswer errors - std::random_device rd; - int serial = rd(); - - ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); - - // not before this moment - X509_gmtime_adj(X509_get_notBefore(x509), 0); - // Cert is valid for 10 years - X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L); - - // set the public key to the key we just generated - X509_set_pubkey(x509, pRsaPrivKey); - - // get the subject name - X509_NAME *name; - name = X509_get_subject_name(x509); - - X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("US"), - -1, -1, 0); - X509_NAME_add_entry_by_txt( - name, "O", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, 0); - X509_NAME_add_entry_by_txt( - name, "CN", MBSTRING_ASC, - reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0); - // set the CSR options - X509_set_issuer_name(x509, name); - - // Sign the certificate with our private key - X509_sign(x509, pRsaPrivKey, EVP_sha256()); - - pFile = fopen(filepath.c_str(), "wt"); - - if (pFile != nullptr) { - PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL); - - PEM_write_X509(pFile, x509); - fclose(pFile); - pFile = NULL; - } - - X509_free(x509); - } +inline void generateSslCertificate(const std::string &filepath) +{ + FILE *pFile = NULL; + std::cout << "Generating new keys\n"; + initOpenssl(); + + // std::cerr << "Generating RSA key"; + // EVP_PKEY *pRsaPrivKey = create_rsa_key(); + + std::cerr << "Generating EC key\n"; + EVP_PKEY *pRsaPrivKey = createEcKey(); + if (pRsaPrivKey != nullptr) + { + std::cerr << "Generating x509 Certificate\n"; + // Use this code to directly generate a certificate + X509 *x509; + x509 = X509_new(); + if (x509 != nullptr) + { + // get a random number from the RNG for the certificate serial + // number If this is not random, regenerating certs throws broswer + // errors + std::random_device rd; + int serial = rd(); + + ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); + + // not before this moment + X509_gmtime_adj(X509_get_notBefore(x509), 0); + // Cert is valid for 10 years + X509_gmtime_adj(X509_get_notAfter(x509), + 60L * 60L * 24L * 365L * 10L); + + // set the public key to the key we just generated + X509_set_pubkey(x509, pRsaPrivKey); + + // get the subject name + X509_NAME *name; + name = X509_get_subject_name(x509); + + X509_NAME_add_entry_by_txt( + name, "C", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("US"), -1, -1, 0); + X509_NAME_add_entry_by_txt( + name, "O", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, + 0); + X509_NAME_add_entry_by_txt( + name, "CN", MBSTRING_ASC, + reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0); + // set the CSR options + X509_set_issuer_name(x509, name); + + // Sign the certificate with our private key + X509_sign(x509, pRsaPrivKey, EVP_sha256()); + + pFile = fopen(filepath.c_str(), "wt"); + + if (pFile != nullptr) + { + PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, + NULL); + + PEM_write_X509(pFile, x509); + fclose(pFile); + pFile = NULL; + } + + X509_free(x509); + } - EVP_PKEY_free(pRsaPrivKey); - pRsaPrivKey = NULL; - } + EVP_PKEY_free(pRsaPrivKey); + pRsaPrivKey = NULL; + } - // cleanup_openssl(); + // cleanup_openssl(); } -EVP_PKEY *createRsaKey() { - RSA *pRSA = NULL; +EVP_PKEY *createRsaKey() +{ + RSA *pRSA = NULL; #if OPENSSL_VERSION_NUMBER < 0x00908000L - pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); + pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL); #else - RSA_generate_key_ex(pRSA, 2048, NULL, NULL); + RSA_generate_key_ex(pRSA, 2048, NULL, NULL); #endif - EVP_PKEY *pKey = EVP_PKEY_new(); - if ((pRSA != nullptr) && (pKey != nullptr) && - EVP_PKEY_assign_RSA(pKey, pRSA)) { - /* pKey owns pRSA from now */ - if (RSA_check_key(pRSA) <= 0) { - fprintf(stderr, "RSA_check_key failed.\n"); - handleOpensslError(); - EVP_PKEY_free(pKey); - pKey = NULL; - } - } else { - handleOpensslError(); - if (pRSA != nullptr) { - RSA_free(pRSA); - pRSA = NULL; + EVP_PKEY *pKey = EVP_PKEY_new(); + if ((pRSA != nullptr) && (pKey != nullptr) && + EVP_PKEY_assign_RSA(pKey, pRSA)) + { + /* pKey owns pRSA from now */ + if (RSA_check_key(pRSA) <= 0) + { + fprintf(stderr, "RSA_check_key failed.\n"); + handleOpensslError(); + EVP_PKEY_free(pKey); + pKey = NULL; + } } - if (pKey != nullptr) { - EVP_PKEY_free(pKey); - pKey = NULL; + else + { + handleOpensslError(); + if (pRSA != nullptr) + { + RSA_free(pRSA); + pRSA = NULL; + } + if (pKey != nullptr) + { + EVP_PKEY_free(pKey); + pKey = NULL; + } } - } - return pKey; + return pKey; } -EVP_PKEY *createEcKey() { - EVP_PKEY *pKey = NULL; - int eccgrp = 0; - eccgrp = OBJ_txt2nid("prime256v1"); - - EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); - if (myecc != nullptr) { - EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); - EC_KEY_generate_key(myecc); - pKey = EVP_PKEY_new(); - if (pKey != nullptr) { - if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) { - /* pKey owns pRSA from now */ - if (EC_KEY_check_key(myecc) <= 0) { - fprintf(stderr, "EC_check_key failed.\n"); +EVP_PKEY *createEcKey() +{ + EVP_PKEY *pKey = NULL; + int eccgrp = 0; + eccgrp = OBJ_txt2nid("prime256v1"); + + EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp); + if (myecc != nullptr) + { + EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE); + EC_KEY_generate_key(myecc); + pKey = EVP_PKEY_new(); + if (pKey != nullptr) + { + if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) + { + /* pKey owns pRSA from now */ + if (EC_KEY_check_key(myecc) <= 0) + { + fprintf(stderr, "EC_check_key failed.\n"); + } + } } - } } - } - return pKey; + return pKey; } -void initOpenssl() { +void initOpenssl() +{ #if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - RAND_load_file("/dev/urandom", 1024); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + RAND_load_file("/dev/urandom", 1024); #endif } -void cleanupOpenssl() { - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); +void cleanupOpenssl() +{ + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); #if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_thread_state(0); + ERR_remove_thread_state(0); #endif - EVP_cleanup(); + EVP_cleanup(); } -void handleOpensslError() { ERR_print_errors_fp(stderr); } -inline void ensureOpensslKeyPresentAndValid(const std::string &filepath) { - bool pemFileValid = false; +void handleOpensslError() +{ + ERR_print_errors_fp(stderr); +} +inline void ensureOpensslKeyPresentAndValid(const std::string &filepath) +{ + bool pemFileValid = false; - pemFileValid = verifyOpensslKeyCert(filepath); + pemFileValid = verifyOpensslKeyCert(filepath); - if (!pemFileValid) { - std::cerr << "Error in verifying signature, regenerating\n"; - generateSslCertificate(filepath); - } + if (!pemFileValid) + { + std::cerr << "Error in verifying signature, regenerating\n"; + generateSslCertificate(filepath); + } } -inline boost::asio::ssl::context getSslContext( - const std::string &ssl_pem_file) { - boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23}; - mSslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3 | - boost::asio::ssl::context::single_dh_use | - boost::asio::ssl::context::no_tlsv1 | - boost::asio::ssl::context::no_tlsv1_1); - - // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); - mSslContext.use_certificate_file(ssl_pem_file, - boost::asio::ssl::context::pem); - mSslContext.use_private_key_file(ssl_pem_file, - boost::asio::ssl::context::pem); - - // Set up EC curves to auto (boost asio doesn't have a method for this) - // There is a pull request to add this. Once this is included in an asio - // drop, use the right way - // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue - if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) { - BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n"; - } - - // From mozilla "compatibility" - std::string mozillaCompatibilityCiphers = - "ECDHE-ECDSA-CHACHA20-POLY1305:" - "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" - "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "DHE-RSA-AES128-GCM-SHA256:" - "DHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-AES128-SHA256:" - "ECDHE-RSA-AES128-SHA256:" - "ECDHE-ECDSA-AES128-SHA:" - "ECDHE-RSA-AES256-SHA384:" - "ECDHE-RSA-AES128-SHA:" - "ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-ECDSA-AES256-SHA:" - "ECDHE-RSA-AES256-SHA:" - "DHE-RSA-AES128-SHA256:" - "DHE-RSA-AES128-SHA:" - "DHE-RSA-AES256-SHA256:" - "DHE-RSA-AES256-SHA:" - "ECDHE-ECDSA-DES-CBC3-SHA:" - "ECDHE-RSA-DES-CBC3-SHA:" - "EDH-RSA-DES-CBC3-SHA:" - "AES128-GCM-SHA256:" - "AES256-GCM-SHA384:" - "AES128-SHA256:" - "AES256-SHA256:" - "AES128-SHA:" - "AES256-SHA:" - "DES-CBC3-SHA:" - "!DSS"; - - // From mozilla "modern" - std::string mozillaModernCiphers = - "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-CHACHA20-POLY1305:" - "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" - "ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA384:" - "ECDHE-ECDSA-AES128-SHA256:" - "ECDHE-RSA-AES128-SHA256"; - - std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; - - if (SSL_CTX_set_cipher_list(mSslContext.native_handle(), - mozillaCompatibilityCiphers.c_str()) != 1) { - BMCWEB_LOG_ERROR << "Error setting cipher list\n"; - } - return mSslContext; +inline boost::asio::ssl::context getSslContext(const std::string &ssl_pem_file) +{ + boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23}; + mSslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::single_dh_use | + boost::asio::ssl::context::no_tlsv1 | + boost::asio::ssl::context::no_tlsv1_1); + + // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer); + mSslContext.use_certificate_file(ssl_pem_file, + boost::asio::ssl::context::pem); + mSslContext.use_private_key_file(ssl_pem_file, + boost::asio::ssl::context::pem); + + // Set up EC curves to auto (boost asio doesn't have a method for this) + // There is a pull request to add this. Once this is included in an asio + // drop, use the right way + // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue + if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) + { + BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n"; + } + + // From mozilla "compatibility" + std::string mozillaCompatibilityCiphers = "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "DHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES128-SHA256:" + "ECDHE-RSA-AES128-SHA256:" + "ECDHE-ECDSA-AES128-SHA:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-RSA-AES128-SHA:" + "ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-ECDSA-AES256-SHA:" + "ECDHE-RSA-AES256-SHA:" + "DHE-RSA-AES128-SHA256:" + "DHE-RSA-AES128-SHA:" + "DHE-RSA-AES256-SHA256:" + "DHE-RSA-AES256-SHA:" + "ECDHE-ECDSA-DES-CBC3-SHA:" + "ECDHE-RSA-DES-CBC3-SHA:" + "EDH-RSA-DES-CBC3-SHA:" + "AES128-GCM-SHA256:" + "AES256-GCM-SHA384:" + "AES128-SHA256:" + "AES256-SHA256:" + "AES128-SHA:" + "AES256-SHA:" + "DES-CBC3-SHA:" + "!DSS"; + + // From mozilla "modern" + std::string mozillaModernCiphers = "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-ECDSA-AES128-SHA256:" + "ECDHE-RSA-AES128-SHA256"; + + std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL"; + + if (SSL_CTX_set_cipher_list(mSslContext.native_handle(), + mozillaCompatibilityCiphers.c_str()) != 1) + { + BMCWEB_LOG_ERROR << "Error setting cipher list\n"; + } + return mSslContext; } -} // namespace ensuressl +} // namespace ensuressl #endif
\ No newline at end of file diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp index 2e286e1..c419c97 100644 --- a/include/token_authorization_middleware.hpp +++ b/include/token_authorization_middleware.hpp @@ -1,359 +1,448 @@ #pragma once -#include <pam_authenticate.hpp> -#include <persistent_data_middleware.hpp> -#include <webassets.hpp> -#include <random> #include <crow/app.h> #include <crow/common.h> #include <crow/http_request.h> #include <crow/http_response.h> + #include <boost/container/flat_set.hpp> +#include <pam_authenticate.hpp> +#include <persistent_data_middleware.hpp> +#include <random> +#include <webassets.hpp> -namespace crow { +namespace crow +{ -namespace token_authorization { +namespace token_authorization +{ -class Middleware { - public: - struct Context { - std::shared_ptr<crow::persistent_data::UserSession> session; - }; +class Middleware +{ + public: + struct Context + { + std::shared_ptr<crow::persistent_data::UserSession> session; + }; - void beforeHandle(crow::Request& req, Response& res, Context& ctx) { - if (isOnWhitelist(req)) { - return; - } + void beforeHandle(crow::Request& req, Response& res, Context& ctx) + { + if (isOnWhitelist(req)) + { + return; + } - ctx.session = performXtokenAuth(req); - if (ctx.session == nullptr) { - ctx.session = performCookieAuth(req); - } - if (ctx.session == nullptr) { - boost::string_view authHeader = req.getHeaderValue("Authorization"); - if (!authHeader.empty()) { - // Reject any kind of auth other than basic or token - if (boost::starts_with(authHeader, "Token ")) { - ctx.session = performTokenAuth(authHeader); - } else if (boost::starts_with(authHeader, "Basic ")) { - ctx.session = performBasicAuth(authHeader); + ctx.session = performXtokenAuth(req); + if (ctx.session == nullptr) + { + ctx.session = performCookieAuth(req); } - } - } + if (ctx.session == nullptr) + { + boost::string_view authHeader = req.getHeaderValue("Authorization"); + if (!authHeader.empty()) + { + // Reject any kind of auth other than basic or token + if (boost::starts_with(authHeader, "Token ")) + { + ctx.session = performTokenAuth(authHeader); + } + else if (boost::starts_with(authHeader, "Basic ")) + { + ctx.session = performBasicAuth(authHeader); + } + } + } + + if (ctx.session == nullptr) + { + BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed"; + + // If it's a browser connecting, don't send the HTTP authenticate + // header, to avoid possible CSRF attacks with basic auth + if (http_helpers::requestPrefersHtml(req)) + { + res.result(boost::beast::http::status::temporary_redirect); + res.addHeader("Location", "/#/login"); + } + else + { + res.result(boost::beast::http::status::unauthorized); + // only send the WWW-authenticate header if this isn't a xhr + // from the browser. most scripts, + if (req.getHeaderValue("User-Agent").empty()) + { + res.addHeader("WWW-Authenticate", "Basic"); + } + } - if (ctx.session == nullptr) { - BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed"; - - // If it's a browser connecting, don't send the HTTP authenticate header, - // to avoid possible CSRF attacks with basic auth - if (http_helpers::requestPrefersHtml(req)) { - res.result(boost::beast::http::status::temporary_redirect); - res.addHeader("Location", "/#/login"); - } else { - res.result(boost::beast::http::status::unauthorized); - // only send the WWW-authenticate header if this isn't a xhr from the - // browser. most scripts, - if (req.getHeaderValue("User-Agent").empty()) { - res.addHeader("WWW-Authenticate", "Basic"); + res.end(); + return; } - } - res.end(); - return; + // TODO get user privileges here and propagate it via MW Context + // else let the request continue unharmed } - // TODO get user privileges here and propagate it via MW Context - // else let the request continue unharmed - } - - template <typename AllContext> - void afterHandle(Request& req, Response& res, Context& ctx, - AllContext& allctx) { - // TODO(ed) THis should really be handled by the persistent data - // middleware, but because it is upstream, it doesn't have access to the - // session information. Should the data middleware persist the current - // user session? - if (ctx.session != nullptr && - ctx.session->persistence == - crow::persistent_data::PersistenceType::SINGLE_REQUEST) { - persistent_data::SessionStore::getInstance().removeSession(ctx.session); + template <typename AllContext> + void afterHandle(Request& req, Response& res, Context& ctx, + AllContext& allctx) + { + // TODO(ed) THis should really be handled by the persistent data + // middleware, but because it is upstream, it doesn't have access to the + // session information. Should the data middleware persist the current + // user session? + if (ctx.session != nullptr && + ctx.session->persistence == + crow::persistent_data::PersistenceType::SINGLE_REQUEST) + { + persistent_data::SessionStore::getInstance().removeSession( + ctx.session); + } } - } - private: - const std::shared_ptr<crow::persistent_data::UserSession> performBasicAuth( - boost::string_view auth_header) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication"; + private: + const std::shared_ptr<crow::persistent_data::UserSession> + performBasicAuth(boost::string_view auth_header) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication"; + + std::string authData; + boost::string_view param = auth_header.substr(strlen("Basic ")); + if (!crow::utility::base64Decode(param, authData)) + { + return nullptr; + } + std::size_t separator = authData.find(':'); + if (separator == std::string::npos) + { + return nullptr; + } - std::string authData; - boost::string_view param = auth_header.substr(strlen("Basic ")); - if (!crow::utility::base64Decode(param, authData)) { - return nullptr; - } - std::size_t separator = authData.find(':'); - if (separator == std::string::npos) { - return nullptr; - } + std::string user = authData.substr(0, separator); + separator += 1; + if (separator > authData.size()) + { + return nullptr; + } + std::string pass = authData.substr(separator); - std::string user = authData.substr(0, separator); - separator += 1; - if (separator > authData.size()) { - return nullptr; - } - std::string pass = authData.substr(separator); + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user; - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user; + if (!pamAuthenticateUser(user, pass)) + { + return nullptr; + } - if (!pamAuthenticateUser(user, pass)) { - return nullptr; + // TODO(ed) generateUserSession is a little expensive for basic + // auth, as it generates some random identifiers that will never be + // used. This should have a "fast" path for when user tokens aren't + // needed. + // This whole flow needs to be revisited anyway, as we can't be + // calling directly into pam for every request + return persistent_data::SessionStore::getInstance().generateUserSession( + user, crow::persistent_data::PersistenceType::SINGLE_REQUEST); } - // TODO(ed) generateUserSession is a little expensive for basic - // auth, as it generates some random identifiers that will never be - // used. This should have a "fast" path for when user tokens aren't - // needed. - // This whole flow needs to be revisited anyway, as we can't be - // calling directly into pam for every request - return persistent_data::SessionStore::getInstance().generateUserSession( - user, crow::persistent_data::PersistenceType::SINGLE_REQUEST); - } - - const std::shared_ptr<crow::persistent_data::UserSession> performTokenAuth( - boost::string_view auth_header) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication"; - - boost::string_view token = auth_header.substr(strlen("Token ")); - auto session = - persistent_data::SessionStore::getInstance().loginSessionByToken(token); - return session; - } - - const std::shared_ptr<crow::persistent_data::UserSession> performXtokenAuth( - const crow::Request& req) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication"; - - boost::string_view token = req.getHeaderValue("X-Auth-Token"); - if (token.empty()) { - return nullptr; - } - auto session = - persistent_data::SessionStore::getInstance().loginSessionByToken(token); - return session; - } - - const std::shared_ptr<crow::persistent_data::UserSession> performCookieAuth( - const crow::Request& req) const { - BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication"; - - boost::string_view cookieValue = req.getHeaderValue("Cookie"); - if (cookieValue.empty()) { - return nullptr; - } + const std::shared_ptr<crow::persistent_data::UserSession> + performTokenAuth(boost::string_view auth_header) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication"; - auto startIndex = cookieValue.find("SESSION="); - if (startIndex == std::string::npos) { - return nullptr; + boost::string_view token = auth_header.substr(strlen("Token ")); + auto session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + token); + return session; } - startIndex += sizeof("SESSION=") - 1; - auto endIndex = cookieValue.find(";", startIndex); - if (endIndex == std::string::npos) { - endIndex = cookieValue.size(); - } - boost::string_view authKey = - cookieValue.substr(startIndex, endIndex - startIndex); - - const std::shared_ptr<crow::persistent_data::UserSession> session = - persistent_data::SessionStore::getInstance().loginSessionByToken( - authKey); - if (session == nullptr) { - return nullptr; + + const std::shared_ptr<crow::persistent_data::UserSession> + performXtokenAuth(const crow::Request& req) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication"; + + boost::string_view token = req.getHeaderValue("X-Auth-Token"); + if (token.empty()) + { + return nullptr; + } + auto session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + token); + return session; } + + const std::shared_ptr<crow::persistent_data::UserSession> + performCookieAuth(const crow::Request& req) const + { + BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication"; + + boost::string_view cookieValue = req.getHeaderValue("Cookie"); + if (cookieValue.empty()) + { + return nullptr; + } + + auto startIndex = cookieValue.find("SESSION="); + if (startIndex == std::string::npos) + { + return nullptr; + } + startIndex += sizeof("SESSION=") - 1; + auto endIndex = cookieValue.find(";", startIndex); + if (endIndex == std::string::npos) + { + endIndex = cookieValue.size(); + } + boost::string_view authKey = + cookieValue.substr(startIndex, endIndex - startIndex); + + const std::shared_ptr<crow::persistent_data::UserSession> session = + persistent_data::SessionStore::getInstance().loginSessionByToken( + authKey); + if (session == nullptr) + { + return nullptr; + } #ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION - // RFC7231 defines methods that need csrf protection - if (req.method() != "GET"_method) { - boost::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN"); - // Make sure both tokens are filled - if (csrf.empty() || session->csrfToken.empty()) { - return nullptr; - } - // Reject if csrf token not available - if (csrf != session->csrfToken) { - return nullptr; - } - } + // RFC7231 defines methods that need csrf protection + if (req.method() != "GET"_method) + { + boost::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN"); + // Make sure both tokens are filled + if (csrf.empty() || session->csrfToken.empty()) + { + return nullptr; + } + // Reject if csrf token not available + if (csrf != session->csrfToken) + { + return nullptr; + } + } #endif - return session; - } - - // checks if request can be forwarded without authentication - bool isOnWhitelist(const crow::Request& req) const { - // it's allowed to GET root node without authentica tion - if ("GET"_method == req.method()) { - if (req.url == "/redfish/v1" || req.url == "/redfish/v1/" || - req.url == "/redfish" || req.url == "/redfish/" || - req.url == "/redfish/v1/odata" || req.url == "/redfish/v1/odata/") { - return true; - } else if (crow::webassets::routes.find(std::string(req.url)) != - crow::webassets::routes.end()) { - return true; - } + return session; } - // it's allowed to POST on session collection & login without - // authentication - if ("POST"_method == req.method()) { - if ((req.url == "/redfish/v1/SessionService/Sessions") || - (req.url == "/redfish/v1/SessionService/Sessions/") || - (req.url == "/login")) { - return true; - } - } + // checks if request can be forwarded without authentication + bool isOnWhitelist(const crow::Request& req) const + { + // it's allowed to GET root node without authentica tion + if ("GET"_method == req.method()) + { + if (req.url == "/redfish/v1" || req.url == "/redfish/v1/" || + req.url == "/redfish" || req.url == "/redfish/" || + req.url == "/redfish/v1/odata" || + req.url == "/redfish/v1/odata/") + { + return true; + } + else if (crow::webassets::routes.find(std::string(req.url)) != + crow::webassets::routes.end()) + { + return true; + } + } + + // it's allowed to POST on session collection & login without + // authentication + if ("POST"_method == req.method()) + { + if ((req.url == "/redfish/v1/SessionService/Sessions") || + (req.url == "/redfish/v1/SessionService/Sessions/") || + (req.url == "/login")) + { + return true; + } + } - return false; - } + return false; + } }; // TODO(ed) see if there is a better way to allow middlewares to request // routes. // Possibly an init function on first construction? -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - static_assert( - black_magic::Contains<persistent_data::Middleware, Middlewares...>::value, - "token_authorization middleware must be enabled in app to use " - "auth routes"); - BMCWEB_ROUTE(app, "/login") - .methods( - "POST"_method)([&](const crow::Request& req, crow::Response& res) { - boost::string_view contentType = req.getHeaderValue("content-type"); - boost::string_view username; - boost::string_view password; - - bool looksLikeIbm = false; - - // This object needs to be declared at this scope so the strings - // within it are not destroyed before we can use them - nlohmann::json loginCredentials; - // Check if auth was provided by a payload - if (contentType == "application/json") { - loginCredentials = nlohmann::json::parse(req.body, nullptr, false); - if (loginCredentials.is_discarded()) { - res.result(boost::beast::http::status::bad_request); - res.end(); - return; - } - - // check for username/password in the root object - // THis method is how intel APIs authenticate - nlohmann::json::iterator userIt = loginCredentials.find("username"); - nlohmann::json::iterator passIt = loginCredentials.find("password"); - if (userIt != loginCredentials.end() && - passIt != loginCredentials.end()) { - const std::string* userStr = userIt->get_ptr<const std::string*>(); - const std::string* passStr = passIt->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; - } - } else { - // Openbmc appears to push a data object that contains the same - // keys (username and password), attempt to use that - auto dataIt = loginCredentials.find("data"); - if (dataIt != loginCredentials.end()) { - // Some apis produce an array of value ["username", - // "password"] - if (dataIt->is_array()) { - if (dataIt->size() == 2) { - nlohmann::json::iterator userIt2 = dataIt->begin(); - nlohmann::json::iterator passIt2 = dataIt->begin() + 1; - looksLikeIbm = true; - if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) { +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + static_assert( + black_magic::Contains<persistent_data::Middleware, + Middlewares...>::value, + "token_authorization middleware must be enabled in app to use " + "auth routes"); + BMCWEB_ROUTE(app, "/login") + .methods( + "POST"_method)([&](const crow::Request& req, crow::Response& res) { + boost::string_view contentType = req.getHeaderValue("content-type"); + boost::string_view username; + boost::string_view password; + + bool looksLikeIbm = false; + + // This object needs to be declared at this scope so the strings + // within it are not destroyed before we can use them + nlohmann::json loginCredentials; + // Check if auth was provided by a payload + if (contentType == "application/json") + { + loginCredentials = + nlohmann::json::parse(req.body, nullptr, false); + if (loginCredentials.is_discarded()) + { + res.result(boost::beast::http::status::bad_request); + res.end(); + return; + } + + // check for username/password in the root object + // THis method is how intel APIs authenticate + nlohmann::json::iterator userIt = + loginCredentials.find("username"); + nlohmann::json::iterator passIt = + loginCredentials.find("password"); + if (userIt != loginCredentials.end() && + passIt != loginCredentials.end()) + { const std::string* userStr = - userIt2->get_ptr<const std::string*>(); + userIt->get_ptr<const std::string*>(); const std::string* passStr = - passIt2->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; + passIt->get_ptr<const std::string*>(); + if (userStr != nullptr && passStr != nullptr) + { + username = *userStr; + password = *passStr; } - } } - - } else if (dataIt->is_object()) { - nlohmann::json::iterator userIt2 = dataIt->find("username"); - nlohmann::json::iterator passIt2 = dataIt->find("password"); - if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) { - const std::string* userStr = - userIt2->get_ptr<const std::string*>(); - const std::string* passStr = - passIt2->get_ptr<const std::string*>(); - if (userStr != nullptr && passStr != nullptr) { - username = *userStr; - password = *passStr; - } + else + { + // Openbmc appears to push a data object that contains the + // same keys (username and password), attempt to use that + auto dataIt = loginCredentials.find("data"); + if (dataIt != loginCredentials.end()) + { + // Some apis produce an array of value ["username", + // "password"] + if (dataIt->is_array()) + { + if (dataIt->size() == 2) + { + nlohmann::json::iterator userIt2 = + dataIt->begin(); + nlohmann::json::iterator passIt2 = + dataIt->begin() + 1; + looksLikeIbm = true; + if (userIt2 != dataIt->end() && + passIt2 != dataIt->end()) + { + const std::string* userStr = + userIt2->get_ptr<const std::string*>(); + const std::string* passStr = + passIt2->get_ptr<const std::string*>(); + if (userStr != nullptr && + passStr != nullptr) + { + username = *userStr; + password = *passStr; + } + } + } + } + else if (dataIt->is_object()) + { + nlohmann::json::iterator userIt2 = + dataIt->find("username"); + nlohmann::json::iterator passIt2 = + dataIt->find("password"); + if (userIt2 != dataIt->end() && + passIt2 != dataIt->end()) + { + const std::string* userStr = + userIt2->get_ptr<const std::string*>(); + const std::string* passStr = + passIt2->get_ptr<const std::string*>(); + if (userStr != nullptr && passStr != nullptr) + { + username = *userStr; + password = *passStr; + } + } + } + } } - } } - } - } else { - // check if auth was provided as a headers - username = req.getHeaderValue("username"); - password = req.getHeaderValue("password"); - } - - if (!username.empty() && !password.empty()) { - if (!pamAuthenticateUser(username, password)) { - res.result(boost::beast::http::status::unauthorized); - } else { - auto session = persistent_data::SessionStore::getInstance() - .generateUserSession(username); - - if (looksLikeIbm) { - // IBM requires a very specific login structure, and doesn't - // actually look at the status code. TODO(ed).... Fix that - // upstream - res.jsonValue = { - {"data", "User '" + std::string(username) + "' logged in"}, - {"message", "200 OK"}, - {"status", "ok"}}; - - // Hack alert. Boost beast by default doesn't let you declare - // multiple headers of the same name, and in most cases this is - // fine. Unfortunately here we need to set the Session cookie, - // which requires the httpOnly attribute, as well as the XSRF - // cookie, which requires it to not have an httpOnly attribute. - // To get the behavior we want, we simply inject the second - // "set-cookie" string into the value header, and get the result - // we want, even though we are technicaly declaring two headers - // here. - res.addHeader("Set-Cookie", - "XSRF-TOKEN=" + session->csrfToken + - "; Secure\r\nSet-Cookie: SESSION=" + - session->sessionToken + "; Secure; HttpOnly"); - } else { - // if content type is json, assume json token - res.jsonValue = {{"token", session->sessionToken}}; + else + { + // check if auth was provided as a headers + username = req.getHeaderValue("username"); + password = req.getHeaderValue("password"); } - } - } else { - res.result(boost::beast::http::status::bad_request); - } - res.end(); - }); - - BMCWEB_ROUTE(app, "/logout") - .methods( - "POST"_method)([&](const crow::Request& req, crow::Response& res) { - auto& session = - app.template getContext<token_authorization::Middleware>(req) - .session; - if (session != nullptr) { - persistent_data::SessionStore::getInstance().removeSession(session); - } - res.end(); - return; - }); + if (!username.empty() && !password.empty()) + { + if (!pamAuthenticateUser(username, password)) + { + res.result(boost::beast::http::status::unauthorized); + } + else + { + auto session = persistent_data::SessionStore::getInstance() + .generateUserSession(username); + + if (looksLikeIbm) + { + // IBM requires a very specific login structure, and + // doesn't actually look at the status code. + // TODO(ed).... Fix that upstream + res.jsonValue = { + {"data", + "User '" + std::string(username) + "' logged in"}, + {"message", "200 OK"}, + {"status", "ok"}}; + + // Hack alert. Boost beast by default doesn't let you + // declare multiple headers of the same name, and in + // most cases this is fine. Unfortunately here we need + // to set the Session cookie, which requires the + // httpOnly attribute, as well as the XSRF cookie, which + // requires it to not have an httpOnly attribute. To get + // the behavior we want, we simply inject the second + // "set-cookie" string into the value header, and get + // the result we want, even though we are technicaly + // declaring two headers here. + res.addHeader("Set-Cookie", + "XSRF-TOKEN=" + session->csrfToken + + "; Secure\r\nSet-Cookie: SESSION=" + + session->sessionToken + + "; Secure; HttpOnly"); + } + else + { + // if content type is json, assume json token + res.jsonValue = {{"token", session->sessionToken}}; + } + } + } + else + { + res.result(boost::beast::http::status::bad_request); + } + res.end(); + }); + + BMCWEB_ROUTE(app, "/logout") + .methods( + "POST"_method)([&](const crow::Request& req, crow::Response& res) { + auto& session = + app.template getContext<token_authorization::Middleware>(req) + .session; + if (session != nullptr) + { + persistent_data::SessionStore::getInstance().removeSession( + session); + } + res.end(); + return; + }); } -} // namespace token_authorization -} // namespace crow +} // namespace token_authorization +} // namespace crow diff --git a/include/web_kvm.hpp b/include/web_kvm.hpp index ad4b352..747a137 100644 --- a/include/web_kvm.hpp +++ b/include/web_kvm.hpp @@ -1,181 +1,202 @@ -#include <string> #include <crow/app.h> -#include <boost/endian/arithmetic.hpp> #include <ast_jpeg_decoder.hpp> #include <ast_video_puller.hpp> +#include <boost/endian/arithmetic.hpp> +#include <string> -namespace crow { -namespace kvm { +namespace crow +{ +namespace kvm +{ static const std::string rfb33VersionString = "RFB 003.003\n"; static const std::string rfb37VersionString = "RFB 003.007\n"; static const std::string rfb38VersionString = "RFB 003.008\n"; -enum class RfbAuthScheme : uint8_t { - connection_failed = 0, - no_authentication = 1, - vnc_authentication = 2 +enum class RfbAuthScheme : uint8_t +{ + connection_failed = 0, + no_authentication = 1, + vnc_authentication = 2 }; -struct PixelFormatStruct { - boost::endian::big_uint8_t bitsPerPixel; - boost::endian::big_uint8_t depth; - boost::endian::big_uint8_t isBigEndian; - boost::endian::big_uint8_t isTrueColor; - boost::endian::big_uint16_t redMax; - boost::endian::big_uint16_t greenMax; - boost::endian::big_uint16_t blueMax; - boost::endian::big_uint8_t redShift; - boost::endian::big_uint8_t greenShift; - boost::endian::big_uint8_t blueShift; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; +struct PixelFormatStruct +{ + boost::endian::big_uint8_t bitsPerPixel; + boost::endian::big_uint8_t depth; + boost::endian::big_uint8_t isBigEndian; + boost::endian::big_uint8_t isTrueColor; + boost::endian::big_uint16_t redMax; + boost::endian::big_uint16_t greenMax; + boost::endian::big_uint16_t blueMax; + boost::endian::big_uint8_t redShift; + boost::endian::big_uint8_t greenShift; + boost::endian::big_uint8_t blueShift; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; }; -struct ServerInitializationMsg { - boost::endian::big_uint16_t framebufferWidth; - boost::endian::big_uint16_t framebufferHeight; - PixelFormatStruct pixelFormat; - boost::endian::big_uint32_t nameLength; +struct ServerInitializationMsg +{ + boost::endian::big_uint16_t framebufferWidth; + boost::endian::big_uint16_t framebufferHeight; + PixelFormatStruct pixelFormat; + boost::endian::big_uint32_t nameLength; }; -enum class client_to_server_msg_type : uint8_t { - set_pixel_format = 0, - fix_color_map_entries = 1, - set_encodings = 2, - framebuffer_update_request = 3, - key_event = 4, - pointer_event = 5, - client_cut_text = 6 +enum class client_to_server_msg_type : uint8_t +{ + set_pixel_format = 0, + fix_color_map_entries = 1, + set_encodings = 2, + framebuffer_update_request = 3, + key_event = 4, + pointer_event = 5, + client_cut_text = 6 }; -enum class server_to_client_message_type : uint8_t { - framebuffer_update = 0, - set_color_map_entries = 1, - bell_message = 2, - server_cut_text = 3 +enum class server_to_client_message_type : uint8_t +{ + framebuffer_update = 0, + set_color_map_entries = 1, + bell_message = 2, + server_cut_text = 3 }; -struct SetPixelFormatMsg { - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; - PixelFormatStruct pixelFormat; +struct SetPixelFormatMsg +{ + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; + PixelFormatStruct pixelFormat; }; -struct FrameBufferUpdateReq { - boost::endian::big_uint8_t incremental; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; - boost::endian::big_uint16_t width; - boost::endian::big_uint16_t height; +struct FrameBufferUpdateReq +{ + boost::endian::big_uint8_t incremental; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; + boost::endian::big_uint16_t width; + boost::endian::big_uint16_t height; }; -struct KeyEventMsg { - boost::endian::big_uint8_t downFlag; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint32_t key; +struct KeyEventMsg +{ + boost::endian::big_uint8_t downFlag; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint32_t key; }; -struct PointerEventMsg { - boost::endian::big_uint8_t buttonMask; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; +struct PointerEventMsg +{ + boost::endian::big_uint8_t buttonMask; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; }; -struct ClientCutTextMsg { - std::vector<uint8_t> data; +struct ClientCutTextMsg +{ + std::vector<uint8_t> data; }; -enum class encoding_type : uint32_t { - raw = 0x00, - copy_rectangle = 0x01, - rising_rectangle = 0x02, - corre = 0x04, - hextile = 0x05, - zlib = 0x06, - tight = 0x07, - zlibhex = 0x08, - ultra = 0x09, - zrle = 0x10, - zywrle = 0x011, - cache_enable = 0xFFFF0001, - xor_enable = 0xFFFF0006, - server_state_ultranvc = 0xFFFF8000, - enable_keepAlive = 0xFFFF8001, - enableftp_protocol_version = 0xFFFF8002, - tight_compress_level_0 = 0xFFFFFF00, - tight_compress_level_9 = 0xFFFFFF09, - x_cursor = 0xFFFFFF10, - rich_cursor = 0xFFFFFF11, - pointer_pos = 0xFFFFFF18, - last_rect = 0xFFFFFF20, - new_framebuffer_size = 0xFFFFFF21, - tight_quality_level_0 = 0xFFFFFFE0, - tight_quality_level_9 = 0xFFFFFFE9 +enum class encoding_type : uint32_t +{ + raw = 0x00, + copy_rectangle = 0x01, + rising_rectangle = 0x02, + corre = 0x04, + hextile = 0x05, + zlib = 0x06, + tight = 0x07, + zlibhex = 0x08, + ultra = 0x09, + zrle = 0x10, + zywrle = 0x011, + cache_enable = 0xFFFF0001, + xor_enable = 0xFFFF0006, + server_state_ultranvc = 0xFFFF8000, + enable_keepAlive = 0xFFFF8001, + enableftp_protocol_version = 0xFFFF8002, + tight_compress_level_0 = 0xFFFFFF00, + tight_compress_level_9 = 0xFFFFFF09, + x_cursor = 0xFFFFFF10, + rich_cursor = 0xFFFFFF11, + pointer_pos = 0xFFFFFF18, + last_rect = 0xFFFFFF20, + new_framebuffer_size = 0xFFFFFF21, + tight_quality_level_0 = 0xFFFFFFE0, + tight_quality_level_9 = 0xFFFFFFE9 }; -struct FramebufferRectangle { - boost::endian::big_uint16_t x{}; - boost::endian::big_uint16_t y{}; - boost::endian::big_uint16_t width{}; - boost::endian::big_uint16_t height{}; - boost::endian::big_uint32_t encoding{}; - std::vector<uint8_t> data; +struct FramebufferRectangle +{ + boost::endian::big_uint16_t x{}; + boost::endian::big_uint16_t y{}; + boost::endian::big_uint16_t width{}; + boost::endian::big_uint16_t height{}; + boost::endian::big_uint32_t encoding{}; + std::vector<uint8_t> data; }; -struct FramebufferUpdateMsg { - boost::endian::big_uint8_t messageType{}; - std::vector<FramebufferRectangle> rectangles; +struct FramebufferUpdateMsg +{ + boost::endian::big_uint8_t messageType{}; + std::vector<FramebufferRectangle> rectangles; }; -inline std::string serialize(const FramebufferUpdateMsg& msg) { - // calculate the size of the needed vector for serialization - size_t vectorSize = 4; - for (const auto& rect : msg.rectangles) { - vectorSize += 12 + rect.data.size(); - } - - std::string serialized(vectorSize, 0); - - size_t i = 0; - serialized[i++] = static_cast<char>( - server_to_client_message_type::framebuffer_update); // Type - serialized[i++] = 0; // Pad byte - boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); - std::memcpy(&serialized[i], &numberOfRectangles, sizeof(numberOfRectangles)); - i += sizeof(numberOfRectangles); - - for (const auto& rect : msg.rectangles) { - // copy the first part of the struct - size_t bufferSize = - sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); - std::memcpy(&serialized[i], &rect, bufferSize); - i += bufferSize; - - std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); - i += rect.data.size(); - } - - return serialized; +inline std::string serialize(const FramebufferUpdateMsg& msg) +{ + // calculate the size of the needed vector for serialization + size_t vectorSize = 4; + for (const auto& rect : msg.rectangles) + { + vectorSize += 12 + rect.data.size(); + } + + std::string serialized(vectorSize, 0); + + size_t i = 0; + serialized[i++] = static_cast<char>( + server_to_client_message_type::framebuffer_update); // Type + serialized[i++] = 0; // Pad byte + boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); + std::memcpy(&serialized[i], &numberOfRectangles, + sizeof(numberOfRectangles)); + i += sizeof(numberOfRectangles); + + for (const auto& rect : msg.rectangles) + { + // copy the first part of the struct + size_t bufferSize = + sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); + std::memcpy(&serialized[i], &rect, bufferSize); + i += bufferSize; + + std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); + i += rect.data.size(); + } + + return serialized; } -enum class VncState { - UNSTARTED, - AWAITING_CLIENT_VERSION, - AWAITING_CLIENT_AUTH_METHOD, - AWAITING_CLIENT_INIT_msg, - MAIN_LOOP +enum class VncState +{ + UNSTARTED, + AWAITING_CLIENT_VERSION, + AWAITING_CLIENT_AUTH_METHOD, + AWAITING_CLIENT_INIT_msg, + MAIN_LOOP }; -class ConnectionMetadata { - public: - ConnectionMetadata(){}; +class ConnectionMetadata +{ + public: + ConnectionMetadata(){}; - VncState vncState{VncState::UNSTARTED}; + VncState vncState{VncState::UNSTARTED}; }; using meta_list = std::vector<ConnectionMetadata>; @@ -183,171 +204,219 @@ meta_list connectionStates(10); ConnectionMetadata meta; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/kvmws") - .websocket() - .onopen([&](crow::websocket::Connection& conn) { - if (meta.vncState == VncState::UNSTARTED) { - meta.vncState = VncState::AWAITING_CLIENT_VERSION; - conn.sendBinary(rfb38VersionString); - } else { // SHould never happen - conn.close(); - } - - }) - .onclose( - [&](crow::websocket::Connection& conn, const std::string& reason) { - meta.vncState = VncState::UNSTARTED; - }) - .onmessage([&](crow::websocket::Connection& conn, const std::string& data, - bool is_binary) { - switch (meta.vncState) { - case VncState::AWAITING_CLIENT_VERSION: { - std::cout << "Client sent: " << data; - if (data == rfb38VersionString || data == rfb37VersionString) { - std::string authTypes{1, - (uint8_t)RfbAuthScheme::no_authentication}; - conn.sendBinary(authTypes); - meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; - } else if (data == rfb33VersionString) { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); - } else { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/kvmws") + .websocket() + .onopen([&](crow::websocket::Connection& conn) { + if (meta.vncState == VncState::UNSTARTED) + { + meta.vncState = VncState::AWAITING_CLIENT_VERSION; + conn.sendBinary(rfb38VersionString); } - } break; - case VncState::AWAITING_CLIENT_AUTH_METHOD: { - std::string securityResult{{0, 0, 0, 0}}; - if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) { - meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; - } else { - // Mark auth as failed - securityResult[3] = 1; - meta.vncState = VncState::UNSTARTED; + else + { // SHould never happen + conn.close(); } - conn.sendBinary(securityResult); - } break; - case VncState::AWAITING_CLIENT_INIT_msg: { - // Now send the server initialization - ServerInitializationMsg serverInitMsg{}; - serverInitMsg.framebufferWidth = 800; - serverInitMsg.framebufferHeight = 600; - serverInitMsg.pixelFormat.bitsPerPixel = 32; - serverInitMsg.pixelFormat.isBigEndian = 0; - serverInitMsg.pixelFormat.isTrueColor = 1; - serverInitMsg.pixelFormat.redMax = 255; - serverInitMsg.pixelFormat.greenMax = 255; - serverInitMsg.pixelFormat.blueMax = 255; - serverInitMsg.pixelFormat.redShift = 16; - serverInitMsg.pixelFormat.greenShift = 8; - serverInitMsg.pixelFormat.blueShift = 0; - serverInitMsg.nameLength = 0; - std::cout << "size: " << sizeof(serverInitMsg); - // TODO(ed) this is ugly. Crow should really have a span type - // interface - // to avoid the copy, but alas, today it does not. - std::string s(reinterpret_cast<char*>(&serverInitMsg), - sizeof(serverInitMsg)); - std::cout << "s.size() " << s.size(); - conn.sendBinary(s); - meta.vncState = VncState::MAIN_LOOP; - } break; - case VncState::MAIN_LOOP: { - if (data.size() >= sizeof(client_to_server_msg_type)) { - auto type = static_cast<client_to_server_msg_type>(data[0]); - std::cout << "Received client message type " - << static_cast<std::size_t>(type) << "\n"; - switch (type) { - case client_to_server_msg_type::set_pixel_format: { - } break; - - case client_to_server_msg_type::fix_color_map_entries: { - } break; - case client_to_server_msg_type::set_encodings: { - } break; - case client_to_server_msg_type::framebuffer_update_request: { - // Make sure the buffer is long enough to handle what we're - // about to do - if (data.size() >= sizeof(FrameBufferUpdateReq) + - sizeof(client_to_server_msg_type)) { - auto msg = reinterpret_cast<const FrameBufferUpdateReq*>( - data.data() + // NOLINT - sizeof(client_to_server_msg_type)); - // TODO(ed) find a better way to do this deserialization - - // Todo(ed) lifecycle of the video puller and decoder - // should be - // with the websocket, not recreated every time - ast_video::SimpleVideoPuller p; - p.initialize(); - auto out = p.readVideo(); - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, - out.ySelector, out.uvSelector); - - FramebufferUpdateMsg bufferUpdateMsg; - - // If the viewer is requesting a full update, force write - // of all pixels - - FramebufferRectangle thisRect; - thisRect.x = msg->xPosition; - thisRect.y = msg->yPosition; - thisRect.width = out.width; - thisRect.height = out.height; - thisRect.encoding = - static_cast<uint8_t>(encoding_type::raw); - std::cout << "Encoding is " << thisRect.encoding; - thisRect.data.reserve( - static_cast<std::size_t>(thisRect.width) * - static_cast<std::size_t>(thisRect.height) * 4); - std::cout << "Width " << out.width << " Height " - << out.height; - - for (int i = 0; i < out.width * out.height; i++) { - auto& pixel = d.outBuffer[i]; - thisRect.data.push_back(pixel.b); - thisRect.data.push_back(pixel.g); - thisRect.data.push_back(pixel.r); - thisRect.data.push_back(0); + }) + .onclose( + [&](crow::websocket::Connection& conn, const std::string& reason) { + meta.vncState = VncState::UNSTARTED; + }) + .onmessage([&](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + switch (meta.vncState) + { + case VncState::AWAITING_CLIENT_VERSION: + { + std::cout << "Client sent: " << data; + if (data == rfb38VersionString || + data == rfb37VersionString) + { + std::string authTypes{ + 1, (uint8_t)RfbAuthScheme::no_authentication}; + conn.sendBinary(authTypes); + meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; + } + else if (data == rfb33VersionString) + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); + } + else + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); } - - bufferUpdateMsg.rectangles.push_back(std::move(thisRect)); - auto serialized = serialize(bufferUpdateMsg); - - conn.sendBinary(serialized); - - } // TODO(Ed) handle error - } - break; - - case client_to_server_msg_type::key_event: { - } break; - - case client_to_server_msg_type::pointer_event: { - } break; - - case client_to_server_msg_type::client_cut_text: { - } break; - - default: - break; - } + case VncState::AWAITING_CLIENT_AUTH_METHOD: + { + std::string securityResult{{0, 0, 0, 0}}; + if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) + { + meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; + } + else + { + // Mark auth as failed + securityResult[3] = 1; + meta.vncState = VncState::UNSTARTED; + } + conn.sendBinary(securityResult); + } + break; + case VncState::AWAITING_CLIENT_INIT_msg: + { + // Now send the server initialization + ServerInitializationMsg serverInitMsg{}; + serverInitMsg.framebufferWidth = 800; + serverInitMsg.framebufferHeight = 600; + serverInitMsg.pixelFormat.bitsPerPixel = 32; + serverInitMsg.pixelFormat.isBigEndian = 0; + serverInitMsg.pixelFormat.isTrueColor = 1; + serverInitMsg.pixelFormat.redMax = 255; + serverInitMsg.pixelFormat.greenMax = 255; + serverInitMsg.pixelFormat.blueMax = 255; + serverInitMsg.pixelFormat.redShift = 16; + serverInitMsg.pixelFormat.greenShift = 8; + serverInitMsg.pixelFormat.blueShift = 0; + serverInitMsg.nameLength = 0; + std::cout << "size: " << sizeof(serverInitMsg); + // TODO(ed) this is ugly. Crow should really have a span + // type interface to avoid the copy, but alas, today it does + // not. + std::string s(reinterpret_cast<char*>(&serverInitMsg), + sizeof(serverInitMsg)); + std::cout << "s.size() " << s.size(); + conn.sendBinary(s); + meta.vncState = VncState::MAIN_LOOP; + } + break; + case VncState::MAIN_LOOP: + { + if (data.size() >= sizeof(client_to_server_msg_type)) + { + auto type = + static_cast<client_to_server_msg_type>(data[0]); + std::cout << "Received client message type " + << static_cast<std::size_t>(type) << "\n"; + switch (type) + { + case client_to_server_msg_type::set_pixel_format: + { + } + break; + + case client_to_server_msg_type:: + fix_color_map_entries: + { + } + break; + case client_to_server_msg_type::set_encodings: + { + } + break; + case client_to_server_msg_type:: + framebuffer_update_request: + { + // Make sure the buffer is long enough to handle + // what we're about to do + if (data.size() >= + sizeof(FrameBufferUpdateReq) + + sizeof(client_to_server_msg_type)) + { + auto msg = reinterpret_cast< + const FrameBufferUpdateReq*>( + data.data() + // NOLINT + sizeof(client_to_server_msg_type)); + // TODO(ed) find a better way to do this + // deserialization + + // Todo(ed) lifecycle of the video puller + // and decoder should be with the websocket, + // not recreated every time + ast_video::SimpleVideoPuller p; + p.initialize(); + auto out = p.readVideo(); + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, + out.mode, out.ySelector, + out.uvSelector); + + FramebufferUpdateMsg bufferUpdateMsg; + + // If the viewer is requesting a full + // update, force write of all pixels + + FramebufferRectangle thisRect; + thisRect.x = msg->xPosition; + thisRect.y = msg->yPosition; + thisRect.width = out.width; + thisRect.height = out.height; + thisRect.encoding = static_cast<uint8_t>( + encoding_type::raw); + std::cout << "Encoding is " + << thisRect.encoding; + thisRect.data.reserve( + static_cast<std::size_t>( + thisRect.width) * + static_cast<std::size_t>( + thisRect.height) * + 4); + std::cout << "Width " << out.width + << " Height " << out.height; + + for (int i = 0; i < out.width * out.height; + i++) + { + auto& pixel = d.outBuffer[i]; + thisRect.data.push_back(pixel.b); + thisRect.data.push_back(pixel.g); + thisRect.data.push_back(pixel.r); + thisRect.data.push_back(0); + } + + bufferUpdateMsg.rectangles.push_back( + std::move(thisRect)); + auto serialized = + serialize(bufferUpdateMsg); + + conn.sendBinary(serialized); + + } // TODO(Ed) handle error + } + + break; + + case client_to_server_msg_type::key_event: + { + } + break; + + case client_to_server_msg_type::pointer_event: + { + } + break; + + case client_to_server_msg_type::client_cut_text: + { + } + break; + + default: + break; + } + } + } + break; + case VncState::UNSTARTED: + // Error? TODO + break; } - - } break; - case VncState::UNSTARTED: - // Error? TODO - break; - } - - }); + }); } -} // namespace kvm -} // namespace crow
\ No newline at end of file +} // namespace kvm +} // namespace crow
\ No newline at end of file diff --git a/include/webassets.hpp b/include/webassets.hpp index 5eabffe..7f1c1f5 100644 --- a/include/webassets.hpp +++ b/include/webassets.hpp @@ -1,137 +1,161 @@ #pragma once -#include <experimental/filesystem> -#include <fstream> -#include <string> #include <crow/app.h> #include <crow/http_request.h> #include <crow/http_response.h> #include <crow/routing.h> + #include <boost/algorithm/string/replace.hpp> #include <boost/container/flat_set.hpp> +#include <experimental/filesystem> +#include <fstream> +#include <string> -namespace crow { -namespace webassets { +namespace crow +{ +namespace webassets +{ namespace filesystem = std::experimental::filesystem; -struct CmpStr { - bool operator()(const char* a, const char* b) const { - return std::strcmp(a, b) < 0; - } +struct CmpStr +{ + bool operator()(const char* a, const char* b) const + { + return std::strcmp(a, b) < 0; + } }; static boost::container::flat_set<std::string> routes; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - const static boost::container::flat_map<const char*, const char*, CmpStr> - contentTypes{ - {{".css", "text/css;charset=UTF-8"}, - {".html", "text/html;charset=UTF-8"}, - {".js", "text/html;charset=UTF-8"}, - {".png", "image/png;charset=UTF-8"}, - {".woff", "application/x-font-woff"}, - {".woff2", "application/x-font-woff2"}, - {".gif", "image/gif"}, - {".ico", "image/x-icon"}, - {".ttf", "application/x-font-ttf"}, - {".svg", "image/svg+xml"}, - {".eot", "application/vnd.ms-fontobject"}, - {".xml", "application/xml"}, - {".jpg", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".json", "application/json"}, - // dev tools don't care about map type, setting to json causes - // browser to show as text - // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files - {".map", "application/json"}}}; - filesystem::path rootpath{"/usr/share/www/"}; - filesystem::recursive_directory_iterator dirIter(rootpath); - // In certain cases, we might have both a gzipped version of the file AND a - // non-gzipped version. To avoid duplicated routes, we need to make sure we - // get the gzipped version first. Because the gzipped path should be longer - // than the non gzipped path, if we sort in Ascending order, we should be - // guaranteed to get the gzip version first. - std::vector<filesystem::directory_entry> paths(filesystem::begin(dirIter), - filesystem::end(dirIter)); - std::sort(paths.rbegin(), paths.rend()); - - for (const filesystem::directory_entry& dir : paths) { - filesystem::path absolutePath = dir.path(); - filesystem::path relativePath{ - absolutePath.string().substr(rootpath.string().size() - 1)}; - if (filesystem::is_directory(dir)) { - // don't recurse into hidden directories or symlinks - if (boost::starts_with(dir.path().filename().string(), ".") || - filesystem::is_symlink(dir)) { - dirIter.disable_recursion_pending(); - } - } else if (filesystem::is_regular_file(dir)) { - std::string extension = relativePath.extension(); - filesystem::path webpath = relativePath; - const char* contentEncoding = nullptr; - - if (extension == ".gz") { - webpath = webpath.replace_extension(""); - // Use the non-gzip version for determining content type - extension = webpath.extension().string(); - contentEncoding = "gzip"; - } - - if (boost::starts_with(webpath.filename().string(), "index.")) { - webpath = webpath.parent_path(); - if (webpath.string().size() == 0 || webpath.string().back() != '/') { - // insert the non-directory version of this path - routes.insert(webpath); - webpath += "/"; +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + const static boost::container::flat_map<const char*, const char*, CmpStr> + contentTypes{ + {{".css", "text/css;charset=UTF-8"}, + {".html", "text/html;charset=UTF-8"}, + {".js", "text/html;charset=UTF-8"}, + {".png", "image/png;charset=UTF-8"}, + {".woff", "application/x-font-woff"}, + {".woff2", "application/x-font-woff2"}, + {".gif", "image/gif"}, + {".ico", "image/x-icon"}, + {".ttf", "application/x-font-ttf"}, + {".svg", "image/svg+xml"}, + {".eot", "application/vnd.ms-fontobject"}, + {".xml", "application/xml"}, + {".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".json", "application/json"}, + // dev tools don't care about map type, setting to json causes + // browser to show as text + // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files + {".map", "application/json"}}}; + filesystem::path rootpath{"/usr/share/www/"}; + filesystem::recursive_directory_iterator dirIter(rootpath); + // In certain cases, we might have both a gzipped version of the file AND a + // non-gzipped version. To avoid duplicated routes, we need to make sure we + // get the gzipped version first. Because the gzipped path should be longer + // than the non gzipped path, if we sort in Ascending order, we should be + // guaranteed to get the gzip version first. + std::vector<filesystem::directory_entry> paths(filesystem::begin(dirIter), + filesystem::end(dirIter)); + std::sort(paths.rbegin(), paths.rend()); + + for (const filesystem::directory_entry& dir : paths) + { + filesystem::path absolutePath = dir.path(); + filesystem::path relativePath{ + absolutePath.string().substr(rootpath.string().size() - 1)}; + if (filesystem::is_directory(dir)) + { + // don't recurse into hidden directories or symlinks + if (boost::starts_with(dir.path().filename().string(), ".") || + filesystem::is_symlink(dir)) + { + dirIter.disable_recursion_pending(); + } } - } - - std::pair<boost::container::flat_set<std::string>::iterator, bool> - inserted = routes.insert(webpath); - - if (!inserted.second) { - // Got a duplicated path. This is expected in certain situations - BMCWEB_LOG_DEBUG << "Got duplicated path " << webpath; - continue; - } - const char* contentType = nullptr; - - auto contentTypeIt = contentTypes.find(extension.c_str()); - if (contentTypeIt == contentTypes.end()) { - BMCWEB_LOG_ERROR << "Cannot determine content-type for " << absolutePath - << " with extension " << extension; - } else { - contentType = contentTypeIt->second; - } - - app.routeDynamic(webpath)( - [absolutePath, contentType, contentEncoding](const crow::Request& req, - crow::Response& res) { - if (contentType != nullptr) { - res.addHeader("Content-Type", contentType); + else if (filesystem::is_regular_file(dir)) + { + std::string extension = relativePath.extension(); + filesystem::path webpath = relativePath; + const char* contentEncoding = nullptr; + + if (extension == ".gz") + { + webpath = webpath.replace_extension(""); + // Use the non-gzip version for determining content type + extension = webpath.extension().string(); + contentEncoding = "gzip"; } - if (contentEncoding != nullptr) { - res.addHeader("Content-Encoding", contentEncoding); + if (boost::starts_with(webpath.filename().string(), "index.")) + { + webpath = webpath.parent_path(); + if (webpath.string().size() == 0 || + webpath.string().back() != '/') + { + // insert the non-directory version of this path + routes.insert(webpath); + webpath += "/"; + } } - // res.set_header("Cache-Control", "public, max-age=86400"); - std::ifstream inf(absolutePath); - if (!inf) { - BMCWEB_LOG_DEBUG << "failed to read file"; - res.result(boost::beast::http::status::internal_server_error); - res.end(); - return; + std::pair<boost::container::flat_set<std::string>::iterator, bool> + inserted = routes.insert(webpath); + + if (!inserted.second) + { + // Got a duplicated path. This is expected in certain + // situations + BMCWEB_LOG_DEBUG << "Got duplicated path " << webpath; + continue; + } + const char* contentType = nullptr; + + auto contentTypeIt = contentTypes.find(extension.c_str()); + if (contentTypeIt == contentTypes.end()) + { + BMCWEB_LOG_ERROR << "Cannot determine content-type for " + << absolutePath << " with extension " + << extension; + } + else + { + contentType = contentTypeIt->second; } - res.body() = {std::istreambuf_iterator<char>(inf), - std::istreambuf_iterator<char>()}; - res.end(); - }); + app.routeDynamic(webpath)( + [absolutePath, contentType, contentEncoding]( + const crow::Request& req, crow::Response& res) { + if (contentType != nullptr) + { + res.addHeader("Content-Type", contentType); + } + + if (contentEncoding != nullptr) + { + res.addHeader("Content-Encoding", contentEncoding); + } + + // res.set_header("Cache-Control", "public, max-age=86400"); + std::ifstream inf(absolutePath); + if (!inf) + { + BMCWEB_LOG_DEBUG << "failed to read file"; + res.result( + boost::beast::http::status::internal_server_error); + res.end(); + return; + } + + res.body() = {std::istreambuf_iterator<char>(inf), + std::istreambuf_iterator<char>()}; + res.end(); + }); + } } - } -} // namespace webassets -} // namespace webassets -} // namespace crow +} // namespace webassets +} // namespace webassets +} // namespace crow |

