LCOV - code coverage report
Current view: top level - extra/webserver/webserver/filter - SPWebMultipartParser.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 370 455 81.3 %
Date: 2024-05-12 00:16:13 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPWebMultipartParser.h"
      24             : #include "SPValid.h"
      25             : #include "SPDbFile.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::web {
      28             : 
      29          75 : MultipartParser::MultipartParser(const db::InputConfig &c, size_t s, const StringView &b)
      30          75 : : InputParser(c, s) {
      31          75 :         boundary.reserve(b.size() + 4);
      32          75 :         boundary.append("\r\n--");
      33          75 :         boundary.append(b.data(), b.size());
      34          75 :         match = 2;
      35          75 : }
      36             : 
      37         375 : Value * MultipartParser::flushVarName(StringView &r) {
      38         375 :         VarState cstate = VarState::Key;
      39         375 :         Value *current = nullptr;
      40        1125 :         while (!r.empty()) {
      41         750 :                 StringView str = r.readUntil<chars::Chars<char, '[', ']'>>();
      42         750 :                 current = flushString(str, current, cstate);
      43         750 :                 if (!current) {
      44           0 :                         break;
      45             :                 }
      46         750 :                 if (!r.empty()) {
      47         650 :                         switch (cstate) {
      48         275 :                         case VarState::Key:
      49         275 :                                 switch (r[0]) {
      50         275 :                                 case '[':                       cstate = VarState::SubKey; break;
      51           0 :                                 default:                        cstate = VarState::End; break;
      52             :                                 }
      53         275 :                                 break;
      54         325 :                         case VarState::SubKey:
      55         325 :                                 switch (r[0]) {
      56         325 :                                 case ']':                       cstate = VarState::SubKeyEnd; break;
      57           0 :                                 default:                        cstate = VarState::End; break;
      58             :                                 }
      59         325 :                                 break;
      60          50 :                         case VarState::SubKeyEnd:
      61          50 :                                 switch (r[0]) {
      62          50 :                                 case '[':                       cstate = VarState::SubKey; break;
      63           0 :                                 default:                        cstate = VarState::End; break;
      64             :                                 }
      65          50 :                                 break;
      66           0 :                         default:
      67           0 :                                 return nullptr;
      68             :                                 break;
      69             :                         }
      70         650 :                         ++ r;
      71             :                 }
      72             :         }
      73         375 :         return current;
      74             : }
      75             : 
      76        1000 : void MultipartParser::flushLiteral(StringView &r, bool quoted) {
      77        1000 :         auto tmp = r;
      78        1000 :         if (!quoted) {
      79         750 :                 tmp.trimChars<StringView::WhiteSpace>();
      80             :         }
      81        1000 :         switch (header) {
      82         125 :         case Header::ContentDispositionFileName:
      83         125 :                 file.assign(tmp.data(), tmp.size());
      84         125 :                 break;
      85         500 :         case Header::ContentDispositionName:
      86         500 :                 name.assign(tmp.data(), tmp.size());
      87         500 :                 break;
      88         125 :         case Header::ContentDispositionSize:
      89         125 :                 size = strtol(r.data(), nullptr, 10);
      90         125 :                 break;
      91         125 :         case Header::ContentType:
      92         125 :                 type.assign(tmp.data(), tmp.size());
      93         125 :                 break;
      94         125 :         case Header::ContentEncoding:
      95         125 :                 encoding.assign(tmp.data(), tmp.size());
      96         125 :                 break;
      97           0 :         default:
      98           0 :                 break;
      99             :         }
     100        1000 : }
     101             : 
     102        8300 : void MultipartParser::flushData(const BytesView &r) {
     103        8300 :         switch (data) {
     104        7750 :         case Data::File:
     105        7750 :                 if (r.size() + files.back().writeSize >= getConfig().maxFileSize) {
     106           0 :                         files.back().close();
     107           0 :                         files.pop_back();
     108           0 :                         data = Data::Skip;
     109             :                 } else {
     110        7750 :                         files.back().write((const char *)r.data(), r.size());
     111             :                 }
     112        7750 :                 break;
     113         175 :         case Data::FileAsData:
     114         175 :                 if (r.size() + streamBuf.size() >= getConfig().maxFileSize) {
     115           0 :                         files.back().close();
     116           0 :                         files.pop_back();
     117           0 :                         data = Data::Skip;
     118             :                 } else {
     119         175 :                         streamBuf.write((const char *)r.data(), r.size());
     120             :                 }
     121         175 :                 break;
     122         375 :         case Data::Var:
     123         375 :                 if (r.size() + buf.size() >= getConfig().maxVarSize) {
     124           0 :                         buf.clear();
     125           0 :                         data = Data::Skip;
     126             :                 } else {
     127         375 :                         buf.put(r.data(), r.size());
     128             :                 }
     129         375 :                 break;
     130           0 :         case Data::Skip:
     131           0 :                 break;
     132             :         }
     133        8300 : }
     134             : 
     135          75 : bool MultipartParser::readBegin(BytesView &r) {
     136          75 :         StringView tmp = r.toStringView();
     137             : 
     138          75 :         if (match == 0) {
     139           0 :                 tmp.skipUntil<StringView::Chars<'-'>>();
     140             :         }
     141        3150 :         while (match < boundary.length() && tmp.is(boundary.at(match))) {
     142        3075 :                 ++ match; ++ tmp;
     143             :         }
     144          75 :         if (tmp.empty()) {
     145           0 :                 r = BytesView((const uint8_t *)tmp.data(), r.size() - (tmp.data() - (const char *)r.data()));
     146           0 :                 return true;
     147          75 :         } else if (match == boundary.length()) {
     148          75 :                 state = State::BeginBlock;
     149          75 :                 target = &root;
     150          75 :                 match = 0;
     151             :         } else {
     152           0 :                 match = 0;
     153           0 :                 return false;
     154             :         }
     155          75 :         buf.clear();
     156             : 
     157          75 :         r = BytesView((const uint8_t *)tmp.data(), r.size() - (tmp.data() - (const char *)r.data()));
     158          75 :         return true;
     159             : }
     160             : 
     161         575 : void MultipartParser::readBlock(BytesView &r) {
     162        1150 :         while (!r.empty()) {
     163        1150 :                 if (buf.size() == 0 && r.is('-')) {
     164          75 :                         buf.putc(char(r[0]));
     165        1075 :                 } else if (buf.size() == 1 && buf.get().is('-') && r.is('-')) {
     166          75 :                         state = State::End;
     167          75 :                         return;
     168             :                 } else {
     169        1000 :                         if (r.is('\n')) {
     170         500 :                                 state = State::HeaderLine;
     171         500 :                                 header = Header::Begin;
     172         500 :                                 name.clear();
     173         500 :                                 type.clear();
     174         500 :                                 encoding.clear();
     175         500 :                                 file.clear();
     176         500 :                                 size = 0;
     177         500 :                                 buf.clear();
     178         500 :                                 ++ r;
     179         500 :                                 return;
     180             :                         } else {
     181         500 :                                 ++ r;
     182             :                         }
     183             :                 }
     184             :         }
     185             : }
     186             : 
     187        1275 : void MultipartParser::readHeaderBegin(StringView &r) {
     188        1275 :         StringView str = r.readUntil<StringView::Chars<'\n', ':'>>();
     189        1275 :         if (r.is(':')) {
     190         775 :                 StringView tmp;
     191         775 :                 if (buf.empty()) {
     192         775 :                         tmp = str;
     193             :                 } else {
     194           0 :                         buf.put(str.data(), str.size());
     195           0 :                         tmp = buf.get();
     196             :                 }
     197             : 
     198         775 :                 tmp.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     199         775 :                 if (strncasecmp(tmp.data(), "Content-Disposition", "Content-Disposition"_len) == 0) {
     200         500 :                         header = Header::ContentDisposition;
     201         275 :                 } else if (strncasecmp(tmp.data(), "Content-Type", "Content-Type"_len) == 0) {
     202         125 :                         header = Header::ContentType;
     203         150 :                 } else if (strncasecmp(tmp.data(), "Content-Transfer-Encoding", "Content-Transfer-Encoding"_len) == 0) {
     204         125 :                         header = Header::ContentEncoding;
     205             :                 } else {
     206          25 :                         header = Header::Unknown;
     207             :                 }
     208             : 
     209         775 :                 buf.clear();
     210         775 :                 r ++;
     211         500 :         } else if (r.empty()) {
     212           0 :                 buf.put(str.data(), str.size());
     213         500 :         } else if (r.is('\n')) {
     214         500 :                 auto tmp = buf.get();
     215         500 :                 str.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     216         500 :                 tmp.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     217         500 :                 if ((tmp.empty() || tmp.is('\r')) && (str.empty() || str.is('\r'))) {
     218         500 :                         state = State::Data;
     219         500 :                         if (!file.empty() || !type.empty()) {
     220         125 :                                 if ((config.required & db::InputConfig::Require::FilesAsData) != db::InputConfig::Require::None
     221          50 :                                                 && (size == 0 || size < getConfig().maxFileSize)
     222         175 :                                                 && db::InputConfig::isFileAsDataSupportedForType(type)) {
     223          50 :                                         streamBuf.clear();
     224          50 :                                         data = Data::FileAsData;
     225          75 :                                 } else if ((config.required & db::InputConfig::Require::Files) != 0
     226          75 :                                                 && (size == 0 || size < getConfig().maxFileSize)) {
     227          75 :                                         files.emplace_back(std::move(name), std::move(type), std::move(encoding), std::move(file), size, files.size());
     228          75 :                                         data = Data::File;
     229             :                                 } else {
     230           0 :                                         data = Data::Skip;
     231             :                                 }
     232             :                         } else {
     233         750 :                                 if ((config.required & db::InputConfig::Require::Data) != 0 &&
     234         375 :                                                 (size == 0 || size < getConfig().maxVarSize)) {
     235         375 :                                         data = Data::Var;
     236             :                                 } else {
     237           0 :                                         data = Data::Skip;
     238             :                                 }
     239             :                         }
     240             : 
     241         500 :                         name.empty();
     242         500 :                         type.empty();
     243         500 :                         encoding.empty();
     244         500 :                         file.empty();
     245         500 :                         size = 0;
     246             :                 }
     247         500 :                 header = Header::Begin; // next header
     248         500 :                 buf.clear();
     249         500 :                 r ++;
     250             :         }
     251        1275 : }
     252             : 
     253         500 : void MultipartParser::readHeaderContentDisposition(StringView &r) {
     254         500 :         StringView str = r.readUntil<StringView::Chars<'\n', ';'>>();
     255         500 :         if (r.is(';')) {
     256         500 :                 StringView tmp;
     257         500 :                 if (buf.empty()) {
     258         500 :                         tmp = str;
     259             :                 } else {
     260           0 :                         buf.put(str.data(), str.size());
     261           0 :                         tmp = buf.get();
     262             :                 }
     263             : 
     264         500 :                 tmp.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     265         500 :                 if (strncasecmp(tmp.data(), "form-data", "form-data"_len) == 0) {
     266         500 :                         header = Header::ContentDispositionParams;
     267             :                 } else {
     268           0 :                         header = Header::Unknown;
     269             :                 }
     270             : 
     271         500 :                 buf.clear();
     272         500 :                 r ++;
     273           0 :         } else if (r.empty()) {
     274           0 :                 buf.put(str.data(), str.size());
     275           0 :         } else if (r.is('\n')) {
     276           0 :                 header = Header::Begin; // next header
     277           0 :                 buf.clear();
     278           0 :                 r ++;
     279             :         }
     280         500 : }
     281             : 
     282         825 : void MultipartParser::readHeaderContentDispositionParam(StringView &r) {
     283         825 :         if (buf.empty()) {
     284         825 :                 r.skipChars<StringView::Chars<';', ' '>>();
     285             :         }
     286         825 :         StringView str = r.readUntil<StringView::Chars<'\n', '='>>();
     287         825 :         if (r.is('=')) {
     288         825 :                 StringView tmp;
     289         825 :                 if (buf.empty()) {
     290         825 :                         tmp = str;
     291             :                 } else {
     292           0 :                         buf.put(str.data(), str.size());
     293           0 :                         tmp = buf.get();
     294             :                 }
     295             : 
     296         825 :                 if (strncasecmp(tmp.data(), "name", "name"_len) == 0) {
     297         500 :                         header = Header::ContentDispositionName;
     298         500 :                         literal = Literal::None;
     299         325 :                 } else if (strncasecmp(tmp.data(), "filename", "filename"_len) == 0) {
     300         125 :                         header = Header::ContentDispositionFileName;
     301         125 :                         literal = Literal::None;
     302         200 :                 } else if (strncasecmp(tmp.data(), "size", "size"_len) == 0) {
     303         125 :                         header = Header::ContentDispositionSize;
     304         125 :                         literal = Literal::None;
     305             :                 } else {
     306          75 :                         header = Header::ContentDispositionUnknown;
     307             :                 }
     308             : 
     309         825 :                 buf.clear();
     310         825 :                 r ++;
     311           0 :         } else if (r.empty()) {
     312           0 :                 buf.put(str.data(), str.size());
     313           0 :         } else if (r.is('\n')) {
     314           0 :                 header = Header::Begin; // next header
     315           0 :                 buf.clear();
     316           0 :                 r ++;
     317             :         }
     318         825 : }
     319             : 
     320         250 : void MultipartParser::readHeaderValue(StringView &r) {
     321         250 :         auto &max = getConfig().maxVarSize;
     322         250 :         StringView str = r.readUntil<StringView::Chars<'\n'>>();
     323         250 :         if (r.empty()) {
     324           0 :                 if (buf.size() + str.size() < max) {
     325           0 :                         buf.put(str.data(), str.size());
     326             :                 } else {
     327           0 :                         header = Header::Unknown; // skip processing
     328             :                 }
     329         250 :         } else if (r.is('\n')) {
     330         250 :                 StringView tmp;
     331         250 :                 if (buf.empty()) {
     332         250 :                         if (str.size() < max) {
     333         250 :                                 tmp = str;
     334             :                         }
     335             :                 } else {
     336           0 :                         if (str.size() + buf.size() < max) {
     337           0 :                                 buf.put(str.data(), str.size());
     338           0 :                                 tmp = buf.get();
     339             :                         }
     340             :                 }
     341             : 
     342         250 :                 if (!tmp.empty()) {
     343         250 :                         flushLiteral(tmp, false);
     344             :                 }
     345             : 
     346         250 :                 header = Header::Begin; // next header
     347         250 :                 literal = Literal::None;
     348         250 :                 buf.clear();
     349         250 :                 r ++;
     350             :         }
     351         250 : }
     352             : 
     353          25 : void MultipartParser::readHeaderDummy(StringView &r) {
     354          25 :         r.skipUntil<StringView::Chars<'\n'>>();
     355          25 :         if (r.is('\n')) {
     356          25 :                 header = Header::Begin; // next header
     357          25 :                 literal = Literal::None;
     358          25 :                 buf.clear();
     359          25 :                 r ++;
     360             :         }
     361          25 : }
     362             : 
     363         500 : void MultipartParser::readPlainLiteral(StringView &r) {
     364         500 :         auto &max = getConfig().maxVarSize;
     365         500 :         StringView str = r.readUntil<StringView::Chars<'\n', ';'>>();
     366         500 :         if (r.is(';') || r.is('\n')) {
     367         500 :                 StringView tmp;
     368         500 :                 if (buf.empty()) {
     369         500 :                         if (str.size() < max) {
     370         500 :                                 tmp = str;
     371             :                         } else {
     372           0 :                                 header = Header::ContentDispositionUnknown;
     373             :                         }
     374             :                 } else {
     375           0 :                         if (str.size() + buf.size() < max) {
     376           0 :                                 buf.put(str.data(), str.size());
     377           0 :                                 tmp = buf.get();
     378             :                         } else {
     379           0 :                                 header = Header::ContentDispositionUnknown;
     380             :                         }
     381             :                 }
     382             : 
     383         500 :                 if (!tmp.empty()) {
     384         500 :                         flushLiteral(tmp, false);
     385             :                 }
     386             : 
     387         500 :                 header = r.is(';') ? Header::ContentDispositionParams : Header::Begin;
     388         500 :                 literal = Literal::None;
     389         500 :                 buf.clear();
     390         500 :                 r ++;
     391           0 :         } else if (r.empty()) {
     392           0 :                 if (str.size() + buf.size() < max) {
     393           0 :                         buf.put(str.data(), str.size());
     394             :                 } else {
     395           0 :                         header = Header::ContentDispositionUnknown;
     396             :                 }
     397             :         }
     398         500 : }
     399             : 
     400         250 : void MultipartParser::readQuotedLiteral(StringView &r) {
     401         250 :         auto &max = getConfig().maxVarSize;
     402         250 :         StringView str = r.readUntil<StringView::Chars<'\n', '"'>>();
     403         250 :         if (r.is('"')) {
     404         250 :                 StringView tmp;
     405         250 :                 if (buf.empty()) {
     406         250 :                         if (str.size() < max) {
     407         250 :                                 tmp = str;
     408             :                         } else {
     409           0 :                                 header = Header::ContentDispositionUnknown;
     410             :                         }
     411             :                 } else {
     412           0 :                         if (buf.size() + str.size() < max) {
     413           0 :                                 buf.put(str.data(), str.size());
     414           0 :                                 tmp = buf.get();
     415             :                         } else {
     416           0 :                                 header = Header::ContentDispositionUnknown;
     417             :                         }
     418             :                 }
     419             : 
     420         250 :                 flushLiteral(tmp, true);
     421         250 :                 buf.clear();
     422         250 :                 r ++;
     423         250 :                 header = Header::ContentDispositionParams;
     424         250 :                 literal = Literal::None;
     425           0 :         } else if (r.empty()) {
     426           0 :                 if (buf.size() + str.size() < max) {
     427           0 :                         buf.put(str.data(), str.size());
     428             :                 } else {
     429           0 :                         header = Header::ContentDispositionUnknown;
     430             :                 }
     431           0 :         } else if (r.is('\n')) {
     432           0 :                 header = Header::Begin; // next header
     433           0 :                 literal = Literal::None;
     434           0 :                 buf.clear();
     435           0 :                 r ++;
     436             :         }
     437         250 : }
     438             : 
     439        1500 : void MultipartParser::readHeaderContentDispositionValue(StringView &r) {
     440        1500 :         switch (literal) {
     441         750 :         case Literal::None:
     442         750 :                 if (r.is('"')) {
     443         250 :                         literal = Literal::Quoted;
     444         250 :                         r ++;
     445         500 :                 } else if (r.is('\n')) {
     446           0 :                         header = Header::Begin; // next header
     447           0 :                         buf.clear();
     448           0 :                         r ++;
     449         500 :                 } else if (!r.is<StringView::CharGroup<CharGroupId::WhiteSpace>>()) {
     450         500 :                         literal = Literal::Plain;
     451             :                 } else {
     452           0 :                         header = Header::ContentDispositionParams;
     453             :                 }
     454         750 :                 break;
     455         500 :         case Literal::Plain:
     456         500 :                 readPlainLiteral(r);
     457         500 :                 break;
     458         250 :         case Literal::Quoted:
     459         250 :                 readQuotedLiteral(r);
     460         250 :                 break;
     461             :         }
     462        1500 : }
     463             : 
     464          75 : void MultipartParser::readHeaderContentDispositionDummy(StringView &r) {
     465          75 :         r.skipUntil<StringView::Chars<'\n', ';'>>();
     466          75 :         if (r.is(';')) {
     467          25 :                 header = Header::ContentDispositionParams;
     468          25 :                 literal = Literal::None;
     469          25 :                 buf.clear();
     470          25 :                 r ++;
     471          50 :         } else if (r.is('\n')) {
     472          50 :                 header = Header::Begin; // next header
     473          50 :                 literal = Literal::None;
     474          50 :                 buf.clear();
     475          50 :                 r ++;
     476             :         }
     477          75 : }
     478             : 
     479        4450 : void MultipartParser::readHeader(BytesView &r) {
     480        4450 :         StringView tmp = r.toStringView();
     481             : 
     482        4450 :         switch (header) {
     483        1275 :         case Header::Begin:
     484        1275 :                 readHeaderBegin(tmp);
     485        1275 :                 break;
     486         500 :         case Header::ContentDisposition:
     487         500 :                 readHeaderContentDisposition(tmp);
     488         500 :                 break;
     489         825 :         case Header::ContentDispositionParams:
     490         825 :                 readHeaderContentDispositionParam(tmp);
     491         825 :                 break;
     492        1500 :         case Header::ContentDispositionName:
     493             :         case Header::ContentDispositionFileName:
     494             :         case Header::ContentDispositionSize:
     495        1500 :                 readHeaderContentDispositionValue(tmp);
     496        1500 :                 break;
     497          75 :         case Header::ContentDispositionUnknown:
     498          75 :                 readHeaderContentDispositionDummy(tmp);
     499          75 :                 break;
     500         250 :         case Header::ContentType:
     501             :         case Header::ContentEncoding:
     502         250 :                 readHeaderValue(tmp);
     503         250 :                 break;
     504          25 :         case Header::Unknown:
     505          25 :                 readHeaderDummy(tmp);
     506          25 :                 break;
     507             :         }
     508             : 
     509        4450 :         r = BytesView((const uint8_t *)tmp.data(), r.size() - (tmp.data() - (const char *)r.data()));
     510        4450 : }
     511             : 
     512        8800 : void MultipartParser::readData(BytesView &r) {
     513        8800 :         if (match == 0) {
     514        4500 :                 flushData(r.readUntil<uint8_t('\r')>());
     515        4500 :                 if (r.empty()) {
     516         200 :                         return;
     517             :                 } else {
     518        4300 :                         match = 1;
     519        4300 :                         r ++;
     520             :                 }
     521             :         } else {
     522       25325 :                 while (!r.empty() && r[0] == boundary[match] && match < boundary.length()) {
     523       21025 :                         match ++;
     524       21025 :                         r ++;
     525             :                 }
     526             : 
     527        4300 :                 if (match == boundary.length()) {
     528         500 :                         state = State::BeginBlock;
     529         500 :                         target = &root;
     530         500 :                         if (data == Data::Var) {
     531         375 :                                 StringView tmp(name);
     532         375 :                                 auto current = flushVarName(tmp);
     533         375 :                                 if (current) {
     534         375 :                                         current->setString(buf.str());
     535             :                                 }
     536         375 :                                 buf.clear();
     537         125 :                         } else if (data == Data::FileAsData) {
     538          50 :                                 root.setValue(data::read<Interface>(streamBuf.weak()), std::move(name));
     539          50 :                                 streamBuf.clear();
     540             :                         }
     541         500 :                         match = 0;
     542        3800 :                 } else if (!r.empty() && r[0] != boundary[match]) {
     543        3800 :                         BytesView tmp((const uint8_t *)boundary.data(), match);
     544        3800 :                         flushData(tmp);
     545        3800 :                         match = 0;
     546             :                 }
     547             :         }
     548             : }
     549             : 
     550         275 : bool MultipartParser::run(BytesView r) {
     551       14175 :         while (!r.empty()) {
     552       13975 :                 switch (state) {
     553          75 :                 case State::Begin: // skip preambula
     554          75 :                         if (!readBegin(r)) {
     555           0 :                                 return false;
     556             :                         }
     557          75 :                         break;
     558         575 :                 case State::BeginBlock: // wait for CRLF then headers or "--" then EOF
     559         575 :                         readBlock(r);
     560         575 :                         break;
     561        4450 :                 case State::HeaderLine:
     562        4450 :                         readHeader(r);
     563        4450 :                         break;
     564        8800 :                 case State::Data:
     565        8800 :                         readData(r);
     566        8800 :                         break;
     567          75 :                 case State::End:
     568          75 :                         return true;
     569             :                         break;
     570             :                 }
     571             :         }
     572         200 :         return true;
     573             : }
     574             : 
     575          75 : void MultipartParser::finalize() {
     576             : 
     577          75 : }
     578             : 
     579         750 : auto MultipartParser::flushString(StringView &r, Value *cur, VarState varState) -> Value * {
     580         750 :         auto str = string::urldecode<Interface>(r);
     581             : 
     582         750 :         switch (varState) {
     583         375 :         case VarState::Key:
     584         375 :                 if (!str.empty()) {
     585         375 :                         if (target->hasValue(str)) {
     586         175 :                                 cur = &target->getValue(str);
     587             :                         } else {
     588         200 :                                 cur = &target->setValue(Value(true), str);
     589             :                         }
     590             :                 }
     591         375 :                 break;
     592         325 :         case VarState::SubKey:
     593         325 :                 if (cur) {
     594         325 :                         if (!str.empty() && valid::validateNumber(str)) {
     595         125 :                                 auto num = StringView(str).readInteger().get();
     596         125 :                                 if (cur->isArray()) {
     597         100 :                                         if (num < int64_t(cur->size())) {
     598          75 :                                                 cur = &cur->getValue(num);
     599         125 :                                                 return cur;
     600          25 :                                         } else if (num == int64_t(cur->size())) {
     601          25 :                                                 cur = &cur->addValue(Value(true));
     602          25 :                                                 return cur;
     603             :                                         }
     604          25 :                                 } else if (!cur->isDictionary() && num == 0) {
     605          25 :                                         cur->setArray(typename Value::ArrayType());
     606          25 :                                         cur = &cur->addValue(Value(true));
     607          25 :                                         return cur;
     608             :                                 }
     609             :                         }
     610         200 :                         if (str.empty()) {
     611         100 :                                 if (!cur->isArray()) {
     612          50 :                                         cur->setArray(typename Value::ArrayType());
     613             :                                 }
     614         100 :                                 cur = &cur->addValue(Value(true));
     615             :                         } else {
     616         100 :                                 if (!cur->isDictionary()) {
     617          50 :                                         cur->setDict(typename Value::DictionaryType());
     618             :                                 }
     619         100 :                                 if (cur->hasValue(str)) {
     620          25 :                                         cur = &cur->getValue(str);
     621             :                                 } else {
     622          75 :                                         cur = &cur->setValue(Value(true), str);
     623             :                                 }
     624             :                         }
     625             :                 }
     626         200 :                 break;
     627           0 :         case VarState::Value:
     628             :         case VarState::End:
     629           0 :                 if (cur) {
     630           0 :                         if (!str.empty()) {
     631           0 :                                 cur->setString(str);
     632             :                         }
     633           0 :                         cur = nullptr;
     634             :                 }
     635           0 :                 break;
     636          50 :         default:
     637          50 :                 break;
     638             :         }
     639             : 
     640         625 :         return cur;
     641         750 : }
     642             : 
     643             : }

Generated by: LCOV version 1.14