ofs | hex dump | ascii |
---|
0000 | 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 04 05 00 00 01 e9 08 06 00 00 00 fa 47 85 | .PNG........IHDR..............G. |
0020 | 2a 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 93 00 00 00 09 70 48 59 73 00 00 0e c4 00 | *....bKGD..............pHYs..... |
0040 | 00 0e c4 01 95 2b 0e 1b 00 00 00 07 74 49 4d 45 07 e1 09 13 0c 2e 04 5b 00 cc d6 00 00 20 00 49 | .....+......tIME.......[.......I |
0060 | 44 41 54 78 da ec dd 7b 74 d4 f5 fd ef fb e7 77 66 32 33 c9 e4 32 b9 91 81 5c 81 00 11 03 44 83 | DATx...{t......wf23..2...\....D. |
0080 | 8a 17 2a 2a fd 15 95 fd 2b 3f a1 1e 5a bc d0 fe 3c 6b a5 9e ee 73 d0 ee b3 8a fd f9 3b d2 bd 75 | ..**....+?..Z...<k...s......;..u |
00a0 | c3 de 75 29 7b 9d 2e 0f fe b6 6d a1 54 cb b6 6a 69 7f b1 46 8d 4a 09 42 2a 20 01 22 84 90 40 6e | ..u){.....m.T..ji..F.J.B*.."..@n |
00c0 | 24 93 64 92 4c 6e 73 9f ef f7 fc 11 66 9c 5c 09 90 c4 00 ef c7 5a 2c 60 be f7 cf f7 3b 97 cf eb | $.d.Lns.....f.\......Z,`....;... |
00e0 | fb f9 7c be 0a c0 13 4f 3c a1 2d 5b b6 8c 6f 7f fb db a8 aa 8a aa aa 08 21 84 10 42 08 21 84 10 | ..|....O<.-[..o.........!..B.!.. |
0100 | e2 fa 12 08 04 f8 d3 9f fe c4 fe fd fb 29 2d 2d 55 0c 00 b7 df 7e 3b df fe f6 b7 71 bb dd f8 7c | .............)--U....~;....q...| |
0120 | 3e 29 25 21 84 10 42 08 21 84 10 e2 3a f5 f0 c3 0f e3 f7 fb 29 2d 2d c5 f0 4f ff f4 4f 9a 04 02 | >)%!..B.!...:.......)--..O..O... |
0140 | 42 08 21 84 10 42 08 21 2e a5 bf bf 9f d6 d6 d6 6f 64 db 69 69 69 58 2c 96 61 af 77 75 75 d1 d9 | B.!..B.!........od.iiiX,.a.wuu.. |
0160 | d9 39 29 db 4c 4a 4a 22 31 31 f1 9a de 4e 74 74 34 d1 d1 d1 e1 f5 03 68 9a c6 ea d5 ab 39 74 e8 | .9).LJJ"11...Ntt4......h.....9t. |
0180 | 90 66 f0 78 3c 04 02 01 09 04 84 10 42 08 21 84 10 42 8c a9 b5 b5 95 f9 f3 e7 63 36 9b 51 14 65 | .f.x<.......B.!..B........c6.Q.e |
01a0 | ca b6 eb 76 bb 39 73 e6 0c 73 e7 ce 1d 36 ad b3 b3 93 d9 b3 67 13 1f 1f 3f a1 db 74 b9 5c 9c 3d | ...v.9s..s...6......g...?..t.\.= |
01c0 | 7b 36 5c 99 9e ac ed f4 f4 f4 70 fe fc f9 49 db 8e cf e7 a3 bf bf 9f e6 e6 66 66 cd 9a 15 7e 5d | {6\.......p...I..........ff...~] |
01e0 | af d7 03 60 08 a5 04 42 08 21 84 10 42 08 21 c4 58 34 4d 23 3a 3a 9a de de de 71 cd df db db cb | ...`...B.!..B.!.X4M#::....q..... |
0200 | 85 0b 17 30 9b cd cc 98 31 03 00 bb dd 8e cf e7 23 33 33 73 c4 3b ff 23 89 8d 8d 1d 73 7a 7c 7c | ...0....1.......#33s.;.#....sz|| |
0220 | 3c 3e 9f 6f c2 6e 76 1b 8d 46 62 62 62 a6 64 3b 23 55 fe 27 7a 3b a1 72 ee ea ea 1a d4 62 20 1c | <>.o.nv..Fbbb.d;#U.'z;.r.....b.. |
0240 | 0a 08 21 84 10 42 08 21 84 10 e3 0d 06 c6 a3 ad ad 8d b4 b4 34 56 ac 58 31 e2 f4 2f bf fc 92 b6 | ..!..B.!............4V.X1../.... |
0260 | b6 36 52 53 53 27 64 bf 26 b2 f5 bb cf e7 c3 68 34 5e 57 db b1 58 2c b4 b7 b7 4b 28 20 84 10 42 | .6RSS'd.&......h4^W..X,...K(...B |
0280 | 08 21 84 10 62 72 b5 b4 b4 50 58 58 c8 cc 99 33 81 81 11 ef df 79 e7 1d 14 45 61 ed da b5 18 0c | .!..br...PXX...3.....y...Ea..... |
02a0 | 06 6e bd f5 56 5a 5a 5a 38 76 ec 18 69 69 69 d7 ec b1 f6 f6 f6 d2 d2 d2 72 c9 d6 13 71 71 71 cc | .n..VZZZ8v..iii.........r...qqq. |
02c0 | 9c 39 93 b8 b8 b8 ab da 56 75 75 35 00 b3 66 cd 0a 97 6f a8 cc 9b 9b 9b 01 98 3f 7f fe b0 ed 18 | .9......Vuu5..f...o.......?..... |
02e0 | 8d 46 dc 6e f7 b0 75 4a 28 20 84 10 42 08 21 84 10 62 c2 74 76 76 92 9e 9e 3e a8 c2 fa 87 3f fc | .F.n..uJ(...B.!..b.tvv...>....?. |
0300 | 81 85 0b 17 02 f0 d6 5b 6f f1 c4 13 4f 00 30 73 e6 4c 5a 5a 5a e8 ea ea c2 6a b5 5e 93 c7 db d2 | .......[o...O.0s.LZZZ....j.^.... |
0320 | d2 c2 a2 45 8b 46 1c ef 20 52 6d 6d 2d 27 4f 9e bc aa 50 a0 ba ba 9a bb ee ba 8b b9 73 e7 b2 7b | ...E.F...Rmm-'O...P.........s..{ |
0340 | f7 ee 41 65 d8 dc dc cc e3 8f 3f 4e 6d 6d 2d 07 0f 1e a4 b0 b0 70 5c eb d4 c9 25 2b 84 10 42 08 | ..Ae......?Nmm-......p\...%+..B. |
0360 | 21 84 10 62 22 2b c9 b7 dc 72 cb a0 d7 fa fb fb c3 ff 1e 7a b7 fa d6 5b 6f e5 dc b9 73 d7 ec f1 | !..b"+...r.........z...[o...s... |
0380 | f6 f6 f6 5e 32 10 00 98 3b 77 ee b8 c7 62 18 cd ac 59 b3 38 78 f0 20 00 8f 3f fe 38 cd cd cd 83 | ...^2...;w...b...Y.8x....?.8.... |
03a0 | 02 01 80 83 07 0f 0e 1a 50 f0 52 a4 a5 80 10 42 08 21 84 10 42 88 09 93 90 90 30 ec b5 cc cc 4c | ........P.R....B.!..B.....0....L |
03c0 | 2e 5c b8 00 40 7a 7a fa b0 e9 d7 72 f7 81 90 b2 b2 32 ea ea ea 46 9c 96 93 93 c3 f2 e5 cb af 7a | .\..@zz....r.....2...F.........z |
03e0 | 1b a1 d6 17 bb 77 ef e6 f1 c7 1f e7 f1 c7 1f a7 ac ac 2c 1c 08 ec de bd 7b 58 b7 02 09 05 84 10 | .....w............,.....{X...... |
0400 | 42 08 21 84 10 42 4c 89 9e 9e 9e f0 53 06 22 3d fc f0 c3 b4 b6 b6 a2 69 1a 36 9b 6d d8 74 8b c5 | B.!..BL.....S."=.......i.6.m.t.. |
0420 | 42 7f 7f ff b8 9f 46 30 1d d5 d5 d5 8d da 64 ff e8 d1 a3 13 12 0a 84 82 81 d8 d8 d8 70 30 10 5a | B.....F0......d.............p0.Z |
0440 | ef ee dd bb 47 1c 4b 40 42 01 21 84 10 42 08 21 84 10 53 46 51 94 61 af b5 b5 b5 d1 da da 0a 80 | ....G.K@B.!..B.!..SFQ.a......... |
0460 | 4e a7 1b 16 1c 28 8a 82 aa aa 13 ba 1f 53 39 00 e0 b5 4c 42 01 21 84 10 42 08 21 84 10 13 22 2e | N....(.......S9...LB.!..B.!...". |
0480 | 2e 2e 3c 02 3e 40 7b 7b 3b ff fe ef ff 4e 7c 7c 7c b8 5b 41 55 55 15 bd bd bd 7c f7 bb df 25 25 | ..<.>@{{;....N|||.[AUU....|...%% |
04a0 | 25 05 80 be be be 11 bb 15 5c 8d 96 96 16 ee ba eb ae 11 5b 2e 44 6a 6b 6b e3 e0 c1 83 d7 4c 28 | %........\.........[.Djkk.....L( |
04c0 | 30 74 0c 81 b2 b2 32 96 2f 5f ce e3 8f 3f 2e dd 07 84 10 42 08 21 84 10 42 7c 73 14 45 c1 e9 74 | 0t....2./_...?.....B.!..B|s.E..t |
04e0 | 86 ff ff d1 47 1f b1 64 c9 92 41 f3 24 25 25 01 f0 e1 87 1f b2 61 c3 06 00 5a 5b 5b 27 3c 14 98 | ....G..d..A.$%%......a...Z[['<.. |
0500 | 6a 39 39 39 1c 3d 7a 74 d4 69 93 11 08 84 42 80 c8 31 06 22 9f 4a f0 8d 86 02 aa aa 52 5f 5f 4f | j999.=zt.i....B..1.".J......R__O |
0520 | 67 67 a7 bc 33 c4 75 21 29 29 89 ec ec 6c 74 3a 79 68 87 10 42 08 21 84 10 a3 b1 d9 6c 7c f9 e5 | gg..3.u!))...lt:yh..B.!.....l|.. |
0540 | 97 dc 7a eb ad dc 7b ef bd 7c f4 d1 47 2c 5a b4 28 fc 3b 5a 55 55 4e 9c 38 c1 3f fe e3 3f 02 70 | ..z...{..|..G,Z.(.;ZUUN.8.?..?.p |
0560 | ec d8 31 66 cf 9e 3d e1 fb 31 73 e6 4c 0e 1e 3c 38 ee ee 03 57 2a 2e 2e 8e da da 5a 96 2f 5f 3e | ..1f..=..1s.L..<8...W*.....Z./_> |
0580 | e6 b8 01 b5 b5 b5 57 dd 1a a1 b9 b9 99 bb ee ba 6b 50 20 30 74 f0 c1 bb ee ba 8b 83 07 0f 7e f3 | ......W.........kP.0t.........~. |
05a0 | a1 40 6b 6b 2b bd bd bd dc 71 c7 1d cc 99 33 47 de 19 e2 9a 76 ee dc 39 8e 1e 3d 4a 6b 6b eb 55 | .@kk+....q....3G....v..9..=Jkk.U |
05c0 | 7d 60 08 21 84 10 42 08 71 bd 4b 4e 4e e6 c2 85 0b cc 9a 35 8b 8c 8c 0c d6 ad 5b c7 fb ef bf cf | }`.!..B.q.KNN......5......[..... |
05e0 | 85 0b 17 50 14 85 dc dc 5c 1e 7d f4 51 e2 e2 e2 b0 db ed 34 37 37 8f 38 f8 e0 d5 8a 8b 8b 9b 92 | ...P....\.}.Q......477.8........ |
0600 | 2e 01 33 67 ce e4 e4 c9 93 e1 47 05 4e 56 f8 00 30 7f fe 7c 0e 1e 3c 18 7e ec 60 68 7d 91 c1 40 | ..3g......G.NV..0..|..<.~.`h}..@ |
0620 | 68 be f1 9a 94 50 40 55 55 da da da 98 3f 7f be 04 02 e2 ba 30 67 ce 1c 7a 7b 7b a9 ae ae 26 2d | h....P@UU....?......0g..z{{...&- |
0640 | 2d 4d 5a 0b 08 21 84 10 42 08 31 86 59 b3 66 71 f8 f0 61 32 32 32 b8 e5 96 5b f8 fe f7 bf 3f 6c | -MZ..!..B.1.Y.fq..a222...[....?l |
0660 | 9e 63 c7 8e 4d 5a 20 30 95 a6 2a 7c 08 6d 6b b4 27 1c cc 9c 39 f3 8a 42 87 49 09 05 1c 0e 07 9a | .c..MZ.0..*|.mk.'...9..B.I...... |
0680 | a6 91 97 97 27 ef 06 71 dd c8 cb cb e3 cc 99 33 38 1c 8e 4b 0e 56 22 84 10 42 08 21 84 04 03 b3 | ....'..q.......38..K.V"..B.!.... |
06a0 | 70 38 1c fc db bf fd 1b 69 69 69 e1 df d0 ed ed ed b4 b6 b6 32 7b f6 ec 71 07 02 23 3d d1 60 28 | p8......iii.........2{..q..#=.`( |
06c0 | a3 d1 88 cf e7 9b 90 7d 37 1a 8d 37 cc 76 26 25 14 08 9d 60 93 c9 24 ef 04 71 dd 30 99 4c cc 9e | .......}7..7.v&%...`..$..q.0.L.. |
06e0 | 3d 9b 86 86 06 09 05 84 10 42 08 21 c4 0d 49 51 14 3c 1e cf b8 ef 8c c7 c5 c5 91 9d 9d 3d e8 b5 | =........B.!..IQ.<...........=.. |
0700 | e4 e4 e4 cb ba 81 ac 69 1a 6e b7 7b cc 60 a0 a7 a7 87 f8 f8 f8 31 2b bf 97 ab a7 a7 67 d8 6b 2e | .......i.n.{.`.......1+.....g.k. |
0720 | 97 8b 98 98 98 49 df ce 54 1d cf a4 84 02 9d 9d 9d f8 7c 3e 69 25 20 ae 4b 79 79 79 d4 d4 d4 d0 | .....I..T.........|>i%..Kyyy.... |
0740 | d9 d9 19 1e 35 55 08 21 84 10 42 88 1b 45 5a 5a 1a d5 d5 d5 df d8 b6 47 92 94 94 c4 f9 f3 e7 27 | ....5U.!..B..EZZ.......G.......' |
0760 | 65 9b 91 bf f9 93 92 92 38 7b f6 ec 94 6c 67 2a 8e 67 d2 42 01 bb dd 4e 46 46 c6 35 f3 8c 47 21 | e.......8{...lg*.g.B...NFF.5..G! |
0780 | 2e 47 5c 5c 1c 19 19 19 d8 ed 76 09 05 84 10 42 08 21 c4 0d c7 62 b1 4c bb 71 e3 12 13 13 49 4c | .G\\......v....B.!...b.L.q....IL |
07a0 | 4c 94 ed 5c a1 09 0d 05 7a 7b 7b 71 bb dd dc 7c f3 cd 97 bd ac a6 c1 38 ba 89 08 f1 8d bb f9 e6 | L..\....z{{q...|.......8........ |
07c0 | 9b f9 f0 c3 0f e9 ed ed 1d 31 fc d2 34 8d ae ae 2e 9c 4e 27 9a a6 4d fa fe 58 ad 56 92 92 92 c6 | .........1..4.....N'..M..X.V.... |
07e0 | d5 cf 4a 08 21 84 10 42 08 21 26 2d 14 b0 db ed a4 a6 a6 5e d1 1d 54 45 81 ba af 7a f8 ea 90 9d | ..J.!..B.!&-.......^..TE...z.... |
0800 | 40 d0 4f 6a 7a 0c 33 e7 c4 93 39 cf 8a 21 4a 2f 67 4a 4c 1b 49 49 49 a4 a6 a6 62 b7 db 47 0c 05 | @.Ojz.3...9..!J/gJL.III...b..G.. |
0820 | 54 55 a5 a3 a3 83 79 f3 e6 61 30 18 26 75 5f ba bb bb b9 70 e1 02 d1 d1 d1 c4 c4 c4 c8 c9 11 42 | TU....y..a0.&u_....p...........B |
0840 | 08 21 84 10 42 5c 96 09 7b ae 9a db ed a6 a7 a7 87 05 0b 16 5c d6 72 9a a6 11 08 a8 fc e1 bf 57 | .!..B\..{...........\.r........W |
0860 | f2 3f 7f fe 05 4d 17 1a b0 ce e9 c7 94 da 8f cb d3 43 77 57 df 94 dc 6d fd e6 1d e6 67 85 f9 6c | .?...M...........CwW...m....g..l |
0880 | 2a bb d2 e9 53 a0 6c 13 f9 85 3f e3 b0 bc 6f 58 b0 60 01 3d 3d 3d b8 dd ee 51 ae e9 00 81 40 60 | *...S.l...?...oX.`.===...Q....@` |
08a0 | d2 f7 23 21 21 81 98 98 18 3a 3a 3a 50 55 55 4e 8c 10 42 08 21 84 10 e2 9b 09 05 ec 76 3b 09 09 | ..#!!....:::PUUN..B.!.......v;.. |
08c0 | 09 64 66 66 5e 56 20 a0 28 0a ff eb e5 93 94 fd b9 86 58 5b 80 0b 55 01 62 12 a2 c8 2f 98 c7 4d | .dff^V..(.........X[..U.b.../..M |
08e0 | 05 d9 c4 27 5e fa ee 67 fd af 1f 21 f7 ee 17 38 ec f9 fa 35 cf e1 17 b8 3b ff 87 bc d3 0a e0 e4 | ...'^..g...!...8...5....;....... |
0900 | cd 47 52 89 8f 8f 27 3e 3e 9e d4 d4 4c f2 ef 7e 88 a2 57 3e a6 3e 62 99 d6 8f 5f e1 fb 77 e7 93 | .GR...'>>...L..~..W>.>b..._..w.. |
0920 | 99 9a 4a 6a 6e 3e 77 3f f2 33 8a 5b 87 6e 6d 7c eb 12 d7 b7 cc cc 4c 12 12 12 b0 db ed 23 5e d7 | ..Jjn>w?.3.[.nm|......L......#^. |
0940 | 46 a3 71 c4 69 93 c1 66 b3 d1 df df 3f 61 8f 2b 11 42 08 21 84 10 42 48 28 70 59 fc 7e 3f 9d 9d | F.q.i..f....?a.+.B.!..BH(pY.~?.. |
0960 | 9d 97 d5 4a 20 14 08 d4 9d ee a2 e2 03 17 39 cb 02 98 53 5c 78 5b 12 a9 f8 b8 03 34 40 d3 11 15 | ...J..........9...S\x[.....4@... |
0980 | 15 75 c9 be d2 d9 ff fc 2a cf a5 bd c9 a6 ad 27 f0 00 78 4e b0 75 d3 bb cc df b6 8d 75 e1 01 2a | .u......*......'..xN.u......u..* |
09a0 | cd dc f3 ea 49 7a 7a 7a 68 6f 3f c9 fb db 37 90 50 fc 63 1e f8 c1 af a9 06 68 7d 93 1f ff e8 37 | ....Izzzho?...7.P.c......h}....7 |
09c0 | 98 9f 7e 8b 93 8d 8d 9c fd fc 3d b6 ff 68 35 8b 46 1c e0 f2 12 eb 9a 44 f2 90 c7 89 f1 d2 4b 2f | ..~.......=..h5.F......D......K/ |
09e0 | f1 f6 db 6f 0f 7b fd ed b7 df e6 a5 97 5e 1a d7 3a 16 2c 58 40 67 67 27 7e bf 7f d0 eb 8a a2 60 | ...o.{.......^..:.,X@gg'~......` |
0a00 | b5 5a 71 38 1c 53 d2 5a c0 6c 36 13 15 15 45 57 57 d7 0d d2 aa 46 08 21 84 10 42 08 31 ad 42 01 | .Zq8.S.Z.l6...EWW....F.!..B.1.B. |
0a20 | bb dd 8e d9 6c be ac 51 28 35 55 43 d3 e0 d4 df 5b 31 a7 b8 49 9e 1b 64 c9 bd 69 04 a2 9c 54 95 | ....l..Q(5UC....[1..I..d..i...T. |
0a40 | 77 d3 74 a1 09 9d 9e 71 56 72 b2 f9 e7 57 7f 41 da bb 9b 78 ed 84 93 13 af 6d a2 78 fe 36 b6 ad | w.t....qVr...W.A...x.....m.x.6.. |
0a60 | 4e 1b 65 7e 2b d9 b7 6d e0 bf bd b7 9d e5 d5 5b 78 e1 1d 27 b4 56 d3 ca 22 1e 5e bb 18 ab d9 8c | N.e~+..m.......[x..'.V..".^..... |
0a80 | 35 6d 3e b7 ad 5e 4e f6 25 b7 3d c2 ba 80 d6 8f 7f c6 f7 ef 2f 24 3f 37 97 dc dc bb f9 fe 2b 87 | 5m>..^N.%.=........./$?7......+. |
0aa0 | 19 98 52 4f f1 cf 1e a1 30 3f 97 dc cc 5c f2 ef 7e 84 57 c2 4d 1c 4c 78 0e 0f b4 56 c8 cd cc 24 | ..RO....0?...\..~.W.M.Lx...V...$ |
0ac0 | f7 ee ef f3 4a 99 73 50 24 d0 7d f8 85 f0 f4 fc fb 8b 78 b3 7a 50 53 07 5e f8 fe dd e4 67 66 92 | ....J.sP$.}.......x.zPS.^....gf. |
0ae0 | 99 9b cf 43 45 6f 72 62 d0 e4 d1 f6 e9 30 3f 2b 7c 88 d7 ca de 61 d3 43 f9 a4 a6 3e c4 af 5b c1 | ...CEorb.....0?+|....a.C...>..[. |
0b00 | 53 fd 26 45 f7 e7 93 99 99 49 fe dd df e7 85 c3 9e eb 26 94 98 37 6f 1e 9f 7e fa 29 bb 76 ed 0a | S.&E.....I........&..7o..~.).v.. |
0b20 | bf b6 6b d7 2e 3e fd f4 53 e6 cd 9b 37 ae 75 cc 99 33 07 b3 d9 3c ac 45 80 4e a7 c3 62 b1 60 32 | ..k..>..S...7.u..3...<.E.N..b.`2 |
0b40 | 99 68 69 69 99 92 e3 99 35 6b 16 dd dd dd d2 5a 40 08 21 84 10 42 08 31 b5 a1 40 30 18 c4 e1 70 | .hii....5k.....Z@.!..B.1..@0...p |
0b60 | b0 60 c1 82 4b de d1 0f 06 54 d4 e0 40 25 5f d1 29 28 0a 2c b8 2d 85 99 37 ab 9c fd 30 81 ca 12 | .`..K....T..@%_.)(.,.-..7...0... |
0b80 | 48 ce 88 c2 d3 a7 71 f0 a3 6a 7a ba 3c e3 1f 51 3d 7b 03 af 3e 67 e5 37 3f 7c 98 1f be 99 cd 2f | H.....q..jz.<..Q={..>g.7?|...../ |
0ba0 | b6 ad 26 ed 92 f5 f9 95 fc e0 1e 13 87 4b 0f c3 e2 b5 ac 9d 77 80 ad 3f 7e 8d b2 2b e9 07 10 b9 | ..&..........K......w..?~..+.... |
0bc0 | 2e 20 21 7b 35 4f ef f8 84 ca 9a 1a 2a df 5b 4b eb ab 3f e3 ad 7a e0 e3 ad 6c 3a 30 8f 1d 47 6a | ..!{5O......*.[K..?..z...l:0..Gj |
0be0 | a8 69 ac e1 c0 5b 5b 58 3b cf 7c 71 25 0d bc 5f 0a cf be 5f 49 4d e3 59 de db d0 cd ab 9b 7f f3 | .i...[[X;.|q%.._..._IM.Y........ |
0c00 | 75 eb 03 ef 59 de 3f 90 c6 73 9f 54 52 d3 78 92 b7 56 37 b0 f9 87 5b 39 01 40 35 af 7d ff c7 94 | u...Y.?..s.TR.x..V7...[9.@5.}... |
0c20 | cd df c6 27 8d 8d 34 56 be c7 8f bc af f0 83 1f 17 13 8a 15 46 dd 27 00 4e f2 ca e6 f7 59 f4 8b | ...'..4V............F.'.N....Y.. |
0c40 | 4f 38 7b f6 3d 36 a4 9d 60 eb 0f 37 53 bd fc b7 9c 6c 6c a4 f2 fd 67 49 28 7d 9f b3 de eb e3 c2 | O8{.=6..`..7S....ll...gI(}...... |
0c60 | 7f f4 d1 47 b9 f3 ce 3b 39 74 e8 10 bb 76 ed 62 d7 ae 5d 1c 3a 74 88 3b ef bc 93 47 1f 7d 74 5c | ...G...;9t...v.b..].:t.;...G.}t\ |
0c80 | eb 50 14 85 05 0b 16 e0 70 38 08 06 83 83 42 01 bd 5e 4f 72 72 32 76 bb 7d 4a fa fa c7 c5 c5 61 |
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
* Copyright (C) 2016 "IoT.bzh"
* Author "Fulup Ar Foll"
* Author José Bollo <jose.bollo@iot.bzh>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contain all generic part to handle REST/API
*
* https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c']
*/
#define _GNU_SOURCE
#include "../include/local-def.h"
#include <dirent.h>
#include <dlfcn.h>
#include <setjmp.h>
#include <signal.h>
#include "afb-apis.h"
#include "session.h"
#define AFB_MSG_JTYPE "AJB_reply"
#define JSON_CONTENT "application/json"
#define FORM_CONTENT "multipart/form-data" /* TODO: replace with MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA */
static json_object *afbJsonType;
// Because of POST call multiple time requestApi we need to free POST handle here
// Note this method is called from http-svc just before closing session
PUBLIC void endPostRequest(AFB_PostHandle * postHandle)
{
if (postHandle->type == AFB_POST_JSON) {
// if (verbose) fprintf(stderr, "End PostJson Request UID=%d\n", postHandle->uid);
}
if (postHandle->type == AFB_POST_FORM) {
if (verbose)
fprintf(stderr, "End PostForm Request UID=%d\n", postHandle->uid);
}
if (postHandle->privatebuf)
free(postHandle->privatebuf);
free(postHandle);
}
// Check of apiurl is declare in this plugin and call it
static AFB_error doCallPluginApi(AFB_request * request, int apiidx, int verbidx, void *context)
{
enum AFB_sessionE session;
json_object *jresp, *jcall, *jreqt;
AFB_clientCtx *clientCtx = NULL;
// Request was found and at least partially executed
jreqt = json_object_new_object();
json_object_get(afbJsonType); // increate jsontype reference count
json_object_object_add(jreqt, "jtype", afbJsonType);
// prepare an object to store calling values
jcall = json_object_new_object();
json_object_object_add(jcall, "prefix", json_object_new_string(request->prefix));
json_object_object_add(jcall, "api", json_object_new_string(request->method));
// Out of SessionNone every call get a client context session
session = afb_apis_get(apiidx, verbidx)->session;
if (AFB_SESSION_NONE != session) {
// add client context to request
clientCtx = ctxClientGet(request);
if (clientCtx == NULL) {
request->errcode = MHD_HTTP_INSUFFICIENT_STORAGE;
json_object_object_add(jcall, "status", json_object_new_string("fail"));
json_object_object_add(jcall, "info", json_object_new_string("Client Session Context Full !!!"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
}
request->context = clientCtx->contexts[apiidx];
request->uuid = clientCtx->uuid;
if (verbose)
fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[%p] Uuid=[%s] Token=[%s]\n", request->prefix, request->method, session, clientCtx, clientCtx->uuid, clientCtx->token);
switch (session) {
case AFB_SESSION_CREATE:
if (clientCtx->token[0] != '\0' && request->config->token[0] != '\0') {
request->errcode = MHD_HTTP_UNAUTHORIZED;
json_object_object_add(jcall, "status", json_object_new_string("exist"));
json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CREATE Session already exist"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
}
if (AFB_SUCCESS != ctxTokenCreate(clientCtx, request)) {
request->errcode = MHD_HTTP_UNAUTHORIZED;
json_object_object_add(jcall, "status", json_object_new_string("fail"));
json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CREATE Invalid Initial Token"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
} else {
json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid));
json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token));
json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout));
}
break;
case AFB_SESSION_RENEW:
if (AFB_SUCCESS != ctxTokenRefresh(clientCtx, request)) {
request->errcode = MHD_HTTP_UNAUTHORIZED;
json_object_object_add(jcall, "status", json_object_new_string("fail"));
json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_REFRESH Broken Exchange Token Chain"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
} else {
json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid));
json_object_object_add(jcall, "token", json_object_new_string(clientCtx->token));
json_object_object_add(jcall, "timeout", json_object_new_int(request->config->cntxTimeout));
}
break;
case AFB_SESSION_CLOSE:
if (AFB_SUCCESS != ctxTokenCheck(clientCtx, request)) {
request->errcode = MHD_HTTP_UNAUTHORIZED;
json_object_object_add(jcall, "status", json_object_new_string("empty"));
json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CLOSE Not a Valid Access Token"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
} else {
json_object_object_add(jcall, "uuid", json_object_new_string(clientCtx->uuid));
}
break;
case AFB_SESSION_CHECK:
default:
// default action is check
if (AFB_SUCCESS != ctxTokenCheck(clientCtx, request)) {
request->errcode = MHD_HTTP_UNAUTHORIZED;
json_object_object_add(jcall, "status", json_object_new_string("fail"));
json_object_object_add(jcall, "info", json_object_new_string("AFB_SESSION_CHECK Invalid Active Token"));
json_object_object_add(jreqt, "request", jcall);
goto ExitOnDone;
}
break;
}
}
// Effectively CALL PLUGIN API with a subset of the context
jresp = afb_apis_get(apiidx, verbidx)->callback(request, context);
// Store context in case it was updated by plugins
if (request->context != NULL)
clientCtx->contexts[apiidx] = request->context;
// handle intermediary Post Iterates out of band
if ((jresp == NULL) && (request->errcode == MHD_HTTP_OK))
return AFB_SUCCESS;
// Session close is done after the API call so API can still use session in closing API
if (AFB_SESSION_CLOSE == session)
ctxTokenReset(clientCtx, request);
// API should return NULL of a valid Json Object
if (jresp == NULL) {
json_object_object_add(jcall, "status", json_object_new_string("null"));
json_object_object_add(jreqt, "request", jcall);
request->errcode = MHD_HTTP_NO_RESPONSE;
} else {
json_object_object_add(jcall, "status", json_object_new_string("processed"));
json_object_object_add(jreqt, "request", jcall);
json_object_object_add(jreqt, "response", jresp);
}
ExitOnDone:
request->jresp = jreqt;
return AFB_DONE;
}
// Check of apiurl is declare in this plugin and call it
extern __thread sigjmp_buf *error_handler;
static AFB_error callPluginApi(AFB_request * request, int apiidx, int verbidx, void *context)
{
sigjmp_buf jmpbuf;
json_object *jcall, *jreqt;
int status;
// save context before calling the API
status = setjmp(jmpbuf);
if (status != 0) {
// Request was found and at least partially executed
jreqt = json_object_new_object();
json_object_get(afbJsonType); // increate jsontype reference count
json_object_object_add(jreqt, "jtype", afbJsonType);
// prepare an object to store calling values
jcall = json_object_new_object();
json_object_object_add(jcall, "prefix", json_object_new_string(request->prefix));
json_object_object_add(jcall, "api", json_object_new_string(request->method));
// Plugin aborted somewhere during its execution
json_object_object_add(jcall, "status", json_object_new_string("abort"));
json_object_object_add(jcall, "info", json_object_new_string("Plugin broke during execution"));
json_object_object_add(jreqt, "request", jcall);
request->jresp = jreqt;
goto ExitOnDone;
} else {
// Trigger a timer to protect from unacceptable long time execution
if (request->config->apiTimeout > 0)
alarm((unsigned)request->config->apiTimeout);
error_handler = &jmpbuf;
doCallPluginApi(request, apiidx, verbidx, context);
error_handler = NULL;
// cancel timeout and plugin signal handle before next call
alarm(0);
}
ExitOnDone:
return AFB_DONE;
}
STATIC AFB_error findAndCallApi(AFB_request * request, void *context)
{
int apiidx, verbidx;
AFB_error status;
if (!request->method || !request->prefix)
return AFB_FAIL;
/* get the plugin if any */
apiidx = afb_apis_get_apiidx(request->prefix, 0);
if (apiidx < 0) {
request->jresp = jsonNewMessage(AFB_FATAL, "No Plugin=[%s] Url=%s", request->prefix, request->url);
request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
return AFB_FAIL;
}
/* get the verb if any */
verbidx = afb_apis_get_verbidx(apiidx, request->method);
if (verbidx < 0) {
request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->method, request->prefix, request->url);
request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
return AFB_FAIL;
}
/* Search for a plugin with this urlpath */
status = callPluginApi(request, apiidx, verbidx, context);
/* plugin callback did not return a valid Json Object */
if (status == AFB_FAIL) {
request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s] url=[%s]", request->method, request->prefix, request->url);
request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY;
return AFB_FAIL;
}
// Everything look OK
return status;
}
// This CB is call for every item with a form post it reformat iterator values
// and callback Plugin API for each Item within PostForm.
STATIC int doPostIterate(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *mimetype, const char *encoding, const char *data, uint64_t offset, size_t size)
{
AFB_error status;
AFB_PostItem item;
// retrieve API request from Post iterator handle
AFB_PostHandle *postHandle = (AFB_PostHandle *) cls;
AFB_request *request = (AFB_request *) postHandle->privatebuf;
AFB_PostRequest postRequest;
if (verbose)
fprintf(stderr, "postHandle key=%s filename=%s len=%zu mime=%s\n", key, filename, size, mimetype);
// Create and Item value for Plugin API
item.kind = kind;
item.key = key;
item.filename = filename;
item.mimetype = mimetype;
item.encoding = encoding;
item.len = size;
item.data = data;
item.offset = offset;
// Reformat Request to make it somehow similar to GET/PostJson case
postRequest.data = (char *)postHandle;
postRequest.len = size;
postRequest.type = AFB_POST_FORM;;
request->post = &postRequest;
// effectively call plugin API
status = findAndCallApi(request, &item);
// when returning no processing of postform stop
if (status != AFB_SUCCESS)
return MHD_NO;
// let's allow iterator to move to next item
return MHD_YES;
}
STATIC void freeRequest(AFB_request * request)
{
free(request->prefix);
free(request->method);
free(request);
}
STATIC AFB_request *createRequest(struct MHD_Connection *connection, AFB_session * session, const char *url)
{
AFB_request *request;
// Start with a clean request
request = calloc(1, sizeof(AFB_request));
char *urlcpy1, *urlcpy2;
char *baseapi, *baseurl;
// Extract plugin urlpath from request and make two copy because strsep overload copy
urlcpy1 = urlcpy2 = strdup(url);
baseurl = strsep(&urlcpy2, "/");
if (baseurl == NULL) {
request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url);
request->errcode = MHD_HTTP_BAD_REQUEST;
goto Done;
}
// let's compute URL and call API
baseapi = strsep(&urlcpy2, "/");
if (baseapi == NULL) {
request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call plugin=[%s] url=[%s]", baseurl, url);
request->errcode = MHD_HTTP_BAD_REQUEST;
goto Done;
}
// build request structure
request->connection = connection;
request->config = session->config;
request->url = url;
request->prefix = strdup(baseurl);
request->method = strdup(baseapi);
Done:
free(urlcpy1);
return (request);
}
static int doRestApiPost(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls)
{
static int postcount = 0; // static counter to debug POST protocol
json_object *errMessage;
AFB_error status;
struct MHD_Response *webResponse;
const char *serialized;
AFB_request *request = NULL;
AFB_PostHandle *postHandle;
AFB_PostRequest postRequest;
int ret;
// fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls);
// if post data may come in multiple calls
const char *encoding, *param;
int contentlen = -1;
postHandle = *con_cls;
// This is the initial post event let's create form post structure POST data come in multiple events
if (postHandle == NULL) {
// allocate application POST processor handle to zero
postHandle = calloc(1, sizeof(AFB_PostHandle));
postHandle->uid = postcount++; // build a UID for DEBUG
*con_cls = postHandle; // update context with posthandle
// Let make sure we have the right encoding and a valid length
encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE);
// We are facing an empty post let's process it as a get
if (encoding == NULL) {
postHandle->type = AFB_POST_EMPTY;
return MHD_YES;
}
// Form post is handle through a PostProcessor and call API once per form key
if (strcasestr(encoding, FORM_CONTENT) != NULL) {
if (verbose)
fprintf(stderr, "Create doPostIterate[uid=%d posthandle=%p]\n", postHandle->uid, postHandle);
request = createRequest(connection, session, url);
if (request->jresp != NULL)
goto ProcessApiCall;
postHandle->type = AFB_POST_FORM;
postHandle->privatebuf = (void *)request;
postHandle->pp = MHD_create_post_processor(connection, MAX_POST_SIZE, &doPostIterate, postHandle);
if (NULL == postHandle->pp) {
fprintf(stderr, "OOPS: Internal error fail to allocate MHD_create_post_processor\n");
free(postHandle);
return MHD_NO;
}
return MHD_YES;
}
// POST json is store into a buffer and present in one piece to API
if (strcasestr(encoding, JSON_CONTENT) != NULL) {
param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
if (param)
sscanf(param, "%i", &contentlen);
// Because PostJson are build in RAM size is constrained
if (contentlen > MAX_POST_SIZE) {
errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE);
goto ExitOnError;
}
// Size is OK, let's allocate a buffer to hold post data
postHandle->type = AFB_POST_JSON;
postHandle->privatebuf = malloc((unsigned)contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string
// if (verbose) fprintf(stderr, "Create PostJson[uid=%d] Size=%d\n", postHandle->uid, contentlen);
return MHD_YES;
} else {
// We only support Json and Form Post format
errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT);
goto ExitOnError;
}
}
// This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless
// return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with
// 'Internal application error, closing connection'.
if (*upload_data_size) {
if (postHandle->type == AFB_POST_FORM) {
// if (verbose) fprintf(stderr, "Processing PostForm[uid=%d]\n", postHandle->uid);
MHD_post_process(postHandle->pp, upload_data, *upload_data_size);
}
// Process JsonPost request when buffer is completed let's call API
if (postHandle->type == AFB_POST_JSON) {
// if (verbose) fprintf(stderr, "Updating PostJson[uid=%d]\n", postHandle->uid);
memcpy(&postHandle->privatebuf[postHandle->len], upload_data, *upload_data_size);
postHandle->len = postHandle->len + *upload_data_size;
}
*upload_data_size = 0;
return MHD_YES;
} else { // we have finish with Post reception let's finish the work
// Create a request structure to finalise the request
request = createRequest(connection, session, url);
if (request->jresp != NULL) {
errMessage = request->jresp;
goto ExitOnError;
}
postRequest.type = postHandle->type;
// Postform add application context handle to request
if (postHandle->type == AFB_POST_FORM) {
postRequest.data = (char *)postHandle;
request->post = &postRequest;
}
if (postHandle->type == AFB_POST_JSON) {
// if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid);
param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH);
if (param)
sscanf(param, "%i", &contentlen);
// At this level we're may verify that we got everything and process DATA
if (postHandle->len != contentlen) {
errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %d", postHandle->uid, contentlen, postHandle->len);
goto ExitOnError;
}
// Before processing data, make sure buffer string is properly ended
postHandle->privatebuf[postHandle->len] = '\0';
postRequest.data = postHandle->privatebuf;
request->post = &postRequest;
// if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", postHandle->uid, request->post->data);
}
}
ProcessApiCall:
// Request is ready let's call API without any extra handle
status = findAndCallApi(request, NULL);
serialized = json_object_to_json_string(request->jresp);
webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY);
// client did not pass token on URI let's use cookies
if ((!request->restfull) && (request->context != NULL)) {
char cookie[256];
snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout);
MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie);
}
// if requested add an error status
if (request->errcode != 0)
ret = MHD_queue_response(connection, request->errcode, webResponse);
else
MHD_queue_response(connection, MHD_HTTP_OK, webResponse);
MHD_destroy_response(webResponse);
json_object_put(request->jresp); // decrease reference rqtcount to free the json object
freeRequest(request);
return MHD_YES;
ExitOnError:
freeRequest(request);
serialized = json_object_to_json_string(errMessage);
webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY);
MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse);
MHD_destroy_response(webResponse);
json_object_put(errMessage); // decrease reference rqtcount to free the json object
return MHD_YES;
}
static int doRestApiGet(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls)
{
AFB_error status;
struct MHD_Response *webResponse;
const char *serialized;
AFB_request *request = NULL;
int ret;
// fprintf (stderr, "doRestAPI method=%s posthandle=%p\n", method, con_cls);
// if post data may come in multiple calls
// this is a get we only need a request
request = createRequest(connection, session, url);
// Request is ready let's call API without any extra handle
status = findAndCallApi(request, NULL);
serialized = json_object_to_json_string(request->jresp);
webResponse = MHD_create_response_from_buffer(strlen(serialized), (void *)serialized, MHD_RESPMEM_MUST_COPY);
// client did not pass token on URI let's use cookies
if ((!request->restfull) && (request->context != NULL)) {
char cookie[256];
snprintf(cookie, sizeof(cookie), "%s-%d=%s; Path=%s; Max-Age=%d; HttpOnly", COOKIE_NAME, request->config->httpdPort, request->uuid, request->config->rootapi, request->config->cntxTimeout);
MHD_add_response_header(webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie);
}
// if requested add an error status
if (request->errcode != 0)
ret = MHD_queue_response(connection, request->errcode, webResponse);
else
MHD_queue_response(connection, MHD_HTTP_OK, webResponse);
MHD_destroy_response(webResponse);
json_object_put(request->jresp); // decrease reference rqtcount to free the json object
freeRequest(request);
return MHD_YES;
}
int doRestApi(struct MHD_Connection *connection, AFB_session * session, const char *url, const char *method, const char *upload_data, size_t * upload_data_size, void **con_cls)
{
int rc;
if (afbJsonType == NULL)
afbJsonType = json_object_new_string (AFB_MSG_JTYPE);
if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
rc = doRestApiPost(connection, session, url, method, upload_data, upload_data_size, con_cls);
} else {
rc = doRestApiGet(connection, session, url, method, upload_data, upload_data_size, con_cls);
}
return rc;
}
|