diff --git a/Tella.xcodeproj/project.pbxproj b/Tella.xcodeproj/project.pbxproj index c834af482..ae41236fc 100644 --- a/Tella.xcodeproj/project.pbxproj +++ b/Tella.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 0ECC7817267A533E00A2ACF2 /* PageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC7816267A533E00A2ACF2 /* PageViewCell.swift */; }; 0ECC7819267A53A700A2ACF2 /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC7818267A53A700A2ACF2 /* PageView.swift */; }; 0ECC781B267A542300A2ACF2 /* UwaziPages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */; }; + 1202C8A32DB985B80013E17D /* BackBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1202C8A22DB985B80013E17D /* BackBottomView.swift */; }; 1202D9582BF4CFF200EAFAB3 /* SelectValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1202D9572BF4CFF200EAFAB3 /* SelectValues.swift */; }; 1203CDDC298C5B9100D09073 /* ConnectionActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203CDDB298C5B9100D09073 /* ConnectionActionType.swift */; }; 1203CDDE298D7C1300D09073 /* FileToUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1203CDDD298D7C1300D09073 /* FileToUpload.swift */; }; @@ -59,6 +60,8 @@ 12199C362940966E0041BD38 /* OpenSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 12199C352940966E0041BD38 /* OpenSans-Italic.ttf */; }; 121AD8222943A2F00056AC2A /* ReportRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121AD8212943A2F00056AC2A /* ReportRepository.swift */; }; 121AD8262943D52B0056AC2A /* SubmitReportResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121AD8252943D52B0056AC2A /* SubmitReportResult.swift */; }; + 121AFF932E4CA9DA007300AD /* NearbySharingStateActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121AFF922E4CA9D6007300AD /* NearbySharingStateActor.swift */; }; + 121AFF952E4CDE7B007300AD /* NearbySharingServerHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121AFF942E4CDE6E007300AD /* NearbySharingServerHandler.swift */; }; 121BAB7F29A7E30600D7E847 /* URLRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121BAB7E29A7E30600D7E847 /* URLRequestExtension.swift */; }; 121C5D7529422C0800A64123 /* ProjectDetailsResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121C5D7429422C0800A64123 /* ProjectDetailsResult.swift */; }; 121C5D7729422D6800A64123 /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121C5D7629422D6800A64123 /* DataModel.swift */; }; @@ -69,6 +72,7 @@ 1220A72E292EB11B000BC24C /* ReportFileGridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220A72D292EB11B000BC24C /* ReportFileGridView.swift */; }; 1221CB7128DB1B5A0042F15F /* KeyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1221CB7028DB1B5A0042F15F /* KeyValue.swift */; }; 1221E05C2906CA6A00BC05F5 /* DraftReportVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1221E05B2906CA6A00BC05F5 /* DraftReportVM.swift */; }; + 1222363F2DD3DDC4006A9F01 /* RecipientPrepareFileTransferVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1222363E2DD3DDC1006A9F01 /* RecipientPrepareFileTransferVM.swift */; }; 12234BE029DAEC4A00D6F981 /* CustomNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12234BDF29DAEC4A00D6F981 /* CustomNavigation.swift */; }; 122389B3293630DA0058593B /* SaveSuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122389B2293630DA0058593B /* SaveSuccessView.swift */; }; 122389B7293635790058593B /* SetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122389B6293635790058593B /* SetExtension.swift */; }; @@ -93,6 +97,7 @@ 122E0AEE2A33B788009B1E33 /* FileDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122E0AED2A33B788009B1E33 /* FileDetails.swift */; }; 122E6A5329A3812A00BDACAD /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122E6A5229A3812A00BDACAD /* ReportViewModel.swift */; }; 122E6A5529A396C400BDACAD /* ServerFileSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122E6A5429A396C400BDACAD /* ServerFileSize.swift */; }; + 123004CD2D8CB767006F3F2D /* RecipientConnectManuallyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123004CC2D8CB752006F3F2D /* RecipientConnectManuallyViewModel.swift */; }; 123048D2295F84BD0015CD96 /* ProgressFileItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123048D1295F84BD0015CD96 /* ProgressFileItemViewModel.swift */; }; 1230BE4727CD6D440055F854 /* AudioAuthorizationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1230BE4627CD6D440055F854 /* AudioAuthorizationStatus.swift */; }; 123125422930241500D3BCD5 /* PhotoVideoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123125412930241500D3BCD5 /* PhotoVideoViewModel.swift */; }; @@ -114,24 +119,44 @@ 1237A71E278F5F8B00D7BC0F /* LanguageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1237A71D278F5F8B00D7BC0F /* LanguageManager.swift */; }; 123AE5A129CFAD7F00814CC7 /* OutboxReportVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123AE5A029CFAD7F00814CC7 /* OutboxReportVM.swift */; }; 123AE5A329CFB7F000814CC7 /* UploadReportOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123AE5A229CFB7F000814CC7 /* UploadReportOperation.swift */; }; + 123B19832D9DD3D200BAD892 /* ConnectionFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123B19822D9DD3D200BAD892 /* ConnectionFailedView.swift */; }; + 123CBAFA2DEEEF810030A9BD /* http.c in Sources */ = {isa = PBXBuildFile; fileRef = 123CBAF62DEEEF810030A9BD /* http.c */; }; + 123CBAFB2DEEEF810030A9BD /* api.c in Sources */ = {isa = PBXBuildFile; fileRef = 123CBAF52DEEEF810030A9BD /* api.c */; }; + 123CBAFC2DEEEF810030A9BD /* llhttp.c in Sources */ = {isa = PBXBuildFile; fileRef = 123CBAF82DEEEF810030A9BD /* llhttp.c */; }; + 123CBAFE2DEF0DCF0030A9BD /* HTTPParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CBAFD2DEF0DCD0030A9BD /* HTTPParser.swift */; }; + 123CBB022DEF34660030A9BD /* HTTPRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CBB012DEF34640030A9BD /* HTTPRequest.swift */; }; + 123CBB042DEF97290030A9BD /* HTTPStatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CBB032DEF97270030A9BD /* HTTPStatusCode.swift */; }; + 123CBB062DEF99470030A9BD /* NearbySharingEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CBB052DEF99410030A9BD /* NearbySharingEndpoint.swift */; }; + 123CBB082DEF998C0030A9BD /* NearbySharingServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CBB072DEF998A0030A9BD /* NearbySharingServerResponse.swift */; }; 123CE54F272028D1005BF386 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 123CE54E272028D1005BF386 /* Roboto-Light.ttf */; }; 123CE84429126B52003C251F /* TellaWebServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123CE84329126B52003C251F /* TellaWebServerViewModel.swift */; }; + 1240917B2DB03FCC005AFE9B /* ManuallyVerificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1240917A2DB03FCC005AFE9B /* ManuallyVerificationViewModel.swift */; }; 124190442A42F3A700E177F3 /* SQLStatementBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124190432A42F3A700E177F3 /* SQLStatementBuilder.swift */; }; 124190462A43230000E177F3 /* SQLiteStatementBuilderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124190452A43230000E177F3 /* SQLiteStatementBuilderExtension.swift */; }; 1242135529B774A40002402D /* DeleteReportConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1242135429B774A40002402D /* DeleteReportConfirmationView.swift */; }; 1244141727E357E80037D469 /* LoadMoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244141627E357E80037D469 /* LoadMoreCell.swift */; }; 1244141927E35D120037D469 /* VaultFileImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244141827E35D120037D469 /* VaultFileImages.swift */; }; + 12444EFA2E09D6F6006CB86A /* NearbySharingServerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12444EF92E09D6F3006CB86A /* NearbySharingServerState.swift */; }; + 1244945E2DD61256002B4A9B /* FileTransferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244945D2DD61256002B4A9B /* FileTransferView.swift */; }; + 124494602DD61267002B4A9B /* SenderFileTransferVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1244945F2DD6125F002B4A9B /* SenderFileTransferVM.swift */; }; 1246DECF2CC7F1CF00AAC633 /* DropboxError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1246DECE2CC7F1C300AAC633 /* DropboxError.swift */; }; + 124852A02E6EF9E000BDDC2D /* TipsToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1248529F2E6EF9E000BDDC2D /* TipsToConnectView.swift */; }; + 124852A22E7173BC00BDDC2D /* IconTextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124852A12E7173BC00BDDC2D /* IconTextButton.swift */; }; + 124866FF2DFCCABA00EBE8F8 /* LocalURLRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124866FE2DFCCAA200EBE8F8 /* LocalURLRequest.swift */; }; 12494A482BC0570E00766DFF /* UwaziSubmissionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12494A472BC0570D00766DFF /* UwaziSubmissionViewModel.swift */; }; 1249B3812AD43A8D009A7023 /* VaultManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1249B3802AD43A8D009A7023 /* VaultManagerInterface.swift */; }; 1249B3832AD43AA7009A7023 /* VaultFilesManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1249B3822AD43AA7009A7023 /* VaultFilesManagerInterface.swift */; }; 124F8E0829119E9400FBB7AC /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124F8E0729119E9400FBB7AC /* StringExtension.swift */; }; + 12505B1E2D899BD900483E34 /* NearbySharingServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12505B1D2D899BD100483E34 /* NearbySharingServer.swift */; }; + 12505B222D89A2A700483E34 /* HTTPResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12505B212D89A2A500483E34 /* HTTPResponse.swift */; }; 1251C3902BB45616002E9898 /* UwaziListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1251C38F2BB45616002E9898 /* UwaziListView.swift */; }; 125299DD2AD02E6F00191CAB /* ImportVaultFileResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125299DC2AD02E6F00191CAB /* ImportVaultFileResult.swift */; }; 125299DF2AD02E9D00191CAB /* FilesActor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125299DE2AD02E9D00191CAB /* FilesActor.swift */; }; 125299E12AD0515600191CAB /* VaultFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125299E02AD0515600191CAB /* VaultFilesManager.swift */; }; 1252F7052BDAB1C30076DF4B /* EntityInstanceToSend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1252F7042BDAB1C30076DF4B /* EntityInstanceToSend.swift */; }; 1252F7072BDAB2000076DF4B /* EntityAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1252F7062BDAB2000076DF4B /* EntityAttachment.swift */; }; + 1252FDE02E006E13002AF5E8 /* NearbySharingURLSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1252FDDF2E006E10002AF5E8 /* NearbySharingURLSessionDelegate.swift */; }; + 125392802D897A9100F96BA2 /* NearbySharingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253927F2D897A8F00F96BA2 /* NearbySharingRepository.swift */; }; 1253B87B2C50481400D675E5 /* NextcloudSubmittedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1253B87A2C50481400D675E5 /* NextcloudSubmittedViewModel.swift */; }; 1254E25E2C6B71E3004C6280 /* NextcloudLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1254E25D2C6B71E3004C6280 /* NextcloudLoginView.swift */; }; 1254E2602C6B8114004C6280 /* NextcloutOutboxDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1254E25F2C6B8114004C6280 /* NextcloutOutboxDetailsView.swift */; }; @@ -160,6 +185,8 @@ 125AAD34295348EC002194D6 /* UploadService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125AAD33295348EC002194D6 /* UploadService.swift */; }; 125AAD362953607D002194D6 /* UploadReportModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125AAD352953607D002194D6 /* UploadReportModels.swift */; }; 125BC1DE2B21F08200A117D0 /* VaultFileDetailsToMerge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125BC1DD2B21F08200A117D0 /* VaultFileDetailsToMerge.swift */; }; + 125C16022DAE762F0086D4F0 /* SecKeyExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125C16012DAE76260086D4F0 /* SecKeyExtension.swift */; }; + 125C16062DAEF5430086D4F0 /* ManuallyVerificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125C16052DAEF5430086D4F0 /* ManuallyVerificationView.swift */; }; 125C789F2C38042700440014 /* ConfirmDeleteConnectionStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125C789E2C38042700440014 /* ConfirmDeleteConnectionStrings.swift */; }; 125C78A22C38056300440014 /* EntityStatusExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125C78A12C38056300440014 /* EntityStatusExtension.swift */; }; 125C78A42C3805C600440014 /* ReportStatusExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125C78A32C3805C600440014 /* ReportStatusExtension.swift */; }; @@ -170,6 +197,9 @@ 125D2326271F896400250FBB /* CustomPinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D2325271F896400250FBB /* CustomPinView.swift */; }; 125D2328271F8B6600250FBB /* LockConfirmPinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D2327271F8B6600250FBB /* LockConfirmPinView.swift */; }; 125D233C2B17344D001EFB8B /* BackgroundActivitiesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D233B2B17344D001EFB8B /* BackgroundActivitiesView.swift */; }; + 125D58AB2E1811AB00365C40 /* TransferProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D58AA2E1811AB00365C40 /* TransferProgressView.swift */; }; + 125D58AD2E1BE9A500365C40 /* ReceiverFileTransferVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D58AC2E1BE9A000365C40 /* ReceiverFileTransferVM.swift */; }; + 125D58AF2E1BE9D900365C40 /* FileTransferVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125D58AE2E1BE9D700365C40 /* FileTransferVM.swift */; }; 125DD9C92796CB090053619D /* ImportFilesProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125DD9C82796CB090053619D /* ImportFilesProgressView.swift */; }; 125EDDAD271DBD5200E2789A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F66E65B023E3207D000F93E5 /* ContentView.swift */; }; 125EDDB1271E40CA00E2789A /* LockViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125EDDB0271E40CA00E2789A /* LockViewModel.swift */; }; @@ -201,7 +231,9 @@ 126AD14A27C6D5EB00081CC9 /* LocalizableCamera.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126AD14927C6D5EB00081CC9 /* LocalizableCamera.swift */; }; 126B20E62DBBA16B00FD7762 /* RotateVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126B20E52DBBA16900FD7762 /* RotateVideoView.swift */; }; 126B20E82DBF806100FD7762 /* URL+VideoRotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126B20E72DBF804F00FD7762 /* URL+VideoRotation.swift */; }; + 126B70122DB1575D00742897 /* CustomText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126B70112DB1575600742897 /* CustomText.swift */; }; 126B898E2D2E97C400CF3B9A /* AVCapturePhotoExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126B898D2D2E97BC00CF3B9A /* AVCapturePhotoExtension.swift */; }; + 126B91BB2E337390002C8B35 /* NearbySharingEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126B91BA2E33738C002C8B35 /* NearbySharingEvent.swift */; }; 126E1AF12C3D7603000AE4CE /* BaseReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E1AF02C3D7603000AE4CE /* BaseReport.swift */; }; 126E1AF32C3D7632000AE4CE /* GDriveReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E1AF22C3D7632000AE4CE /* GDriveReport.swift */; }; 126E1AFD2C3D8426000AE4CE /* WebServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E1AFB2C3D8426000AE4CE /* WebServer.swift */; }; @@ -213,11 +245,14 @@ 126E1B092C3EB3DE000AE4CE /* NextcloudDraftViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E1B082C3EB3DE000AE4CE /* NextcloudDraftViewModel.swift */; }; 126E813B272CAFA300688B64 /* UINavigationControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126E813A272CAFA300688B64 /* UINavigationControllerExtension.swift */; }; 126ECFEA27C57FA600ED5161 /* CameraState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126ECFE927C57FA600ED5161 /* CameraState.swift */; }; + 126FAACE2D4B956400F6BCD8 /* LocalizableNearbySharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FAACD2D4B954F00F6BCD8 /* LocalizableNearbySharing.swift */; }; + 126FAAD32D4B9D2100F6BCD8 /* NearbySharingMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FAAD22D4B9D2100F6BCD8 /* NearbySharingMainView.swift */; }; 126FE4D727A427E200AE4188 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FE4D627A427E200AE4188 /* Constants.swift */; }; 126FEE0A2AC4993900B99298 /* ReportPages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 126FEE092AC4993900B99298 /* ReportPages.swift */; }; 127092D42C2D8BA5002030AA /* NextcloudDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127092D32C2D8BA5002030AA /* NextcloudDatabase.swift */; }; 127092D72C2DD1A1002030AA /* NextcloudServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127092D62C2DD1A1002030AA /* NextcloudServer.swift */; }; 127138382C35EA34004D520B /* CommonCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127138372C35EA34004D520B /* CommonCardViewModel.swift */; }; + 127296732D70A46500CB9FF0 /* CertificateGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127296722D70A46100CB9FF0 /* CertificateGenerator.swift */; }; 1272F25D27C91ACD0054F2E2 /* ImportFileProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F25C27C91ACD0054F2E2 /* ImportFileProgress.swift */; }; 1272F25F27C91BD70054F2E2 /* ImportFilesFromCameraProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F25E27C91BD70054F2E2 /* ImportFilesFromCameraProgress.swift */; }; 1272F26127C91CD90054F2E2 /* ImportFilesProgressModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1272F26027C91CD90054F2E2 /* ImportFilesProgressModels.swift */; }; @@ -233,6 +268,8 @@ 1275A8442C5AA4460071A97D /* NextcloudUploadProgressInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1275A8432C5AA4460071A97D /* NextcloudUploadProgressInfo.swift */; }; 1275A8492C5E6EFF0071A97D /* NextcloudConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1275A8482C5E6EFF0071A97D /* NextcloudConstants.swift */; }; 1275A84B2C5E96B70071A97D /* NextcloudReportFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1275A84A2C5E96B70071A97D /* NextcloudReportFile.swift */; }; + 1276B7392E1E56F10024C38A /* ProgressViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1276B7382E1E56F00024C38A /* ProgressViewModel.swift */; }; + 1276B73F2E1EA8F10024C38A /* FileReceivingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1276B73E2E1EA8F10024C38A /* FileReceivingView.swift */; }; 12776A9027BA742700CDC5DC /* CameraControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12776A8F27BA742700CDC5DC /* CameraControlsView.swift */; }; 12776A9527BA761C00CDC5DC /* CustomCameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12776A9427BA761C00CDC5DC /* CustomCameraController.swift */; }; 12776A9827BAC87A00CDC5DC /* CameraViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12776A9727BAC87A00CDC5DC /* CameraViewModel.swift */; }; @@ -247,7 +284,18 @@ 127C155827DA486A009EC15B /* TransitionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C155727DA486A009EC15B /* TransitionView.swift */; }; 127C155C27DA69FF009EC15B /* TransitionViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C155B27DA69FF009EC15B /* TransitionViewData.swift */; }; 127D50A82DCE3F0500332973 /* EditMediaProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127D50A72DCE3F0200332973 /* EditMediaProtocol.swift */; }; + 127E81A32DB7C4AC002AED4C /* TextStyleModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127E81A22DB7C4A9002AED4C /* TextStyleModifier.swift */; }; + 127E81A82DB9008B002AED4C /* TypographyStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127E81A72DB90089002AED4C /* TypographyStyle.swift */; }; 1280B9902C4082A6004B19F1 /* NextcloudOutboxViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1280B98F2C4082A6004B19F1 /* NextcloudOutboxViewModel.swift */; }; + 1282479D2D5E1ADC00B7E63E /* RecipientConnectToDeviceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282479C2D5E1AD900B7E63E /* RecipientConnectToDeviceViewModel.swift */; }; + 1282479F2D5F50B300B7E63E /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282479E2D5F50A600B7E63E /* QRCodeScannerView.swift */; }; + 128247A12D5F923100B7E63E /* SenderConnectToDeviceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 128247A02D5F922900B7E63E /* SenderConnectToDeviceViewModel.swift */; }; + 128247A42D5F935D00B7E63E /* ConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 128247A32D5F935A00B7E63E /* ConnectionInfo.swift */; }; + 1282C09E2D8AD79C0033E4CA /* PrepareUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282C09D2D8AD7990033E4CA /* PrepareUpload.swift */; }; + 1282C0A02D8AEA1E0033E4CA /* Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282C09F2D8AEA1A0033E4CA /* Register.swift */; }; + 1282C0A22D8AEE130033E4CA /* FileUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282C0A12D8AEE110033E4CA /* FileUpload.swift */; }; + 1282C0A42D8AF3D70033E4CA /* CloseConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282C0A32D8AF3D50033E4CA /* CloseConnection.swift */; }; + 1282DE512DDE1021004D2176 /* NWConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1282DE502DDE101E004D2176 /* NWConnection.swift */; }; 1283C257272AA6F6009180CA /* OpenSans-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1283C256272AA6F6009180CA /* OpenSans-Semibold.ttf */; }; 1285BF402A3C5D63005D5D23 /* OpenXmlFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1285BF3F2A3C5D63005D5D23 /* OpenXmlFormats.swift */; }; 128754D92BBCE09B00D215B5 /* SummaryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 128754D82BBCE09B00D215B5 /* SummaryViewModel.swift */; }; @@ -308,6 +356,7 @@ 12964D8B2BA9C6F8003E1E0A /* EntityStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12964D8A2BA9C6F8003E1E0A /* EntityStatus.swift */; }; 12964D8D2BA9CF0D003E1E0A /* UwaziEntityInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12964D8C2BA9CF0D003E1E0A /* UwaziEntityInstance.swift */; }; 12964D922BAB44C8003E1E0A /* UwaziEntityInstanceFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12964D912BAB44C8003E1E0A /* UwaziEntityInstanceFile.swift */; }; + 129919BE2E31144300A4417D /* NWConnectionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129919BD2E31143400A4417D /* NWConnectionExtension.swift */; }; 129B844F28BF5E4700F1B344 /* TextfieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129B844E28BF5E4700F1B344 /* TextfieldView.swift */; }; 129B845128BF6C7800F1B344 /* ServersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129B845028BF6C7800F1B344 /* ServersViewModel.swift */; }; 129B845328BFB7AC00F1B344 /* AddServerAccessChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129B845228BFB7AC00F1B344 /* AddServerAccessChoiceView.swift */; }; @@ -316,7 +365,7 @@ 129BC3A62CBE582A001C7323 /* FileHandleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129BC3A52CBE582A001C7323 /* FileHandleExtension.swift */; }; 12A1488F27209F2800A1ADDC /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A1488E27209F2800A1ADDC /* ViewExtension.swift */; }; 12A148922720A32000A1ADDC /* LockPinData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148912720A32000A1ADDC /* LockPinData.swift */; }; - 12A148942720A89E00A1ADDC /* BottomLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148932720A89E00A1ADDC /* BottomLockView.swift */; }; + 12A148942720A89E00A1ADDC /* NavigationBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148932720A89E00A1ADDC /* NavigationBottomView.swift */; }; 12A148962720B2B400A1ADDC /* PasswordTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148952720B2B400A1ADDC /* PasswordTextFieldView.swift */; }; 12A148982720B47100A1ADDC /* LockDescriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148972720B47100A1ADDC /* LockDescriptionView.swift */; }; 12A1489A2720B53800A1ADDC /* ConfirmPasswordErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A148992720B53800A1ADDC /* ConfirmPasswordErrorView.swift */; }; @@ -332,6 +381,7 @@ 12A6B66D2D143D030002F74B /* ManagelimitedPhotoBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A6B66C2D143D030002F74B /* ManagelimitedPhotoBottomSheet.swift */; }; 12A6B66F2D1443DC0002F74B /* AssetGridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A6B66E2D1443DC0002F74B /* AssetGridView.swift */; }; 12A8B09D2AFC0CEE007C838F /* LocalizableCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A8B09C2AFC0CEE007C838F /* LocalizableCommon.swift */; }; + 12A9ECD72D9F3FC800711BA4 /* SenderPrepareFileTransferVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A9ECD62D9F3FC300711BA4 /* SenderPrepareFileTransferVM.swift */; }; 12AA5A3528576E9C00134A9B /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12AA5A3428576E9C00134A9B /* ArrayExtension.swift */; }; 12AB30D3272C36500020FFBF /* HomeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12AB30D2272C36500020FFBF /* HomeData.swift */; }; 12ADF98B28BD130C00FA96EB /* ServersListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12ADF98A28BD130C00FA96EB /* ServersListView.swift */; }; @@ -360,6 +410,7 @@ 12C07ECC2B06603A0030AD6E /* EncryptionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C07ECB2B06603A0030AD6E /* EncryptionService.swift */; }; 12C07F052B0748D50030AD6E /* TopSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C07F042B0748D50030AD6E /* TopSheetView.swift */; }; 12C2BDE82847676100488060 /* LockTimeoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C2BDE72847676100488060 /* LockTimeoutView.swift */; }; + 12C454D82DAD24300085FCD6 /* X509 in Frameworks */ = {isa = PBXBuildFile; productRef = 12C454D72DAD24300085FCD6 /* X509 */; }; 12D37A942721752B00E43BDB /* TellaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D37A932721752B00E43BDB /* TellaApp.swift */; }; 12D37A962721A49D00E43BDB /* NavigationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D37A952721A49D00E43BDB /* NavigationContainerView.swift */; }; 12D37A982721EDB700E43BDB /* PinKeyboardModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D37A972721EDB600E43BDB /* PinKeyboardModel.swift */; }; @@ -384,6 +435,10 @@ 12F5D28D27A326EB00BF3309 /* AboutAndHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F5D28C27A326EB00BF3309 /* AboutAndHelpView.swift */; }; 12FAC0CF2799FF230068EC2B /* ImportProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC0CE2799FF230068EC2B /* ImportProgress.swift */; }; 12FAC12D2B226DEF00DD9615 /* EncryptionOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C07EC92B0660240030AD6E /* EncryptionOperation.swift */; }; + 12FAC4572E2519F5007A4651 /* FileSendingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC4562E2519F5007A4651 /* FileSendingView.swift */; }; + 12FAC4592E2571E0007A4651 /* NearbySharingResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC4582E2571E0007A4651 /* NearbySharingResultView.swift */; }; + 12FAC45B2E257A3E007A4651 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC45A2E257A3E007A4651 /* ResultView.swift */; }; + 12FAC45D2E265BF7007A4651 /* NearbySharingResultVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FAC45C2E265BE4007A4651 /* NearbySharingResultVM.swift */; }; 12FDEDD5272AFE99005C17AC /* LocalizableHome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FDEDD4272AFE99005C17AC /* LocalizableHome.swift */; }; 12FFEFA528BE96E00081DDB2 /* TellaWebAddServerURLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FFEFA428BE96E00081DDB2 /* TellaWebAddServerURLView.swift */; }; 1501401D2B71243A00991D69 /* LocalizableResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1501401C2B71243A00991D69 /* LocalizableResources.swift */; }; @@ -444,7 +499,6 @@ 15E3D50A2C1B71E200B7FECC /* GDriveDraftView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E3D5092C1B71E200B7FECC /* GDriveDraftView.swift */; }; 15E3D50E2C1B87AC00B7FECC /* GDriveDraftViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E3D50D2C1B87AC00B7FECC /* GDriveDraftViewModel.swift */; }; 15E54AB92C5824DA00290DE8 /* LocalizableGDrive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E54AB82C5824DA00290DE8 /* LocalizableGDrive.swift */; }; - 15E656AB2C2A147E00BDEC91 /* AddFilesToDraftView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E656AA2C2A147E00BDEC91 /* AddFilesToDraftView.swift */; }; 15E656AD2C2A14F500BDEC91 /* DraftMainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E656AC2C2A14F500BDEC91 /* DraftMainViewModel.swift */; }; 15EE30FE2B5073E20033BBBB /* Pages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15EE30FD2B5073E20033BBBB /* Pages.swift */; }; 15F64C9D2B6D7904008A57AA /* DownloadedResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F64C9C2B6D7904008A57AA /* DownloadedResources.swift */; }; @@ -466,9 +520,12 @@ 200399012CE3F1E900DCFCF0 /* EditVideoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 200399002CE3F1E900DCFCF0 /* EditVideoViewModel.swift */; }; 200399032CE60B2B00DCFCF0 /* ResizableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 200399022CE60B2B00DCFCF0 /* ResizableImage.swift */; }; 200399062CE69E9500DCFCF0 /* EditMediaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 200399052CE69E9500DCFCF0 /* EditMediaViewModel.swift */; }; + 200751D42D5B91F1000CC076 /* NearbySharingParticipant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 200751D32D5B91F1000CC076 /* NearbySharingParticipant.swift */; }; + 2012D5422D5695D3000E0443 /* RecipientConnectToDeviceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2012D5412D5695D3000E0443 /* RecipientConnectToDeviceView.swift */; }; 20219B302C8B33F900AD6D53 /* GdriveReportMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20219B2F2C8B33F900AD6D53 /* GdriveReportMainView.swift */; }; 20219B332C8B3E1900AD6D53 /* GDriveSubmittedDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20219B322C8B3E1900AD6D53 /* GDriveSubmittedDetailsView.swift */; }; 20219B352C8B3F5500AD6D53 /* GdriveOutboxDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20219B342C8B3F5500AD6D53 /* GdriveOutboxDetailsView.swift */; }; + 203DCBAE2D6DBE4B00B7BEED /* SenderPrepareFileTransferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 203DCBAD2D6DBE4B00B7BEED /* SenderPrepareFileTransferView.swift */; }; 203F4D802CE77B1500D15086 /* CustomThumbnailSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 203F4D7F2CE77B1500D15086 /* CustomThumbnailSlider.swift */; }; 20473A5F2C8B9B0A0015A9B5 /* TellaServerReportsMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20473A5E2C8B9B0A0015A9B5 /* TellaServerReportsMainView.swift */; }; 20473A612C8BA6FE0015A9B5 /* TellaServerSubmittedDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20473A602C8BA6FE0015A9B5 /* TellaServerSubmittedDetailsView.swift */; }; @@ -478,6 +535,9 @@ 204F7BAF2CEF8A3D00BA2F62 /* EditMediaControlButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 204F7BAE2CEF8A3D00BA2F62 /* EditMediaControlButtonsView.swift */; }; 208A06532BF6057A004BA033 /* EditImageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 208A06522BF6057A004BA033 /* EditImageViewModel.swift */; }; 208A06552BF607BF004BA033 /* ImageCropper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 208A06542BF607BF004BA033 /* ImageCropper.swift */; }; + 209107F32D538CF200808BCF /* SenderConnectToDeviceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209107F22D538CF200808BCF /* SenderConnectToDeviceView.swift */; }; + 209107F72D53BA7800808BCF /* SenderConnectToDeviceManuallyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209107F62D53BA7800808BCF /* SenderConnectToDeviceManuallyView.swift */; }; + 209107F92D53BD0300808BCF /* ConnectToDeviceManuallyVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209107F82D53BD0200808BCF /* ConnectToDeviceManuallyVM.swift */; }; 20985A5D2BE158020092CCC2 /* EditImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20985A5C2BE158020092CCC2 /* EditImageView.swift */; }; 209AC6782C2D5C9800492356 /* AddServerURLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AC6772C2D5C9800492356 /* AddServerURLView.swift */; }; 209AC67A2C2D5E3500492356 /* ServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AC6792C2D5E3500492356 /* ServerViewModel.swift */; }; @@ -493,10 +553,23 @@ 209AC6952C332F6000492356 /* PHPhotoLibraryExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AC6912C332F5F00492356 /* PHPhotoLibraryExtension.swift */; }; 209AC6962C332F6000492356 /* CLLocationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209AC6922C332F6000492356 /* CLLocationExtension.swift */; }; 209FFD182CED19B8009B7D51 /* EditMediaHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 209FFD172CED19B8009B7D51 /* EditMediaHeaderView.swift */; }; + 20A3BD472D5A0D8D009E5E12 /* RecipientConnectToDeviceManuallyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A3BD462D5A0D8D009E5E12 /* RecipientConnectToDeviceManuallyView.swift */; }; + 20A3BD492D5A495D009E5E12 /* CardItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A3BD482D5A495D009E5E12 /* CardItemView.swift */; }; + 20A8BBB22D6F2C27009D52AF /* AddFileGridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBB12D6F2C27009D52AF /* AddFileGridView.swift */; }; + 20A8BBB42D6F2C70009D52AF /* AddFileGridItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBB32D6F2C70009D52AF /* AddFileGridItemView.swift */; }; + 20A8BBB62D6F4777009D52AF /* AddFileBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBB52D6F4777009D52AF /* AddFileBottomSheetView.swift */; }; + 20A8BBB82D6F47CD009D52AF /* AddFilesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBB72D6F47CD009D52AF /* AddFilesViewModel.swift */; }; + 20A8BBBA2D6F4830009D52AF /* AddFileCameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBB92D6F4830009D52AF /* AddFileCameraView.swift */; }; + 20A8BBBC2D6F4843009D52AF /* AddFileRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBBB2D6F4843009D52AF /* AddFileRecordView.swift */; }; + 20A8BBBE2D6F4857009D52AF /* AddFilePhotoVideoPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20A8BBBD2D6F4857009D52AF /* AddFilePhotoVideoPickerView.swift */; }; + 20AA1F9C2D5F38A20092810B /* RecipientFileTransferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20AA1F9B2D5F38A20092810B /* RecipientFileTransferView.swift */; }; 20AE31922BF4BB0700FF4256 /* CustomCropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20AE31912BF4BB0700FF4256 /* CustomCropViewController.swift */; }; 20AF68142C89B3ED00503D30 /* NextcloudSubmittedDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20AF68132C89B3ED00503D30 /* NextcloudSubmittedDetailsView.swift */; }; 20AF68162C89B44E00503D30 /* NextcloudReportMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20AF68152C89B44E00503D30 /* NextcloudReportMainView.swift */; }; 20BA0BDF2C33428A00823A27 /* ServerLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BA0BDE2C33428A00823A27 /* ServerLoginView.swift */; }; + 20BA4FAF2D50DFEB006DD8B9 /* GetConnectedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BA4FAE2D50DFEB006DD8B9 /* GetConnectedViewModel.swift */; }; + 20BA4FB12D522551006DD8B9 /* CardModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BA4FB02D522551006DD8B9 /* CardModifier.swift */; }; + 20BB11BB2D4D1AB5004B1BBF /* GetConnectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BB11BA2D4D1AB5004B1BBF /* GetConnectedView.swift */; }; 20BF26732C98481000E81535 /* ScrollviewViewContentViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BF26722C98481000E81535 /* ScrollviewViewContentViewModifier.swift */; }; 20DC9F642CB1CAC000649160 /* EditAudioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DC9F632CB1CAC000649160 /* EditAudioView.swift */; }; 20DC9F662CB1CAE100649160 /* EditAudioViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DC9F652CB1CAE100649160 /* EditAudioViewModel.swift */; }; @@ -521,7 +594,7 @@ CC5CEB892AF0551900BF238B /* UwaziAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */; }; CC5DF8782B02B06A0068F622 /* UwaziServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5DF8772B02B06A0068F622 /* UwaziServer.swift */; }; CC9C01B829D223FC00FDF341 /* LocalizableReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9C01B729D223FC00FDF341 /* LocalizableReport.swift */; }; - CC9DEE5F2AE80ED400C8ED29 /* UwaziFileSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */; }; + CC9DEE5F2AE80ED400C8ED29 /* UwaziSelectFileComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC9DEE5E2AE80ED400C8ED29 /* UwaziSelectFileComponent.swift */; }; CCA66C9C29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA66C9B29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift */; }; CCAE3A682AC217EC00A38C6D /* UwaziViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCAE3A672AC217EC00A38C6D /* UwaziViewModel.swift */; }; CCBC2F6929A93E4900888779 /* SettingCheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCBC2F6829A93E4900888779 /* SettingCheckboxItem.swift */; }; @@ -544,7 +617,6 @@ CCDC398A2AF2BCE400674735 /* MultipartRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDC39892AF2BCE400674735 /* MultipartRequest.swift */; }; CCE875B32A96699000C576B9 /* LocalizableUwazi.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE875B22A96699000C576B9 /* LocalizableUwazi.swift */; }; CCE8CCDD2AF41E410088B61D /* UwaziFileUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE8CCDC2AF41E410088B61D /* UwaziFileUtility.swift */; }; - CCE8CCDF2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */; }; CCEAE5922AE6F6EE0069204A /* EntityCreationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCEAE5912AE6F6EE0069204A /* EntityCreationResponse.swift */; }; CCEAE5942AE7188F0069204A /* UwaziSelectWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCEAE5932AE7188F0069204A /* UwaziSelectWidget.swift */; }; CCF47F442ABE18D7000ABBBC /* TemplateActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF47F432ABE18D7000ABBBC /* TemplateActionType.swift */; }; @@ -684,6 +756,7 @@ 0ECC7818267A53A700A2ACF2 /* PageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = ""; }; 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziPages.swift; sourceTree = ""; }; 0F3DC3E42523951C0024B4A0 /* BundleInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleInfo.swift; sourceTree = ""; }; + 1202C8A22DB985B80013E17D /* BackBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackBottomView.swift; sourceTree = ""; }; 1202D9572BF4CFF200EAFAB3 /* SelectValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectValues.swift; sourceTree = ""; }; 1203CDDB298C5B9100D09073 /* ConnectionActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionActionType.swift; sourceTree = ""; }; 1203CDDD298D7C1300D09073 /* FileToUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileToUpload.swift; sourceTree = ""; }; @@ -716,6 +789,8 @@ 12199C352940966E0041BD38 /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Italic.ttf"; sourceTree = ""; }; 121AD8212943A2F00056AC2A /* ReportRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportRepository.swift; sourceTree = ""; }; 121AD8252943D52B0056AC2A /* SubmitReportResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitReportResult.swift; sourceTree = ""; }; + 121AFF922E4CA9D6007300AD /* NearbySharingStateActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingStateActor.swift; sourceTree = ""; }; + 121AFF942E4CDE6E007300AD /* NearbySharingServerHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingServerHandler.swift; sourceTree = ""; }; 121BAB7E29A7E30600D7E847 /* URLRequestExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLRequestExtension.swift; sourceTree = ""; }; 121C5D7429422C0800A64123 /* ProjectDetailsResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsResult.swift; sourceTree = ""; }; 121C5D7629422D6800A64123 /* DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = ""; }; @@ -726,6 +801,7 @@ 1220A72D292EB11B000BC24C /* ReportFileGridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportFileGridView.swift; sourceTree = ""; }; 1221CB7028DB1B5A0042F15F /* KeyValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValue.swift; sourceTree = ""; }; 1221E05B2906CA6A00BC05F5 /* DraftReportVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftReportVM.swift; sourceTree = ""; }; + 1222363E2DD3DDC1006A9F01 /* RecipientPrepareFileTransferVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientPrepareFileTransferVM.swift; sourceTree = ""; }; 12227E4468563A646AC52A27 /* Pods-Tella-TellaUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tella-TellaUITests.debug.xcconfig"; path = "Target Support Files/Pods-Tella-TellaUITests/Pods-Tella-TellaUITests.debug.xcconfig"; sourceTree = ""; }; 12234BDF29DAEC4A00D6F981 /* CustomNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigation.swift; sourceTree = ""; }; 122389B2293630DA0058593B /* SaveSuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveSuccessView.swift; sourceTree = ""; }; @@ -750,6 +826,7 @@ 122E0AED2A33B788009B1E33 /* FileDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDetails.swift; sourceTree = ""; }; 122E6A5229A3812A00BDACAD /* ReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = ""; }; 122E6A5429A396C400BDACAD /* ServerFileSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerFileSize.swift; sourceTree = ""; }; + 123004CC2D8CB752006F3F2D /* RecipientConnectManuallyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientConnectManuallyViewModel.swift; sourceTree = ""; }; 123048D1295F84BD0015CD96 /* ProgressFileItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressFileItemViewModel.swift; sourceTree = ""; }; 1230BE4627CD6D440055F854 /* AudioAuthorizationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioAuthorizationStatus.swift; sourceTree = ""; }; 123125412930241500D3BCD5 /* PhotoVideoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoVideoViewModel.swift; sourceTree = ""; }; @@ -771,24 +848,46 @@ 1237A71D278F5F8B00D7BC0F /* LanguageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageManager.swift; sourceTree = ""; }; 123AE5A029CFAD7F00814CC7 /* OutboxReportVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboxReportVM.swift; sourceTree = ""; }; 123AE5A229CFB7F000814CC7 /* UploadReportOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadReportOperation.swift; sourceTree = ""; }; + 123B19822D9DD3D200BAD892 /* ConnectionFailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionFailedView.swift; sourceTree = ""; }; + 123CBAEC2DEEEA880030A9BD /* Tella-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tella-Bridging-Header.h"; sourceTree = ""; }; + 123CBAF52DEEEF810030A9BD /* api.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = api.c; sourceTree = ""; }; + 123CBAF62DEEEF810030A9BD /* http.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = http.c; sourceTree = ""; }; + 123CBAF72DEEEF810030A9BD /* llhttp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = llhttp.h; sourceTree = ""; }; + 123CBAF82DEEEF810030A9BD /* llhttp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = llhttp.c; sourceTree = ""; }; + 123CBAFD2DEF0DCD0030A9BD /* HTTPParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPParser.swift; sourceTree = ""; }; + 123CBB012DEF34640030A9BD /* HTTPRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPRequest.swift; sourceTree = ""; }; + 123CBB032DEF97270030A9BD /* HTTPStatusCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatusCode.swift; sourceTree = ""; }; + 123CBB052DEF99410030A9BD /* NearbySharingEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingEndpoint.swift; sourceTree = ""; }; + 123CBB072DEF998A0030A9BD /* NearbySharingServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingServerResponse.swift; sourceTree = ""; }; 123CE54E272028D1005BF386 /* Roboto-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Light.ttf"; sourceTree = ""; }; 123CE84329126B52003C251F /* TellaWebServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TellaWebServerViewModel.swift; sourceTree = ""; }; + 1240917A2DB03FCC005AFE9B /* ManuallyVerificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManuallyVerificationViewModel.swift; sourceTree = ""; }; 124190432A42F3A700E177F3 /* SQLStatementBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLStatementBuilder.swift; sourceTree = ""; }; 124190452A43230000E177F3 /* SQLiteStatementBuilderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SQLiteStatementBuilderExtension.swift; sourceTree = ""; }; 1242135429B774A40002402D /* DeleteReportConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteReportConfirmationView.swift; sourceTree = ""; }; 1244141627E357E80037D469 /* LoadMoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreCell.swift; sourceTree = ""; }; 1244141827E35D120037D469 /* VaultFileImages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultFileImages.swift; sourceTree = ""; }; + 12444EF92E09D6F3006CB86A /* NearbySharingServerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingServerState.swift; sourceTree = ""; }; + 1244945D2DD61256002B4A9B /* FileTransferView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTransferView.swift; sourceTree = ""; }; + 1244945F2DD6125F002B4A9B /* SenderFileTransferVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderFileTransferVM.swift; sourceTree = ""; }; 1246DECE2CC7F1C300AAC633 /* DropboxError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropboxError.swift; sourceTree = ""; }; + 1248529F2E6EF9E000BDDC2D /* TipsToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipsToConnectView.swift; sourceTree = ""; }; + 124852A12E7173BC00BDDC2D /* IconTextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconTextButton.swift; sourceTree = ""; }; + 124866FE2DFCCAA200EBE8F8 /* LocalURLRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalURLRequest.swift; sourceTree = ""; }; 12494A472BC0570D00766DFF /* UwaziSubmissionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSubmissionViewModel.swift; sourceTree = ""; }; 1249B3802AD43A8D009A7023 /* VaultManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultManagerInterface.swift; sourceTree = ""; }; 1249B3822AD43AA7009A7023 /* VaultFilesManagerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultFilesManagerInterface.swift; sourceTree = ""; }; 124F8E0729119E9400FBB7AC /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; + 12505B1D2D899BD100483E34 /* NearbySharingServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingServer.swift; sourceTree = ""; }; + 12505B212D89A2A500483E34 /* HTTPResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponse.swift; sourceTree = ""; }; 1251C38F2BB45616002E9898 /* UwaziListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziListView.swift; sourceTree = ""; }; 125299DC2AD02E6F00191CAB /* ImportVaultFileResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportVaultFileResult.swift; sourceTree = ""; }; 125299DE2AD02E9D00191CAB /* FilesActor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilesActor.swift; sourceTree = ""; }; 125299E02AD0515600191CAB /* VaultFilesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultFilesManager.swift; sourceTree = ""; }; 1252F7042BDAB1C30076DF4B /* EntityInstanceToSend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityInstanceToSend.swift; sourceTree = ""; }; 1252F7062BDAB2000076DF4B /* EntityAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityAttachment.swift; sourceTree = ""; }; + 1252FDDF2E006E10002AF5E8 /* NearbySharingURLSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingURLSessionDelegate.swift; sourceTree = ""; }; + 1253927F2D897A8F00F96BA2 /* NearbySharingRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingRepository.swift; sourceTree = ""; }; 1253B87A2C50481400D675E5 /* NextcloudSubmittedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudSubmittedViewModel.swift; sourceTree = ""; }; 1254E25D2C6B71E3004C6280 /* NextcloudLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudLoginView.swift; sourceTree = ""; }; 1254E25F2C6B8114004C6280 /* NextcloutOutboxDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloutOutboxDetailsView.swift; sourceTree = ""; }; @@ -817,6 +916,8 @@ 125AAD33295348EC002194D6 /* UploadService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadService.swift; sourceTree = ""; }; 125AAD352953607D002194D6 /* UploadReportModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadReportModels.swift; sourceTree = ""; }; 125BC1DD2B21F08200A117D0 /* VaultFileDetailsToMerge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultFileDetailsToMerge.swift; sourceTree = ""; }; + 125C16012DAE76260086D4F0 /* SecKeyExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecKeyExtension.swift; sourceTree = ""; }; + 125C16052DAEF5430086D4F0 /* ManuallyVerificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManuallyVerificationView.swift; sourceTree = ""; }; 125C789E2C38042700440014 /* ConfirmDeleteConnectionStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmDeleteConnectionStrings.swift; sourceTree = ""; }; 125C78A12C38056300440014 /* EntityStatusExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityStatusExtension.swift; sourceTree = ""; }; 125C78A32C3805C600440014 /* ReportStatusExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusExtension.swift; sourceTree = ""; }; @@ -827,6 +928,9 @@ 125D2325271F896400250FBB /* CustomPinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPinView.swift; sourceTree = ""; }; 125D2327271F8B6600250FBB /* LockConfirmPinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockConfirmPinView.swift; sourceTree = ""; }; 125D233B2B17344D001EFB8B /* BackgroundActivitiesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundActivitiesView.swift; sourceTree = ""; }; + 125D58AA2E1811AB00365C40 /* TransferProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransferProgressView.swift; sourceTree = ""; }; + 125D58AC2E1BE9A000365C40 /* ReceiverFileTransferVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiverFileTransferVM.swift; sourceTree = ""; }; + 125D58AE2E1BE9D700365C40 /* FileTransferVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTransferVM.swift; sourceTree = ""; }; 125DD9C82796CB090053619D /* ImportFilesProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportFilesProgressView.swift; sourceTree = ""; }; 125EDDB0271E40CA00E2789A /* LockViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockViewModel.swift; sourceTree = ""; }; 125EFACC274A447000D93DF6 /* FileSortMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSortMenu.swift; sourceTree = ""; }; @@ -855,7 +959,9 @@ 126AD14927C6D5EB00081CC9 /* LocalizableCamera.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableCamera.swift; sourceTree = ""; }; 126B20E52DBBA16900FD7762 /* RotateVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotateVideoView.swift; sourceTree = ""; }; 126B20E72DBF804F00FD7762 /* URL+VideoRotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+VideoRotation.swift"; sourceTree = ""; }; + 126B70112DB1575600742897 /* CustomText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomText.swift; sourceTree = ""; }; 126B898D2D2E97BC00CF3B9A /* AVCapturePhotoExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVCapturePhotoExtension.swift; sourceTree = ""; }; + 126B91BA2E33738C002C8B35 /* NearbySharingEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingEvent.swift; sourceTree = ""; }; 126E1AF02C3D7603000AE4CE /* BaseReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseReport.swift; sourceTree = ""; }; 126E1AF22C3D7632000AE4CE /* GDriveReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDriveReport.swift; sourceTree = ""; }; 126E1AFB2C3D8426000AE4CE /* WebServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebServer.swift; sourceTree = ""; }; @@ -866,11 +972,14 @@ 126E1B082C3EB3DE000AE4CE /* NextcloudDraftViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudDraftViewModel.swift; sourceTree = ""; }; 126E813A272CAFA300688B64 /* UINavigationControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UINavigationControllerExtension.swift; sourceTree = ""; }; 126ECFE927C57FA600ED5161 /* CameraState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraState.swift; sourceTree = ""; }; + 126FAACD2D4B954F00F6BCD8 /* LocalizableNearbySharing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableNearbySharing.swift; sourceTree = ""; }; + 126FAAD22D4B9D2100F6BCD8 /* NearbySharingMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingMainView.swift; sourceTree = ""; }; 126FE4D627A427E200AE4188 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 126FEE092AC4993900B99298 /* ReportPages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportPages.swift; sourceTree = ""; }; 127092D32C2D8BA5002030AA /* NextcloudDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudDatabase.swift; sourceTree = ""; }; 127092D62C2DD1A1002030AA /* NextcloudServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudServer.swift; sourceTree = ""; }; 127138372C35EA34004D520B /* CommonCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonCardViewModel.swift; sourceTree = ""; }; + 127296722D70A46100CB9FF0 /* CertificateGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateGenerator.swift; sourceTree = ""; }; 1272F25C27C91ACD0054F2E2 /* ImportFileProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportFileProgress.swift; sourceTree = ""; }; 1272F25E27C91BD70054F2E2 /* ImportFilesFromCameraProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportFilesFromCameraProgress.swift; sourceTree = ""; }; 1272F26027C91CD90054F2E2 /* ImportFilesProgressModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportFilesProgressModels.swift; sourceTree = ""; }; @@ -886,6 +995,8 @@ 1275A8432C5AA4460071A97D /* NextcloudUploadProgressInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudUploadProgressInfo.swift; sourceTree = ""; }; 1275A8482C5E6EFF0071A97D /* NextcloudConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudConstants.swift; sourceTree = ""; }; 1275A84A2C5E96B70071A97D /* NextcloudReportFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudReportFile.swift; sourceTree = ""; }; + 1276B7382E1E56F00024C38A /* ProgressViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewModel.swift; sourceTree = ""; }; + 1276B73E2E1EA8F10024C38A /* FileReceivingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileReceivingView.swift; sourceTree = ""; }; 12776A8F27BA742700CDC5DC /* CameraControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraControlsView.swift; sourceTree = ""; }; 12776A9427BA761C00CDC5DC /* CustomCameraController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCameraController.swift; sourceTree = ""; }; 12776A9727BAC87A00CDC5DC /* CameraViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraViewModel.swift; sourceTree = ""; }; @@ -897,8 +1008,19 @@ 127C155727DA486A009EC15B /* TransitionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionView.swift; sourceTree = ""; }; 127C155B27DA69FF009EC15B /* TransitionViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionViewData.swift; sourceTree = ""; }; 127D50A72DCE3F0200332973 /* EditMediaProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMediaProtocol.swift; sourceTree = ""; }; + 127E81A22DB7C4A9002AED4C /* TextStyleModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStyleModifier.swift; sourceTree = ""; }; + 127E81A72DB90089002AED4C /* TypographyStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypographyStyle.swift; sourceTree = ""; }; 1280B98F2C4082A6004B19F1 /* NextcloudOutboxViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudOutboxViewModel.swift; sourceTree = ""; }; + 1282479C2D5E1AD900B7E63E /* RecipientConnectToDeviceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientConnectToDeviceViewModel.swift; sourceTree = ""; }; + 1282479E2D5F50A600B7E63E /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = ""; }; + 128247A02D5F922900B7E63E /* SenderConnectToDeviceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderConnectToDeviceViewModel.swift; sourceTree = ""; }; + 128247A32D5F935A00B7E63E /* ConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionInfo.swift; sourceTree = ""; }; + 1282C09D2D8AD7990033E4CA /* PrepareUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrepareUpload.swift; sourceTree = ""; }; + 1282C09F2D8AEA1A0033E4CA /* Register.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Register.swift; sourceTree = ""; }; + 1282C0A12D8AEE110033E4CA /* FileUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUpload.swift; sourceTree = ""; }; + 1282C0A32D8AF3D50033E4CA /* CloseConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseConnection.swift; sourceTree = ""; }; 1282DE472DDD0302004D2176 /* sn-ZW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sn-ZW"; path = "sn-ZW.lproj/Localizable.strings"; sourceTree = ""; }; + 1282DE502DDE101E004D2176 /* NWConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NWConnection.swift; sourceTree = ""; }; 1283C256272AA6F6009180CA /* OpenSans-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Semibold.ttf"; sourceTree = ""; }; 1285BF3F2A3C5D63005D5D23 /* OpenXmlFormats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenXmlFormats.swift; sourceTree = ""; }; 128754D82BBCE09B00D215B5 /* SummaryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummaryViewModel.swift; sourceTree = ""; }; @@ -976,6 +1098,7 @@ 12964D8C2BA9CF0D003E1E0A /* UwaziEntityInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityInstance.swift; sourceTree = ""; }; 12964D912BAB44C8003E1E0A /* UwaziEntityInstanceFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziEntityInstanceFile.swift; sourceTree = ""; }; 129919BC2E2EC92900A4417D /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; sourceTree = ""; }; + 129919BD2E31143400A4417D /* NWConnectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NWConnectionExtension.swift; sourceTree = ""; }; 129B844E28BF5E4700F1B344 /* TextfieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextfieldView.swift; sourceTree = ""; }; 129B845028BF6C7800F1B344 /* ServersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersViewModel.swift; sourceTree = ""; }; 129B845228BFB7AC00F1B344 /* AddServerAccessChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddServerAccessChoiceView.swift; sourceTree = ""; }; @@ -984,7 +1107,7 @@ 129BC3A52CBE582A001C7323 /* FileHandleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileHandleExtension.swift; sourceTree = ""; }; 12A1488E27209F2800A1ADDC /* ViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = ""; }; 12A148912720A32000A1ADDC /* LockPinData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockPinData.swift; sourceTree = ""; }; - 12A148932720A89E00A1ADDC /* BottomLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomLockView.swift; sourceTree = ""; }; + 12A148932720A89E00A1ADDC /* NavigationBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBottomView.swift; sourceTree = ""; }; 12A148952720B2B400A1ADDC /* PasswordTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTextFieldView.swift; sourceTree = ""; }; 12A148972720B47100A1ADDC /* LockDescriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockDescriptionView.swift; sourceTree = ""; }; 12A148992720B53800A1ADDC /* ConfirmPasswordErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPasswordErrorView.swift; sourceTree = ""; }; @@ -998,6 +1121,7 @@ 12A6B66C2D143D030002F74B /* ManagelimitedPhotoBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagelimitedPhotoBottomSheet.swift; sourceTree = ""; }; 12A6B66E2D1443DC0002F74B /* AssetGridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetGridView.swift; sourceTree = ""; }; 12A8B09C2AFC0CEE007C838F /* LocalizableCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableCommon.swift; sourceTree = ""; }; + 12A9ECD62D9F3FC300711BA4 /* SenderPrepareFileTransferVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderPrepareFileTransferVM.swift; sourceTree = ""; }; 12AA5A3428576E9C00134A9B /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.swift; sourceTree = ""; }; 12AB30D2272C36500020FFBF /* HomeData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeData.swift; sourceTree = ""; }; 12ADF98A28BD130C00FA96EB /* ServersListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersListView.swift; sourceTree = ""; }; @@ -1049,6 +1173,10 @@ 12F2C65B2B86567F008457EB /* FileDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDetailsViewModel.swift; sourceTree = ""; }; 12F5D28C27A326EB00BF3309 /* AboutAndHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutAndHelpView.swift; sourceTree = ""; }; 12FAC0CE2799FF230068EC2B /* ImportProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportProgress.swift; sourceTree = ""; }; + 12FAC4562E2519F5007A4651 /* FileSendingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSendingView.swift; sourceTree = ""; }; + 12FAC4582E2571E0007A4651 /* NearbySharingResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingResultView.swift; sourceTree = ""; }; + 12FAC45A2E257A3E007A4651 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; + 12FAC45C2E265BE4007A4651 /* NearbySharingResultVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingResultVM.swift; sourceTree = ""; }; 12FDEDD4272AFE99005C17AC /* LocalizableHome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableHome.swift; sourceTree = ""; }; 12FFEFA428BE96E00081DDB2 /* TellaWebAddServerURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TellaWebAddServerURLView.swift; sourceTree = ""; }; 1501401C2B71243A00991D69 /* LocalizableResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableResources.swift; sourceTree = ""; }; @@ -1109,7 +1237,6 @@ 15E3D5092C1B71E200B7FECC /* GDriveDraftView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDriveDraftView.swift; sourceTree = ""; }; 15E3D50D2C1B87AC00B7FECC /* GDriveDraftViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDriveDraftViewModel.swift; sourceTree = ""; }; 15E54AB82C5824DA00290DE8 /* LocalizableGDrive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableGDrive.swift; sourceTree = ""; }; - 15E656AA2C2A147E00BDEC91 /* AddFilesToDraftView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFilesToDraftView.swift; sourceTree = ""; }; 15E656AC2C2A14F500BDEC91 /* DraftMainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftMainViewModel.swift; sourceTree = ""; }; 15EE30FD2B5073E20033BBBB /* Pages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pages.swift; sourceTree = ""; }; 15F64C9C2B6D7904008A57AA /* DownloadedResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadedResources.swift; sourceTree = ""; }; @@ -1131,9 +1258,12 @@ 200399002CE3F1E900DCFCF0 /* EditVideoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditVideoViewModel.swift; sourceTree = ""; }; 200399022CE60B2B00DCFCF0 /* ResizableImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImage.swift; sourceTree = ""; }; 200399052CE69E9500DCFCF0 /* EditMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMediaViewModel.swift; sourceTree = ""; }; + 200751D32D5B91F1000CC076 /* NearbySharingParticipant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearbySharingParticipant.swift; sourceTree = ""; }; + 2012D5412D5695D3000E0443 /* RecipientConnectToDeviceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientConnectToDeviceView.swift; sourceTree = ""; }; 20219B2F2C8B33F900AD6D53 /* GdriveReportMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GdriveReportMainView.swift; sourceTree = ""; }; 20219B322C8B3E1900AD6D53 /* GDriveSubmittedDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDriveSubmittedDetailsView.swift; sourceTree = ""; }; 20219B342C8B3F5500AD6D53 /* GdriveOutboxDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GdriveOutboxDetailsView.swift; sourceTree = ""; }; + 203DCBAD2D6DBE4B00B7BEED /* SenderPrepareFileTransferView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderPrepareFileTransferView.swift; sourceTree = ""; }; 203F4D7F2CE77B1500D15086 /* CustomThumbnailSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomThumbnailSlider.swift; sourceTree = ""; }; 20473A5E2C8B9B0A0015A9B5 /* TellaServerReportsMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TellaServerReportsMainView.swift; sourceTree = ""; }; 20473A602C8BA6FE0015A9B5 /* TellaServerSubmittedDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TellaServerSubmittedDetailsView.swift; sourceTree = ""; }; @@ -1143,6 +1273,9 @@ 204F7BAE2CEF8A3D00BA2F62 /* EditMediaControlButtonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMediaControlButtonsView.swift; sourceTree = ""; }; 208A06522BF6057A004BA033 /* EditImageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditImageViewModel.swift; sourceTree = ""; }; 208A06542BF607BF004BA033 /* ImageCropper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCropper.swift; sourceTree = ""; }; + 209107F22D538CF200808BCF /* SenderConnectToDeviceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderConnectToDeviceView.swift; sourceTree = ""; }; + 209107F62D53BA7800808BCF /* SenderConnectToDeviceManuallyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderConnectToDeviceManuallyView.swift; sourceTree = ""; }; + 209107F82D53BD0200808BCF /* ConnectToDeviceManuallyVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToDeviceManuallyVM.swift; sourceTree = ""; }; 20985A5C2BE158020092CCC2 /* EditImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditImageView.swift; sourceTree = ""; }; 209AC6772C2D5C9800492356 /* AddServerURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddServerURLView.swift; sourceTree = ""; }; 209AC6792C2D5E3500492356 /* ServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerViewModel.swift; sourceTree = ""; }; @@ -1158,10 +1291,23 @@ 209AC6912C332F5F00492356 /* PHPhotoLibraryExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PHPhotoLibraryExtension.swift; sourceTree = ""; }; 209AC6922C332F6000492356 /* CLLocationExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CLLocationExtension.swift; sourceTree = ""; }; 209FFD172CED19B8009B7D51 /* EditMediaHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMediaHeaderView.swift; sourceTree = ""; }; + 20A3BD462D5A0D8D009E5E12 /* RecipientConnectToDeviceManuallyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientConnectToDeviceManuallyView.swift; sourceTree = ""; }; + 20A3BD482D5A495D009E5E12 /* CardItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardItemView.swift; sourceTree = ""; }; + 20A8BBB12D6F2C27009D52AF /* AddFileGridView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFileGridView.swift; sourceTree = ""; }; + 20A8BBB32D6F2C70009D52AF /* AddFileGridItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFileGridItemView.swift; sourceTree = ""; }; + 20A8BBB52D6F4777009D52AF /* AddFileBottomSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFileBottomSheetView.swift; sourceTree = ""; }; + 20A8BBB72D6F47CD009D52AF /* AddFilesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFilesViewModel.swift; sourceTree = ""; }; + 20A8BBB92D6F4830009D52AF /* AddFileCameraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFileCameraView.swift; sourceTree = ""; }; + 20A8BBBB2D6F4843009D52AF /* AddFileRecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFileRecordView.swift; sourceTree = ""; }; + 20A8BBBD2D6F4857009D52AF /* AddFilePhotoVideoPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFilePhotoVideoPickerView.swift; sourceTree = ""; }; + 20AA1F9B2D5F38A20092810B /* RecipientFileTransferView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecipientFileTransferView.swift; sourceTree = ""; }; 20AE31912BF4BB0700FF4256 /* CustomCropViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCropViewController.swift; sourceTree = ""; }; 20AF68132C89B3ED00503D30 /* NextcloudSubmittedDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudSubmittedDetailsView.swift; sourceTree = ""; }; 20AF68152C89B44E00503D30 /* NextcloudReportMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudReportMainView.swift; sourceTree = ""; }; 20BA0BDE2C33428A00823A27 /* ServerLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerLoginView.swift; sourceTree = ""; }; + 20BA4FAE2D50DFEB006DD8B9 /* GetConnectedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetConnectedViewModel.swift; sourceTree = ""; }; + 20BA4FB02D522551006DD8B9 /* CardModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardModifier.swift; sourceTree = ""; }; + 20BB11BA2D4D1AB5004B1BBF /* GetConnectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetConnectedView.swift; sourceTree = ""; }; 20BF26722C98481000E81535 /* ScrollviewViewContentViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollviewViewContentViewModifier.swift; sourceTree = ""; }; 20DC9F632CB1CAC000649160 /* EditAudioView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAudioView.swift; sourceTree = ""; }; 20DC9F652CB1CAE100649160 /* EditAudioViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAudioViewModel.swift; sourceTree = ""; }; @@ -1192,7 +1338,7 @@ CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziAttachment.swift; sourceTree = ""; }; CC5DF8772B02B06A0068F622 /* UwaziServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziServer.swift; sourceTree = ""; }; CC9C01B729D223FC00FDF341 /* LocalizableReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableReport.swift; sourceTree = ""; }; - CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziFileSelector.swift; sourceTree = ""; }; + CC9DEE5E2AE80ED400C8ED29 /* UwaziSelectFileComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSelectFileComponent.swift; sourceTree = ""; }; CCA66C9B29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationBottomSheet.swift; sourceTree = ""; }; CCAE3A672AC217EC00A38C6D /* UwaziViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziViewModel.swift; sourceTree = ""; }; CCBC2F6829A93E4900888779 /* SettingCheckboxItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingCheckboxItem.swift; sourceTree = ""; }; @@ -1215,7 +1361,6 @@ CCDC39892AF2BCE400674735 /* MultipartRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartRequest.swift; sourceTree = ""; }; CCE875B22A96699000C576B9 /* LocalizableUwazi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizableUwazi.swift; sourceTree = ""; }; CCE8CCDC2AF41E410088B61D /* UwaziFileUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziFileUtility.swift; sourceTree = ""; }; - CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziAttachmentsActionItems.swift; sourceTree = ""; }; CCEAE5912AE6F6EE0069204A /* EntityCreationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityCreationResponse.swift; sourceTree = ""; }; CCEAE5932AE7188F0069204A /* UwaziSelectWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UwaziSelectWidget.swift; sourceTree = ""; }; CCF47F432ABE18D7000ABBBC /* TemplateActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateActionType.swift; sourceTree = ""; }; @@ -1319,6 +1464,7 @@ 129209032C9DB00500767760 /* NextcloudKit in Frameworks */, D50FA6872613ABCA00D6D63B /* VaultManager.framework in Frameworks */, 89EDF57136D5C3761B8AB12D /* libPods-Tella.a in Frameworks */, + 12C454D82DAD24300085FCD6 /* X509 in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1424,6 +1570,15 @@ path = Tabs; sourceTree = ""; }; + 1202C8A02DB971BC0013E17D /* ManuallyVerification */ = { + isa = PBXGroup; + children = ( + 125C16052DAEF5430086D4F0 /* ManuallyVerificationView.swift */, + 1240917A2DB03FCC005AFE9B /* ManuallyVerificationViewModel.swift */, + ); + path = ManuallyVerification; + sourceTree = ""; + }; 1203CDE429915C6600D09073 /* Database */ = { isa = PBXGroup; children = ( @@ -1489,6 +1644,8 @@ CCDC39882AF2BCC600674735 /* Utils */, 1233007728E30C7900B8F7BA /* APIRequest.swift */, 12D7D11B28E3581E008121C8 /* URLRequest.swift */, + 124866FE2DFCCAA200EBE8F8 /* LocalURLRequest.swift */, + 1252FDDF2E006E10002AF5E8 /* NearbySharingURLSessionDelegate.swift */, 1203CDE82992C19D00D09073 /* JSONStringEncoder.swift */, 1203CDEA2992C1CB00D09073 /* HTTPCodes.swift */, 1233007928E3115500B8F7BA /* Request.swift */, @@ -1546,6 +1703,7 @@ 1293F6762AD88A0400F6CFBD /* FeedbackDTO.swift */, 15FABA372B7405B800B2EFEE /* ResourceDTO.swift */, 15BD9CB22BC7472A00C3932B /* UwaziRelationshipDTO.swift */, + 1282C09C2D8AD7890033E4CA /* NearbySharing */, ); path = Models; sourceTree = ""; @@ -1709,6 +1867,7 @@ 1236EE8328E2226F00973EEA /* Networking */ = { isa = PBXGroup; children = ( + 12505B1C2D899B4500483E34 /* NearbySharing */, 1203CDEC2992C63C00D09073 /* APICall */, 125F9B962AD965520073748C /* Feedback */, 125AAD32295348D2002194D6 /* Upload */, @@ -1719,6 +1878,55 @@ path = Networking; sourceTree = ""; }; + 123CBAF92DEEEF810030A9BD /* llhttp */ = { + isa = PBXGroup; + children = ( + 123CBAF52DEEEF810030A9BD /* api.c */, + 123CBAF62DEEEF810030A9BD /* http.c */, + 123CBAF72DEEEF810030A9BD /* llhttp.h */, + 123CBAF82DEEEF810030A9BD /* llhttp.c */, + ); + path = llhttp; + sourceTree = ""; + }; + 1244945B2DD60FB0002B4A9B /* Sending */ = { + isa = PBXGroup; + children = ( + 1244945F2DD6125F002B4A9B /* SenderFileTransferVM.swift */, + 12FAC4562E2519F5007A4651 /* FileSendingView.swift */, + ); + path = Sending; + sourceTree = ""; + }; + 1244945C2DD60FD9002B4A9B /* Preparing */ = { + isa = PBXGroup; + children = ( + 203DCBAD2D6DBE4B00B7BEED /* SenderPrepareFileTransferView.swift */, + 12A9ECD62D9F3FC300711BA4 /* SenderPrepareFileTransferVM.swift */, + ); + path = Preparing; + sourceTree = ""; + }; + 12505B1C2D899B4500483E34 /* NearbySharing */ = { + isa = PBXGroup; + children = ( + 1253927F2D897A8F00F96BA2 /* NearbySharingRepository.swift */, + 12505B1D2D899BD100483E34 /* NearbySharingServer.swift */, + 121AFF922E4CA9D6007300AD /* NearbySharingStateActor.swift */, + 12444EF92E09D6F3006CB86A /* NearbySharingServerState.swift */, + 126B91BA2E33738C002C8B35 /* NearbySharingEvent.swift */, + 121AFF942E4CDE6E007300AD /* NearbySharingServerHandler.swift */, + 1282DE502DDE101E004D2176 /* NWConnection.swift */, + 123CBAFD2DEF0DCD0030A9BD /* HTTPParser.swift */, + 123CBB012DEF34640030A9BD /* HTTPRequest.swift */, + 12505B212D89A2A500483E34 /* HTTPResponse.swift */, + 123CBB032DEF97270030A9BD /* HTTPStatusCode.swift */, + 123CBB052DEF99410030A9BD /* NearbySharingEndpoint.swift */, + 123CBB072DEF998A0030A9BD /* NearbySharingServerResponse.swift */, + ); + path = NearbySharing; + sourceTree = ""; + }; 1251C38E2BB455E5002E9898 /* EntityInstances */ = { isa = PBXGroup; children = ( @@ -1893,6 +2101,8 @@ 120D86632CB56FC500C007B4 /* ResultExtension.swift */, 129BC3A52CBE582A001C7323 /* FileHandleExtension.swift */, 12937FE12DC227A90019ECC4 /* AVPlayerItem+VideoRotation.swift */, + 125C16012DAE76260086D4F0 /* SecKeyExtension.swift */, + 129919BD2E31143400A4417D /* NWConnectionExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -1993,6 +2203,20 @@ path = Views; sourceTree = ""; }; + 126FAAD12D4B9CFE00F6BCD8 /* NearbySharing */ = { + isa = PBXGroup; + children = ( + 126FAAD22D4B9D2100F6BCD8 /* NearbySharingMainView.swift */, + 1276B7372E1E56840024C38A /* FileTransfer */, + 1202C8A02DB971BC0013E17D /* ManuallyVerification */, + 2012D5442D5697B9000E0443 /* Recipient */, + 2012D5432D5697B0000E0443 /* Sender */, + 20BA4FAD2D50DFB2006DD8B9 /* GetConnected */, + 128247A22D5F935100B7E63E /* Models */, + ); + path = NearbySharing; + sourceTree = ""; + }; 126FEE082AC4992800B99298 /* Models */ = { isa = PBXGroup; children = ( @@ -2067,6 +2291,38 @@ path = Nextcloud; sourceTree = ""; }; + 1276B7372E1E56840024C38A /* FileTransfer */ = { + isa = PBXGroup; + children = ( + 1244945D2DD61256002B4A9B /* FileTransferView.swift */, + 125D58AA2E1811AB00365C40 /* TransferProgressView.swift */, + 125D58AE2E1BE9D700365C40 /* FileTransferVM.swift */, + 1276B7382E1E56F00024C38A /* ProgressViewModel.swift */, + ); + path = FileTransfer; + sourceTree = ""; + }; + 1276B73A2E1E68970024C38A /* Preparing */ = { + isa = PBXGroup; + children = ( + 20AA1F9B2D5F38A20092810B /* RecipientFileTransferView.swift */, + 1222363E2DD3DDC1006A9F01 /* RecipientPrepareFileTransferVM.swift */, + ); + path = Preparing; + sourceTree = ""; + }; + 1276B73B2E1E68A10024C38A /* Receiving */ = { + isa = PBXGroup; + children = ( + 125D58AC2E1BE9A000365C40 /* ReceiverFileTransferVM.swift */, + 1276B73E2E1EA8F10024C38A /* FileReceivingView.swift */, + 12FAC4582E2571E0007A4651 /* NearbySharingResultView.swift */, + 12FAC45C2E265BE4007A4651 /* NearbySharingResultVM.swift */, + 12FAC45A2E257A3E007A4651 /* ResultView.swift */, + ); + path = Receiving; + sourceTree = ""; + }; 12776A9627BAC86200CDC5DC /* ViewModel */ = { isa = PBXGroup; children = ( @@ -2100,12 +2356,14 @@ 12567FF8271742C500A2D356 /* Localizable.swift */, 126FE4D627A427E200AE4188 /* Constants.swift */, D516397B2631113B003A11B5 /* Styles.swift */, + 127E81A72DB90089002AED4C /* TypographyStyle.swift */, 12BCB2E62816972200889E5A /* Localizable */, 12B6481C28367C1D00CBE541 /* SheetManager.swift */, 12935478294A3BFC00735ED0 /* MIMEType.swift */, 1289B45528B62A99005DA687 /* DeviceOrientationHelper.swift */, 1233076629F15A290019DF7C /* NetworkMonitor.swift */, 1587D6EA2C98C8C1003E0B3D /* ConfigurationManager.swift */, + 127296722D70A46100CB9FF0 /* CertificateGenerator.swift */, ); path = Utils; sourceTree = ""; @@ -2155,6 +2413,35 @@ path = Models; sourceTree = ""; }; + 127E81A42DB7C69A002AED4C /* Texts Styles */ = { + isa = PBXGroup; + children = ( + 127E81A22DB7C4A9002AED4C /* TextStyleModifier.swift */, + 126B70112DB1575600742897 /* CustomText.swift */, + ); + path = "Texts Styles"; + sourceTree = ""; + }; + 128247A22D5F935100B7E63E /* Models */ = { + isa = PBXGroup; + children = ( + 200751D32D5B91F1000CC076 /* NearbySharingParticipant.swift */, + 128247A32D5F935A00B7E63E /* ConnectionInfo.swift */, + ); + path = Models; + sourceTree = ""; + }; + 1282C09C2D8AD7890033E4CA /* NearbySharing */ = { + isa = PBXGroup; + children = ( + 1282C09D2D8AD7990033E4CA /* PrepareUpload.swift */, + 1282C09F2D8AEA1A0033E4CA /* Register.swift */, + 1282C0A12D8AEE110033E4CA /* FileUpload.swift */, + 1282C0A32D8AF3D50033E4CA /* CloseConnection.swift */, + ); + path = NearbySharing; + sourceTree = ""; + }; 1283C258272AABC9009180CA /* Views */ = { isa = PBXGroup; children = ( @@ -2358,6 +2645,7 @@ 129380092DC284D50019ECC4 /* Localizable.strings */, 1293800A2DC284D50019ECC4 /* Tella.entitlements */, 1293800B2DC284D50019ECC4 /* TellaConfig.xcconfig */, + 123CBAEC2DEEEA880030A9BD /* Tella-Bridging-Header.h */, ); path = "Supporting Files"; sourceTree = ""; @@ -2404,7 +2692,6 @@ 12A148902720A0E500A1ADDC /* CommonView */ = { isa = PBXGroup; children = ( - 12A148932720A89E00A1ADDC /* BottomLockView.swift */, 12A148952720B2B400A1ADDC /* PasswordTextFieldView.swift */, 12A148972720B47100A1ADDC /* LockDescriptionView.swift */, 12A148992720B53800A1ADDC /* ConfirmPasswordErrorView.swift */, @@ -2421,6 +2708,80 @@ path = Common; sourceTree = ""; }; + 12A9ECCE2D9F2DCA00711BA4 /* ConnectToDeviceManually */ = { + isa = PBXGroup; + children = ( + 209107F62D53BA7800808BCF /* SenderConnectToDeviceManuallyView.swift */, + 209107F82D53BD0200808BCF /* ConnectToDeviceManuallyVM.swift */, + ); + path = ConnectToDeviceManually; + sourceTree = ""; + }; + 12A9ECCF2D9F2DE500711BA4 /* ConnectToDeviceQRCode */ = { + isa = PBXGroup; + children = ( + 1282479E2D5F50A600B7E63E /* QRCodeScannerView.swift */, + 209107F22D538CF200808BCF /* SenderConnectToDeviceView.swift */, + 128247A02D5F922900B7E63E /* SenderConnectToDeviceViewModel.swift */, + ); + path = ConnectToDeviceQRCode; + sourceTree = ""; + }; + 12A9ECD02D9F2E3400711BA4 /* SendFiles */ = { + isa = PBXGroup; + children = ( + 1244945C2DD60FD9002B4A9B /* Preparing */, + 1244945B2DD60FB0002B4A9B /* Sending */, + ); + path = SendFiles; + sourceTree = ""; + }; + 12A9ECD12D9F332F00711BA4 /* Connection */ = { + isa = PBXGroup; + children = ( + 12A9ECCF2D9F2DE500711BA4 /* ConnectToDeviceQRCode */, + 12A9ECCE2D9F2DCA00711BA4 /* ConnectToDeviceManually */, + 123B19822D9DD3D200BAD892 /* ConnectionFailedView.swift */, + ); + path = Connection; + sourceTree = ""; + }; + 12A9ECD22D9F3AC600711BA4 /* Connection */ = { + isa = PBXGroup; + children = ( + 12A9ECD32D9F3AEB00711BA4 /* ConnectToDeviceManually */, + 12A9ECD42D9F3B0800711BA4 /* ConnectToDeviceQRCode */, + ); + path = Connection; + sourceTree = ""; + }; + 12A9ECD32D9F3AEB00711BA4 /* ConnectToDeviceManually */ = { + isa = PBXGroup; + children = ( + 123004CC2D8CB752006F3F2D /* RecipientConnectManuallyViewModel.swift */, + 20A3BD462D5A0D8D009E5E12 /* RecipientConnectToDeviceManuallyView.swift */, + ); + path = ConnectToDeviceManually; + sourceTree = ""; + }; + 12A9ECD42D9F3B0800711BA4 /* ConnectToDeviceQRCode */ = { + isa = PBXGroup; + children = ( + 1282479C2D5E1AD900B7E63E /* RecipientConnectToDeviceViewModel.swift */, + 2012D5412D5695D3000E0443 /* RecipientConnectToDeviceView.swift */, + ); + path = ConnectToDeviceQRCode; + sourceTree = ""; + }; + 12A9ECD52D9F3B2400711BA4 /* ReceiveFiles */ = { + isa = PBXGroup; + children = ( + 1276B73A2E1E68970024C38A /* Preparing */, + 1276B73B2E1E68A10024C38A /* Receiving */, + ); + path = ReceiveFiles; + sourceTree = ""; + }; 12AB30D1272C33D90020FFBF /* Home */ = { isa = PBXGroup; children = ( @@ -2544,6 +2905,7 @@ 15E54AB82C5824DA00290DE8 /* LocalizableGDrive.swift */, 15A173432C78F64100E84F55 /* LocalizableError.swift */, 1587D6EC2C99CD15003E0B3D /* LocalizableDropbox.swift */, + 126FAACD2D4B954F00F6BCD8 /* LocalizableNearbySharing.swift */, ); path = Localizable; sourceTree = ""; @@ -2593,6 +2955,7 @@ 12D7F5F52743F28700EBA3A2 /* Components */ = { isa = PBXGroup; children = ( + 20A8BBB02D6F2BF0009D52AF /* AddFileGridViewComponent */, 20BF26712C9843C200E81535 /* Modifiers */, 12C07F032B0748BE0030AD6E /* TopSheetView */, 122E4D53291AD99A00562EB1 /* TellaButton */, @@ -2626,6 +2989,11 @@ 1503C87D2C07A080000F2209 /* ServerConnectionHeaderView.swift */, 20F3E6002CB5D9EC00B570E3 /* EditFileCancelBottomSheet.swift */, 12A6B6642D132E290002F74B /* CardFrame.swift */, + 20A3BD482D5A495D009E5E12 /* CardItemView.swift */, + 12A148932720A89E00A1ADDC /* NavigationBottomView.swift */, + 1202C8A22DB985B80013E17D /* BackBottomView.swift */, + 124852A12E7173BC00BDDC2D /* IconTextButton.swift */, + 127E81A42DB7C69A002AED4C /* Texts Styles */, ); path = Components; sourceTree = ""; @@ -2838,7 +3206,6 @@ isa = PBXGroup; children = ( 152D38502C2C61DF00323CE7 /* DraftView.swift */, - 15E656AA2C2A147E00BDEC91 /* AddFilesToDraftView.swift */, 1220A72D292EB11B000BC24C /* ReportFileGridView.swift */, ); path = Draft; @@ -2925,6 +3292,24 @@ path = EditMedia; sourceTree = ""; }; + 2012D5432D5697B0000E0443 /* Sender */ = { + isa = PBXGroup; + children = ( + 12A9ECD12D9F332F00711BA4 /* Connection */, + 12A9ECD02D9F2E3400711BA4 /* SendFiles */, + ); + path = Sender; + sourceTree = ""; + }; + 2012D5442D5697B9000E0443 /* Recipient */ = { + isa = PBXGroup; + children = ( + 12A9ECD22D9F3AC600711BA4 /* Connection */, + 12A9ECD52D9F3B2400711BA4 /* ReceiveFiles */, + ); + path = Recipient; + sourceTree = ""; + }; 20985A5B2BE157D00092CCC2 /* EditImageView */ = { isa = PBXGroup; children = ( @@ -2945,10 +3330,35 @@ path = Connections; sourceTree = ""; }; + 20A8BBB02D6F2BF0009D52AF /* AddFileGridViewComponent */ = { + isa = PBXGroup; + children = ( + 20A8BBB12D6F2C27009D52AF /* AddFileGridView.swift */, + 20A8BBB32D6F2C70009D52AF /* AddFileGridItemView.swift */, + 20A8BBB52D6F4777009D52AF /* AddFileBottomSheetView.swift */, + 20A8BBB72D6F47CD009D52AF /* AddFilesViewModel.swift */, + 20A8BBB92D6F4830009D52AF /* AddFileCameraView.swift */, + 20A8BBBB2D6F4843009D52AF /* AddFileRecordView.swift */, + 20A8BBBD2D6F4857009D52AF /* AddFilePhotoVideoPickerView.swift */, + ); + path = AddFileGridViewComponent; + sourceTree = ""; + }; + 20BA4FAD2D50DFB2006DD8B9 /* GetConnected */ = { + isa = PBXGroup; + children = ( + 20BB11BA2D4D1AB5004B1BBF /* GetConnectedView.swift */, + 1248529F2E6EF9E000BDDC2D /* TipsToConnectView.swift */, + 20BA4FAE2D50DFEB006DD8B9 /* GetConnectedViewModel.swift */, + ); + path = GetConnected; + sourceTree = ""; + }; 20BF26712C9843C200E81535 /* Modifiers */ = { isa = PBXGroup; children = ( 20BF26722C98481000E81535 /* ScrollviewViewContentViewModifier.swift */, + 20BA4FB02D522551006DD8B9 /* CardModifier.swift */, ); path = Modifiers; sourceTree = ""; @@ -3076,7 +3486,6 @@ 0ECC781A267A542300A2ACF2 /* UwaziPages.swift */, CCC1B43C2AC75C5D0075B819 /* UwaziConstants.swift */, CC5CEB882AF0551900BF238B /* UwaziAttachment.swift */, - CCE8CCDE2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift */, 153AACFA2BE41C010075D5B2 /* UwaziEntityRelationshipItem.swift */, ); path = Models; @@ -3109,6 +3518,7 @@ 0DD99E0126910378009327B3 /* Camera */, D75F9E9A253424F7006F6212 /* Audio */, 0D4BC3BE2675737F00DCDC30 /* Reports */, + 126FAAD12D4B9CFE00F6BCD8 /* NearbySharing */, ); path = Scenes; sourceTree = ""; @@ -3343,7 +3753,7 @@ CCC1B4322AC75B410075B819 /* UwaziEntityMandatoryTextView.swift */, CCC1B4302AC75B120075B819 /* UwaziEntityButtonView.swift */, CCC1B42E2AC75AF60075B819 /* UwaziDividerWidget.swift */, - CC9DEE5E2AE80ED400C8ED29 /* UwaziFileSelector.swift */, + CC9DEE5E2AE80ED400C8ED29 /* UwaziSelectFileComponent.swift */, CC5CEB862AF037B600BF238B /* FileDropdown.swift */, 15BD9CAA2BC740D700C3932B /* EntitySelectorView.swift */, 15CD890F2BF297BE00F724F5 /* SelectedEntityView.swift */, @@ -3375,6 +3785,7 @@ isa = PBXGroup; children = ( F66E65AB23E3207D000F93E5 /* Tella */, + 123CBAF92DEEEF810030A9BD /* llhttp */, F66E65C223E32080000F93E5 /* TellaTests */, F66E65CD23E32080000F93E5 /* TellaUITests */, D50FA6812613ABC900D6D63B /* VaultManager */, @@ -3485,6 +3896,7 @@ name = Tella; packageProductDependencies = ( 129209022C9DB00500767760 /* NextcloudKit */, + 12C454D72DAD24300085FCD6 /* X509 */, ); productName = Tella; productReference = F66E65A923E3207D000F93E5 /* Tella.app */; @@ -3586,6 +3998,7 @@ mainGroup = F66E65A023E3207D000F93E5; packageReferences = ( 129209012C9DAFFA00767760 /* XCRemoteSwiftPackageReference "NextcloudKit" */, + 12C454D62DAD24300085FCD6 /* XCRemoteSwiftPackageReference "swift-certificates" */, ); productRefGroup = F66E65AA23E3207D000F93E5 /* Products */; projectDirPath = ""; @@ -3781,7 +4194,7 @@ 20DC9F642CB1CAC000649160 /* EditAudioView.swift in Sources */, 15C540A82CA5BBDF00D45B06 /* DropboxReportFile.swift in Sources */, E1E7C5842AAB70A500DDB07E /* UwaziDictionaryRow.swift in Sources */, - 15E656AB2C2A147E00BDEC91 /* AddFilesToDraftView.swift in Sources */, + 20BB11BB2D4D1AB5004B1BBF /* GetConnectedView.swift in Sources */, 12A1489A2720B53800A1ADDC /* ConfirmPasswordErrorView.swift in Sources */, 1551C32A2C93738400DFA6EF /* DropboxDraftViewModel.swift in Sources */, 128B15CB27DA30C800E1C1E0 /* OnboardingEndView.swift in Sources */, @@ -3792,18 +4205,21 @@ CC0F1DD42B47320F00720010 /* UwaziDatePicker.swift in Sources */, 128A760A2CD23DDA0033F1C2 /* OutboxDetailsView.swift in Sources */, 128A760B2CD23DDA0033F1C2 /* SubmittedDetailsView.swift in Sources */, + 123B19832D9DD3D200BAD892 /* ConnectionFailedView.swift in Sources */, 128A760C2CD23DDA0033F1C2 /* SubmittedDetailsItemView.swift in Sources */, 128A760D2CD23DDA0033F1C2 /* OutboxDetailsItemView.swift in Sources */, 1209FD3027AD8AC400D8C17E /* AudioPlayerManager.swift in Sources */, 12BAFA7728B3B8D300930451 /* UIImageOrientation.swift in Sources */, CCE875B32A96699000C576B9 /* LocalizableUwazi.swift in Sources */, 123CE84429126B52003C251F /* TellaWebServerViewModel.swift in Sources */, + 2012D5422D5695D3000E0443 /* RecipientConnectToDeviceView.swift in Sources */, 20BF26732C98481000E81535 /* ScrollviewViewContentViewModifier.swift in Sources */, 122DA88F29CB1F21003801CD /* AutoUpload.swift in Sources */, 15BD9CA92BC740B300C3932B /* UwaziRelationshipWidget.swift in Sources */, 15F64CA02B6D794B008A57AA /* SectionTitle.swift in Sources */, CC1313882A5CB4550057271C /* DeleteAfterFailOptionsStatus.swift in Sources */, 1293F67A2AD88B8400F6CFBD /* FeedbackAPI.swift in Sources */, + 12505B222D89A2A700483E34 /* HTTPResponse.swift in Sources */, D7F55FDA253490BE0028739E /* RecordingAudioManager.swift in Sources */, 0E78B3A82656B03600F9BDBC /* ConfirmBottomSheet.swift in Sources */, 157EDEAB2CB055EB00663549 /* DropboxCreateFolderResponse.swift in Sources */, @@ -3814,6 +4230,7 @@ 120451FA28C7525200D572A7 /* EditSettingsServerView.swift in Sources */, 12781F4727EA247500FE9609 /* DocumentPickerView.swift in Sources */, 12B3AD8C28C1090700AC9AF9 /* SuccessLoginView.swift in Sources */, + 128247A12D5F923100B7E63E /* SenderConnectToDeviceViewModel.swift in Sources */, 129B845528BFEC8600F1B344 /* TopServerView.swift in Sources */, 1209FD2B27AD851E00D8C17E /* AudioPlayerView.swift in Sources */, 128D6E182804531B0082AB18 /* AddPhotoVideoItems.swift in Sources */, @@ -3823,6 +4240,7 @@ CC0D56EF2AC4B59B00AA5D81 /* AddTemplateVM.swift in Sources */, CCF8D5C22AE994BF000D27C5 /* SupportingFileWidget.swift in Sources */, 12AFF1B82D3FACBC00C2455C /* NavigationHeaderTypes.swift in Sources */, + 1282479F2D5F50B300B7E63E /* QRCodeScannerView.swift in Sources */, 12E6772F27A9719F00DC1E1C /* UIImageExtension.swift in Sources */, 15994A0E2BFBE15F0017F153 /* SelectSharedDrive.swift in Sources */, D5B823EB262774E00008E04B /* MainView.swift in Sources */, @@ -3830,6 +4248,7 @@ 151308172BF3DBD9000C51D7 /* UwaziRelationshipList.swift in Sources */, 153F96492BFD306F00FE5464 /* CreateDriveFolderView.swift in Sources */, 125C78A22C38056300440014 /* EntityStatusExtension.swift in Sources */, + 1276B73F2E1EA8F10024C38A /* FileReceivingView.swift in Sources */, 153F96492BFD306F00FE5464 /* CreateDriveFolderView.swift in Sources */, 1220A72E292EB11B000BC24C /* ReportFileGridView.swift in Sources */, 125FA6D6278DE49A004B9D06 /* URLExtension.swift in Sources */, @@ -3837,6 +4256,7 @@ 12A148922720A32000A1ADDC /* LockPinData.swift in Sources */, CCC1B43B2AC75BED0075B819 /* UwaziEntityParserProtocol.swift in Sources */, D57F5E9026152A4800C1DA47 /* Datable.swift in Sources */, + 200751D42D5B91F1000CC076 /* NearbySharingParticipant.swift in Sources */, CC1313822A5CB0DB0057271C /* DeleteAfterFailOption.swift in Sources */, 15F924482C17929300C436C5 /* ViewModelState.swift in Sources */, 20473A632C8BA7FE0015A9B5 /* TellaServerOutboxDetailsView.swift in Sources */, @@ -3849,6 +4269,7 @@ 125AAD34295348EC002194D6 /* UploadService.swift in Sources */, 1237A71E278F5F8B00D7BC0F /* LanguageManager.swift in Sources */, E1CF5A2B2AB3533100365036 /* ServerDatabase.swift in Sources */, + 123CBAFE2DEF0DCF0030A9BD /* HTTPParser.swift in Sources */, CCC1B4372AC75B700075B819 /* UwaziEntityTitleView.swift in Sources */, CC9C01B829D223FC00FDF341 /* LocalizableReport.swift in Sources */, 200399012CE3F1E900DCFCF0 /* EditVideoViewModel.swift in Sources */, @@ -3858,17 +4279,23 @@ CCDC398A2AF2BCE400674735 /* MultipartRequest.swift in Sources */, E108B75729FE79340045CCBE /* UwaziServerViewModel.swift in Sources */, 1293F66A2AD7F29B00F6CFBD /* FeedbackViewModel.swift in Sources */, + 124852A22E7173BC00BDDC2D /* IconTextButton.swift in Sources */, 126E1AFF2C3D8462000AE4CE /* CreateNextcloudFolderView.swift in Sources */, 1289B98B27C53C2400315FCE /* CameraTypeItemView.swift in Sources */, 12B84B912C333DD0006363F0 /* ReportMainView.swift in Sources */, 155541B22C0672C20031D3A5 /* GDriveRepository.swift in Sources */, 126AD14A27C6D5EB00081CC9 /* LocalizableCamera.swift in Sources */, + 126FAACE2D4B956400F6BCD8 /* LocalizableNearbySharing.swift in Sources */, + 1244945E2DD61256002B4A9B /* FileTransferView.swift in Sources */, 1535A5F12BAA06490009EA42 /* DownloadedResource.swift in Sources */, E1E7C58B2AAB7F2000DDB07E /* UwaziTranslationContext.swift in Sources */, E1E7C5732AAB4DCC00DDB07E /* CollectedTemplate.swift in Sources */, E1CF5A252AB3477C00365036 /* UwaziDatabase.swift in Sources */, 128754D92BBCE09B00D215B5 /* SummaryViewModel.swift in Sources */, 125680002717907900A2D356 /* LockPasswordView.swift in Sources */, + 1252FDE02E006E13002AF5E8 /* NearbySharingURLSessionDelegate.swift in Sources */, + 203DCBAE2D6DBE4B00B7BEED /* SenderPrepareFileTransferView.swift in Sources */, + 123CBB062DEF99470030A9BD /* NearbySharingEndpoint.swift in Sources */, 1224DAF2271A3393001C0ED0 /* AppViewState.swift in Sources */, D7F55FCD25348FB80028739E /* AudioRecorderManager.swift in Sources */, 121BAB7F29A7E30600D7E847 /* URLRequestExtension.swift in Sources */, @@ -3899,9 +4326,13 @@ E1CF5A272AB3488E00365036 /* JSONDecoderExtension.swift in Sources */, 1291F4F227B52BF9006A34D3 /* QueuePlayer.swift in Sources */, 12A6B66F2D1443DC0002F74B /* AssetGridView.swift in Sources */, + 1282C0A22D8AEE130033E4CA /* FileUpload.swift in Sources */, + 128247A42D5F935D00B7E63E /* ConnectionInfo.swift in Sources */, + 125C16022DAE762F0086D4F0 /* SecKeyExtension.swift in Sources */, 1221CB7128DB1B5A0042F15F /* KeyValue.swift in Sources */, 1234179229CD19E6004CFAEA /* BaseUploadOperation.swift in Sources */, 125EFACE274A834300D93DF6 /* FileActionMenu.swift in Sources */, + 123CBB042DEF97290030A9BD /* HTTPStatusCode.swift in Sources */, 12351C1A28D2209400F3CB7C /* Report.swift in Sources */, D53B77012601CA3A0001CD34 /* VaultManager.swift in Sources */, 121C5D7B29424F4E00A64123 /* ReportAPI.swift in Sources */, @@ -3915,8 +4346,10 @@ 12ADF98B28BD130C00FA96EB /* ServersListView.swift in Sources */, 12568002271790AF00A2D356 /* LockPinView.swift in Sources */, 1209FD3227ADBDDE00D8C17E /* TimeIntervalExtension.swift in Sources */, + 126B91BB2E337390002C8B35 /* NearbySharingEvent.swift in Sources */, + 20A8BBBC2D6F4843009D52AF /* AddFileRecordView.swift in Sources */, 125A89562A94D7AB009347C3 /* VaultDataBase.swift in Sources */, - 12A148942720A89E00A1ADDC /* BottomLockView.swift in Sources */, + 12A148942720A89E00A1ADDC /* NavigationBottomView.swift in Sources */, 1236645128FFF23000FA839B /* ReportsViewModel.swift in Sources */, 1233007A28E3115500B8F7BA /* Request.swift in Sources */, 121C5D7D2942710600A64123 /* ProjectAPI.swift in Sources */, @@ -3938,8 +4371,9 @@ 1234179429CD2CFC004CFAEA /* URLSessionTaskResponse.swift in Sources */, E1F6D7832AB17FB5000A9B64 /* UwaziValue.swift in Sources */, 126B20E62DBBA16B00FD7762 /* RotateVideoView.swift in Sources */, - CCE8CCDF2AF4297B0088B61D /* UwaziAttachmentsActionItems.swift in Sources */, 122DA89729CB2709003801CD /* SettingsBottomView.swift in Sources */, + 127E81A82DB9008B002AED4C /* TypographyStyle.swift in Sources */, + 20A8BBBE2D6F4857009D52AF /* AddFilePhotoVideoPickerView.swift in Sources */, 1216124028DE144200E6D7B6 /* JoinCondition.swift in Sources */, 12AE8D1E278CD4900007C781 /* IntExtension.swift in Sources */, 12730E67279F11C300DC0135 /* LanguageListView.swift in Sources */, @@ -3949,6 +4383,7 @@ 1236EE8028E1B40200973EEA /* DataBaseHelper.swift in Sources */, 204F7BAF2CEF8A3D00BA2F62 /* EditMediaControlButtonsView.swift in Sources */, 125299E12AD0515600191CAB /* VaultFilesManager.swift in Sources */, + 121AFF952E4CDE7B007300AD /* NearbySharingServerHandler.swift in Sources */, 1237A719278F172E00D7BC0F /* NSUIImage.swift in Sources */, 126818C32A38B69E004606BD /* FileTypeHelper.swift in Sources */, D572EACB263A43C100CE191A /* FileDetailsView.swift in Sources */, @@ -3964,9 +4399,11 @@ 1293547B294A454900735ED0 /* AppDelegate.swift in Sources */, 20F3E6012CB5D9EC00B570E3 /* EditFileCancelBottomSheet.swift in Sources */, 12B52580284903AE00B3D1C0 /* AboutAndHelpItem.swift in Sources */, + 20A8BBB62D6F4777009D52AF /* AddFileBottomSheetView.swift in Sources */, CCC1B4332AC75B410075B819 /* UwaziEntityMandatoryTextView.swift in Sources */, 12E9DD9927DF65E80002FD00 /* MoveFilesView.swift in Sources */, E1975EE72AB032B10060228F /* UwaziEntryPrompt.swift in Sources */, + 123004CD2D8CB767006F3F2D /* RecipientConnectManuallyViewModel.swift in Sources */, 1291A83628BE3948001A6055 /* SettingsLinkItemView.swift in Sources */, 15DCD93C2C9C78B2001339A7 /* DropboxOutboxViewModel.swift in Sources */, 0ECC7819267A53A700A2ACF2 /* PageView.swift in Sources */, @@ -3984,6 +4421,7 @@ 15A173442C78F64100E84F55 /* LocalizableError.swift in Sources */, 125D2321271F823100250FBB /* UIApplicationExtension.swift in Sources */, 12234BE029DAEC4A00D6F981 /* CustomNavigation.swift in Sources */, + 1282C0A42D8AF3D70033E4CA /* CloseConnection.swift in Sources */, 126E1AF12C3D7603000AE4CE /* BaseReport.swift in Sources */, 125D2324271F891500250FBB /* PinView.swift in Sources */, 157E4F532BA385B500147345 /* PDFKitView.swift in Sources */, @@ -3994,6 +4432,7 @@ 125FA6D8278E2828004B9D06 /* DoubleExtension.swift in Sources */, 12964D8D2BA9CF0D003E1E0A /* UwaziEntityInstance.swift in Sources */, 153C23972CB49C3300C157D9 /* DropboxFileInfo.swift in Sources */, + 20A3BD492D5A495D009E5E12 /* CardItemView.swift in Sources */, CCEAE5942AE7188F0069204A /* UwaziSelectWidget.swift in Sources */, 15F64C9D2B6D7904008A57AA /* DownloadedResources.swift in Sources */, 129533C42739D5BC0053FA4B /* TextFieldBottomSheetView.swift in Sources */, @@ -4003,7 +4442,9 @@ E14E0CF629FAD68E004FC4CD /* UwaziSuccessView.swift in Sources */, 12A1488F27209F2800A1ADDC /* ViewExtension.swift in Sources */, CC5CEB892AF0551900BF238B /* UwaziAttachment.swift in Sources */, + 209107F32D538CF200808BCF /* SenderConnectToDeviceView.swift in Sources */, CCC1B4312AC75B120075B819 /* UwaziEntityButtonView.swift in Sources */, + 20BA4FAF2D50DFEB006DD8B9 /* GetConnectedViewModel.swift in Sources */, 1272F26127C91CD90054F2E2 /* ImportFilesProgressModels.swift in Sources */, 1291A83228BE0086001A6055 /* SettingsAddServerCardView.swift in Sources */, 1254E2602C6B8114004C6280 /* NextcloutOutboxDetailsView.swift in Sources */, @@ -4013,10 +4454,12 @@ 1205F9C22BA1DE52003DB9ED /* ImportedFile.swift in Sources */, 122B61362AE72D8D00631403 /* FeedbackService.swift in Sources */, 0E87C875268BDDFB00EB5E34 /* ImageViewer.swift in Sources */, + 1240917B2DB03FCC005AFE9B /* ManuallyVerificationViewModel.swift in Sources */, 209AC6952C332F6000492356 /* PHPhotoLibraryExtension.swift in Sources */, 12A6B6622D12CE080002F74B /* EmptyFileView.swift in Sources */, 12A6B6672D13628B0002F74B /* LimitedPhotoLibraryActionType.swift in Sources */, 1233076729F15A290019DF7C /* NetworkMonitor.swift in Sources */, + 123CBB082DEF998C0030A9BD /* NearbySharingServerResponse.swift in Sources */, 1249B3832AD43AA7009A7023 /* VaultFilesManagerInterface.swift in Sources */, 15F64CA62B6D80F7008A57AA /* ResourceCardViewModel.swift in Sources */, 0DA213CB268028A200526995 /* FileSortOptions.swift in Sources */, @@ -4037,6 +4480,7 @@ 12F2C65C2B86567F008457EB /* FileDetailsViewModel.swift in Sources */, 12C07ECC2B06603A0030AD6E /* EncryptionService.swift in Sources */, 1244141727E357E80037D469 /* LoadMoreCell.swift in Sources */, + 125392802D897A9100F96BA2 /* NearbySharingRepository.swift in Sources */, CCC1B4292AC75A8E0075B819 /* RenderPropertyComponentView.swift in Sources */, 1254E2722C6FDCFB004C6280 /* MoreButtonView.swift in Sources */, 125BC1DE2B21F08200A117D0 /* VaultFileDetailsToMerge.swift in Sources */, @@ -4080,13 +4524,15 @@ 1289B98727C5397200315FCE /* ScreenExtension.swift in Sources */, 1227FC8F2922BDBC002FD6FB /* CameraModel.swift in Sources */, E1EEE29629E6796E009FE227 /* ServerSelectionView.swift in Sources */, + 12FAC45B2E257A3E007A4651 /* ResultView.swift in Sources */, E131BA3B2A1B45A30095BC91 /* UwaziServerRepositories.swift in Sources */, 20AF68162C89B44E00503D30 /* NextcloudReportMainView.swift in Sources */, 15DCD9402C9CB18F001339A7 /* DropboxSubmittedViewModel.swift in Sources */, 12730E69279F420400DC0135 /* SettingsViewModel.swift in Sources */, 12567FF9271742C500A2D356 /* Localizable.swift in Sources */, - CC9DEE5F2AE80ED400C8ED29 /* UwaziFileSelector.swift in Sources */, + CC9DEE5F2AE80ED400C8ED29 /* UwaziSelectFileComponent.swift in Sources */, 122DBE012A5C1E0500D6561A /* Toast.swift in Sources */, + 126FAAD32D4B9D2100F6BCD8 /* NearbySharingMainView.swift in Sources */, 1236EE8228E1E1D700973EEA /* DatabaseExtension.swift in Sources */, 15B81F3A2C1A281C007C0E88 /* GDriveViewModel.swift in Sources */, 12B84B942C333FE3006363F0 /* ReportCardViewModel.swift in Sources */, @@ -4095,6 +4541,7 @@ 121F87AA2964383D00E6BA0D /* SubmittedReportVM.swift in Sources */, 0DD99E052692D24D009327B3 /* SwipeToDeleteView.swift in Sources */, 121C5D7529422C0800A64123 /* ProjectDetailsResult.swift in Sources */, + 20BA4FB12D522551006DD8B9 /* CardModifier.swift in Sources */, 15EE30FE2B5073E20033BBBB /* Pages.swift in Sources */, CCC1B4272AC75A380075B819 /* CreateEntityView.swift in Sources */, 127138382C35EA34004D520B /* CommonCardViewModel.swift in Sources */, @@ -4102,6 +4549,7 @@ 1251C3902BB45616002E9898 /* UwaziListView.swift in Sources */, CC0797BE2A8D46CB00AAE63F /* UnlockView.swift in Sources */, 1216123C28DE13AC00E6D7B6 /* DatabaseUtilities.swift in Sources */, + 20A8BBB82D6F47CD009D52AF /* AddFilesViewModel.swift in Sources */, 1551C3252C936F2D00DFA6EF /* DropboxViewModel.swift in Sources */, CCC1B4392AC75B8D0075B819 /* UwaziEntityParser.swift in Sources */, 1551C3282C9370D300DFA6EF /* DropboxReportMainView.swift in Sources */, @@ -4110,14 +4558,20 @@ 126E1B072C3EB28D000AE4CE /* NextcloudDraftView.swift in Sources */, 12D7D11C28E3581E008121C8 /* URLRequest.swift in Sources */, 209AC6932C332F6000492356 /* PHAssetExtension.swift in Sources */, + 12444EFA2E09D6F6006CB86A /* NearbySharingServerState.swift in Sources */, 128C7BC42745AB0100A680B3 /* FileListViewModel.swift in Sources */, E1EAA0552AA74FEC00492078 /* UwaziLanguageContext.swift in Sources */, 126818C12A38B0C2004606BD /* FileInformation.swift in Sources */, 156986252B88FCB600534720 /* ResourcePDFView.swift in Sources */, 152D38512C2C61DF00323CE7 /* DraftView.swift in Sources */, + 12FAC45D2E265BF7007A4651 /* NearbySharingResultVM.swift in Sources */, + 1282DE512DDE1021004D2176 /* NWConnection.swift in Sources */, 129354772949F04700735ED0 /* EmptyResult.swift in Sources */, + 1222363F2DD3DDC4006A9F01 /* RecipientPrepareFileTransferVM.swift in Sources */, + 124494602DD61267002B4A9B /* SenderFileTransferVM.swift in Sources */, 1290743B274CEB9B00F38A81 /* FileGridItem.swift in Sources */, 1289B45A28B64221005DA687 /* UIDeviceOrientationExtension.swift in Sources */, + 12A9ECD72D9F3FC800711BA4 /* SenderPrepareFileTransferVM.swift in Sources */, 121C5D7729422D6800A64123 /* DataModel.swift in Sources */, 12EF3D692B136D6F007A2910 /* UpdateQuery.swift in Sources */, 12B84B982C3413AC006363F0 /* CommonReportListView.swift in Sources */, @@ -4134,6 +4588,7 @@ 15791C1A2B6AC34100D67C74 /* ResourcesView.swift in Sources */, 15CD89102BF297BE00F724F5 /* SelectedEntityView.swift in Sources */, 12FAC0CF2799FF230068EC2B /* ImportProgress.swift in Sources */, + 20A8BBBA2D6F4830009D52AF /* AddFileCameraView.swift in Sources */, 20219B352C8B3F5500AD6D53 /* GdriveOutboxDetailsView.swift in Sources */, 1293F6662AD7E52800F6CFBD /* FeedbackView.swift in Sources */, 1291A83428BE0D51001A6055 /* SettingsServerItemView.swift in Sources */, @@ -4146,12 +4601,15 @@ 1242135529B774A40002402D /* DeleteReportConfirmationView.swift in Sources */, 12B5257E2849037E00B3D1C0 /* LockTimeoutOptionsStatus.swift in Sources */, 12E8129A27A1570A007DDDC0 /* LocalizableSettings.swift in Sources */, + 125D58AF2E1BE9D900365C40 /* FileTransferVM.swift in Sources */, 15E54AB92C5824DA00290DE8 /* LocalizableGDrive.swift in Sources */, 1275A8402C5A78FB0071A97D /* NextcloudMetadata.swift in Sources */, + 1282C0A02D8AEA1E0033E4CA /* Register.swift in Sources */, 129B845328BFB7AC00F1B344 /* AddServerAccessChoiceView.swift in Sources */, 1293F6772AD88A0400F6CFBD /* FeedbackDTO.swift in Sources */, 126E1B012C3D913D000AE4CE /* NextcloudReport.swift in Sources */, 12C07F052B0748D50030AD6E /* TopSheetView.swift in Sources */, + 20A8BBB42D6F2C70009D52AF /* AddFileGridItemView.swift in Sources */, 128B929D27D7A08700E92ACF /* FileItemsView.swift in Sources */, 1EAE6BF524146C2100114244 /* QuickLook.swift in Sources */, 1268554127D2ACB200385E18 /* RecentFilesListView.swift in Sources */, @@ -4159,6 +4617,7 @@ E1E7C57F2AAB5DB500DDB07E /* UwaziSetting.swift in Sources */, E1E7C57B2AAB54BE00DDB07E /* UwaziCommonProperty.swift in Sources */, 1254E25E2C6B71E3004C6280 /* NextcloudLoginView.swift in Sources */, + 126B70122DB1575D00742897 /* CustomText.swift in Sources */, 127092D72C2DD1A1002030AA /* NextcloudServer.swift in Sources */, 20985A5D2BE158020092CCC2 /* EditImageView.swift in Sources */, 127C155827DA486A009EC15B /* TransitionView.swift in Sources */, @@ -4181,12 +4640,16 @@ 1252F7052BDAB1C30076DF4B /* EntityInstanceToSend.swift in Sources */, 15E3D50A2C1B71E200B7FECC /* GDriveDraftView.swift in Sources */, 1267D7782D2ED4780059D50F /* AVCapturePhotoSettingsExtension.swift in Sources */, + 12FAC4592E2571E0007A4651 /* NearbySharingResultView.swift in Sources */, 209AC6832C332DA700492356 /* VaultFilesManagerExtension.swift in Sources */, 128FD0B427F774FB00B24915 /* RecentFile.swift in Sources */, 126B20E82DBF806100FD7762 /* URL+VideoRotation.swift in Sources */, 209AC6962C332F6000492356 /* CLLocationExtension.swift in Sources */, 12D37A962721A49D00E43BDB /* NavigationContainerView.swift in Sources */, E1EEE29E29EEDA4B009FE227 /* UwaziServerAccessSelectionView.swift in Sources */, + 123CBAFA2DEEEF810030A9BD /* http.c in Sources */, + 123CBAFB2DEEEF810030A9BD /* api.c in Sources */, + 123CBAFC2DEEEF810030A9BD /* llhttp.c in Sources */, 1252F7072BDAB2000076DF4B /* EntityAttachment.swift in Sources */, 209AC67D2C2D65EB00492356 /* NextcloudAddServerURLView.swift in Sources */, E17736402A83FFA200FE01C0 /* UwaziDictionaryDTO.swift in Sources */, @@ -4199,17 +4662,23 @@ 12BA188628343BE5002A11D7 /* MoreFileActionButton.swift in Sources */, 208A06532BF6057A004BA033 /* EditImageViewModel.swift in Sources */, 1246DECF2CC7F1CF00AAC633 /* DropboxError.swift in Sources */, + 124852A02E6EF9E000BDDC2D /* TipsToConnectView.swift in Sources */, CCC1B43D2AC75C5D0075B819 /* UwaziConstants.swift in Sources */, + 125D58AD2E1BE9A500365C40 /* ReceiverFileTransferVM.swift in Sources */, 1268553E27D2572100385E18 /* FileInfoView.swift in Sources */, 1289B45828B63187005DA687 /* RotationViewModifier.swift in Sources */, 126818BF2A376A0E004606BD /* OOXMLContentTypeParser.swift in Sources */, + 1282479D2D5E1ADC00B7E63E /* RecipientConnectToDeviceViewModel.swift in Sources */, 125D2326271F896400250FBB /* CustomPinView.swift in Sources */, 20FA030D2CD282C3001121F1 /* TrimMediaSliderView.swift in Sources */, E1EEE29A29EECF25009FE227 /* UwaziAddServerURLView.swift in Sources */, + 1202C8A32DB985B80013E17D /* BackBottomView.swift in Sources */, 15DCD93E2C9C79A6001339A7 /* DropboxOutboxDetailsView.swift in Sources */, CC13138C2A5DB79B0057271C /* BottomButtonsView.swift in Sources */, + 125C16062DAEF5430086D4F0 /* ManuallyVerificationView.swift in Sources */, 20473A5F2C8B9B0A0015A9B5 /* TellaServerReportsMainView.swift in Sources */, 150913422B841C3F001E782A /* ResourceActionType.swift in Sources */, + 12505B1E2D899BD900483E34 /* NearbySharingServer.swift in Sources */, 129B845128BF6C7800F1B344 /* ServersViewModel.swift in Sources */, 1203CDDC298C5B9100D09073 /* ConnectionActionType.swift in Sources */, 12181ABD2D146F1500CDF0F4 /* AssetItem.swift in Sources */, @@ -4219,10 +4688,12 @@ 1254E2682C6CF794004C6280 /* NextcloudServerModel.swift in Sources */, 12494A482BC0570E00766DFF /* UwaziSubmissionViewModel.swift in Sources */, 128D6E1E2804DA2A0082AB18 /* ListActionSheetItem.swift in Sources */, + 1282C09E2D8AD79C0033E4CA /* PrepareUpload.swift in Sources */, 15BD9CAD2BC7410E00C3932B /* SearchBarView.swift in Sources */, 15BD9CB32BC7472A00C3932B /* UwaziRelationshipDTO.swift in Sources */, 12623DCC299EA31400E033ED /* ReportVaultFile.swift in Sources */, 125AAD3129534259002194D6 /* UploadProgressInfo.swift in Sources */, + 129919BE2E31144300A4417D /* NWConnectionExtension.swift in Sources */, D572EAC9263A384B00CE191A /* FileGroupsView.swift in Sources */, 15C51C462BFE769500DD9AD0 /* GDriveServerViewModel.swift in Sources */, 12567FEF2716B38F00A2D356 /* LockChoiceView.swift in Sources */, @@ -4232,9 +4703,12 @@ 1503C87E2C07A080000F2209 /* ServerConnectionHeaderView.swift in Sources */, 12751EAA2A13A22100FAD7C6 /* SettingsModel.swift in Sources */, 15994A0C2BFBC6F40017F153 /* SelectDriveConnectionView.swift in Sources */, + 124866FF2DFCCABA00EBE8F8 /* LocalURLRequest.swift in Sources */, 209AC67A2C2D5E3500492356 /* ServerViewModel.swift in Sources */, + 12FAC4572E2519F5007A4651 /* FileSendingView.swift in Sources */, 1203CDDE298D7C1300D09073 /* FileToUpload.swift in Sources */, 1227FC912922BE24002FD6FB /* CameraPreview.swift in Sources */, + 127296732D70A46500CB9FF0 /* CertificateGenerator.swift in Sources */, 125A895B2A965BED009347C3 /* FilterType.swift in Sources */, 121AD8262943D52B0056AC2A /* SubmitReportResult.swift in Sources */, 1203CDE02991429200D09073 /* FileStatus.swift in Sources */, @@ -4242,6 +4716,7 @@ 1275A8442C5AA4460071A97D /* NextcloudUploadProgressInfo.swift in Sources */, 12742C8F2BEB9FBC00919FAE /* EncodableExtension.swift in Sources */, 125F9B952AD933AB0073748C /* FeedbackStatus.swift in Sources */, + 125D58AB2E1811AB00365C40 /* TransferProgressView.swift in Sources */, 122389B929367ABA0058593B /* ServerType.swift in Sources */, CCD586D42A7C294800014F87 /* TemplateItemView.swift in Sources */, CCA66C9C29C3CB22008C5AC4 /* ConfirmationBottomSheet.swift in Sources */, @@ -4283,6 +4758,8 @@ 0ECC781B267A542300A2ACF2 /* UwaziPages.swift in Sources */, 20E4DA872C380E3C00123843 /* ServerCreateFolderView.swift in Sources */, 120451FE28C7AB8100D572A7 /* AdvancedSettingsViews.swift in Sources */, + 121AFF932E4CA9DA007300AD /* NearbySharingStateActor.swift in Sources */, + 209107F92D53BD0300808BCF /* ConnectToDeviceManuallyVM.swift in Sources */, 12A43B4F29941574003EF33E /* ReportFile.swift in Sources */, 0E998553266E57FC0019FDF4 /* HeaderView.swift in Sources */, 12935479294A3BFC00735ED0 /* MIMEType.swift in Sources */, @@ -4314,12 +4791,14 @@ CCE8CCDD2AF41E410088B61D /* UwaziFileUtility.swift in Sources */, 122E6A5529A396C400BDACAD /* ServerFileSize.swift in Sources */, 1275A8492C5E6EFF0071A97D /* NextcloudConstants.swift in Sources */, + 20A3BD472D5A0D8D009E5E12 /* RecipientConnectToDeviceManuallyView.swift in Sources */, 126E1AFD2C3D8426000AE4CE /* WebServer.swift in Sources */, 125EFACD274A447100D93DF6 /* FileSortMenu.swift in Sources */, 15FABA362B73F96800B2EFEE /* ResourceRepository.swift in Sources */, 15994A112BFBF0E50017F153 /* SharedDrive.swift in Sources */, 20473A612C8BA6FE0015A9B5 /* TellaServerSubmittedDetailsView.swift in Sources */, 12607D58279042CE00E2B8CC /* VideoPlayer.swift in Sources */, + 20A8BBB22D6F2C27009D52AF /* AddFileGridView.swift in Sources */, 121AD8222943A2F00056AC2A /* ReportRepository.swift in Sources */, E1E7C5792AAB515100DDB07E /* UwaziTemplateRow.swift in Sources */, 12567FFC27178EE200A2D356 /* LockButton.swift in Sources */, @@ -4339,10 +4818,14 @@ 12F5D28D27A326EB00BF3309 /* AboutAndHelpView.swift in Sources */, 15FABA382B7405B800B2EFEE /* ResourceDTO.swift in Sources */, 15622F4A2B72B1FF00289816 /* DeleteConfirmation.swift in Sources */, + 1276B7392E1E56F10024C38A /* ProgressViewModel.swift in Sources */, + 127E81A32DB7C4AC002AED4C /* TextStyleModifier.swift in Sources */, 1230BE4727CD6D440055F854 /* AudioAuthorizationStatus.swift in Sources */, 122E6A5329A3812A00BDACAD /* ReportViewModel.swift in Sources */, 12A148982720B47100A1ADDC /* LockDescriptionView.swift in Sources */, 1221E05C2906CA6A00BC05F5 /* DraftReportVM.swift in Sources */, + 123CBB022DEF34660030A9BD /* HTTPRequest.swift in Sources */, + 209107F72D53BA7800808BCF /* SenderConnectToDeviceManuallyView.swift in Sources */, 12B2BECE2B17D84F00DC223F /* BackgroundActivitiesItemView.swift in Sources */, 129533C227399A1B0053FA4B /* PhotoVideoPickerView.swift in Sources */, 12AE8D1C278CD46A0007C781 /* FileManagerExtension.swift in Sources */, @@ -4356,6 +4839,7 @@ 122C54BC28C614C2003B2DBC /* TellaButtonStyleProtocol.swift in Sources */, E1F4A8E22AAF3F62001C8E61 /* UwaziServerLanguageProtocol.swift in Sources */, 12964D8B2BA9C6F8003E1E0A /* EntityStatus.swift in Sources */, + 20AA1F9C2D5F38A20092810B /* RecipientFileTransferView.swift in Sources */, 1551C32C2C93740500DFA6EF /* DropboxDraftView.swift in Sources */, 0DC267A8267AEDAD00E55AFA /* DefaultFileManager.swift in Sources */, 123125422930241500D3BCD5 /* PhotoVideoViewModel.swift in Sources */, @@ -4633,7 +5117,7 @@ CODE_SIGN_ENTITLEMENTS = "Tella/Supporting Files/Tella.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 98; + CURRENT_PROJECT_VERSION = 104; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = 6ZG9T42688; ENABLE_PREVIEWS = YES; @@ -4644,10 +5128,11 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; - MARKETING_VERSION = 1.16.0; + MARKETING_VERSION = 3.0.0; PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Tella/Supporting Files/Tella-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -4662,7 +5147,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 98; + CURRENT_PROJECT_VERSION = 104; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 6ZG9T42688; @@ -4674,11 +5159,12 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.4; - MARKETING_VERSION = 1.16.0; + MARKETING_VERSION = 3.0.0; PRODUCT_BUNDLE_IDENTIFIER = org.wearehorizontal.tella; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Tella (AppStore)"; + SWIFT_OBJC_BRIDGING_HEADER = "Tella/Supporting Files/Tella-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -4713,8 +5199,8 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 2RXQS7CK9P; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = TellaTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -4724,6 +5210,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.anessapetteruti.TellaTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Tella.app/Tella"; @@ -4831,6 +5318,14 @@ minimumVersion = 4.0.8; }; }; + 12C454D62DAD24300085FCD6 /* XCRemoteSwiftPackageReference "swift-certificates" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-certificates.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.8.1; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -4839,6 +5334,11 @@ package = 129209012C9DAFFA00767760 /* XCRemoteSwiftPackageReference "NextcloudKit" */; productName = NextcloudKit; }; + 12C454D72DAD24300085FCD6 /* X509 */ = { + isa = XCSwiftPackageProductDependency; + package = 12C454D62DAD24300085FCD6 /* XCRemoteSwiftPackageReference "swift-certificates" */; + productName = X509; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = F66E65A123E3207D000F93E5 /* Project object */; diff --git a/Tella/Application/AppDelegate.swift b/Tella/Application/AppDelegate.swift index e08f755a8..3f9584c73 100644 --- a/Tella/Application/AppDelegate.swift +++ b/Tella/Application/AppDelegate.swift @@ -15,7 +15,8 @@ class AppDelegate: NSObject, UIApplicationDelegate { var backgroundSessionCompletionHandler: (() -> Void)? static private(set) var instance: AppDelegate! = nil @Published var shouldHandleTimeout : Bool = false - + @Published var appWillTerminate : Bool = false + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { AppDelegate.instance = self @@ -24,6 +25,10 @@ class AppDelegate: NSObject, UIApplicationDelegate { return true } + func applicationWillTerminate(_ application: UIApplication) { + appWillTerminate = true + } + func application( _ application: UIApplication, handleEventsForBackgroundURLSession diff --git a/Tella/Application/TellaApp.swift b/Tella/Application/TellaApp.swift index ff6f7c4e2..940989568 100644 --- a/Tella/Application/TellaApp.swift +++ b/Tella/Application/TellaApp.swift @@ -2,7 +2,7 @@ // TellaApp.swift // Tella // -// +// // Copyright © 2021 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -27,6 +27,10 @@ struct TellaApp: App { if value { self.saveData(lockApptype: .finishBackgroundTasks) } + }.onReceive(appDelegate.$appWillTerminate) { willTerminate in + if willTerminate { + clearTmpDirectory() + } } }.onChange(of: scenePhase) { phase in @@ -43,10 +47,9 @@ struct TellaApp: App { } } } - + func saveData(lockApptype:LockApptype) { - - + appViewState.homeViewModel.saveLockTimeoutStartDate() UploadService.shared.cancelTasksIfNeeded() @@ -57,10 +60,11 @@ struct TellaApp: App { let hasFileOnBackground = lockApptype == .enterInBackground ? UploadService.shared.hasFilesToUploadOnBackground : false if shouldResetApp && !hasFileOnBackground { + appViewState.homeViewModel.nearbySharingServer?.resetFullServerState() appViewState.homeViewModel.shouldSaveCurrentData = true DispatchQueue.main.asyncAfter(deadline: .now() + delayTimeInSecond, execute: { - appViewState.homeViewModel.vaultManager.clearTmpDirectory() // TO FIX for server doesn't allow upload in Background + clearTmpDirectory() // TO FIX for server doesn't allow upload in Background appViewState.resetApp() }) appViewState.homeViewModel.shouldSaveCurrentData = false @@ -70,20 +74,26 @@ struct TellaApp: App { let hasFileOnBackground = UploadService.shared.hasFilesToUploadOnBackground let appEnterInBackground = appViewState.homeViewModel.appEnterInBackground let shouldResetApp = appViewState.homeViewModel.shouldResetApp() - + if shouldResetApp && appEnterInBackground && !hasFileOnBackground { UIApplication.getTopViewController()?.dismiss(animated: false) - + + appViewState.homeViewModel.nearbySharingServer?.resetFullServerState() + DispatchQueue.main.async { appViewState.shouldHidePresentedView = true appViewState.resetApp() appViewState.shouldHidePresentedView = false } - appViewState.homeViewModel.vaultManager.clearTmpDirectory() + clearTmpDirectory() } appViewState.homeViewModel.appEnterInBackground = false appViewState.homeViewModel.shouldShowSecurityScreen = false } + + func clearTmpDirectory() { + appViewState.homeViewModel.vaultManager.clearTmpDirectory() + } } enum LockApptype { diff --git a/Tella/Components/AddFileGridViewComponent/AddFileBottomSheetView.swift b/Tella/Components/AddFileGridViewComponent/AddFileBottomSheetView.swift new file mode 100644 index 000000000..b0ea29c45 --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFileBottomSheetView.swift @@ -0,0 +1,77 @@ +// +// AddFileBottomSheetView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct AddFileBottomSheetView: View { + + @ObservedObject var viewModel: AddFilesViewModel + @EnvironmentObject var sheetManager: SheetManager + var content: () -> Content + var moreAction: (() -> ())? = nil + + init(viewModel: AddFilesViewModel, content: @escaping () -> Content, moreAction: (() -> ())? = nil ) { + self.viewModel = viewModel + self.content = content + self.moreAction = moreAction + } + var body: some View { + Button { + UIApplication.shared.endEditing() + moreAction?() + showAddFileSheet() + } label: { + content() + } + } + + func showAddFileSheet() { + sheetManager.showBottomSheet(modalHeight: CGFloat(viewModel.bottomSheetItems.count * 50 + 90), content: { + ActionListBottomSheet(items: viewModel.bottomSheetItems, + headerTitle: LocalizableVault.manageFilesSheetTitle.localized, + action: { item in + self.handleActions(item: item) + }) + }) + } + + private func handleActions(item: ListActionSheetItem) { + guard let type = item.type as? ManageFileType else { return } + + switch type { + case .camera: + viewModel.showingCamera = true + + case .recorder: + viewModel.showingRecordView = true + + case .fromDevice: + showAddPhotoVideoSheet() + + case .tellaFile: + navigateTo(destination: fileListView) + + default: + break + } + sheetManager.hide() + } + + func showAddPhotoVideoSheet() { + viewModel.showingImagePicker = true + } + + var fileListView: some View { + FileListView(appModel: viewModel.mainAppModel, + filterType: viewModel.shouldShowDocumentsOnly ? .documents : .audioPhotoVideo, + title: LocalizableReport.selectFiles.localized, + fileListType: .selectFiles, + resultFile: $viewModel.resultFile) + } +} diff --git a/Tella/Components/AddFileGridViewComponent/AddFileCameraView.swift b/Tella/Components/AddFileGridViewComponent/AddFileCameraView.swift new file mode 100644 index 000000000..4446347ad --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFileCameraView.swift @@ -0,0 +1,24 @@ +// +// AddFileCameraView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI +struct AddFileCameraView: View { + @ObservedObject var viewModel: AddFilesViewModel + + var sourceView = SourceView.addFile + + var body: some View { + viewModel.showingCamera ? + CameraView(sourceView: sourceView, + showingCameraView: $viewModel.showingCamera, + resultFile: $viewModel.resultFile, + mainAppModel: viewModel.mainAppModel) : nil + } +} + diff --git a/Tella/Components/AddFileGridViewComponent/AddFileGridItemView.swift b/Tella/Components/AddFileGridViewComponent/AddFileGridItemView.swift new file mode 100644 index 000000000..5482ff58d --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFileGridItemView.swift @@ -0,0 +1,54 @@ +// +// AddFileGridItemView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct AddFileGridItemView: View { + + var file: VaultFileDB + + @StateObject var viewModel: AddFilesViewModel + + var body: some View { + gridItemView + .overlay(deleteButton, alignment: .topTrailing) + } + + var gridItemView : some View { + ZStack { + file.gridImage + self.fileNameText + } + } + + var deleteButton : some View { + Button { + viewModel.deleteFile(fileId: file.id) + } label: { + Image("delete.cross.icon") + .padding(.all, 10) + } + } + + @ViewBuilder + var fileNameText: some View { + + if self.file.tellaFileType != .image || self.file.tellaFileType != .video { + VStack { + Spacer() + CustomText(self.file.name, + style: .body3Style) + .lineLimit(1) + Spacer() + .frame(height: 6) + }.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)) + } + } +} + diff --git a/Tella/Components/AddFileGridViewComponent/AddFileGridView.swift b/Tella/Components/AddFileGridViewComponent/AddFileGridView.swift new file mode 100644 index 000000000..80892e815 --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFileGridView.swift @@ -0,0 +1,50 @@ +// +// AddFileGridView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct AddFileGridView: View { + + private let gridLayout: [GridItem] = [GridItem(spacing: 12), + GridItem(spacing: 12), + GridItem(spacing: 12)] + + private let gridItemHeight = (UIScreen.screenWidth - 64.0) / 3 + + @ObservedObject var viewModel: AddFilesViewModel + + var titleText: String + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + + attachFilesTextView + + itemsGridView + + AddFileBottomSheetView(viewModel: viewModel) { + Image("add.file.icon") + } + } + } + + + var attachFilesTextView: some View { + CustomText(titleText, style: .body1Style) + } + + var itemsGridView: some View { + LazyVGrid(columns: gridLayout, alignment: .center, spacing: 12) { + ForEach(viewModel.files.sorted{$0.created < $1.created}, id: \.id) { file in + AddFileGridItemView(file: file, viewModel: viewModel) + .frame(height: gridItemHeight) + } + } + } + } diff --git a/Tella/Components/AddFileGridViewComponent/AddFilePhotoVideoPickerView.swift b/Tella/Components/AddFileGridViewComponent/AddFilePhotoVideoPickerView.swift new file mode 100644 index 000000000..9301c1efc --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFilePhotoVideoPickerView.swift @@ -0,0 +1,23 @@ +// +// AddFilePhotoVideoPickerView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct AddFilePhotoVideoPickerView: View { + + @ObservedObject var viewModel: AddFilesViewModel + + var body: some View { + PhotoVideoPickerView(showingImagePicker: $viewModel.showingImagePicker, + showingImportDocumentPicker: $viewModel.showingImportDocumentPicker, + appModel: viewModel.mainAppModel, + resultFile: $viewModel.resultFile) + + } +} diff --git a/Tella/Components/AddFileGridViewComponent/AddFileRecordView.swift b/Tella/Components/AddFileGridViewComponent/AddFileRecordView.swift new file mode 100644 index 000000000..f9ba6ecd6 --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFileRecordView.swift @@ -0,0 +1,23 @@ +// +// AddFileRecordView.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI +struct AddFileRecordView: View { + + @ObservedObject var viewModel: AddFilesViewModel + + var body: some View { + viewModel.showingRecordView ? + RecordView(appModel: viewModel.mainAppModel, + sourceView: .addReportFile, + showingRecoredrView: $viewModel.showingRecordView, + resultFile: $viewModel.resultFile) : nil + + } +} diff --git a/Tella/Components/AddFileGridViewComponent/AddFilesViewModel.swift b/Tella/Components/AddFileGridViewComponent/AddFilesViewModel.swift new file mode 100644 index 000000000..cf09de694 --- /dev/null +++ b/Tella/Components/AddFileGridViewComponent/AddFilesViewModel.swift @@ -0,0 +1,73 @@ +// +// AddFilesViewModel.swift +// Tella +// +// Created by RIMA on 26.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +class AddFilesViewModel: ObservableObject { + + @Published var files: Set = [] + @Published var showingImagePicker : Bool = false + @Published var showingImportDocumentPicker : Bool = false + @Published var showingFileList : Bool = false + @Published var showingRecordView : Bool = false + @Published var showingCamera : Bool = false + @Published var resultFile : [VaultFileDB]? + var shouldShowDocumentsOnly: Bool = false + + var mainAppModel: MainAppModel + var subscribers = Set() + + init(mainAppModel: MainAppModel, shouldShowDocumentsOnly: Bool = false) { + self.mainAppModel = mainAppModel + self.shouldShowDocumentsOnly = shouldShowDocumentsOnly + + bindVaultFileTaken() + } + var bottomSheetItems: [ListActionSheetItem] { + var items: [ListActionSheetItem] = [] + + let basicItems = [ + ListActionSheetItem(imageName: "gallery.icon", + content: LocalizableReport.galleryFilled.localized, + type: ManageFileType.tellaFile), + ListActionSheetItem(imageName: "phone.icon", + content: LocalizableReport.phoneFilled.localized, + type: ManageFileType.fromDevice) + ] + + let secondaryItems = [ListActionSheetItem(imageName: "camera-filled.icon", + content: LocalizableReport.cameraFilled.localized, + type: ManageFileType.camera), + ListActionSheetItem(imageName: "mic-filled.icon", + content: LocalizableReport.micFilled.localized, + type: ManageFileType.recorder)] + + if !shouldShowDocumentsOnly { + items = basicItems + secondaryItems + }else { + items = basicItems + } + return items + } + + + func bindVaultFileTaken() { + $resultFile.sink(receiveValue: { value in + guard let value else { return } + self.files.insert(value) + }).store(in: &subscribers) + } + + func deleteFile(fileId: String?) { + guard let index = files.firstIndex(where: { $0.id == fileId}) else {return } + files.remove(at: index) + } +} + diff --git a/Tella/Components/BackBottomView.swift b/Tella/Components/BackBottomView.swift new file mode 100644 index 000000000..408db2174 --- /dev/null +++ b/Tella/Components/BackBottomView.swift @@ -0,0 +1,49 @@ +// +// BackBottomView.swift +// Tella +// +// Created by Dhekra Rouatbi on 23/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct BackBottomView: View { + + @Environment(\.presentationMode) var presentationMode: Binding + + var backAction : (() -> Void)? + + var body: some View { + HStack { + BottomButtonActionView(title: LocalizableLock.actionBack.localized, isValid: true) { + self.backAction?() + self.presentationMode.wrappedValue.dismiss() + } + Spacer() + } + .frame(height: 44) + } + + func BottomButtonActionView(title:String,isValid:Bool, action: (() -> Void)?) -> some View { + Button { + UIApplication.shared.endEditing() + action?() + } label: { + Text(title) + } + .style(.link1Style) + .foregroundColor(isValid ? Color.white : Color.gray) + .padding(EdgeInsets(top: 17, leading: 34, bottom: 45, trailing: 34)) + .disabled(!isValid) + } + +} + +#Preview { + BackBottomView() +} + + + diff --git a/Tella/Components/BottomSheet/ConfirmBottomSheet.swift b/Tella/Components/BottomSheet/ConfirmBottomSheet.swift index 2d47f1985..4714a1d42 100644 --- a/Tella/Components/BottomSheet/ConfirmBottomSheet.swift +++ b/Tella/Components/BottomSheet/ConfirmBottomSheet.swift @@ -1,5 +1,5 @@ // -// Copyright © 2021 HORIZONTAL. +// Copyright © 2021 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -10,11 +10,10 @@ struct ConfirmBottomSheet : View { var imageName : String? = nil var titleText = "" var msgText = "" - var cancelText = "" + var cancelText : String? var discardText : String? var actionText = "" var destructive : Bool = false - var withDrag : Bool = true var shouldHideSheet : Bool = true var didConfirmAction : () -> () @@ -28,29 +27,42 @@ struct ConfirmBottomSheet : View { } var contentView: some View { - VStack(alignment: .leading, spacing: 9) { + VStack(alignment: .leading) { - if let imageName = imageName { - HStack() { - Spacer() - Image(imageName) - Spacer() - }.frame(height: 90) - } + imageView + + CustomText(self.titleText, + style: .heading2Style) + Spacer() + .frame(height: 9) - Text(self.titleText) - .foregroundColor(.white) - .font(Font.custom(Styles.Fonts.semiBoldFontName, size: 17)) - .lineLimit(nil) - .fixedSize(horizontal: false, vertical: true) - Text(self.msgText) - .foregroundColor(.white) - .font(Font.custom(Styles.Fonts.regularFontName, size: 14)) - .lineLimit(nil) - .fixedSize(horizontal: false, vertical: true) + CustomText(self.msgText, + style: .body1Style) Spacer() - HStack(alignment: .lastTextBaseline ){ + + buttonsView + + } .padding(EdgeInsets(top: 20, leading: 24, bottom: 20, trailing: 24)) + + } + + @ViewBuilder + var imageView: some View { + if let imageName = imageName { + HStack() { + Spacer() + Image(imageName) Spacer() + }.frame(height: 90) + } + } + + var buttonsView: some View { + HStack(alignment: .lastTextBaseline ){ + Spacer() + + if let cancelText { + Button(action: { didCancelAction?() if shouldHideSheet { @@ -58,41 +70,38 @@ struct ConfirmBottomSheet : View { } }){ - Text(self.cancelText) + Text(cancelText) }.buttonStyle(ButtonSheetStyle()) - if let discardText = discardText { - Spacer() - .frame(width: 10) - - Button(action: { - didDiscardAction?() - if shouldHideSheet { - sheetManager.hide() - } - - }){ - Text(discardText) - }.buttonStyle(ButtonSheetStyle()) - - } + } + if let discardText = discardText { Spacer() .frame(width: 10) Button(action: { - self.didConfirmAction() + didDiscardAction?() if shouldHideSheet { sheetManager.hide() } + }){ - Text(self.actionText.uppercased()) - .foregroundColor(destructive ? Color.red : Color.white) + Text(discardText) }.buttonStyle(ButtonSheetStyle()) } - } .padding(EdgeInsets(top: 28, leading: 24, bottom: 24, trailing: 10)) + Spacer() + .frame(width: 10) + + Button(action: { + self.didConfirmAction() + if shouldHideSheet { + sheetManager.hide() + } + }){ + Text(self.actionText.uppercased()) + .foregroundColor(destructive ? Color.red : Color.white) + }.buttonStyle(ButtonSheetStyle()) + } } - - } struct ButtonSheetStyle: ButtonStyle { @@ -100,7 +109,7 @@ struct ButtonSheetStyle: ButtonStyle { func makeBody(configuration: Self.Configuration) -> some View { configuration.label .foregroundColor(configuration.isPressed ? Color.white.opacity(0.3) : Color.white) - .font(Font.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .style(.buttonSStyle) .padding() } } diff --git a/Tella/Components/CardItemView.swift b/Tella/Components/CardItemView.swift new file mode 100644 index 000000000..e5ec8a991 --- /dev/null +++ b/Tella/Components/CardItemView.swift @@ -0,0 +1,31 @@ +// +// CardItemView.swift +// Tella +// +// Created by RIMA on 10.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct CardItemView: View { + + var title: String + var subtitle: String + + var body: some View { + HStack { + CustomText(title, + style: .body1Style) + Spacer() + CustomText(subtitle, + style: .body1Style) + }.cardModifier() + .frame(height: 53) + } +} + +#Preview { + CardItemView(title: "Title", subtitle: "Subtitle") +} diff --git a/Tella/Components/IconTextButton.swift b/Tella/Components/IconTextButton.swift new file mode 100644 index 000000000..7f6a9f8b4 --- /dev/null +++ b/Tella/Components/IconTextButton.swift @@ -0,0 +1,58 @@ +// +// IconTextButton.swift +// Tella +// +// Created by Dhekra Rouatbi on 10/9/2025. +// Copyright © 2025 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +protocol IconTextButtonConfig { + var title : String { get } + var description: String { get } + var imageName: String { get } +} + +private struct MockIconTextButtonConfig: IconTextButtonConfig { + let title: String = "Mock Icon Text Title" + let description: String = "Mock Icon Text Desription" + let imageName: String = "lock.fill" +} + +struct IconTextButton : View { + + var buttonConfig : IconTextButtonConfig + var destination : Destination + + var body: some View { + + Button { + navigateTo(destination: destination) + } label: { + HStack(spacing: 20) { + + Image(buttonConfig.imageName) + .aspectRatio(contentMode: .fit) + + VStack(alignment:.leading, spacing: 3 ) { + + CustomText(buttonConfig.title, + style: .buttonLStyle) + + CustomText(buttonConfig.description, + style: .buttonDetailRegularStyle) + + }.frame(maxWidth: .infinity, alignment: .leading) + + } .frame(maxWidth: .infinity, alignment: .leading) + .padding(.all, 16) + } + .buttonStyle(TellaButtonStyle(buttonStyle: ClearButtonStyle(), isValid: true)) + } +} + +#Preview { + IconTextButton(buttonConfig: MockIconTextButtonConfig(), + destination: Text("Destination")) +} diff --git a/Tella/Components/Modifiers/CardModifier.swift b/Tella/Components/Modifiers/CardModifier.swift new file mode 100644 index 000000000..12a63ee77 --- /dev/null +++ b/Tella/Components/Modifiers/CardModifier.swift @@ -0,0 +1,30 @@ +// +// CardModifier.swift +// Tella +// +// Created by RIMA on 04.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct CardModifier: ViewModifier { + var cornerRadius: CGFloat = 20 + var padding: CGFloat = 16 + var backgroundColor: Color = .white.opacity(0.08) + func body(content: Content) -> some View { + content + .padding(padding) + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(backgroundColor) + ) + } +} + +extension View { + func cardModifier(cornerRadius: CGFloat = 16, padding: CGFloat = 16, backgroundColor: Color = .white.opacity(0.08)) -> some View { + self.modifier(CardModifier(cornerRadius: cornerRadius, padding: padding, backgroundColor: backgroundColor)) + } +} diff --git a/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift b/Tella/Components/NavigationBottomView.swift similarity index 85% rename from Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift rename to Tella/Components/NavigationBottomView.swift index 83fde7e7d..583eb6a86 100644 --- a/Tella/Scenes/Authentication/Views/Lock/CommonView/BottomLockView.swift +++ b/Tella/Components/NavigationBottomView.swift @@ -1,5 +1,5 @@ // -// BottomLockView.swift +// NavigationBottomView.swift // Tella // // @@ -10,14 +10,14 @@ import SwiftUI -struct BottomLockView:View { +struct NavigationBottomView:View { - @Binding var isValid : Bool + @Binding var shouldActivateNext : Bool var shouldEnableBackButton : Bool = true @Environment(\.presentationMode) var presentationMode: Binding - var nextButtonAction: NextButtonAction + var nextButtonAction: NextButtonAction = .none var destination: Destination? var shouldHideNext : Bool = false var shouldHideBack: Bool = false @@ -34,7 +34,7 @@ struct BottomLockView:View { } Spacer() if !shouldHideNext { - BottomButtonActionView(title: LocalizableLock.actionNext.localized,isValid: isValid) { + BottomButtonActionView(title: LocalizableLock.actionNext.localized,isValid: shouldActivateNext) { if nextButtonAction == .action { self.nextAction?() } @@ -54,7 +54,7 @@ struct BottomLockView:View { } label: { Text(title) } - .font(.custom(Styles.Fonts.lightFontName, size: 16)) + .style(.link1Style) .foregroundColor(isValid ? Color.white : Color.gray) .padding(EdgeInsets(top: 17, leading: 34, bottom: 45, trailing: 34)) .disabled(!isValid) @@ -63,7 +63,7 @@ struct BottomLockView:View { struct BottomLockView_Previews: PreviewProvider { static var previews: some View { - BottomLockView(isValid: .constant(true), + NavigationBottomView(shouldActivateNext: .constant(true), nextButtonAction: .action, destination: EmptyView(), nextAction: {}, diff --git a/Tella/Components/ServerConnectionHeaderView.swift b/Tella/Components/ServerConnectionHeaderView.swift index be0103451..c912709b9 100644 --- a/Tella/Components/ServerConnectionHeaderView.swift +++ b/Tella/Components/ServerConnectionHeaderView.swift @@ -13,24 +13,25 @@ import SwiftUI struct ServerConnectionHeaderView: View { var title: String - var subtitle: String + var subtitle: String? = nil var imageIconName: String var subtitleTextAlignment: TextAlignment = .center - + var alignment: Alignment = .center + var body: some View { VStack(spacing: 8) { + Image(imageIconName) .padding(.bottom, 16) - Text(title) - .font(.custom(Styles.Fonts.semiBoldFontName, size: 18)) - .foregroundColor(.white) - .multilineTextAlignment(subtitleTextAlignment) - .fixedSize(horizontal: false, vertical: true) - Text(subtitle) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(.white) - .multilineTextAlignment(subtitleTextAlignment) - .fixedSize(horizontal: false, vertical: true) + CustomText(title,style: .heading1Style, + alignment: .center) + + if let subtitle { + CustomText(subtitle, + style: .body1Style, + alignment: subtitleTextAlignment) + .frame(maxWidth: .infinity, alignment: alignment) + } } } } diff --git a/Tella/Components/TellaButton/TellaButtonStyleProtocol.swift b/Tella/Components/TellaButton/TellaButtonStyleProtocol.swift index 8792454de..99e241dbd 100644 --- a/Tella/Components/TellaButton/TellaButtonStyleProtocol.swift +++ b/Tella/Components/TellaButton/TellaButtonStyleProtocol.swift @@ -12,13 +12,19 @@ enum ButtonType { case yellow case clear } +enum ButtonRole { + case primary + case secondary +} protocol TellaButtonStyleProtocol { var backgroundColor : Color {get} var disabledBackgroundColor : Color {get} var pressedBackgroundColor : Color {get} var overlayColor : Color {get} - + var foregroundColor : Color {get} + var disabledForegroundColor : Color {get} + } struct ClearButtonStyle : TellaButtonStyleProtocol { @@ -26,7 +32,8 @@ struct ClearButtonStyle : TellaButtonStyleProtocol { var disabledBackgroundColor = Color.white.opacity(0.16) var pressedBackgroundColor = Color.white.opacity(0.32) var overlayColor = Color.white.opacity(0.64) - + var foregroundColor = Color.white + var disabledForegroundColor = Color.white.opacity(0.38) } struct YellowButtonStyle : TellaButtonStyleProtocol { @@ -34,4 +41,6 @@ struct YellowButtonStyle : TellaButtonStyleProtocol { var disabledBackgroundColor = Styles.Colors.disabledYellow var pressedBackgroundColor = Color(UIColor(hexValue: 0xe0ad6a)) var overlayColor = Color.white.opacity(0.64) + var foregroundColor = Color.black + var disabledForegroundColor = Color.black.opacity(0.38) } diff --git a/Tella/Components/TellaButton/TellaButtonView.swift b/Tella/Components/TellaButton/TellaButtonView.swift index 4fd969e61..c67da8bc1 100644 --- a/Tella/Components/TellaButton/TellaButtonView.swift +++ b/Tella/Components/TellaButton/TellaButtonView.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -28,30 +28,81 @@ struct TellaButtonView : View { return ClearButtonStyle() } } + var buttonRole: ButtonRole = .primary + init( + title: String, + nextButtonAction: NextButtonAction, + buttonType: ButtonType = .clear, + isOverlay: Bool = false, + destination: Destination, + isValid: Binding, + buttonRole: ButtonRole = .primary, + action: (() -> ())? = nil + ) { + self.title = title + self.nextButtonAction = nextButtonAction + self.buttonType = buttonType + self.isOverlay = isOverlay + self.destination = destination + self._isValid = isValid + self.buttonRole = buttonRole + self.action = action + } + + // MARK: - Initializer without Destination + init( + title: String, + nextButtonAction: NextButtonAction, + buttonType: ButtonType = .clear, + isOverlay: Bool = false, + isValid: Binding, + buttonRole: ButtonRole = .primary, + action: (() -> ())? = nil + ) where Destination == EmptyView { + self.title = title + self.nextButtonAction = nextButtonAction + self.buttonType = buttonType + self.isOverlay = isOverlay + self.destination = nil + self._isValid = isValid + self.buttonRole = buttonRole + self.action = action + } var body: some View { - Button { - - UIApplication.shared.endEditing() - - if nextButtonAction == .action { - action?() - } - if (destination != nil) { - navigateTo(destination: destination) - } - } label: { - Text(title) + GeometryReader { geometry in + Button { + + UIApplication.shared.endEditing() + + if nextButtonAction == .action { + action?() + } + if (destination != nil) && nextButtonAction == .destination { + navigateTo(destination: destination) + } + } label: { + + CustomText(title, + style: buttonRole == .primary ? .buttonLStyle : .buttonSStyle) .frame(maxWidth:.infinity) .frame(height: 55) .contentShape(Rectangle()) - - }.cornerRadius(20) - .buttonStyle(TellaButtonStyle(buttonStyle: buttonStyle, isValid: isValid)) + } + .cornerRadius(getCornerRadius(geometry: geometry)) + .buttonStyle(TellaButtonStyle(buttonStyle: buttonStyle, + isValid: isValid, + cornerRadius: getCornerRadius(geometry: geometry), + buttonRole: buttonRole)) .disabled(isValid == false) .overlay(self.isOverlay ? - RoundedRectangle(cornerRadius: 20) + RoundedRectangle(cornerRadius: getCornerRadius(geometry: geometry)) .stroke(.white, lineWidth: 4) : nil) + }.frame(height: 55) + } + + func getCornerRadius(geometry:GeometryProxy) -> CGFloat { + return buttonRole == .primary ? 20 : geometry.size.height / 2 } } @@ -59,31 +110,38 @@ struct TellaButtonStyle : ButtonStyle { var buttonStyle : TellaButtonStyleProtocol var isValid : Bool + var cornerRadius: CGFloat = 20 + var buttonRole: ButtonRole = .primary func makeBody(configuration: Configuration) -> some View { + configuration.label .background(configuration.isPressed ? buttonStyle.pressedBackgroundColor : getBackgroundColor()) - .cornerRadius(20) + .cornerRadius(cornerRadius) .overlay( - configuration.isPressed && isValid ? RoundedRectangle(cornerRadius: 20) - .stroke(buttonStyle.overlayColor, lineWidth: 4) : RoundedRectangle(cornerRadius: 20).stroke(Color.clear, lineWidth: 0) + configuration.isPressed && isValid ? + RoundedRectangle(cornerRadius: cornerRadius).stroke(buttonStyle.overlayColor, lineWidth: 3) : + RoundedRectangle(cornerRadius: cornerRadius).stroke(Color.clear, lineWidth: 0) ) - .foregroundColor(isValid ? .white : .white.opacity(0.38)) - .font(.custom(Styles.Fonts.boldFontName, size: 16)) + .foregroundColor(getForegroundColor()) } func getBackgroundColor() -> Color { isValid ? buttonStyle.backgroundColor : buttonStyle.disabledBackgroundColor } + + func getForegroundColor() -> Color { + isValid ? buttonStyle.foregroundColor : buttonStyle.disabledForegroundColor + } } struct TellaButtonView_Previews: PreviewProvider { static var previews: some View { ContainerView { - TellaButtonView(title: "Ok", - nextButtonAction: .action, - buttonType: .yellow, - isValid: .constant(false)) + TellaButtonView(title: "Ok", + nextButtonAction: .action, + buttonType: .yellow, + isValid: .constant(false)) } } } diff --git a/Tella/Components/TextfieldView.swift b/Tella/Components/TextfieldView.swift index 7541b7238..52e014ada 100644 --- a/Tella/Components/TextfieldView.swift +++ b/Tella/Components/TextfieldView.swift @@ -14,6 +14,10 @@ enum FieldType { case password case code case folderName + case ipAddress + case pin + case port + } struct TextfieldView : View { @@ -41,8 +45,10 @@ struct TextfieldView : View { return .URL case .text: return .alphabet - case .code: + case .code, .pin, .port: return .numberPad + case .ipAddress: + return .numbersAndPunctuation default: return . default } @@ -71,7 +77,7 @@ struct TextfieldView : View { ZStack { Text(placeholder) .offset(y: fieldContent.isEmpty ? 0 : -22) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .style(.body1Style) .frame(maxWidth: .infinity,alignment: .leading) .contentShape(Rectangle()) @@ -162,7 +168,7 @@ struct TextfieldView : View { var errorMessageView : some View { if let formattedErrorMessage { Text(formattedErrorMessage) - .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .style(.body3Style) .foregroundColor(Color(UIColor(hexValue: 0xFF2D2D))) .frame(maxWidth: .infinity, alignment: .leading) } @@ -188,9 +194,18 @@ struct TextfieldView : View { case .folderName: self.isValid = value.folderNameValidator() + case .ipAddress: + self.isValid = value.ipAddressValidator() + case .pin: + self.isValid = value.pinValidator() + case .port: + self.isValid = value.portValidator() + } + if shouldValidateOnChange { + self.shouldShowError = !self.isValid shouldShowErrorOnChange = !self.isValid } else { self.shouldShowError = false @@ -206,7 +221,7 @@ struct TextfieldStyle: TextFieldStyle { func _body(configuration: TextField) -> some View { configuration - .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .style(.body1Style) .foregroundColor(Color.white) .accentColor(.white) .multilineTextAlignment(.leading) diff --git a/Tella/Components/Texts Styles/CustomText.swift b/Tella/Components/Texts Styles/CustomText.swift new file mode 100644 index 000000000..4094e0b71 --- /dev/null +++ b/Tella/Components/Texts Styles/CustomText.swift @@ -0,0 +1,36 @@ +// +// CustomText.swift +// Tella +// +// Created by Dhekra Rouatbi on 17/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct CustomText: View { + + let text: String + let style: TypographyStyle + let alignment: TextAlignment + let color: Color + + init(_ text: String, + style: TypographyStyle, + alignment: TextAlignment = .leading, + color: Color = .white) { + self.text = text + self.style = style + self.alignment = alignment + self.color = color + } + + var body: some View { + Text(text) + .style(style) + .foregroundColor(color) + .fixedSize(horizontal: false, vertical: true) + .multilineTextAlignment(alignment) + } +} diff --git a/Tella/Components/Texts Styles/TextStyleModifier.swift b/Tella/Components/Texts Styles/TextStyleModifier.swift new file mode 100644 index 000000000..57e71bb56 --- /dev/null +++ b/Tella/Components/Texts Styles/TextStyleModifier.swift @@ -0,0 +1,33 @@ +// +// TextStyleModifier.swift +// Tella +// +// Created by Dhekra Rouatbi on 22/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct TextStyleModifier: ViewModifier { + let style: TypographyStyle + + func body(content: Content) -> some View { + if #available(iOS 16.0, *) { + content + .font(.custom(style.name, size: style.fontSize)) + .lineSpacing(style.lineSpacing) + .kerning(style.characterSpacing) + } else { + content + .font(.custom(style.name, size: style.fontSize)) + .lineSpacing(style.lineSpacing) + } + } +} + +extension View { + func style(_ style: TypographyStyle) -> some View { + self.modifier(TextStyleModifier(style: style)) + } +} diff --git a/Tella/Data/Crypto/CryptoManager.swift b/Tella/Data/Crypto/CryptoManager.swift index 80e0ba8f5..97dc312c3 100644 --- a/Tella/Data/Crypto/CryptoManager.swift +++ b/Tella/Data/Crypto/CryptoManager.swift @@ -413,22 +413,3 @@ extension CryptoManager: CryptoManagerInterface { } } } - - -extension SecKey { - func getString() -> String? { - guard let data = getData() else { - return nil - } - return data.base64EncodedString() - } - - func getData() -> Data? { - var error:Unmanaged? - guard let cfdata = SecKeyCopyExternalRepresentation(self, &error) else { - return nil - } - let data:Data = cfdata as Data - return data - } -} diff --git a/Tella/Data/MainAppModel.swift b/Tella/Data/MainAppModel.swift index 8f652229d..b4b9567f5 100644 --- a/Tella/Data/MainAppModel.swift +++ b/Tella/Data/MainAppModel.swift @@ -47,7 +47,8 @@ class MainAppModel: ObservableObject { @Published var importOption: ImportOption? @Published var shouldUpdateLanguage = true - var networkMonitor : NetworkMonitor + var networkMonitor: NetworkMonitor + var nearbySharingServer: NearbySharingServer? private var cancellable: Set = [] @@ -96,7 +97,9 @@ class MainAppModel: ObservableObject { self.vaultFilesManager = try VaultFilesManager(vaultDataBase: vaultDatabase, vaultManager: self.vaultManager) encryptionService = EncryptionService(vaultFilesManager: self.vaultFilesManager, mainAppModel: self) - self.tellaData = try TellaData(database: tellaDataBase, vaultManager: self.vaultManager) + self.tellaData = try TellaData(database: tellaDataBase, vaultManager: self.vaultManager) + + self.nearbySharingServer = NearbySharingServer() } catch { Toast.displayToast(message: "Error opening the app") } diff --git a/Tella/Data/Networking/APICall/APIError.swift b/Tella/Data/Networking/APICall/APIError.swift index c353db199..8c00c7487 100644 --- a/Tella/Data/Networking/APICall/APIError.swift +++ b/Tella/Data/Networking/APICall/APIError.swift @@ -20,6 +20,7 @@ enum APIError: Swift.Error { case dropboxApiError(DropboxError) case errorOccured case nextcloudError(HTTPCode) + case cancelAuthenticationChallenge(String?) } @@ -49,6 +50,9 @@ extension APIError: LocalizedError { return customDropboxErrorMessage(error: error) case .tooManyRequests: return LocalizableError.ncTooManyRequests.localized + case .cancelAuthenticationChallenge: + return "Cancelled" + } } diff --git a/Tella/Data/Networking/APICall/APIRequest.swift b/Tella/Data/Networking/APICall/APIRequest.swift index a8565df57..abcc93759 100644 --- a/Tella/Data/Networking/APICall/APIRequest.swift +++ b/Tella/Data/Networking/APICall/APIRequest.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -13,7 +13,7 @@ public protocol APIRequest { var urlQueryParameters : [String : String?]? { get } var baseURL: String { get } - var path: String { get } + var path: String? { get } var httpMethod: HTTPMethod { get } var encoding: Encoding { get } var decoder: JSONDecoder { get } @@ -25,6 +25,7 @@ public protocol APIRequest { var apiSession: URLSession? { get } var multipartBody: Data? { get } var multipartHeader: String? {get} + var trustedPublicKeyHash: String? {get} } public extension APIRequest { @@ -45,12 +46,12 @@ public extension APIRequest { var decoder: JSONDecoder { JSONDecoder() } var fileToUpload: FileInfo? { nil } - var url: URL? { return URL(string: baseURL + path) } + var url: URL? { return URL(string: baseURL + (path ?? "")) } var uploadsSession: URLSession? { return nil } var apiSession: URLSession? { return nil } var multipartBody: Data? { nil } var multipartHeader: String? { nil } - + var trustedPublicKeyHash: String? { nil } } extension APIRequest { @@ -74,22 +75,23 @@ extension APIRequest { request.httpMethod = httpMethod.rawValue if encoding == .form { request.setValue(multipartHeader, forHTTPHeaderField: "Content-Type") - + request.httpBody = multipartBody } else { request.httpBody = try body() } - + request.timeoutInterval = TimeInterval(30) return request } -} +} extension APIRequest { func body(boundary: String? = nil) throws -> Data? { let keyValues = keyValues?.compactMapValues { $0 } ?? [:] + // Convert to JSON-safe dictionary let queryItemsDictionary = keyValues .reduce(into: [:]) { result, tuple in result[tuple.key.apiString] = tuple.value @@ -99,13 +101,9 @@ extension APIRequest { options: .prettyPrinted ) } -// if let fileToUpload { -// return getHttpBody(fieldInfo: fileToUpload) -// } return nil } - private func addURLQueryParameters(toURL url: URL) -> URL { guard let urlQueryParameters else { return url } @@ -126,14 +124,6 @@ extension APIRequest { return url } - -// func getHttpBody(fieldInfo:FileInfo) -> Data? { -// let data = NSMutableData() -// if let fieldInfoData = fieldInfo.data { -// data.append(fieldInfoData) -// } -// return data as Data -// } } extension NSMutableData { diff --git a/Tella/Data/Networking/APICall/HTTPCodes.swift b/Tella/Data/Networking/APICall/HTTPCodes.swift index 04aa3f331..948b7a7d7 100644 --- a/Tella/Data/Networking/APICall/HTTPCodes.swift +++ b/Tella/Data/Networking/APICall/HTTPCodes.swift @@ -19,7 +19,8 @@ enum HTTPErrorCodes: Int { case forbidden = 403 case notFound = 404 case requestTimeout = 408 - case need2FA = 409 + case conflict = 409 + case tooManyRequests = 429 case internalServerError = 500 case unknown } diff --git a/Tella/Data/Networking/APICall/LocalURLRequest.swift b/Tella/Data/Networking/APICall/LocalURLRequest.swift new file mode 100644 index 000000000..49232ca85 --- /dev/null +++ b/Tella/Data/Networking/APICall/LocalURLRequest.swift @@ -0,0 +1,129 @@ +// +// LocalURLRequest.swift +// Tella +// +// Created by Dhekra Rouatbi on 13/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +enum NearbySharingUploadResponse { + case initial + case didCreateTask(task: URLSessionTask) + case progress(progress: Int) +} + +extension WebRepository { + + func getLocalAPIResponse(endpoint: any APIRequest) -> APIResponse + where Value: Decodable { + performLocalRequest(endpoint: endpoint) + .decodeJSONResponse() + .eraseToAnyPublisher() + } + + func uploadFile(endpoint: any APIRequest) -> AnyPublisher { + do { + + let request = try endpoint.urlRequest() + let configuration = URLSessionConfiguration.default + request.curlRepresentation() + + let delegate = NearbySharingURLSessionDelegate( + path:endpoint.path, + trustedCertificateHash: endpoint.trustedPublicKeyHash + ) + guard let fileURL = endpoint.fileToUpload?.url else { return Fail(error: APIError.errorOccured) + .eraseToAnyPublisher() + } + + let _ = fileURL.startAccessingSecurityScopedResource() + defer { fileURL.stopAccessingSecurityScopedResource() } + + let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) + let task = session.uploadTask(with: request, fromFile: fileURL) + + task.resume() + + return delegate.response.eraseToAnyPublisher() + + + + } catch { + return Fail(error: APIError.invalidURL) + .eraseToAnyPublisher() + } + } + + private func performLocalRequest(endpoint: any APIRequest) -> AnyPublisher { + do { + + let request = try endpoint.urlRequest() + let configuration = URLSessionConfiguration.default + configuration.waitsForConnectivity = false + request.curlRepresentation() + let delegate = NearbySharingURLSessionDelegate( + path:endpoint.path, + trustedCertificateHash: endpoint.trustedPublicKeyHash + ) + + return URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) + .dataTaskPublisher(for: request) + .map({ ServerResponse(data: $0, response: $1)}) + .mapError { $0 as Error } + .eraseToAnyPublisher() + } catch { + return Fail(error: APIError.invalidURL) + .eraseToAnyPublisher() + } + } + + func fetchServerPublicKeyHash(endpoint: any APIRequest) -> AnyPublisher { + do { + let request = try endpoint.urlRequest() + request.curlRepresentation() + + let configuration = URLSessionConfiguration.default + configuration.waitsForConnectivity = false + + var capturedServerHash: String? + + let delegate = NearbySharingURLSessionDelegate( + path: endpoint.path, + trustedCertificateHash: endpoint.trustedPublicKeyHash, + onReceiveServerCertificateHash: { hash in + capturedServerHash = hash + } + ) + + return Future { promise in + let session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil) + + let task = session.dataTask(with: request) { data, response, error in + + if let hash = capturedServerHash { + promise(.success(hash)) + } else if let error { + debugLog("Network error while fetching hash: \(error)") + promise(.failure(error)) + } else { + debugLog("Unexpected response: no error, no server hash") + promise(.failure(APIError.unexpectedResponse)) + } + } + + task.resume() + } + .receive(on: DispatchQueue.main) + .eraseToAnyPublisher() + + } catch { + debugLog("Failed to create URLRequest: \(error)") + return Fail(error: APIError.invalidURL) + .eraseToAnyPublisher() + } + } +} diff --git a/Tella/Data/Networking/APICall/NearbySharingURLSessionDelegate.swift b/Tella/Data/Networking/APICall/NearbySharingURLSessionDelegate.swift new file mode 100644 index 000000000..0e570734d --- /dev/null +++ b/Tella/Data/Networking/APICall/NearbySharingURLSessionDelegate.swift @@ -0,0 +1,100 @@ +// +// NearbySharingURLSessionDelegate.swift +// Tella +// +// Created by Dhekra Rouatbi on 16/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// +import Foundation +import Combine + +class NearbySharingURLSessionDelegate: NSObject, URLSessionDelegate, URLSessionDataDelegate { + + var trustedCertificateHash : String? + var path: String? + var onReceiveServerCertificateHash: ((String) -> Void)? + var response = CurrentValueSubject(.initial) + + init(path: String?, trustedCertificateHash: String? = nil, onReceiveServerCertificateHash: ((String) -> Void)? = nil) { + self.path = path + self.trustedCertificateHash = trustedCertificateHash + self.onReceiveServerCertificateHash = onReceiveServerCertificateHash + } + + func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { + response.send(NearbySharingUploadResponse.didCreateTask(task: task)) + } + + func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + response.send(NearbySharingUploadResponse.progress(progress:Int(bytesSent))) + } + + func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + } + + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + if let error { + let nsError = error as NSError + response.send(completion: .failure(APIError.httpCode(nsError.code))) + } else { + response.send(completion: .finished) + } + } + + func urlSession(_ session: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + + let protectionSpace = challenge.protectionSpace + let host = protectionSpace.host + + guard let serverTrust = protectionSpace.serverTrust else { + debugLog("Missing serverTrust for host") + completionHandler(.cancelAuthenticationChallenge, nil) + return + } + + guard let certificateData = extractCertificateData(from: serverTrust) else { + debugLog("Failed to extract certificate data from serverTrust for host") + completionHandler(.cancelAuthenticationChallenge, nil) + return + } + + let serverCertificateHash = certificateData.sha256() + + // No trusted public key hash: potentially first connection + guard let trustedHash = trustedCertificateHash else { + if path == NearbySharingEndpoint.ping.rawValue { + let credential = URLCredential(trust: serverTrust) + completionHandler(.useCredential, credential) + onReceiveServerCertificateHash?(serverCertificateHash) + } else { + debugLog("No trusted hash and path is non-nil; canceling authentication.") + completionHandler(.cancelAuthenticationChallenge, nil) + } + return + } + + // Compare hashes + guard trustedHash == serverCertificateHash else { + debugLog("Public key hash mismatch") + completionHandler(.cancelAuthenticationChallenge, nil) + return + } + + let credential = URLCredential(trust: serverTrust) + completionHandler(.useCredential, credential) + } + + func extractCertificateData(from trust: SecTrust) -> Data? { + guard let certificate = SecTrustGetCertificateAtIndex(trust, 0) else { + return nil + } + let certificateData = SecCertificateCopyData(certificate) + return Data(certificateData as Data) + } +} diff --git a/Tella/Data/Networking/APICall/Request.swift b/Tella/Data/Networking/APICall/Request.swift index 444b5b36c..c0240555f 100644 --- a/Tella/Data/Networking/APICall/Request.swift +++ b/Tella/Data/Networking/APICall/Request.swift @@ -10,16 +10,19 @@ import Foundation public enum ContentType: String { case json = "application/json" case data = "multipart/form-data" + case octetStream = "application/octet-stream" } public enum HTTPHeaderField: String { case contentType = "Content-Type" + case contentLength = "Content-Length" case authorization = "Authorization" case bearer = "Bearer " case cookie = "Cookie" case xRequestedWith = "X-Requested-With" case tellaPlatform = "X-Tella-Platform" case ByPassCaptchaHeader = "Bypass-Captcha" + case connection = "Connection" } public enum XRequestedWithValue: String { diff --git a/Tella/Data/Networking/APICall/URLRequest.swift b/Tella/Data/Networking/APICall/URLRequest.swift index 2d9c46ebb..ab554cee0 100644 --- a/Tella/Data/Networking/APICall/URLRequest.swift +++ b/Tella/Data/Networking/APICall/URLRequest.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -8,13 +8,42 @@ import Foundation import Combine -typealias APIResponse = AnyPublisher<(Value,[AnyHashable:Any]?), APIError> -typealias APIDataResponse = AnyPublisher<(Data,[AnyHashable:Any]?), APIError> +typealias APIResponse = AnyPublisher<(APIResult), APIError> +typealias APIDataResponse = AnyPublisher<(APIResult), APIError> + +class APIResult { + let response: Value + let headers: [AnyHashable: Any]? + + init(response: Value, headers: [AnyHashable : Any]?) { + self.response = response + self.headers = headers + } +} + +struct ServerResponse { + let data: Data + let response: URLResponse +} protocol WebRepository {} extension WebRepository { - private func fetchData(endpoint: any APIRequest) -> AnyPublisher<(data: Data, response: URLResponse), Error> { + + func getAPIResponse(endpoint: any APIRequest) -> APIResponse + where Value: Decodable { + performRemoteRequest(endpoint: endpoint) + .decodeJSONResponse() + .eraseToAnyPublisher() + } + + func getAPIResponseForBinaryData(endpoint: any APIRequest) -> APIResponse { + performRemoteRequest(endpoint: endpoint) + .extractData() + .eraseToAnyPublisher() + } + + private func performRemoteRequest(endpoint: any APIRequest) -> AnyPublisher { do { guard NetworkMonitor.shared.isConnected else { return Fail(error: APIError.noInternetConnection) @@ -24,8 +53,10 @@ extension WebRepository { let configuration = URLSessionConfiguration.default configuration.waitsForConnectivity = false request.curlRepresentation() + return URLSession(configuration: configuration) .dataTaskPublisher(for: request) + .map({ ServerResponse(data: $0, response: $1)}) .mapError { $0 as Error } .eraseToAnyPublisher() } catch { @@ -33,27 +64,18 @@ extension WebRepository { .eraseToAnyPublisher() } } - - func getAPIResponse(endpoint: any APIRequest) -> APIResponse - where Value: Decodable { - fetchData(endpoint: endpoint) - .requestJSON() - .eraseToAnyPublisher() - } - - func getAPIResponseForBinaryData(endpoint: any APIRequest) -> APIResponse { - fetchData(endpoint: endpoint) - .requestData() - .eraseToAnyPublisher() - } + } + // MARK: - Helpers -extension Publisher where Output == URLSession.DataTaskPublisher.Output { - func requestJSON() -> APIResponse where Value: Decodable { - return requestData() - .tryMap({ (data, allHeaderFields) in - let decodedData : Value = try data.decoded() - return (decodedData, allHeaderFields) +extension Publisher where Output == ServerResponse { + func decodeJSONResponse() -> APIResponse where Value: Decodable { + let apiDataResponse = extractData() + + return apiDataResponse + .tryMap({ response in + let decodedData : Value = try response.response.decoded() + return APIResult(response: decodedData, headers: response.headers) }) .mapError{ if let error = $0 as? APIError { @@ -61,24 +83,24 @@ extension Publisher where Output == URLSession.DataTaskPublisher.Output { } else { return APIError.unexpectedResponse } - + } .eraseToAnyPublisher() } } -extension Publisher where Output == URLSession.DataTaskPublisher.Output { - func requestData() -> APIDataResponse { +extension Publisher where Output == ServerResponse { + func extractData() -> APIDataResponse { return tryMap { - guard let code = ($0.1 as? HTTPURLResponse)?.statusCode else { + guard let code = ($0.response as? HTTPURLResponse)?.statusCode else { throw APIError.unexpectedResponse } - + guard HTTPCodes.success.contains(code) else { debugLog("Error code: \(code)") throw APIError.httpCode(code) } - return ($0.0, ($0.1 as? HTTPURLResponse)?.allHeaderFields) + return APIResult(response: $0.data , headers:($0.response as? HTTPURLResponse)?.allHeaderFields) } .mapError{ error in if let error = error as? APIError { diff --git a/Tella/Data/Networking/Feedback/FeedbackOperation.swift b/Tella/Data/Networking/Feedback/FeedbackOperation.swift index 89f4c7015..325756544 100644 --- a/Tella/Data/Networking/Feedback/FeedbackOperation.swift +++ b/Tella/Data/Networking/Feedback/FeedbackOperation.swift @@ -42,7 +42,7 @@ class FeedbackOperation:Operation, WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: FeedbackRepository.API.submitFeedback(text)) apiResponse - .compactMap{$0.0.toDomain() as? FeedbackAPI} + .compactMap{$0.response.toDomain() as? FeedbackAPI} .sink { result in self.handleFeedbackResult(result:result, feedbackId:feedbackId) } receiveValue: { feedbackAPI in diff --git a/Tella/Data/Networking/Models/NearbySharing/CloseConnection.swift b/Tella/Data/Networking/Models/NearbySharing/CloseConnection.swift new file mode 100644 index 000000000..1e0e52d2e --- /dev/null +++ b/Tella/Data/Networking/Models/NearbySharing/CloseConnection.swift @@ -0,0 +1,17 @@ +// +// CloseConnection.swift +// Tella +// +// Created by Dhekra Rouatbi on 19/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +// MARK: - CloseConnectionRequest +struct CloseConnectionRequest: Codable { + let sessionID: String? + + enum CodingKeys: String, CodingKey { + case sessionID = "sessionId" + } +} diff --git a/Tella/Data/Networking/Models/NearbySharing/FileUpload.swift b/Tella/Data/Networking/Models/NearbySharing/FileUpload.swift new file mode 100644 index 000000000..5799a9fa5 --- /dev/null +++ b/Tella/Data/Networking/Models/NearbySharing/FileUpload.swift @@ -0,0 +1,23 @@ +// +// FileUpload.swift +// Tella +// +// Created by Dhekra Rouatbi on 19/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + + +// MARK: - FileUploadRequest +struct FileUploadRequest: Codable { + + let sessionID, transmissionID, fileID: String? + + enum CodingKeys: String, CodingKey { + case sessionID = "sessionId" + case transmissionID = "transmissionId" + case fileID = "fileId" + } +} diff --git a/Tella/Data/Networking/Models/NearbySharing/PrepareUpload.swift b/Tella/Data/Networking/Models/NearbySharing/PrepareUpload.swift new file mode 100644 index 000000000..40d805cc0 --- /dev/null +++ b/Tella/Data/Networking/Models/NearbySharing/PrepareUpload.swift @@ -0,0 +1,76 @@ +// +// PrepareUploadRequest.swift +// Tella +// +// Created by Dhekra Rouatbi on 19/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +// MARK: - PrepareUpload +struct PrepareUploadRequest: Codable { + let title, sessionID: String? + let files: [NearbySharingFile]? + + enum CodingKeys: String, CodingKey { + case title + case sessionID = "sessionId" + case files + } +} + +// MARK: - NearbySharingFile +class NearbySharingFile: Codable { + var id, fileName: String? + var size: Int? + var fileType, sha256: String? + var thumbnail: Data? + init(id: String?, + fileName: String?, + size: Int?, + fileType: String?, + thumbnail: Data?) { + self.id = id + self.fileName = fileName + self.size = size + self.fileType = fileType + self.thumbnail = thumbnail + } + + func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(self.id, forKey: .id) + try container.encodeIfPresent(self.fileName, forKey: .fileName) + try container.encodeIfPresent(self.size, forKey: .size) + try container.encodeIfPresent(self.fileType, forKey: .fileType) + try container.encodeIfPresent(self.thumbnail?.base64EncodedString(), forKey: .thumbnail) + } +} + +extension NearbySharingFile { + convenience init(vaultFile: VaultFileDB) { + self.init(id: vaultFile.id, + fileName: vaultFile.name, + size: vaultFile.size, + fileType: vaultFile.mimeType, + thumbnail: vaultFile.thumbnail) + } +} + +// MARK: - PrepareUploadResponse +struct PrepareUploadResponse: Codable { + var files: [NearbySharingFileResponse]? + +} + +struct NearbySharingFileResponse: Codable { + let id: String? + let transmissionID: String? + + enum CodingKeys: String, CodingKey { + case id + case transmissionID = "transmissionId" + } +} diff --git a/Tella/Data/Networking/Models/NearbySharing/Register.swift b/Tella/Data/Networking/Models/NearbySharing/Register.swift new file mode 100644 index 000000000..f4df68b1c --- /dev/null +++ b/Tella/Data/Networking/Models/NearbySharing/Register.swift @@ -0,0 +1,22 @@ +// +// Register.swift +// Tella +// +// Created by Dhekra Rouatbi on 19/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +struct RegisterRequest:Codable { + var pin : String? + var nonce : String? // "random-uuid-number" + + init(pin: String? = nil, nonce: String? = nil) { + self.pin = pin + self.nonce = nonce + } +} + +struct RegisterResponse:Codable { + var sessionId : String? +} diff --git a/Tella/Data/Networking/Models/ProjectDetailsResult.swift b/Tella/Data/Networking/Models/ProjectDetailsResult.swift index cb22c220e..a64a880b3 100644 --- a/Tella/Data/Networking/Models/ProjectDetailsResult.swift +++ b/Tella/Data/Networking/Models/ProjectDetailsResult.swift @@ -58,5 +58,8 @@ class BoolResponse: DataModel, Codable { return BoolModel(success: success) } + init(success: Bool?) { + self.success = success + } } diff --git a/Tella/Data/Networking/NearbySharing/HTTPParser.swift b/Tella/Data/Networking/NearbySharing/HTTPParser.swift new file mode 100644 index 000000000..a7320e259 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/HTTPParser.swift @@ -0,0 +1,237 @@ +// +// HTTPParser.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +final class HTTPParser: @unchecked Sendable { + + private var parser = llhttp_t() + private var settings = llhttp_settings_t() + private var url = "" + private var endpoint = "" + private var contentLength: Int? + private var contentTypeString: String? + private var currentHeaderField: String? + private var body = "" + private var pausedData: Data? + private var queryParameters: [String:String] = [:] + private var headers: Headers? + + var bodyFullyReceived: Bool = false + var parserIsPaused: Bool = false + + var queryParametersAreVerified : Bool = false + + private var fileHandle: FileHandle? + var fileURL: URL? { + didSet { + if fileURL != nil { + createFileHandle() + } + } + } + var onReceiveBody: ((Int) -> Void)? + var onReceiveQueryParameters: (() -> Void)? + + var request: HTTPRequest { + return HTTPRequest( + endpoint: endpoint, + queryParameters: queryParameters, + headers: Headers(contentLength: contentLength, contentType: contentTypeString), + body: body, + bodyFullyReceived: bodyFullyReceived + ) + } + var contentType: ContentType? { + return ContentType(rawValue: contentTypeString ?? "") + } + + init() { + + llhttp_settings_init(&settings) + + settings.on_url = { parser, at, length in + guard let at = at else { return 0 } + let instance = Unmanaged.fromOpaque(parser!.pointee.data).takeUnretainedValue() + let value = String(decoding: UnsafeBufferPointer(start: UnsafeRawPointer(at).assumingMemoryBound(to: UInt8.self), count: length), as: UTF8.self) + instance.url += value + + if let urlComponents = URLComponents(string: instance.url) { + + instance.endpoint = urlComponents.path + var queryParams: [String: String] = [:] + if let items = urlComponents.queryItems { + for item in items { + queryParams[item.name] = item.value ?? "" + } + } + instance.queryParameters = queryParams + } + + if instance.queryParameters.isEmpty { + return 0 + } else { + instance.onReceiveQueryParameters?() + instance.parserIsPaused = true + return Int32(HPE_PAUSED.rawValue) + } + } + + settings.on_header_field = { parser, at, length in + guard let at = at else { return 0 } + let instance = Unmanaged.fromOpaque(parser!.pointee.data).takeUnretainedValue() + let value = String(decoding: UnsafeBufferPointer(start: UnsafeRawPointer(at).assumingMemoryBound(to: UInt8.self), count: length), as: UTF8.self) + instance.currentHeaderField = value + return 0 + } + + settings.on_header_value = { parser, at, length in + guard let at = at else { return 0 } + let instance = Unmanaged.fromOpaque(parser!.pointee.data).takeUnretainedValue() + let value = String(decoding: UnsafeBufferPointer(start: UnsafeRawPointer(at).assumingMemoryBound(to: UInt8.self), count: length), as: UTF8.self) + if let key = instance.currentHeaderField { + if key == HTTPHeaderField.contentLength.rawValue { + instance.contentLength = Int(value) + + + } else if key == HTTPHeaderField.contentType.rawValue { + instance.contentTypeString = value + } + instance.currentHeaderField = nil + } + return 0 + } + + settings.on_body = { parser, at, length in + + guard let parser = parser, let at = at else { return 0 } + let instance = Unmanaged.fromOpaque(parser.pointee.data).takeUnretainedValue() + + let buffer = UnsafeBufferPointer( + start: UnsafeRawPointer(at).assumingMemoryBound(to: UInt8.self), + count: length + ) + + switch ContentType.detect(from: instance.contentTypeString) { + case .json: + let value = String(decoding: buffer, as: UTF8.self) + instance.body += value + case .octetStream: + do { + try instance.fileHandle?.seekToEnd() + try instance.fileHandle?.write(contentsOf: buffer) + instance.onReceiveBody?(buffer.count) + } catch { + debugLog("Write error: \(error)") + } + default: + break + } + + return 0 + } + + settings.on_message_complete = { parser in + + let instance = Unmanaged.fromOpaque(parser!.pointee.data).takeUnretainedValue() + instance.bodyFullyReceived = true + debugLog("on_message_complete") + instance.closeFile() + return 0 + } + + llhttp_init(&parser, HTTP_REQUEST, &settings) + parser.data = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + } + + func parse(data: Data) throws { + guard data.count <= UInt32.max else { + throw RuntimeError("Request too large") + } + + let result = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in + return llhttp_execute(&parser, ptr.bindMemory(to: Int8.self).baseAddress, data.count) + } + + if result == HPE_PAUSED { + let errorPos = llhttp_get_error_pos(&parser) + + // Calculate offset using pointer arithmetic + let dataStart = data.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: Int8.self) } + let consumed = dataStart.distance(to: errorPos!) + + if consumed < data.count { + pausedData = data.subdata(in: consumed.. ContentType? { + guard let raw = raw?.lowercased() else { return nil } + if raw.starts(with: ContentType.json.rawValue) { + return .json + } else if raw.starts(with: ContentType.data.rawValue) { + return .data + } else if raw.starts(with: ContentType.octetStream.rawValue) { + return .octetStream + } else { + return nil + } + } +} diff --git a/Tella/Data/Networking/NearbySharing/HTTPRequest.swift b/Tella/Data/Networking/NearbySharing/HTTPRequest.swift new file mode 100644 index 000000000..83de7b730 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/HTTPRequest.swift @@ -0,0 +1,22 @@ +// +// HTTPRequest.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +struct HTTPRequest { + var endpoint: String + var queryParameters: [String:String] + var headers: Headers + var body: String + var bytesReceived: Int32 = 0 + var bodyFullyReceived: Bool +} + +struct Headers { + var contentLength: Int? + var contentType: String? +} diff --git a/Tella/Data/Networking/NearbySharing/HTTPResponse.swift b/Tella/Data/Networking/NearbySharing/HTTPResponse.swift new file mode 100644 index 000000000..46d47b5ad --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/HTTPResponse.swift @@ -0,0 +1,77 @@ +// +// HTTPResponse.swift +// Tella +// +// Created by Dhekra Rouatbi on 18/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +final class HTTPResponseBuilder { + private var response: HTTPResponse + + init(serverStatus: ServerStatus) { + self.response = HTTPResponse(serverStatus: serverStatus) + } + + func setContentType(_ contentType: ContentType) -> Self { + response.addHeader(name: HTTPHeaderField.contentType.rawValue, value: contentType.rawValue) + return self + } + + func setBody(_ body: T?) -> Self { + guard let data = body?.jsonData else { + response.addHeader(name: HTTPHeaderField.contentLength.rawValue, value: "0") + return self + } + response.body = data + response.addHeader(name: HTTPHeaderField.contentLength.rawValue, value: "\(data.count)") + return self + } + + func closeConnection() -> Self { + response.addHeader(name: HTTPHeaderField.connection.rawValue, value: "close") + return self + } + + func build() -> Data? { + return response.serialized() + } +} + +fileprivate struct HTTPField { + let name: String + let value: String +} + +struct ErrorResponse: Codable { + let error: String +} + +fileprivate struct HTTPResponse { + + var serverStatus: ServerStatus + var headerFields: [HTTPField] = [] + var body: Data? + + mutating func addHeader(name: String, value: String) { + headerFields.append(HTTPField(name: name, value: value)) + } + + func serialized() -> Data? { + var responseString = "HTTP/1.1 \(serverStatus.code.rawValue) \(serverStatus.message.rawValue)\r\n" + for field in headerFields { + responseString += "\(field.name): \(field.value)\r\n" + } + responseString += "\r\n" + + var responseData = responseString.data + if let body = body { + responseData?.append(body) + } + return responseData + } +} + diff --git a/Tella/Data/Networking/NearbySharing/HTTPStatusCode.swift b/Tella/Data/Networking/NearbySharing/HTTPStatusCode.swift new file mode 100644 index 000000000..0d0f47390 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/HTTPStatusCode.swift @@ -0,0 +1,39 @@ +// +// HTTPStatusCode.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +enum HTTPStatusCode: Int, Error { + case ok = 200 + case internalServerError = 500 + case notFound = 404 + case unauthorized = 401 + case badRequest = 400 + case forbidden = 403 + case conflict = 409 + case tooManyRequests = 429 +} + +enum ServerMessage: String { + case ok = "OK" + case invalidRequestFormat = "Invalid request format" + case invalidPIN = "Invalid PIN" + case activeSessionAlreadyExists = "Active session already exists" + case tooManyRequests = "Too many requests" + case invalidSessionID = "Invalid session ID" + case rejected = "Rejected" + case invalidTransmissionID = "Invalid transmission ID" + case transferAlreadyCompleted = "Transfer already completed" + case missingRequiredParameters = "Missing required parameters" + case sessionAlreadyClosed = "Session already closed" + case serverError = "Server error" +} + +struct ServerStatus : Error { + let code : HTTPStatusCode + let message : ServerMessage +} diff --git a/Tella/Data/Networking/NearbySharing/NWConnection.swift b/Tella/Data/Networking/NearbySharing/NWConnection.swift new file mode 100644 index 000000000..af33caed0 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NWConnection.swift @@ -0,0 +1,282 @@ +// +// NetworkManager.swift +// Tella +// +// Created by Dhekra Rouatbi on 21/5/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Network +import Foundation + +// MARK: - ConnectionContext + +struct ConnectionContext { + let connection: NWConnection + var request: HTTPRequest +} + +// MARK: - NetworkManagerDelegate + +protocol NetworkManagerDelegate: AnyObject { + func networkManager(didReceiveCompleteRequest context: ConnectionContext) + func networkManager(verifyParametersFor context: ConnectionContext) async -> URL? + func networkManager(didReceive progress: Int, for context: ConnectionContext) + func networkManagerDidStartListening() + func networkManager(didFailWith error: Error?, context: ConnectionContext?) + func networkManager(didFailWithListener error: Error?) +} + +actor NetworkManager { + + // MARK: - Properties + + private var listener: NWListener? + private let connections = ConnectionStore() + private weak var delegate: NetworkManagerDelegate? + + private let minIncompleteLength = 1 + private let maxLength = 1024 * 1024 + let networkQueue = DispatchQueue( + label: "org.wearehorizontal.tella.nearbysharing.listener", + qos: .userInitiated + ) + + // MARK: - Delegate + + func setDelegate(_ delegate: NetworkManagerDelegate?) { + self.delegate = delegate + } + + // MARK: - Public API + + func startListening(port: Int, clientIdentity: SecIdentity) { + if let l = listener, l.state != .cancelled { + return + } + + do { + let parameters = try createNetworkParameters(clientIdentity: clientIdentity) + let portValue = NWEndpoint.Port(integerLiteral: UInt16(port)) + let listener = try NWListener(using: parameters, on: portValue) + + self.listener = listener + configureListener(listener) + listener.start(queue: networkQueue) + } catch { + self.delegate?.networkManager(didFailWithListener: error) + } + } + + func stopListening() { + if listener?.state != .cancelled { + listener?.cancel() + } + Task { + for conn in await connections.allConnections() { + conn.cancel() + } + await connections.removeAll() + } + } + + func cleanConnections() { + Task { await connections.removeAll() } + } + + func sendData(to connection: NWConnection, data: Data) async throws { + return try await withCheckedThrowingContinuation { continuation in + connection.send(content: data, completion: .contentProcessed { error in + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + }) + } + } + + // MARK: - TLS Parameters + + private func createNetworkParameters(clientIdentity: SecIdentity) throws -> NWParameters { + let tlsOptions = NWProtocolTLS.Options() + guard let identity = sec_identity_create(clientIdentity) else { + throw RuntimeError("Invalid certificate") + } + sec_protocol_options_set_local_identity(tlsOptions.securityProtocolOptions, identity) + + sec_protocol_options_set_challenge_block(tlsOptions.securityProtocolOptions, { _, completion in + completion(identity) + }, networkQueue) + + return NWParameters(tls: tlsOptions) + } + + // MARK: - Listener Configuration + + private func configureListener(_ listener: NWListener) { + listener.stateUpdateHandler = { [weak self] state in + guard let self else { return } + Task { + switch state { + case .ready: + debugLog("Listener ready") + let delegate = await self.delegate + delegate?.networkManagerDidStartListening() + case .failed(let error): + debugLog("Listener failed") + let delegate = await self.delegate + delegate?.networkManager(didFailWithListener: error) + await self.stopListening() + case .cancelled: + debugLog("Listener cancelled") + default: + break + } + } + } + + listener.newConnectionHandler = { [weak self] connection in + Task { await self?.handleNewConnection(connection) } + } + } + + // MARK: - Connection Handling + + private func handleNewConnection(_ connection: NWConnection) { + let parser = HTTPParser() + configureParser(parser, for: connection) + + let context = ConnectionContext(connection: connection, request: parser.request) + Task { await connections.set(context, for: connection.id) } + + connection.start(queue: networkQueue) + receiveData(on: connection, using: parser) + } + + private func receiveData(on connection: NWConnection, using parser: HTTPParser) { + connection.receive(minimumIncompleteLength: minIncompleteLength, + maximumLength: maxLength) { [weak self] data, _, _, error in + guard let self else { return } + Task { + if let error = error { + await self.handleConnectionError(connection, error: error) + return + } + guard let data = data else { + await self.handleConnectionError(connection, error: nil) + return + } + await self.processIncomingData(data, on: connection, with: parser) + } + } + } + + private func processIncomingData(_ data: Data, on connection: NWConnection, with parser: HTTPParser) async { + do { + try parser.parse(data: data) + _ = await self.updateContext(for: connection, with: parser.request) + if parser.parserIsPaused { + return + } + continueReceiving(connection: connection, parser: parser) + } catch { + debugLog("Parsing error") + await self.handleConnectionError(connection, error: error) + } + } + + private func continueReceiving(connection: NWConnection, parser: HTTPParser) { + guard parser.request.bodyFullyReceived else { + receiveData(on: connection, using: parser) + return + } + Task { + if let ctx = await self.connectionContext(for: connection) { + self.delegate?.networkManager(didReceiveCompleteRequest: ctx) + } + } + } + + // MARK: - Parser Configuration + + private func configureParser(_ parser: HTTPParser, for connection: NWConnection) { + parser.onReceiveBody = { [weak self] length in + guard let self else { return } + Task { + if let context = await self.updateContext(for: connection, with: parser.request) { + await self.delegate?.networkManager(didReceive: length, for: context) + } + } + } + + parser.onReceiveQueryParameters = { [weak self] in + guard let self else { return } + Task { + if let context = await self.updateContext(for: connection, with: parser.request), + let url = await self.delegate?.networkManager(verifyParametersFor: context) { + parser.fileURL = url + do { + try parser.resumeParsing() + _ = await self.updateContext(for: connection, with: parser.request) + await self.continueReceiving(connection: connection, parser: parser) + } catch { + await self.handleConnectionError(connection, error: error) + } + } + } + } + } + + // MARK: - Helpers + + private func handleConnectionError(_ connection: NWConnection, error: Error?) async { + let ctx = await self.connectionContext(for: connection) + self.delegate?.networkManager(didFailWith: error, context: ctx) + await self.connections.remove(for: connection.id) + connection.cancel() + } + + private func connectionContext(for connection: NWConnection) async -> ConnectionContext? { + await connections.get(for: connection.id) + } + + @discardableResult + private func updateContext(for connection: NWConnection, with request: HTTPRequest) async -> ConnectionContext? { + await connections.update(for: connection.id, with: request) + } +} + +actor ConnectionStore { + + private var storage: [ObjectIdentifier: ConnectionContext] = [:] + + func set(_ context: ConnectionContext, for id: ObjectIdentifier) { + storage[id] = context + } + + func get(for id: ObjectIdentifier) -> ConnectionContext? { + storage[id] + } + + @discardableResult + func update(for id: ObjectIdentifier, with request: HTTPRequest) -> ConnectionContext? { + guard var ctx = storage[id] else { return nil } + ctx.request = request + storage[id] = ctx + return ctx + } + + func remove(for id: ObjectIdentifier) { + storage.removeValue(forKey: id) + } + + func removeAll() { + storage.removeAll() + } + + func allConnections() -> [NWConnection] { + storage.values.map { $0.connection } + } +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingEndpoint.swift b/Tella/Data/Networking/NearbySharing/NearbySharingEndpoint.swift new file mode 100644 index 000000000..7d52eb8b0 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingEndpoint.swift @@ -0,0 +1,16 @@ +// +// NearbySharingEndpoint.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +enum NearbySharingEndpoint: String { + case ping = "/api/v1/ping" + case register = "/api/v1/register" + case prepareUpload = "/api/v1/prepare-upload" + case upload = "/api/v1/upload" + case closeConnection = "/api/v1/close-connection" +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingEvent.swift b/Tella/Data/Networking/NearbySharing/NearbySharingEvent.swift new file mode 100644 index 000000000..3c7f8e9c3 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingEvent.swift @@ -0,0 +1,22 @@ +// +// NearbySharingEvent.swift +// Tella +// +// Created by Dhekra Rouatbi on 25/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +/// Represents events and notifications from the NearbySharingEvent. +@MainActor +enum NearbySharingEvent { + case serverStarted + case serverStartFailed(Error?) + case didRegister(success: Bool, manual: Bool) + case verificationRequested // Received a ping; show verification hash to user. + case prepareUploadReceived(files: [NearbySharingFile]?) + case prepareUploadResponseSent(success: Bool) + case connectionClosed + case fileTransferProgress(NearbySharingTransferredFile) + case errorOccured +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingRepository.swift b/Tella/Data/Networking/NearbySharing/NearbySharingRepository.swift new file mode 100644 index 000000000..d660a2ea5 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingRepository.swift @@ -0,0 +1,206 @@ +// +// NearbySharingRepository.swift +// Tella +// +// Created by Dhekra Rouatbi on 18/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +class NearbySharingRepository: NSObject, WebRepository { + + private var connectionInfo:ConnectionInfo? + private var uploadTasks : [URLSessionTask] = [] + + func getHash(connectionInfo:ConnectionInfo) -> AnyPublisher { + + let apiResponse = fetchServerPublicKeyHash(endpoint: API.ping(connectionInfo: connectionInfo)) + return apiResponse + .eraseToAnyPublisher() + } + + func register(connectionInfo:ConnectionInfo, registerRequest:RegisterRequest) -> AnyPublisher { + + let apiResponse : APIResponse = getLocalAPIResponse(endpoint: API.register(connectionInfo:connectionInfo, + registerRequest: registerRequest)) + return apiResponse + .compactMap{$0.response} + .handleEvents(receiveOutput: { [weak self] result in + self?.connectionInfo = connectionInfo + }) + .eraseToAnyPublisher() + } + + func prepareUpload(prepareUpload: PrepareUploadRequest) -> AnyPublisher { + + guard let connectionInfo else {return Fail(error: APIError.badServer) + .eraseToAnyPublisher()} + + let apiResponse : APIResponse = getLocalAPIResponse( + endpoint: API.prepareUpload(connectionInfo:connectionInfo, + prepareUpload: prepareUpload)) + return apiResponse + .compactMap{$0.response} + .eraseToAnyPublisher() + } + + func uploadFile(fileUploadRequest:FileUploadRequest, fileURL: URL) -> AnyPublisher { + guard let connectionInfo else { + return Fail(error: APIError.badServer) + .eraseToAnyPublisher() + } + + do { + let apiResponse = uploadFile( + endpoint: API.uploadFile(connectionInfo:connectionInfo, + fileUploadRequest: fileUploadRequest, + fileURL: fileURL)) + return apiResponse + .compactMap { output in + switch output { + case .progress(let progress): + return progress + case .didCreateTask(let task): + self.uploadTasks.append(task) + return nil + default: + return nil + } + } + .eraseToAnyPublisher() + } + } + @discardableResult + func closeConnection(closeConnectionRequest: CloseConnectionRequest) -> AnyPublisher { + guard let connectionInfo else {return Fail(error: APIError.badServer) + .eraseToAnyPublisher()} + + let apiResponse : APIResponse = getLocalAPIResponse(endpoint: API.closeConnection(connectionInfo:connectionInfo, + closeConnectionRequest: closeConnectionRequest)) + return apiResponse + .compactMap{$0.response} + .eraseToAnyPublisher() + } + + func cancelUpload() { + _ = uploadTasks.compactMap({$0.cancel()}) + } +} + +extension NearbySharingRepository { + + enum API { + case ping(connectionInfo:ConnectionInfo) + case register(connectionInfo:ConnectionInfo, registerRequest:RegisterRequest) + case prepareUpload(connectionInfo:ConnectionInfo, prepareUpload: PrepareUploadRequest) + case uploadFile(connectionInfo:ConnectionInfo, fileUploadRequest:FileUploadRequest, fileURL:URL) + case closeConnection(connectionInfo:ConnectionInfo, closeConnectionRequest: CloseConnectionRequest) + } +} + +extension NearbySharingRepository.API: APIRequest { + + typealias Value = Any + + var urlQueryParameters: [String: String?]? { + switch self { + case .uploadFile (_, let fileUploadRequest, _): + return fileUploadRequest.dictionary.compactMapValues { $0 as? String } + default: + return nil + } + } + + var keyValues: [Key : Value?]? { + + switch self { + case .ping: + return nil + case .register(_, let registerRequest): + return registerRequest .dictionary + case .prepareUpload(_, let prepareUpload): + return prepareUpload.dictionary + case .uploadFile: + return nil + case .closeConnection(_, let closeConnectionRequest): + return closeConnectionRequest.dictionary + } + } + + var headers: [String: String]? { + switch self { + case .ping: + return nil + case .uploadFile: + return [HTTPHeaderField.contentType.rawValue : ContentType.octetStream.rawValue] + default: + return [HTTPHeaderField.contentType.rawValue : ContentType.json.rawValue] + } + } + + var baseURL: String { + switch self { + case .ping(let connectionInfos), + .register(let connectionInfos, _), + .prepareUpload(let connectionInfos, _), + .uploadFile(let connectionInfos, _, _), + .closeConnection(let connectionInfos, _): + return "https://" + connectionInfos.ipAddress + ":\(connectionInfos.port)" + } + } + + var path: String? { + switch self { + + case .ping: + return NearbySharingEndpoint.ping.rawValue + + case .register: + return NearbySharingEndpoint.register.rawValue + + case .prepareUpload: + return NearbySharingEndpoint.prepareUpload.rawValue + + case .uploadFile: + return NearbySharingEndpoint.upload.rawValue + + case .closeConnection: + return NearbySharingEndpoint.closeConnection.rawValue + + } + } + + var httpMethod: HTTPMethod { + switch self { + case .register, .prepareUpload, .closeConnection, .ping: + return HTTPMethod.post + case .uploadFile : + return HTTPMethod.put + } + } + + var fileToUpload: FileInfo? { + switch self { + case .uploadFile (_, let file ,let fileURL): + return FileInfo(withFileURL: fileURL, fileId: file.fileID) + default: + return nil + } + } + + var trustedPublicKeyHash: String? { + switch self { + case .register( let connectionInfo, _), + .prepareUpload(let connectionInfo, _), + .uploadFile(let connectionInfo, _, _), + .closeConnection(let connectionInfo, _): + return connectionInfo.certificateHash + + default: + return nil + } + } +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingServer.swift b/Tella/Data/Networking/NearbySharing/NearbySharingServer.swift new file mode 100644 index 000000000..10d06ecf7 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingServer.swift @@ -0,0 +1,502 @@ +// +// NearbySharingServer.swift +// Tella +// +// Created by Dhekra Rouatbi on 18/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Network +import Combine +import Foundation + +final class NearbySharingServer { + + let state = NearbySharingStateActor() + private let networkManager = NetworkManager() + + /// Single Combine publisher for all server events. + var eventPublisher = PassthroughSubject() + + init() { + Task { + await networkManager.setDelegate(self) + } + } + + // MARK: - Server Lifecycle + + func startListening(port: Int, pin: String, clientIdentity: SecIdentity) { + Task { + await state.setPin(pin) + await networkManager.startListening(port: port, clientIdentity: clientIdentity) + } + } + + func stopServer() { + Task { + await networkManager.stopListening() + } + } + + func resetServerState() { + stopServer() + Task { await state.resetConnectionState() } + eventPublisher = PassthroughSubject() + } + + func resetFullServerState() { + stopServer() + cleanServer() + } + + func cleanServer() { + Task { + await networkManager.cleanConnections() + await state.removeTempFiles() + await state.resetConnectionState() + } + eventPublisher = PassthroughSubject() + } + + // MARK: - Response Helpers + + private func sendSuccessResponse(connection: NWConnection, payload: Encodable, endpoint: NearbySharingEndpoint) { + guard let responseData = HTTPResponseBuilder(serverStatus: ServerStatus(code: .ok, message: .ok)) + .setContentType(.json) + .setBody(payload) + .closeConnection() + .build() else { + sendInternalServerError(connection: connection) + return + } + let response = NearbySharingServerResponse(dataResponse: responseData, response: .success, endpoint: endpoint) + sendData(connection: connection, serverResponse: response) + } + + private func sendErrorResponse(_ error: ServerStatus, connection: NWConnection, endpoint: NearbySharingEndpoint? = nil) { + let errorPayload = ErrorResponse(error: error.message.rawValue) + let responseData = HTTPResponseBuilder(serverStatus: error) + .setContentType(.json) + .setBody(errorPayload) + .closeConnection() + .build() + let response = NearbySharingServerResponse(dataResponse: responseData, response: .failure, endpoint: endpoint) + sendData(connection: connection, serverResponse: response) + } + + private func sendInternalServerError(connection: NWConnection) { + let error = ServerStatus(code: .internalServerError, message: .serverError) + sendErrorResponse(error,connection: connection) + } + + private func sendData(connection: NWConnection, serverResponse: NearbySharingServerResponse) { + guard let data = serverResponse.dataResponse else { + debugLog("No data to send for response.") + eventPublisher.send(.errorOccured) + return + } + + Task { + do { + try await networkManager.sendData(to: connection, data: data) + handleResponseSent(serverResponse) + } catch { + debugLog("Failed to send response data: \(error)") + eventPublisher.send(.errorOccured) + } + } + } + + private func handleResponseSent(_ serverResponse: NearbySharingServerResponse) { + guard let endpoint = serverResponse.endpoint else { + eventPublisher.send(.errorOccured) + return + } + let success = (serverResponse.response == .success) + + switch endpoint { + case .register: + Task { + let manual = await state.isManualConnection() + eventPublisher.send(.didRegister(success: success, manual: manual)) + } + case .prepareUpload: + eventPublisher.send(.prepareUploadResponseSent(success: success)) + case .closeConnection: + eventPublisher.send(.connectionClosed) + resetServerState() + case .upload, .ping: + break + } + } + + private func handleError(for request: HTTPRequest?, on connection: NWConnection) { + guard let req = request, + let endpoint = NearbySharingEndpoint(rawValue: req.endpoint) else { + return + } + + if endpoint == .upload { + handleUploadFailure(connection: connection, request: req) + } else { + sendInternalServerError(connection: connection) + } + } + + // MARK: - Request Processing + + private func processRequest(connection: NWConnection, httpRequest: HTTPRequest) { + guard let endpoint = NearbySharingEndpoint(rawValue: httpRequest.endpoint) else { + debugLog("Received request for unknown endpoint") + return + } + + switch endpoint { + case .ping: + handlePingRequest(on: connection) + case .register: + handleRegisterRequest(on: connection, request: httpRequest) + case .prepareUpload: + handlePrepareUploadRequest(on: connection, request: httpRequest) + case .upload: + handleReceivedCompleteRequest(on: connection, request: httpRequest) + case .closeConnection: + handleCloseConnectionRequest(on: connection, request: httpRequest) + } + } +} + +// MARK: - NetworkManagerDelegate + +extension NearbySharingServer: NetworkManagerDelegate { + + func networkManagerDidStartListening() { + debugLog("Server is now listening on the specified port.") + eventPublisher.send(.serverStarted) + } + + func networkManager(didFailWithListener error: Error?) { + debugLog("Server failed to start") + eventPublisher.send(.serverStartFailed(error)) + } + + func networkManager(didFailWith error: Error?, context: ConnectionContext?) { + guard let context else { return } + handleError(for: context.request, on: context.connection) + } + + func networkManager(didReceiveCompleteRequest context: ConnectionContext) { + processRequest(connection: context.connection, httpRequest: context.request) + } + + func networkManager(verifyParametersFor context: ConnectionContext) async -> URL? { + guard context.request.endpoint == NearbySharingEndpoint.upload.rawValue else { + return nil + } + return await handleFileUploadRequest(on: context.connection, request: context.request) + } + + func networkManager(didReceive bytes: Int, for context: ConnectionContext) { + processProgress(connection: context.connection, bytesReceived: bytes, for: context.request) + } +} + +// MARK: - PingHandler + +extension NearbySharingServer: PingHandler { + func handlePingRequest(on connection: NWConnection) { + eventPublisher.send(.verificationRequested) + Task { await state.markManualConnection() } + let payload = BoolResponse(success: true) + sendSuccessResponse(connection: connection,payload: payload, endpoint: .ping) + } +} + +// MARK: - RegisterHandler + +extension NearbySharingServer: RegisterHandler { + func handleRegisterRequest(on connection: NWConnection, request: HTTPRequest) { + Task { + if await state.isManualConnection() { + + if let pendingRegisterResponse = await state.getPendingRegisterResponse() { + await sendRegistrationResponse(accept: pendingRegisterResponse, + connection: connection, + request: request) + } else { + await state.setPendingRegister(connection: connection, request: request) + } + + } else { + await acceptRegisterRequest(connection: connection, httpRequest: request) + } + } + } + + func acceptRegisterRequest(connection: NWConnection, httpRequest: HTTPRequest) async { + // 1) existing session / rate limiting + if await state.hasSession() { + let error = ServerStatus(code: .conflict, message: .activeSessionAlreadyExists) + sendErrorResponse(error, connection: connection) + } + if await state.tooManyAttempts() { + let error = ServerStatus(code: .tooManyRequests, message: .tooManyRequests) + sendErrorResponse(error, connection: connection) + return + } + + // 2) decode body + guard let regReq = httpRequest.body.decodeJSON(RegisterRequest.self) else { + let error = ServerStatus(code: .badRequest, message: .invalidRequestFormat) + sendErrorResponse(error, connection: connection) + return + } + + // 3) verify PIN + guard let pin = regReq.pin, await state.pinMatches(pin) else { + await state.incrementFailedAttempts() + if await state.tooManyAttempts() { + let error = ServerStatus(code: .tooManyRequests, message: .tooManyRequests) + sendErrorResponse(error, connection: connection) + return + } + let error = ServerStatus(code: .unauthorized, message: .invalidPIN) + sendErrorResponse(error, connection: connection) + return + } + + // 4) create session + let newSessionId = await state.createSessionAndReturnID() + + // 5) response + let payload = RegisterResponse(sessionId: newSessionId) + sendSuccessResponse(connection: connection, + payload: payload, + endpoint: .register) + } + + func respondToRegistrationRequest(accept: Bool) { + Task { + guard let (connection, request) = await state.getPendingRegister() else { + await state.savePendingRegisterResponse(accept: accept) + return + } + await sendRegistrationResponse(accept: accept, + connection: connection, + request: request) + } + } + + private func sendRegistrationResponse(accept: Bool, + connection: NWConnection, + request: HTTPRequest) async { + if accept { + await acceptRegisterRequest(connection: connection, httpRequest: request) + } else { + discardRegisterRequest(connection: connection) + } + } + + private func discardRegisterRequest(connection: NWConnection) { + let error = ServerStatus(code: .forbidden, message: .rejected) + sendErrorResponse(error, connection: connection) + } +} + +// MARK: - PrepareUploadHandler + +extension NearbySharingServer: PrepareUploadHandler { + + func handlePrepareUploadRequest(on connection: NWConnection, request: HTTPRequest) { + guard let prepReq = request.body.decodeJSON(PrepareUploadRequest.self) else { + let error = ServerStatus(code: .badRequest, message: .invalidRequestFormat) + sendErrorResponse(error, connection: connection) + return + } + + Task { + let sessionID = await state.currentSessionID() + guard prepReq.sessionID == sessionID else { + let error = ServerStatus(code: .unauthorized, message: .invalidSessionID) + sendErrorResponse(error, connection: connection) + return + } + + await state.storePrepareUpload(prepReq) + await state.setPendingUploadConnection(connection) + eventPublisher.send(.prepareUploadReceived(files: prepReq.files)) + } + } + + func respondToFileOffer(accept: Bool) { + Task { + guard let connection = await state.getPendingUploadConnection() else { return } + await sendPrepareUploadResponse(connection: connection, accept: accept) + } + } + + private func sendPrepareUploadResponse(connection: NWConnection, accept: Bool) async { + accept ? await sendAcceptUploadResponse(connection: connection) + : sendRejectUploadResponse(connection:connection) + } + + private func sendAcceptUploadResponse(connection: NWConnection) async { + let fileTuples = await state.assignTransmissionIDs() + let filesResponse = fileTuples.map { + NearbySharingFileResponse(id: $0.originalID, transmissionID: $0.transmissionID) + } + + let payload = PrepareUploadResponse(files: filesResponse) + sendSuccessResponse(connection: connection, + payload: payload, + endpoint: .prepareUpload) + } + + private func sendRejectUploadResponse(connection: NWConnection) { + let error = ServerStatus(code: .forbidden, message: .rejected) + sendErrorResponse(error, connection: connection,endpoint: .prepareUpload) + } +} + +// MARK: - UploadHandler Implementation + +extension NearbySharingServer: UploadHandler { + + func handleFileUploadRequest(on connection: NWConnection, request: HTTPRequest) async -> URL? { + do { + let uploadReq: FileUploadRequest = try request.queryParameters.decode(FileUploadRequest.self) + + // --- Validate query params --- + guard let fileID = uploadReq.fileID, + let transmissionID = uploadReq.transmissionID, + let sessionID = uploadReq.sessionID else { + let error = ServerStatus(code: .badRequest, message: .missingRequiredParameters) + sendErrorResponse(error, connection: connection) + return nil + } + + // --- Validate session --- + let currentID = await state.currentSessionID() + guard sessionID == currentID else { + let error = ServerStatus(code: .unauthorized, message: .invalidSessionID) + sendErrorResponse(error, connection: connection) + return nil + } + + // --- Validate file + transmission, grab fileName for mutations --- + guard let fileInfo = await state.fileInfo(for: fileID), + fileInfo.transmissionId == transmissionID, + let fileType = fileInfo.file.fileType else { + let error = ServerStatus(code: .forbidden, message: .invalidTransmissionID) + sendErrorResponse(error, connection: connection) + return nil + } + + if fileInfo.status == .finished { + let error = ServerStatus(code: .conflict, message: .transferAlreadyCompleted) + sendErrorResponse(error, connection: connection) + return nil + } + + let url = await state.beginUpload(fileID: fileID, fileType: fileType) + return url + + } catch { + debugLog("Error processing file upload request: \(error)") + let error = ServerStatus(code: .badRequest, message: .missingRequiredParameters) + sendErrorResponse(error, connection: connection) + return nil + } + } + + func handleReceivedCompleteRequest(on connection: NWConnection, request: HTTPRequest) { + + Task { do { + + let uploadReq: FileUploadRequest = try request.queryParameters.decode(FileUploadRequest.self) + + guard let fileID = uploadReq.fileID else { + return + } + + await state.markUploadFinished(fileID: fileID) + + if let fileInfo = await state.fileInfo(for: fileID) { + eventPublisher.send(.fileTransferProgress(fileInfo)) + } + + let payload = BoolResponse(success: true) + sendSuccessResponse(connection: connection, payload: payload, endpoint: .upload) + + } catch { + debugLog("Error processing file upload request") + } + } + } + + func processProgress(connection: NWConnection, bytesReceived: Int, for request: HTTPRequest) { + guard let uploadReq: FileUploadRequest = try? request.queryParameters.decode(FileUploadRequest.self), + let fileID = uploadReq.fileID else { + return + } + + Task { + if let updated = await state.updateUploadProgress(fileID: fileID, bytes: bytesReceived) { + eventPublisher.send(.fileTransferProgress(updated)) + } + } + } + + func handleUploadFailure(connection: NWConnection, request: HTTPRequest?) { + guard + let req = request, + let uploadReq: FileUploadRequest = try? req.queryParameters.decode(FileUploadRequest.self), + let fileID = uploadReq.fileID + else { return } + + Task { + await state.markUploadFailed(fileID: fileID) + + if let fileInfo = await state.fileInfo(for: fileID) { + eventPublisher.send(.fileTransferProgress(fileInfo)) + } + } + } +} + +// MARK: - CloseConnectionHandler + +extension NearbySharingServer: CloseConnectionHandler { + + func handleCloseConnectionRequest(on connection: NWConnection, request: HTTPRequest) { + Task { + guard let closeReq = request.body.decodeJSON(CloseConnectionRequest.self) else { + + let error = ServerStatus(code: .badRequest, message: .invalidRequestFormat) + sendErrorResponse(error,connection: connection) + return + } + + let sessionID = await state.currentSessionID() + guard closeReq.sessionID == sessionID else { + + let error = ServerStatus(code: .unauthorized, message: .activeSessionAlreadyExists) + sendErrorResponse(error,connection: connection) + return + } + + let payload = BoolResponse(success: true) + sendSuccessResponse(connection: connection, + payload: payload, + endpoint: .closeConnection) + } + } +} + +// MARK: - Stub + +extension NearbySharingServer { + static func stub() -> NearbySharingServer { NearbySharingServer() } +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingServerHandler.swift b/Tella/Data/Networking/NearbySharing/NearbySharingServerHandler.swift new file mode 100644 index 000000000..69793ad48 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingServerHandler.swift @@ -0,0 +1,37 @@ +// +// NearbySharingServerHandler.swift +// Tella +// +// Created by Dhekra Rouatbi on 13/8/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + + +import Network +import Foundation + +protocol PingHandler { + func handlePingRequest(on connection: NWConnection) +} + +protocol RegisterHandler { + func handleRegisterRequest(on connection: NWConnection, request: HTTPRequest) + func acceptRegisterRequest(connection: NWConnection, httpRequest: HTTPRequest) async + func respondToRegistrationRequest(accept: Bool) +} + +protocol PrepareUploadHandler { + func handlePrepareUploadRequest(on connection: NWConnection, request: HTTPRequest) + func respondToFileOffer(accept: Bool) +} + +protocol UploadHandler { + func handleFileUploadRequest(on connection: NWConnection, request: HTTPRequest) async -> URL? + func handleReceivedCompleteRequest(on connection: NWConnection, request: HTTPRequest) + func processProgress(connection: NWConnection, bytesReceived: Int, for request: HTTPRequest) +} + +protocol CloseConnectionHandler { + func handleCloseConnectionRequest(on connection: NWConnection, request: HTTPRequest) +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingServerResponse.swift b/Tella/Data/Networking/NearbySharing/NearbySharingServerResponse.swift new file mode 100644 index 000000000..c8a0a42f4 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingServerResponse.swift @@ -0,0 +1,22 @@ +// +// NearbySharingServerResponse.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +struct NearbySharingServerResponse { + let dataResponse: Data? + let response: ServerResponseStatus + let endpoint: NearbySharingEndpoint? +} + +enum ServerResponseStatus { + case success + case failure +} + diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingServerState.swift b/Tella/Data/Networking/NearbySharing/NearbySharingServerState.swift new file mode 100644 index 000000000..e321c26f7 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingServerState.swift @@ -0,0 +1,123 @@ +// +// NearbySharingServerState.swift +// Tella +// +// Created by Dhekra Rouatbi on 23/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +final class NearbySharingServerState { + + var pin: String? + var session: NearbySharingSession? + var isUsingManualConnection: Bool + + private(set) var failedAttempts: Int + private let maxFailedAttempts = 3 + + var hasReachedMaxAttempts: Bool { + return failedAttempts >= maxFailedAttempts + } + + init(pin: String? = nil, + session: NearbySharingSession? = nil, + failedAttempts: Int = 0, + isUsingManualConnection: Bool = false) { + self.pin = pin + self.session = session + self.failedAttempts = failedAttempts + self.isUsingManualConnection = isUsingManualConnection + } + + func incrementFailedAttempts() { + failedAttempts += 1 + } + + func reset() { + pin = nil + session = nil + failedAttempts = 0 + isUsingManualConnection = false + } +} + +final class NearbySharingSession { + + let sessionId: String + var status: SessionStatus + var title: String? + var files: [String: NearbySharingTransferredFile] + + init(sessionId: String, + status: SessionStatus = .waiting, + files: [String: NearbySharingTransferredFile] = [:], + title: String? = nil) { + self.sessionId = sessionId + self.status = status + self.files = files + self.title = title + } + + var isActive: Bool { + return status == .waiting || status == .sending + } + + var hasFiles: Bool { + return !files.isEmpty + } +} + +final class NearbySharingTransferredFile: Codable { + + var file: NearbySharingFile + var vaultFile: VaultFileDB + var status: NearbySharingFileStatus + var transmissionId: String? + var url: URL? + var bytesReceived: Int = 0 + + init(file: NearbySharingFile, + status: NearbySharingFileStatus = .queue, + transmissionId: String? = nil, + url: URL? = nil, + bytesReceived: Int = 0) { + self.file = file + self.vaultFile = VaultFileDB(file: file) + self.status = status + self.transmissionId = transmissionId + self.url = url + self.bytesReceived = bytesReceived + } + + init(vaultFile: VaultFileDB, + status: NearbySharingFileStatus = .queue, + transmissionId: String? = nil, + url: URL? = nil, + bytesReceived: Int64 = 0) { + self.vaultFile = vaultFile + self.file = NearbySharingFile(vaultFile: vaultFile) + self.status = status + self.transmissionId = transmissionId + self.url = url + self.bytesReceived = Int(bytesReceived) + } +} + +enum NearbySharingFileStatus: String, Codable { + case queue + case transferring + case saving + case failed + case finished + case saved +} + +enum SessionStatus: String, Codable { + case waiting + case sending + case finished + case finishedWithErrors +} diff --git a/Tella/Data/Networking/NearbySharing/NearbySharingStateActor.swift b/Tella/Data/Networking/NearbySharing/NearbySharingStateActor.swift new file mode 100644 index 000000000..513805d02 --- /dev/null +++ b/Tella/Data/Networking/NearbySharing/NearbySharingStateActor.swift @@ -0,0 +1,154 @@ +// +// NearbySharingStateActor.swift +// Tella +// +// Created by Dhekra Rouatbi on 13/8/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Network +import Foundation + +actor NearbySharingStateActor { + + // MARK: - Stored State + + var state = NearbySharingServerState() + var pendingRegisterConnection: NWConnection? + var pendingRegisterResponse: Bool? + var pendingRegisterRequest: HTTPRequest? + var pendingUploadConnection: NWConnection? + + // MARK: - General + + func currentSessionID() -> String? { state.session?.sessionId } + func currentSession() -> NearbySharingSession? { state.session } + + func resetConnectionState() { + state.reset() + pendingRegisterConnection = nil + pendingRegisterRequest = nil + pendingUploadConnection = nil + pendingRegisterResponse = nil + } + + func removeTempFiles() { + let urls = state.session?.files.values.compactMap { $0.url } ?? [] + urls.forEach { $0.remove() } + } + + // MARK: - Register + + func setPin(_ pin: String) { state.pin = pin } + func pinMatches(_ pin: String) -> Bool { state.pin == pin } + func markManualConnection() { state.isUsingManualConnection = true } + func isManualConnection() -> Bool { state.isUsingManualConnection } + + func hasSession() -> Bool { state.session != nil } + func tooManyAttempts() -> Bool { state.hasReachedMaxAttempts } + func incrementFailedAttempts() { state.incrementFailedAttempts() } + + func createSessionAndReturnID() -> String { + let session = NearbySharingSession(sessionId: UUID().uuidString) + state.session = session + return session.sessionId + } + + func setPendingRegister(connection: NWConnection?, request: HTTPRequest?) { + pendingRegisterConnection = connection + pendingRegisterRequest = request + } + + func getPendingRegister() -> (NWConnection, HTTPRequest)? { + guard let connection = pendingRegisterConnection, let request = pendingRegisterRequest else { return nil } + pendingRegisterConnection = nil + pendingRegisterRequest = nil + return (connection, request) + } + + func getPendingRegisterResponse() -> Bool? { + let response = pendingRegisterResponse + pendingRegisterResponse = nil + return response + } + + func savePendingRegisterResponse(accept: Bool) { + pendingRegisterResponse = accept + } + + // MARK: - Prepare Upload + + func storePrepareUpload(_ request: PrepareUploadRequest) { + state.session?.title = request.title + request.files?.forEach { file in + if let id = file.id { + state.session?.files[id] = NearbySharingTransferredFile(file: file) + } + } + } + + func setPendingUploadConnection(_ connection: NWConnection?) { pendingUploadConnection = connection } + + func getPendingUploadConnection() -> NWConnection? { + defer { pendingUploadConnection = nil } + return pendingUploadConnection + } + + func assignTransmissionIDs() -> [(originalID: String?, transmissionID: String)] { + var result: [(String?, String)] = [] + state.session?.files.forEach { fileID, fileInfo in + let tid = UUID().uuidString + state.session?.files[fileID]?.transmissionId = tid + result.append((fileInfo.file.id, tid)) + } + return result + } + + // MARK: - Upload + + func fileInfo(for fileID: String) -> NearbySharingTransferredFile? { + guard let file = state.session?.files[fileID] else { return nil } + return file + } + + func markUploadFinished(fileID: String) { + guard let file = state.session?.files[fileID] else { return } + guard file.status == .transferring else { return } + file.status = .finished + state.session?.files[fileID] = file + } + + @discardableResult + func beginUpload(fileID: String, fileType: String) -> URL? { + let fileName = UUID().uuidString + "." + (fileType.fileExtensionFromMimeType() ?? "") + let url = FileManager.tempDirectory(withFileName: fileName) + guard let file = state.session?.files[fileID], + file.status == .queue + else { + return nil + } + file.status = .transferring + file.url = url + state.session?.files[fileID] = file + return url + } + + func updateUploadProgress(fileID: String, bytes: Int) -> NearbySharingTransferredFile? { + guard let file = state.session?.files[fileID] else { return nil } + guard file.status == .transferring else { return nil } + + file.bytesReceived += bytes + state.session?.files[fileID] = file + return file + } + + func markUploadFailed(fileID: String) { + state.session?.files[fileID]?.status = .failed + } + + func allTransfersCompleted() -> Bool { + guard let files = state.session?.files else { return false } + return files.values.first { $0.status == .transferring || $0.status == .queue } == nil + } +} diff --git a/Tella/Data/Networking/Repositories/FeedbackRepository.swift b/Tella/Data/Networking/Repositories/FeedbackRepository.swift index bb4bdaff0..b986a9cf9 100644 --- a/Tella/Data/Networking/Repositories/FeedbackRepository.swift +++ b/Tella/Data/Networking/Repositories/FeedbackRepository.swift @@ -17,7 +17,7 @@ struct FeedbackRepository: WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: FeedbackRepository.API.submitFeedback(feedback.text)) return apiResponse - .compactMap{$0.0.toDomain() as? FeedbackAPI} + .compactMap{$0.response.toDomain() as? FeedbackAPI} .eraseToAnyPublisher() } } @@ -48,7 +48,7 @@ extension FeedbackRepository.API: APIRequest { } } - var path: String { + var path: String? { switch self { case .submitFeedback: return FeedbackConstants.opinionsPath diff --git a/Tella/Data/Networking/Repositories/Nextcloud/NextcloudRepository.swift b/Tella/Data/Networking/Repositories/Nextcloud/NextcloudRepository.swift index 795fd3c3e..76b9a6c8a 100644 --- a/Tella/Data/Networking/Repositories/Nextcloud/NextcloudRepository.swift +++ b/Tella/Data/Networking/Repositories/Nextcloud/NextcloudRepository.swift @@ -351,7 +351,7 @@ class NextcloudRepository: NextcloudRepositoryProtocol { requestBody: NextcloudConstants.filesRequestBody.data(using: .utf8), account: "", options: option) { account, files, _, error in - print(error.errorCode) + debugLog(error.errorCode) if error == .success, let _ = files.first { continuation.resume(returning: true) } else if error.errorCode == HTTPErrorCodes.notFound.rawValue { diff --git a/Tella/Data/Networking/Repositories/ReportRepository.swift b/Tella/Data/Networking/Repositories/ReportRepository.swift index ad24100f8..93b7cb138 100644 --- a/Tella/Data/Networking/Repositories/ReportRepository.swift +++ b/Tella/Data/Networking/Repositories/ReportRepository.swift @@ -76,7 +76,7 @@ extension ReportRepository.API: APIRequest { } } - var path: String { + var path: String? { switch self { case .createReport(let report): diff --git a/Tella/Data/Networking/Repositories/ResourceRepository.swift b/Tella/Data/Networking/Repositories/ResourceRepository.swift index a573736d2..bc8974b48 100644 --- a/Tella/Data/Networking/Repositories/ResourceRepository.swift +++ b/Tella/Data/Networking/Repositories/ResourceRepository.swift @@ -15,7 +15,7 @@ struct ResourceRepository: WebRepository { func getResourcesByProject(server: TellaServer) -> AnyPublisher<[Resource], APIError> { let apiResponse: APIResponse<[ProjectDTO]> = getAPIResponse(endpoint: API.getResourcesByProject(serverUrl: server.url ?? "", projectId: server.projectId ?? "", token: server.accessToken ?? "")) return apiResponse - .compactMap{$0.0.first?.toDomain() as? Project} + .compactMap{$0.response.first?.toDomain() as? Project} .compactMap{$0.resources} .eraseToAnyPublisher() } @@ -24,7 +24,7 @@ struct ResourceRepository: WebRepository { let apiResponse = getAPIResponseForBinaryData(endpoint: API.getResourceByFileName(serverUrl: server.url ?? "", fileName: fileName, token: server.accessToken ?? "")) return apiResponse - .compactMap { $0.0 } + .compactMap { $0.response } .eraseToAnyPublisher() } } @@ -56,7 +56,7 @@ extension ResourceRepository.API: APIRequest { } } - var path: String { + var path: String? { switch self { case .getResourcesByProject(_, let projectId, _): return "/resource/projects?projectId[]=\(projectId)" diff --git a/Tella/Data/Networking/Repositories/ServerRepository.swift b/Tella/Data/Networking/Repositories/ServerRepository.swift index 53fcc7fba..ba090fb37 100644 --- a/Tella/Data/Networking/Repositories/ServerRepository.swift +++ b/Tella/Data/Networking/Repositories/ServerRepository.swift @@ -17,7 +17,7 @@ struct ServerRepository: WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: API.login((username: username, password: password, serverURL: serverURL))) return apiResponse - .map{$0.0} + .map{$0.response} .eraseToAnyPublisher() } @@ -26,7 +26,7 @@ struct ServerRepository: WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: API.getProjetDetails((projectURL: projectURL, token: token))) return apiResponse - .compactMap{$0.0.toDomain() as? ProjectAPI} + .compactMap{$0.response.toDomain() as? ProjectAPI} .eraseToAnyPublisher() } } @@ -73,12 +73,12 @@ extension ServerRepository.API: APIRequest { } } - var path: String { + var path: String? { switch self { case .login: return "/login" case .getProjetDetails(_): - return "" + return nil } } diff --git a/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift b/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift index efd722337..764eaab4f 100644 --- a/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift +++ b/Tella/Data/Networking/Repositories/UwaziServerRepositories.swift @@ -19,8 +19,8 @@ class UwaziServerRepository: WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: API.login((username: username, password: password, serverURL: serverURL))) return apiResponse - .tryMap({ (response, allHeaderFields) in - let token = self.handleToken(response: response, allHeaderFields: allHeaderFields) + .tryMap({ apiResponse in + let token = self.handleToken(response: apiResponse.response, allHeaderFields: apiResponse.headers) return token }) .mapError {$0 as! APIError} @@ -34,8 +34,8 @@ class UwaziServerRepository: WebRepository { let apiResponse : APIResponse = getAPIResponse(endpoint: API.twoFactorAuthentication((username: username, password: password,token: token, serverURL: serverURL))) return apiResponse - .tryMap({ (response, allHeaderFields) in - return self.handleToken(response: response, allHeaderFields: allHeaderFields) + .tryMap({ apiResponse in + return self.handleToken(response: apiResponse.response, allHeaderFields: apiResponse.headers) }) .mapError {$0 as! APIError} .eraseToAnyPublisher() @@ -61,14 +61,14 @@ class UwaziServerRepository: WebRepository { func checkServerURL(serverURL: String, cookie: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.checkURL(serverURL: serverURL, cookie: cookie)) return apiResponse - .compactMap{$0.0.toDomain() as? UwaziCheckURL} + .compactMap{$0.response.toDomain() as? UwaziCheckURL} .eraseToAnyPublisher() } func getLanguage(serverURL: String, cookie: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getLanguage(serverURL: serverURL, cookie: cookie)) return apiResponse - .compactMap{$0.0.toDomain() as? UwaziLanguage} + .compactMap{$0.response.toDomain() as? UwaziLanguage} .eraseToAnyPublisher() } @@ -80,7 +80,7 @@ class UwaziServerRepository: WebRepository { func getTemplate(serverURL: String, cookieList: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getTemplate(serverURL: serverURL, cookieList: cookieList)) return apiResponse - .compactMap{$0.0} + .compactMap{$0.response} .eraseToAnyPublisher() } /// Get all the setting related to the Uwazi server to determine the whitelisted templates @@ -91,7 +91,7 @@ class UwaziServerRepository: WebRepository { func getSettings(serverURL: String, cookieList: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getSetting(serverURL: serverURL, cookieList: cookieList)) return apiResponse - .compactMap{$0.0} + .compactMap{$0.response} .eraseToAnyPublisher() } /// Get all the options that are related to properties of the template related to the selected Uwazi server @@ -102,7 +102,7 @@ class UwaziServerRepository: WebRepository { func getDictionaries(serverURL: String, cookieList: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getDictionary(serverURL: serverURL, cookieList: cookieList)) return apiResponse - .compactMap{$0.0} + .compactMap{$0.response} .eraseToAnyPublisher() } /// Get all the translation of the text related to the selected Uwazi server @@ -113,14 +113,14 @@ class UwaziServerRepository: WebRepository { func getTranslations(serverURL: String, cookieList: String) -> AnyPublisher { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getTranslations(serverURL: serverURL, cookieList: cookieList)) return apiResponse - .compactMap{$0.0} + .compactMap{$0.response} .eraseToAnyPublisher() } func getProjetDetails(projectURL: String,token: String) -> AnyPublisher { let apiResponse : APIResponse = getAPIResponse(endpoint: API.getProjetDetails((projectURL: projectURL, token: token))) return apiResponse - .compactMap{$0.0.toDomain() as? ProjectAPI } + .compactMap{$0.response.toDomain() as? ProjectAPI } .eraseToAnyPublisher() } @@ -129,7 +129,7 @@ class UwaziServerRepository: WebRepository { let apiResponse: APIResponse = getAPIResponse(endpoint: API.submitPublicEntity(serverURL: serverURL, cookie: cookie, multipartHeader: multipartHeader, multipartBody: multipartBody)) return apiResponse .compactMap{ response in - EntityResult.publicEntity(response.0) + EntityResult.publicEntity(response.response) } .eraseToAnyPublisher() } @@ -137,7 +137,7 @@ class UwaziServerRepository: WebRepository { let apiResponse: APIResponse = getAPIResponse(endpoint: API.submitEntity(serverURL: serverURL, cookie: cookie, multipartHeader: multipartHeader, multipartBody: multipartBody)) return apiResponse .compactMap{response in - EntityResult.authorizedEntity(response.0) + EntityResult.authorizedEntity(response.response) } .eraseToAnyPublisher() } @@ -145,7 +145,7 @@ class UwaziServerRepository: WebRepository { func getRelationshipEntities(serverURL: String, cookie: String, relatedEntityIds: [String]) -> AnyPublisher<[UwaziRelationshipList], APIError> { let apiResponse: APIResponse = getAPIResponse(endpoint: API.getRelationshipEntities(serverURL:serverURL, cookie:cookie)) return apiResponse - .compactMap{ $0.0 } + .compactMap{ $0.response } .map { dto in let shouldFetchAllTemplates = relatedEntityIds.contains { $0.isEmpty } return dto.rows @@ -404,7 +404,7 @@ extension UwaziServerRepository.API: APIRequest { } } - var path: String { + var path: String? { switch self { case .login, .twoFactorAuthentication: return "/api/login" diff --git a/Tella/Data/VaultManager/VaultFilesManager.swift b/Tella/Data/VaultManager/VaultFilesManager.swift index 4b6b74183..37771c9df 100644 --- a/Tella/Data/VaultManager/VaultFilesManager.swift +++ b/Tella/Data/VaultManager/VaultFilesManager.swift @@ -1,5 +1,5 @@ // -// Copyright © 2023 HORIZONTAL. +// Copyright © 2023 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -62,6 +62,23 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { return subject.eraseToAnyPublisher() } + func addVaultFile(importedFile: ImportedFile) async -> VaultFileDB? { + + guard let fileDetails = await getFileDetails(importedFile: importedFile), + let filePath = importedFile.urlFile, + let isSaved = self.vaultManager?.save(filePath, vaultFileId: fileDetails.file.id) + else { + return nil + } + + if isSaved { + self.vaultDataBase.addVaultFile(file: fileDetails.file, parentId: fileDetails.importedFile.parentId) + return fileDetails.file + } else { + return nil + } + } + private func updateImportedFilesURLs(_ importedFiles: inout [ImportedFile]) async { for index in importedFiles.indices { await updateURL(importedFile: &importedFiles[index]) @@ -80,7 +97,7 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { importProgress.currentFile += 1 subject.send(.importProgress(importProgress: importProgress)) - + if self.shouldCancelImportAndEncryption.value { break } @@ -175,7 +192,7 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { return subject.eraseToAnyPublisher() } - + private func handleDatabaseAddition(fileDetails:VaultFileDetails, subject : CurrentValueSubject ) { @@ -256,6 +273,17 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { return result } + func addFolderFile(name: String) -> Result? { + let file = VaultFileDB(type: .directory, name: name) + let result = self.vaultDataBase.addVaultFile(file: file,parentId: nil) + switch result { + case .success: + return .success(file.id) + case .failure(let error): + return .failure(error) + } + } + func getVaultFiles(parentId: String?, filter: FilterType, sort: FileSortOptions?) -> [VaultFileDB] { return self.vaultDataBase.getVaultFiles(parentId: parentId, filter: filter, sort: sort) } @@ -278,14 +306,21 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { guard let filePath = importedFile.urlFile else {return nil} - let id = UUID().uuidString + let id = importedFile.fileId == nil ? UUID().uuidString : importedFile.fileId let _ = filePath.startAccessingSecurityScopedResource() defer { filePath.stopAccessingSecurityScopedResource() } async let thumnail = await filePath.thumbnail() - let fileName = filePath.deletingPathExtension().lastPathComponent - let path = filePath.path + var fileName : String + + if let name = importedFile.fileName { + fileName = name + } else { + fileName = filePath.deletingPathExtension().lastPathComponent + } + + let path = filePath.relativePath let pathExtension = filePath.pathExtension var width : Double? @@ -311,6 +346,20 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { return (VaultFileDetails(file: vaultFile, importedFile: importedFile)) } + private func getIncrementedName(name:String) -> String { + + var copyNumber = 0 + + // Generate the new filename and check if it exists + var newFileName = name + while self.vaultFileExists(name: newFileName) == true { + copyNumber += 1 + newFileName = name + "-" + "\(copyNumber)" + } + return newFileName + } + + func getFilesTotalSize(filePaths: [URL]) -> Int { var totalSizeArray : [Int] = [] @@ -331,7 +380,7 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { func vaultFileExists(name: String) -> Bool { return self.vaultDataBase.vaultFileExists(name: name) } - + func getVaultFile(id: String?) -> VaultFileDB? { return self.vaultDataBase.getVaultFile(id: id) } @@ -381,7 +430,7 @@ class VaultFilesManager :ObservableObject, VaultFilesManagerInterface { let result = self.vaultDataBase.deleteVaultFile(ids: fileIds) shouldReloadFiles.send(true) return result - + } @discardableResult diff --git a/Tella/Domain/Entity/ImportedFile.swift b/Tella/Domain/Entity/ImportedFile.swift index 6079df5e0..b0da4b9d3 100644 --- a/Tella/Domain/Entity/ImportedFile.swift +++ b/Tella/Domain/Entity/ImportedFile.swift @@ -17,6 +17,8 @@ struct ImportedFile { var shouldPreserveMetadata : Bool = false var deleteOriginal : Bool = false var fileSource : FileSource + var fileId : String? + var fileName : String? } enum FileSource { diff --git a/Tella/Domain/Entity/Report/SettingsModel.swift b/Tella/Domain/Entity/Report/SettingsModel.swift index c6551e08d..d703370df 100644 --- a/Tella/Domain/Entity/Report/SettingsModel.swift +++ b/Tella/Domain/Entity/Report/SettingsModel.swift @@ -51,6 +51,8 @@ class SettingsModel: ObservableObject, Codable { /// - on lock it returns false @Published var shouldMergeVaultFilesToDb: Bool? = nil + @Published var nearbySharing: Bool = true + enum CodingKeys: CodingKey { case offlineMode case quickDelete diff --git a/Tella/Domain/Entity/Report/Vault/VaultFileDB.swift b/Tella/Domain/Entity/Report/Vault/VaultFileDB.swift index 3832fa41a..7cf44fe0c 100644 --- a/Tella/Domain/Entity/Report/Vault/VaultFileDB.swift +++ b/Tella/Domain/Entity/Report/Vault/VaultFileDB.swift @@ -77,6 +77,16 @@ class VaultFileDB : Codable, Hashable, ObservableObject { self.created = Date() } + init(file: NearbySharingFile) { + self.id = UUID().uuidString + self.type = .file + self.name = file.fileName ?? "" + self.thumbnail = file.thumbnail + self.size = file.size ?? 0 + self.mimeType = file.fileType + self.created = Date() + } + init(vaultFile :VaultFile) { self.id = vaultFile.containerName self.type = vaultFile.type == .folder ? .directory : .file diff --git a/Tella/NewAssets.xcassets/settings.checked-circle.imageset/Contents.json b/Tella/NewAssets.xcassets/add.file.icon.imageset/Contents.json similarity index 84% rename from Tella/NewAssets.xcassets/settings.checked-circle.imageset/Contents.json rename to Tella/NewAssets.xcassets/add.file.icon.imageset/Contents.json index b99207d92..d44272796 100644 --- a/Tella/NewAssets.xcassets/settings.checked-circle.imageset/Contents.json +++ b/Tella/NewAssets.xcassets/add.file.icon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "settings.checked-circle.pdf", + "filename" : "reports.add.pdf", "idiom" : "universal", "scale" : "1x" }, diff --git a/Tella/NewAssets.xcassets/add.file.icon.imageset/reports.add.pdf b/Tella/NewAssets.xcassets/add.file.icon.imageset/reports.add.pdf new file mode 100644 index 000000000..748153e93 Binary files /dev/null and b/Tella/NewAssets.xcassets/add.file.icon.imageset/reports.add.pdf differ diff --git a/Tella/NewAssets.xcassets/arrow.down.imageset/Contents.json b/Tella/NewAssets.xcassets/arrow.down.imageset/Contents.json new file mode 100644 index 000000000..986ad54cb --- /dev/null +++ b/Tella/NewAssets.xcassets/arrow.down.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "arrow.down.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/arrow.down.imageset/arrow.down.pdf b/Tella/NewAssets.xcassets/arrow.down.imageset/arrow.down.pdf new file mode 100644 index 000000000..bbdfa1a32 Binary files /dev/null and b/Tella/NewAssets.xcassets/arrow.down.imageset/arrow.down.pdf differ diff --git a/Tella/NewAssets.xcassets/arrow.up.imageset/Contents.json b/Tella/NewAssets.xcassets/arrow.up.imageset/Contents.json new file mode 100644 index 000000000..50f58a3dc --- /dev/null +++ b/Tella/NewAssets.xcassets/arrow.up.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "arrow.up.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/arrow.up.imageset/arrow.up.pdf b/Tella/NewAssets.xcassets/arrow.up.imageset/arrow.up.pdf new file mode 100644 index 000000000..9d0cf5283 Binary files /dev/null and b/Tella/NewAssets.xcassets/arrow.up.imageset/arrow.up.pdf differ diff --git a/Tella/NewAssets.xcassets/camera-filled.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/camera-filled.icon.imageset/Contents.json new file mode 100644 index 000000000..6d0afe13d --- /dev/null +++ b/Tella/NewAssets.xcassets/camera-filled.icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "report.camera-filled.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/camera-filled.icon.imageset/report.camera-filled.pdf b/Tella/NewAssets.xcassets/camera-filled.icon.imageset/report.camera-filled.pdf new file mode 100644 index 000000000..d0d34f7a7 Binary files /dev/null and b/Tella/NewAssets.xcassets/camera-filled.icon.imageset/report.camera-filled.pdf differ diff --git a/Tella/NewAssets.xcassets/checkbox.off.imageset/Contents.json b/Tella/NewAssets.xcassets/checkbox.off.imageset/Contents.json new file mode 100644 index 000000000..daac9fcc3 --- /dev/null +++ b/Tella/NewAssets.xcassets/checkbox.off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checkbox.off.icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/checkbox.off.imageset/checkbox.off.icon.pdf b/Tella/NewAssets.xcassets/checkbox.off.imageset/checkbox.off.icon.pdf new file mode 100644 index 000000000..3d3257d1c Binary files /dev/null and b/Tella/NewAssets.xcassets/checkbox.off.imageset/checkbox.off.icon.pdf differ diff --git a/Tella/NewAssets.xcassets/checkbox.on.imageset/Contents.json b/Tella/NewAssets.xcassets/checkbox.on.imageset/Contents.json new file mode 100644 index 000000000..72eab1462 --- /dev/null +++ b/Tella/NewAssets.xcassets/checkbox.on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checkbox.on.icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/checkbox.on.imageset/checkbox.on.icon.pdf b/Tella/NewAssets.xcassets/checkbox.on.imageset/checkbox.on.icon.pdf new file mode 100644 index 000000000..1a413c93c Binary files /dev/null and b/Tella/NewAssets.xcassets/checkbox.on.imageset/checkbox.on.icon.pdf differ diff --git a/Tella/NewAssets.xcassets/checked-circle.imageset/Contents.json b/Tella/NewAssets.xcassets/checked-circle.imageset/Contents.json new file mode 100644 index 000000000..fcb5d56d5 --- /dev/null +++ b/Tella/NewAssets.xcassets/checked-circle.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checked-circle.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/settings.checked-circle.imageset/settings.checked-circle.pdf b/Tella/NewAssets.xcassets/checked-circle.imageset/checked-circle.pdf similarity index 100% rename from Tella/NewAssets.xcassets/settings.checked-circle.imageset/settings.checked-circle.pdf rename to Tella/NewAssets.xcassets/checked-circle.imageset/checked-circle.pdf diff --git a/Tella/NewAssets.xcassets/clock.imageset/Contents.json b/Tella/NewAssets.xcassets/clock.imageset/Contents.json new file mode 100644 index 000000000..e899d634d --- /dev/null +++ b/Tella/NewAssets.xcassets/clock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "clock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/clock.imageset/clock.pdf b/Tella/NewAssets.xcassets/clock.imageset/clock.pdf new file mode 100644 index 000000000..e9f4f3faf Binary files /dev/null and b/Tella/NewAssets.xcassets/clock.imageset/clock.pdf differ diff --git a/Tella/NewAssets.xcassets/delete.cross.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/delete.cross.icon.imageset/Contents.json new file mode 100644 index 000000000..1d971c745 --- /dev/null +++ b/Tella/NewAssets.xcassets/delete.cross.icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "report.delete.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/delete.cross.icon.imageset/report.delete.pdf b/Tella/NewAssets.xcassets/delete.cross.icon.imageset/report.delete.pdf new file mode 100644 index 000000000..9c585e9d1 Binary files /dev/null and b/Tella/NewAssets.xcassets/delete.cross.icon.imageset/report.delete.pdf differ diff --git a/Tella/NewAssets.xcassets/device.imageset/Contents.json b/Tella/NewAssets.xcassets/device.imageset/Contents.json new file mode 100644 index 000000000..583079776 --- /dev/null +++ b/Tella/NewAssets.xcassets/device.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "device.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/device.imageset/device.pdf b/Tella/NewAssets.xcassets/device.imageset/device.pdf new file mode 100644 index 000000000..bb42104b2 Binary files /dev/null and b/Tella/NewAssets.xcassets/device.imageset/device.pdf differ diff --git a/Tella/NewAssets.xcassets/file.add.imageset/Contents.json b/Tella/NewAssets.xcassets/file.add.imageset/Contents.json new file mode 100644 index 000000000..4d43ff465 --- /dev/null +++ b/Tella/NewAssets.xcassets/file.add.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "file.add.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/file.add.imageset/file.add.pdf b/Tella/NewAssets.xcassets/file.add.imageset/file.add.pdf new file mode 100644 index 000000000..f72ec9c5a Binary files /dev/null and b/Tella/NewAssets.xcassets/file.add.imageset/file.add.pdf differ diff --git a/Tella/NewAssets.xcassets/folders.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/folders.icon.imageset/Contents.json new file mode 100644 index 000000000..04da4f090 --- /dev/null +++ b/Tella/NewAssets.xcassets/folders.icon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folders.icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/folders.icon.imageset/folders.icon.pdf b/Tella/NewAssets.xcassets/folders.icon.imageset/folders.icon.pdf new file mode 100644 index 000000000..f0ae68344 Binary files /dev/null and b/Tella/NewAssets.xcassets/folders.icon.imageset/folders.icon.pdf differ diff --git a/Tella/NewAssets.xcassets/gallery.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/gallery.icon.imageset/Contents.json new file mode 100644 index 000000000..fcfcb252f --- /dev/null +++ b/Tella/NewAssets.xcassets/gallery.icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "report.gallery.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/gallery.icon.imageset/report.gallery.pdf b/Tella/NewAssets.xcassets/gallery.icon.imageset/report.gallery.pdf new file mode 100644 index 000000000..277bf8b40 Binary files /dev/null and b/Tella/NewAssets.xcassets/gallery.icon.imageset/report.gallery.pdf differ diff --git a/Tella/NewAssets.xcassets/help.yellow.imageset/Contents.json b/Tella/NewAssets.xcassets/help.yellow.imageset/Contents.json new file mode 100644 index 000000000..b8d0cf036 --- /dev/null +++ b/Tella/NewAssets.xcassets/help.yellow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "help.yellow.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/help.yellow.imageset/help.yellow.pdf b/Tella/NewAssets.xcassets/help.yellow.imageset/help.yellow.pdf new file mode 100644 index 000000000..a44ecbc64 Binary files /dev/null and b/Tella/NewAssets.xcassets/help.yellow.imageset/help.yellow.pdf differ diff --git a/Tella/NewAssets.xcassets/home.add.imageset/Contents.json b/Tella/NewAssets.xcassets/home.add.imageset/Contents.json index e06eb916e..d94e166e9 100644 --- a/Tella/NewAssets.xcassets/home.add.imageset/Contents.json +++ b/Tella/NewAssets.xcassets/home.add.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "home.add.pdf", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/Tella/NewAssets.xcassets/home.add.imageset/home.add.pdf b/Tella/NewAssets.xcassets/home.add.imageset/home.add.pdf index f72ec9c5a..148010074 100644 Binary files a/Tella/NewAssets.xcassets/home.add.imageset/home.add.pdf and b/Tella/NewAssets.xcassets/home.add.imageset/home.add.pdf differ diff --git a/Tella/NewAssets.xcassets/mic-filled.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/mic-filled.icon.imageset/Contents.json new file mode 100644 index 000000000..6ebadbe6c --- /dev/null +++ b/Tella/NewAssets.xcassets/mic-filled.icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "report.mic-filled.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/mic-filled.icon.imageset/report.mic-filled.pdf b/Tella/NewAssets.xcassets/mic-filled.icon.imageset/report.mic-filled.pdf new file mode 100644 index 000000000..4fa7d6421 Binary files /dev/null and b/Tella/NewAssets.xcassets/mic-filled.icon.imageset/report.mic-filled.pdf differ diff --git a/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/Contents.json b/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/Contents.json new file mode 100644 index 000000000..8fa0d77cc --- /dev/null +++ b/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "nearby-sharing.connect.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/nearby-sharing.connect.pdf b/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/nearby-sharing.connect.pdf new file mode 100644 index 000000000..7bae71f41 Binary files /dev/null and b/Tella/NewAssets.xcassets/nearby-sharing.connect.imageset/nearby-sharing.connect.pdf differ diff --git a/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/Contents.json b/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/Contents.json new file mode 100644 index 000000000..67a8467e5 --- /dev/null +++ b/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "nearby-sharing.home.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/nearby-sharing.home.pdf b/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/nearby-sharing.home.pdf new file mode 100644 index 000000000..4594ef175 Binary files /dev/null and b/Tella/NewAssets.xcassets/nearby-sharing.home.imageset/nearby-sharing.home.pdf differ diff --git a/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/Contents.json b/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/Contents.json new file mode 100644 index 000000000..1da139751 --- /dev/null +++ b/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "nearby-sharing.main.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/nearby-sharing.main.pdf b/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/nearby-sharing.main.pdf new file mode 100644 index 000000000..ec6a3960e Binary files /dev/null and b/Tella/NewAssets.xcassets/nearby-sharing.main.imageset/nearby-sharing.main.pdf differ diff --git a/Tella/NewAssets.xcassets/phone.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/phone.icon.imageset/Contents.json new file mode 100644 index 000000000..18fcd80ff --- /dev/null +++ b/Tella/NewAssets.xcassets/phone.icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "report.phone.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/phone.icon.imageset/report.phone.pdf b/Tella/NewAssets.xcassets/phone.icon.imageset/report.phone.pdf new file mode 100644 index 000000000..00f5695ec Binary files /dev/null and b/Tella/NewAssets.xcassets/phone.icon.imageset/report.phone.pdf differ diff --git a/Tella/NewAssets.xcassets/qrCode.icon.imageset/Contents.json b/Tella/NewAssets.xcassets/qrCode.icon.imageset/Contents.json new file mode 100644 index 000000000..ee50321dd --- /dev/null +++ b/Tella/NewAssets.xcassets/qrCode.icon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "qrCode.icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/qrCode.icon.imageset/qrCode.icon.pdf b/Tella/NewAssets.xcassets/qrCode.icon.imageset/qrCode.icon.pdf new file mode 100644 index 000000000..701cc514f Binary files /dev/null and b/Tella/NewAssets.xcassets/qrCode.icon.imageset/qrCode.icon.pdf differ diff --git a/Tella/NewAssets.xcassets/warning.imageset/Contents.json b/Tella/NewAssets.xcassets/warning.imageset/Contents.json new file mode 100644 index 000000000..0b55f782a --- /dev/null +++ b/Tella/NewAssets.xcassets/warning.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "warning.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tella/NewAssets.xcassets/warning.imageset/warning.pdf b/Tella/NewAssets.xcassets/warning.imageset/warning.pdf new file mode 100644 index 000000000..0455b465a Binary files /dev/null and b/Tella/NewAssets.xcassets/warning.imageset/warning.pdf differ diff --git a/Tella/Scenes/Audio/Recorder/Views/SaveAudioConfirmationView.swift b/Tella/Scenes/Audio/Recorder/Views/SaveAudioConfirmationView.swift index da4640a7e..e9fe6f4f3 100644 --- a/Tella/Scenes/Audio/Recorder/Views/SaveAudioConfirmationView.swift +++ b/Tella/Scenes/Audio/Recorder/Views/SaveAudioConfirmationView.swift @@ -21,8 +21,7 @@ struct SaveAudioConfirmationView: View { msgText: LocalizableRecorder.saveRecordingSheetExpl.localized, cancelText: LocalizableRecorder.saveRecordingCancelSheetAction.localized, discardText: LocalizableRecorder.saveRecordingDiscardSheetAction .localized, - actionText: LocalizableRecorder.saveRecordingSaveSheetAction.localized, - withDrag: false) { + actionText: LocalizableRecorder.saveRecordingSaveSheetAction.localized) { sheetManager.hide() diff --git a/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockButton.swift b/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockButton.swift index 89d89aa8c..8b888652d 100644 --- a/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockButton.swift +++ b/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockButton.swift @@ -10,19 +10,13 @@ import Foundation -protocol LockButtonProtocol { - var title : String { get } - var description: String { get } - var imageName: String { get } -} - -struct PasswordLockButton : LockButtonProtocol { +struct PasswordLockButton : IconTextButtonConfig { var title = LocalizableLock.lockSelectActionPassword.localized var description = LocalizableLock.lockSelectActionExplPassword.localized var imageName = "lock.password" } -struct PINLockButton : LockButtonProtocol { +struct PINLockButton : IconTextButtonConfig { var title = LocalizableLock.lockSelectActionPin.localized var description = LocalizableLock.lockSelectActionExplPin.localized var imageName = "lock.pin" diff --git a/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockChoiceView.swift b/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockChoiceView.swift index af62779cc..c5263ca68 100644 --- a/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockChoiceView.swift +++ b/Tella/Scenes/Authentication/Views/Lock/LockChoice/LockChoiceView.swift @@ -3,7 +3,7 @@ // Tella // // -// Copyright © 2021 HORIZONTAL. +// Copyright © 2021 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -54,10 +54,10 @@ struct LockChoiceView: View { VStack(spacing: 15) { - LockButtonView(lockButtonProtocol: PasswordLockButton(), + IconTextButton(buttonConfig: PasswordLockButton(), destination: LockPasswordView().environmentObject(lockViewModel)) - LockButtonView(lockButtonProtocol: PINLockButton(), + IconTextButton(buttonConfig: PINLockButton(), destination: LockPinView().environmentObject(lockViewModel)) } Spacer() @@ -67,52 +67,6 @@ struct LockChoiceView: View { } } -struct LockButtonView : View { - - var lockButtonProtocol : LockButtonProtocol - var destination : Destination - - var body: some View { - - Button { - navigateTo(destination: destination) - } label: { - HStack(spacing: 20) { - - Image(lockButtonProtocol.imageName) - .frame(width: 42, height: 42) - .aspectRatio(contentMode: .fit) - - VStack(alignment:.leading, spacing: 3 ) { - Text(lockButtonProtocol.title) - .font(.custom(Styles.Fonts.boldFontName, size: 16)) - .foregroundColor(.white) - - Text(lockButtonProtocol.description) - .font(.custom(Styles.Fonts.regularFontName, size: 13.5)) - .foregroundColor(.white) - } - Spacer() - } .padding(16) - .background( Color.white.opacity(0.16)) - .cornerRadius(20) - .buttonStyle(LockButtonStyle()) - } - } -} - -struct LockButtonStyle : ButtonStyle { - - func makeBody(configuration: Configuration) -> some View { - configuration.label - .background(configuration.isPressed ? Color.white.opacity(0.32) : Color.white.opacity(0.16)) - .cornerRadius(20) - .overlay( - configuration.isPressed ? RoundedRectangle(cornerRadius: 20).stroke(Color.white.opacity(0.8), lineWidth: 3) : RoundedRectangle(cornerRadius: 20).stroke(Color.clear, lineWidth: 0) - ) - } -} - struct LockChoiceView_Previews: PreviewProvider { static var previews: some View { LockChoiceView() diff --git a/Tella/Scenes/Authentication/Views/Lock/Password/PasswordView.swift b/Tella/Scenes/Authentication/Views/Lock/Password/PasswordView.swift index bf0bbf253..0a43dc9ad 100644 --- a/Tella/Scenes/Authentication/Views/Lock/Password/PasswordView.swift +++ b/Tella/Scenes/Authentication/Views/Lock/Password/PasswordView.swift @@ -12,6 +12,7 @@ import SwiftUI import UIKit enum NextButtonAction { + case none case action case destination } @@ -57,7 +58,7 @@ struct PasswordView: View { ConfirmPasswordErrorView(errorMessage: LocalizableLock.lockPasswordConfirmErrorPasswordsDoNotMatch.localized) } - BottomLockView(isValid: $isValid, + NavigationBottomView(shouldActivateNext: $isValid, shouldEnableBackButton: shouldEnableBackButton, nextButtonAction: nextButtonAction, destination:destination, diff --git a/Tella/Scenes/Authentication/Views/Lock/Pin/CustomPinView.swift b/Tella/Scenes/Authentication/Views/Lock/Pin/CustomPinView.swift index 71150f1cf..053e3e78e 100644 --- a/Tella/Scenes/Authentication/Views/Lock/Pin/CustomPinView.swift +++ b/Tella/Scenes/Authentication/Views/Lock/Pin/CustomPinView.swift @@ -72,7 +72,7 @@ struct CustomPinView: View { Spacer() } - BottomLockView(isValid: $isValid, + NavigationBottomView(shouldActivateNext: $isValid, shouldEnableBackButton: shouldEnableBackButton, nextButtonAction: nextButtonAction, destination:destination, diff --git a/Tella/Scenes/Camera/Views/CustomCameraController.swift b/Tella/Scenes/Camera/Views/CustomCameraController.swift index 48904288f..78318b3eb 100644 --- a/Tella/Scenes/Camera/Views/CustomCameraController.swift +++ b/Tella/Scenes/Camera/Views/CustomCameraController.swift @@ -20,7 +20,6 @@ public class CameraService: NSObject, ObservableObject, AVCapturePhotoCaptureDel private var photoOutput: AVCapturePhotoOutput? private var videoOutput: AVCaptureMovieFileOutput? - private var cameraPreviewLayer: AVCaptureVideoPreviewLayer? private var deviceOrientation : UIDeviceOrientation = UIDevice.current.orientation private let sessionQueue = DispatchQueue(label: "session queue") diff --git a/Tella/Scenes/CommonConnectionReport/ViewModels/DraftMainViewModel.swift b/Tella/Scenes/CommonConnectionReport/ViewModels/DraftMainViewModel.swift index e4e374f1a..9d13c2685 100644 --- a/Tella/Scenes/CommonConnectionReport/ViewModels/DraftMainViewModel.swift +++ b/Tella/Scenes/CommonConnectionReport/ViewModels/DraftMainViewModel.swift @@ -19,7 +19,6 @@ class DraftMainViewModel: ObservableObject { @Published var reportId : Int? @Published var title : String = "" @Published var description : String = "" - @Published var files : Set = [] @Published var server : Server? @Published var status : ReportStatus? @Published var apiID : String? @@ -31,17 +30,12 @@ class DraftMainViewModel: ObservableObject { @Published var reportIsValid : Bool = false @Published var reportIsDraft : Bool = false - @Published var resultFile : [VaultFileDB]? - - @Published var showingImagePicker : Bool = false - @Published var showingImportDocumentPicker : Bool = false - @Published var showingFileList : Bool = false - @Published var showingRecordView : Bool = false - @Published var showingCamera : Bool = false - @Published var successSavingReport : Bool = false @Published var failureSavingReport : Bool = false + //MARK: -AddFilesViewModel + @Published var addFilesViewModel: AddFilesViewModel + var successSavingReportPublisher: Published.Publisher { $successSavingReport } var failureSavingReportPublisher: Published.Publisher { $failureSavingReport } @@ -64,27 +58,13 @@ class DraftMainViewModel: ObservableObject { return reportId == nil } - var addFileToDraftItems : [ListActionSheetItem] { return [ - - ListActionSheetItem(imageName: "report.camera-filled", - content: LocalizableReport.cameraFilled.localized, - type: ManageFileType.camera), - ListActionSheetItem(imageName: "report.mic-filled", - content: LocalizableReport.micFilled.localized, - type: ManageFileType.recorder), - ListActionSheetItem(imageName: "report.gallery", - content: LocalizableReport.galleryFilled.localized, - type: ManageFileType.tellaFile), - ListActionSheetItem(imageName: "report.phone", - content: LocalizableReport.phoneFilled.localized, - type: ManageFileType.fromDevice) - ]} - + init(reportId:Int? = nil, reportsMainViewModel: ReportsMainViewModel) { self.mainAppModel = reportsMainViewModel.mainAppModel self.reportsMainViewModel = reportsMainViewModel - + self.addFilesViewModel = AddFilesViewModel(mainAppModel: reportsMainViewModel.mainAppModel) + self.validateReport() self.getServers() @@ -97,7 +77,7 @@ class DraftMainViewModel: ObservableObject { } func validateReport() { - Publishers.CombineLatest4($server,$isValidTitle, $isValidDescription, $files) + Publishers.CombineLatest4($server,$isValidTitle, $isValidDescription, addFilesViewModel.$files) .map { server, isValidTitle, isValidDescription, files in ((server != nil) && isValidTitle && (isValidDescription || !files.isEmpty)) } diff --git a/Tella/Scenes/CommonConnectionReport/ViewModels/OutboxMainViewModel.swift b/Tella/Scenes/CommonConnectionReport/ViewModels/OutboxMainViewModel.swift index 6c97d6d87..a6a781183 100644 --- a/Tella/Scenes/CommonConnectionReport/ViewModels/OutboxMainViewModel.swift +++ b/Tella/Scenes/CommonConnectionReport/ViewModels/OutboxMainViewModel.swift @@ -36,7 +36,6 @@ class OutboxMainViewModel: ObservableObject { @Published var shouldShowLoginView : Bool = false var subscribers = Set() - var filesToUpload : [FileToUpload] = [] var uploadButtonTitle: String { @@ -118,7 +117,7 @@ class OutboxMainViewModel: ObservableObject { self.uploadedFiles = "\(filesCount), \(fileUploaded)" - self.progressFileItems = self.reportViewModel.files.compactMap{ProgressFileItemViewModel(file: $0, progression: ($0.bytesSent.getFormattedFileSize()) + "/" + ($0.size.getFormattedFileSize()))} + self.progressFileItems = self.reportViewModel.files.compactMap{ProgressFileItemViewModel(vaultFile: $0, transferSummary: ($0.bytesSent.getFormattedFileSize()) + "/" + ($0.size.getFormattedFileSize()))} self.objectWillChange.send() } @@ -235,12 +234,12 @@ class OutboxMainViewModel: ObservableObject { self.uploadedFiles = " \(self.reportViewModel.files.count) files, \(formattedTotalUploaded)/\(formattedTotalSize) uploaded" } //Progress File Item - if let currentItem = self.progressFileItems.first(where: {$0.file.id == uploadProgressInfo.fileId}) { + if let currentItem = self.progressFileItems.first(where: {$0.vaultFile.id == uploadProgressInfo.fileId}) { - let size = currentItem.file.size.getFormattedFileSize() + let size = currentItem.vaultFile.size.getFormattedFileSize() let currentFileTotalBytesSent = currentFileTotalBytesSent.getFormattedFileSize().getFileSizeWithoutUnit() - currentItem.progression = "\(currentFileTotalBytesSent)/\(size )" + currentItem.transferSummary = "\(currentFileTotalBytesSent)/\(size )" } publishUpdates() } diff --git a/Tella/Scenes/CommonConnectionReport/ViewModels/ProgressFileItemViewModel.swift b/Tella/Scenes/CommonConnectionReport/ViewModels/ProgressFileItemViewModel.swift index d1c961751..086b33b19 100644 --- a/Tella/Scenes/CommonConnectionReport/ViewModels/ProgressFileItemViewModel.swift +++ b/Tella/Scenes/CommonConnectionReport/ViewModels/ProgressFileItemViewModel.swift @@ -6,14 +6,34 @@ import Foundation -class ProgressFileItemViewModel { - var file : VaultFileDB - @Published var progression : String +class ProgressFileItemViewModel: ObservableObject { - init(file: VaultFileDB, progression: String) { - self.file = file - self.progression = progression + let vaultFile: VaultFileDB + + @Published var transferSummary: String + @Published var transferProgress: Double? + @Published var fileStatus: NearbySharingFileStatus? + + init(vaultFile: VaultFileDB, + transferSummary: String, + transferProgress: Double? = nil, + fileStatus: NearbySharingFileStatus? = nil) { + self.vaultFile = vaultFile + self.transferSummary = transferSummary + self.transferProgress = transferProgress + self.fileStatus = fileStatus } } - +extension NearbySharingFileStatus { + var statusIcon: String? { + switch self { + case .saving: + return "home.progress-circle" + case .saved: + return "report.submitted" + default: + return nil + } + } +} diff --git a/Tella/Scenes/CommonConnectionReport/ViewModels/SubmittedMainViewModel.swift b/Tella/Scenes/CommonConnectionReport/ViewModels/SubmittedMainViewModel.swift index 1124cf9f9..feba52f86 100644 --- a/Tella/Scenes/CommonConnectionReport/ViewModels/SubmittedMainViewModel.swift +++ b/Tella/Scenes/CommonConnectionReport/ViewModels/SubmittedMainViewModel.swift @@ -10,6 +10,7 @@ import Foundation + class SubmittedMainViewModel: ObservableObject { var mainAppModel : MainAppModel @@ -60,7 +61,7 @@ class SubmittedMainViewModel: ObservableObject { self.files = Array(vaultFileResult) // todo -> progress bar - progressFileItems = self.files.compactMap{ProgressFileItemViewModel(file: $0, progression:$0.size.getFormattedFileSize() + "/" + $0.size.getFormattedFileSize())} + progressFileItems = self.files.compactMap{ProgressFileItemViewModel(vaultFile: $0, transferSummary:$0.size.getFormattedFileSize() + "/" + $0.size.getFormattedFileSize())} let totalSize = self.files.reduce(0) { $0 + $1.size } if let date = report.createdDate { diff --git a/Tella/Scenes/CommonConnectionReport/Views/Draft/AddFilesToDraftView.swift b/Tella/Scenes/CommonConnectionReport/Views/Draft/AddFilesToDraftView.swift deleted file mode 100644 index abb7e5084..000000000 --- a/Tella/Scenes/CommonConnectionReport/Views/Draft/AddFilesToDraftView.swift +++ /dev/null @@ -1,109 +0,0 @@ -// -// AddFilesToDraftView.swift -// Tella -// -// Created by gus valbuena on 6/24/24. -// Copyright © 2024 HORIZONTAL. -// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) -// - - -import SwiftUI - -struct AddFilesToDraftView: View { - - @EnvironmentObject var appModel: MainAppModel - @EnvironmentObject var sheetManager: SheetManager - @StateObject var draftReportVM: DraftMainViewModel - - private let gridLayout: [GridItem] = [GridItem(spacing: 12), - GridItem(spacing: 12), - GridItem(spacing: 12)] - - private let gridItemHeight = (UIScreen.screenWidth - 64.0) / 3 - - var body: some View { - VStack(alignment: .leading, spacing: 12) { - - attachFilesTextView - - itemsGridView - - addButtonView - } - } - - var attachFilesTextView: some View { - Text(LocalizableReport.attachFiles.localized) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(.white) - .multilineTextAlignment(.leading) - } - - var itemsGridView: some View { - LazyVGrid(columns: gridLayout, alignment: .center, spacing: 12) { - ForEach(draftReportVM.files.sorted{$0.created < $1.created}, id: \.id) { file in - ReportFileGridView(file: file) - .frame(height: (UIScreen.screenWidth - 64) / 3 ) - .environmentObject(draftReportVM) - } - } - } - - var addButtonView: some View { - Button { - UIApplication.shared.endEditing() - showAddFileSheet() - } label: { - Image("reports.add") - } - } - - var fileListView : some View { - FileListView(appModel: appModel, - filterType: .audioPhotoVideo, - title: LocalizableReport.selectFiles.localized, - fileListType: .selectFiles, - resultFile: $draftReportVM.resultFile) - } - - func showAddFileSheet() { - - sheetManager.showBottomSheet( modalHeight: CGFloat(draftReportVM.addFileToDraftItems.count * 50 + 90), content: { - ActionListBottomSheet(items: draftReportVM.addFileToDraftItems, - headerTitle: LocalizableVault.manageFilesSheetTitle.localized, - action: {item in - self.handleActions(item : item) - }) - }) - } - - func showAddPhotoVideoSheet() { - draftReportVM.showingImagePicker = true - } - - private func handleActions(item: ListActionSheetItem) { - - guard let type = item.type as? ManageFileType else { return } - - switch type { - - case .camera: - draftReportVM.showingCamera = true - - case .recorder: - draftReportVM.showingRecordView = true - - case .fromDevice: - showAddPhotoVideoSheet() - - case .tellaFile: - navigateTo(destination: fileListView) - - default: - break - } - - sheetManager.hide() - } -} diff --git a/Tella/Scenes/CommonConnectionReport/Views/Draft/DraftView.swift b/Tella/Scenes/CommonConnectionReport/Views/Draft/DraftView.swift index ab4925181..17b6e1f98 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/Draft/DraftView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/Draft/DraftView.swift @@ -31,7 +31,7 @@ struct DraftView: View { } serverListMenuView - photoVideoPickerView + AddFilePhotoVideoPickerView(viewModel: viewModel.addFilesViewModel) } .onReceive(viewModel.successSavingReportPublisher) { successSavingReport in @@ -44,8 +44,8 @@ struct DraftView: View { handleReportFailure() } } - .overlay(recordView) - .overlay(cameraView) + .overlay(AddFileRecordView(viewModel: viewModel.addFilesViewModel)) + .overlay(AddFileCameraView(viewModel: viewModel.addFilesViewModel, sourceView: SourceView.addReportFile)) } var navigationBarView: some View { @@ -123,7 +123,7 @@ struct DraftView: View { Spacer() .frame(height: 24) - AddFilesToDraftView(draftReportVM: viewModel) + AddFileGridView(viewModel: viewModel.addFilesViewModel, titleText: LocalizableReport.attachFiles.localized) Spacer() }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) @@ -185,7 +185,7 @@ struct DraftView: View { } var submitButton: some View { - TellaButtonView( + TellaButtonView( title: LocalizableReport.reportsSubmit.localized, nextButtonAction: .action, buttonType: .yellow, @@ -195,29 +195,6 @@ struct DraftView: View { }.padding(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8)) } - var photoVideoPickerView: some View { - PhotoVideoPickerView(showingImagePicker: $viewModel.showingImagePicker, - showingImportDocumentPicker: $viewModel.showingImportDocumentPicker, - appModel: viewModel.mainAppModel, - resultFile: $viewModel.resultFile) - } - - var recordView: some View { - viewModel.showingRecordView ? - RecordView(appModel: viewModel.mainAppModel, - sourceView: .addReportFile, - showingRecoredrView: $viewModel.showingRecordView, - resultFile: $viewModel.resultFile) : nil - } - - var cameraView: some View { - viewModel.showingCamera ? - CameraView(sourceView: SourceView.addReportFile, - showingCameraView: $viewModel.showingCamera, - resultFile: $viewModel.resultFile, - mainAppModel: viewModel.mainAppModel) : nil - } - private func backAction() { UIApplication.shared.endEditing() showSaveReportConfirmationView() diff --git a/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsItemView.swift b/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsItemView.swift index 6eccc670c..89c54b110 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsItemView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsItemView.swift @@ -8,7 +8,7 @@ import SwiftUI struct OutboxDetailsItemView: View { - @Binding var item : ProgressFileItemViewModel + @ObservedObject var item : ProgressFileItemViewModel var body: some View { @@ -17,36 +17,41 @@ struct OutboxDetailsItemView: View { .fill(Color.white.opacity(0.2)) .frame(width: 35, height: 35, alignment: .center) .overlay( - item.file.listImage + item.vaultFile.listImage .frame(width: 35, height: 35) .cornerRadius(5) ) VStack(alignment: .leading, spacing: 0){ Spacer() - Text(item.file.name) - .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) - .foregroundColor(Color.white) - .lineLimit(1) + CustomText(item.vaultFile.name, + style: .subheading1Style) + .lineLimit(1) + Spacer() .frame(height: 2) - Text(item.progression) - .font(.custom(Styles.Fonts.regularFontName, size: 10)) - .foregroundColor(Color.white) - + CustomText(item.transferSummary, + style: .body3Style) + Spacer() } .padding(EdgeInsets(top: 0, leading: 18, bottom: 0, trailing: 40)) Spacer() - } } + + if let fileStatus = item.fileStatus, + let statusIcon = fileStatus.statusIcon { + Image(statusIcon) + } + } + } } struct ReportDetailsItemView_Previews: PreviewProvider { static var previews: some View { - OutboxDetailsItemView(item: .constant(ProgressFileItemViewModel(file: VaultFileDB.stub(), progression: "0/4.5 MB") )) + OutboxDetailsItemView(item: ProgressFileItemViewModel(vaultFile: VaultFileDB.stub(), transferSummary: "0/4.5 MB") ) } } diff --git a/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsView.swift b/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsView.swift index db3e83ecf..2f87cb2ba 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/Outbox/OutboxDetailsView.swift @@ -82,10 +82,9 @@ struct OutboxDetailsView: View { private var buttonView :some View { VStack { Spacer() - TellaButtonView (title: outboxReportVM.uploadButtonTitle, + TellaButtonView(title: outboxReportVM.uploadButtonTitle, nextButtonAction: .action, buttonType: .yellow, - destination: nil, isValid: .constant(true)) { outboxReportVM.isSubmissionInProgress ? outboxReportVM.pauseSubmission() : outboxReportVM.submitReport() @@ -157,7 +156,7 @@ struct OutboxDetailsView: View { private var itemsListView: some View { LazyVStack(spacing: 1) { - ForEach($outboxReportVM.progressFileItems, id: \.file.id) { file in + ForEach(outboxReportVM.progressFileItems, id: \.vaultFile.id) { file in OutboxDetailsItemView(item: file) .frame(height: 60) } diff --git a/Tella/Scenes/CommonConnectionReport/Views/ReportMainView.swift b/Tella/Scenes/CommonConnectionReport/Views/ReportMainView.swift index 8674bbf87..baa8b2466 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/ReportMainView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/ReportMainView.swift @@ -80,7 +80,7 @@ struct ReportMainView: View { Spacer() } - TellaButtonView (title: LocalizableReport.reportsCreateNew.localized, + TellaButtonView(title: LocalizableReport.reportsCreateNew.localized, nextButtonAction: .action, buttonType: .yellow, isValid: .constant(true)) { diff --git a/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsItemView.swift b/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsItemView.swift index 8a4e54514..e5474e413 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsItemView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsItemView.swift @@ -18,13 +18,13 @@ struct SubmittedDetailsItemView: View { .fill(Color.white.opacity(0.2)) .frame(width: 35, height: 35, alignment: .center) .overlay( - item.file.listImage + item.vaultFile.listImage .frame(width: 35, height: 35) .cornerRadius(5) ) VStack(alignment: .leading, spacing: 0){ Spacer() - Text(item.file.name) + Text(item.vaultFile.name) .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) .foregroundColor(Color.white) .lineLimit(1) @@ -32,7 +32,7 @@ struct SubmittedDetailsItemView: View { Spacer() .frame(height: 2) - Text(item.progression) + Text(item.transferSummary) .font(.custom(Styles.Fonts.regularFontName, size: 10)) .foregroundColor(Color.white) @@ -51,6 +51,6 @@ struct SubmittedDetailsItemView: View { struct SubmittedDetailsItemView_Previews: PreviewProvider { static var previews: some View { - OutboxDetailsItemView(item: .constant(ProgressFileItemViewModel(file: VaultFileDB.stub(), progression: "4.5/4.5 MB") )) + OutboxDetailsItemView(item: ProgressFileItemViewModel(vaultFile: VaultFileDB.stub(), transferSummary: "4.5/4.5 MB") ) } } diff --git a/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsView.swift b/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsView.swift index 9063fcfa1..94f8038b6 100644 --- a/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsView.swift +++ b/Tella/Scenes/CommonConnectionReport/Views/Submitted/SubmittedDetailsView.swift @@ -33,11 +33,6 @@ struct SubmittedDetailsView: View { Toast.displayToast(message: submittedReportVM.toastMessage) } } - .onReceive(submittedReportVM.$shouldShowMainView, perform: { value in - if value { - dismissViews() - } - }) } var navigationBarView: some View { @@ -115,7 +110,7 @@ struct SubmittedDetailsView: View { private var itemsListView: some View { LazyVStack(spacing: 1) { - ForEach($submittedReportVM.progressFileItems, id: \.file.id) { file in + ForEach($submittedReportVM.progressFileItems, id: \.vaultFile.id) { file in SubmittedDetailsItemView(item: file) .frame(height: 60) } diff --git a/Tella/Scenes/Dropbox/ViewModel/DropboxDraftViewModel.swift b/Tella/Scenes/Dropbox/ViewModel/DropboxDraftViewModel.swift index f0b920732..05594604d 100644 --- a/Tella/Scenes/Dropbox/ViewModel/DropboxDraftViewModel.swift +++ b/Tella/Scenes/Dropbox/ViewModel/DropboxDraftViewModel.swift @@ -32,7 +32,7 @@ class DropboxDraftViewModel: DraftMainViewModel { self.description = report.description ?? "" if let vaultFileResult = mainAppModel.vaultFilesManager?.getVaultFiles(ids: report.reportFiles?.compactMap{ $0.fileId } ?? []) { - self.files = Set(vaultFileResult) + addFilesViewModel.files = Set(vaultFileResult) } validateTitleAndDescription() @@ -45,7 +45,7 @@ class DropboxDraftViewModel: DraftMainViewModel { description: description, status: status ?? .unknown, server: server as? DropboxServer, - vaultFiles: self.files.compactMap { DropboxReportFile( fileId: $0.id, + vaultFiles: addFilesViewModel.files.compactMap { DropboxReportFile( fileId: $0.id, status: .notSubmitted, bytesSent: 0, createdDate: Date(), @@ -81,16 +81,4 @@ class DropboxDraftViewModel: DraftMainViewModel { private func getServer() { self.server = mainAppModel.tellaData?.getDropboxServers().first } - - override func deleteFile(fileId: String?) { - guard let index = files.firstIndex(where: { $0.id == fileId}) else {return } - files.remove(at: index) - } - - override func bindVaultFileTaken() { - $resultFile.sink(receiveValue: { value in - guard let value else { return } - self.files.insert(value) - }).store(in: &subscribers) - } } diff --git a/Tella/Scenes/GDrive/ViewModel/GDriveDraftViewModel.swift b/Tella/Scenes/GDrive/ViewModel/GDriveDraftViewModel.swift index a7fe82098..aeaba54ba 100644 --- a/Tella/Scenes/GDrive/ViewModel/GDriveDraftViewModel.swift +++ b/Tella/Scenes/GDrive/ViewModel/GDriveDraftViewModel.swift @@ -28,7 +28,7 @@ class GDriveDraftViewModel: DraftMainViewModel { self.description = report.description ?? "" if let vaultFileResult = mainAppModel.vaultFilesManager?.getVaultFiles(ids: report.reportFiles?.compactMap{ $0.fileId } ?? []) { - self.files = Set(vaultFileResult) + addFilesViewModel.files = Set(vaultFileResult) } } @@ -43,7 +43,7 @@ class GDriveDraftViewModel: DraftMainViewModel { status: status ?? .unknown, server: server as? GDriveServer, folderId: nil, - vaultFiles: self.files.compactMap { ReportFile( fileId: $0.id, + vaultFiles: addFilesViewModel.files.compactMap { ReportFile( fileId: $0.id, status: .notSubmitted, bytesSent: 0, createdDate: Date(), @@ -78,16 +78,4 @@ class GDriveDraftViewModel: DraftMainViewModel { private func getServer() { self.server = mainAppModel.tellaData?.getDriveServers().first } - - override func deleteFile(fileId: String?) { - guard let index = files.firstIndex(where: { $0.id == fileId}) else {return } - files.remove(at: index) - } - - override func bindVaultFileTaken() { - $resultFile.sink(receiveValue: { value in - guard let value else { return } - self.files.insert(value) - }).store(in: &subscribers) - } } diff --git a/Tella/Scenes/HomeView/ViewModel/FileListViewModel.swift b/Tella/Scenes/HomeView/ViewModel/FileListViewModel.swift index 3c0e55754..0e690e991 100644 --- a/Tella/Scenes/HomeView/ViewModel/FileListViewModel.swift +++ b/Tella/Scenes/HomeView/ViewModel/FileListViewModel.swift @@ -22,6 +22,7 @@ enum FileListType { case recordList case fileList case selectFiles + case nearbySharing } class FileListViewModel: ObservableObject { @@ -136,7 +137,7 @@ class FileListViewModel: ObservableObject { } var shouldHideAddFileButton: Bool { - return fileListType == .cameraGallery || fileListType == .recordList || fileListType == .selectFiles + return fileListType == .cameraGallery || fileListType == .recordList || fileListType == .selectFiles || fileListType == .nearbySharing } var shouldShowSelectButton: Bool { @@ -314,7 +315,7 @@ class FileListViewModel: ObservableObject { viewType = .list case .selectFiles: selectingFiles = true - case .fileList: + default: break } } diff --git a/Tella/Scenes/HomeView/Views/EditImageView/EditImageViewModel.swift b/Tella/Scenes/HomeView/Views/EditImageView/EditImageViewModel.swift index 0b743392b..1eb8291ac 100644 --- a/Tella/Scenes/HomeView/Views/EditImageView/EditImageViewModel.swift +++ b/Tella/Scenes/HomeView/Views/EditImageView/EditImageViewModel.swift @@ -28,7 +28,7 @@ class EditImageViewModel: ObservableObject { self.currenFile = fileListViewModel.currentSelectedVaultFile } - func getEditedImageData(_ image: UIImage) -> Data?{ + func getEditedImageData(_ image: UIImage) -> Data? { switch self.currenFile?.fileExtension.uppercased() { case FileExtension.heic.rawValue.uppercased(): return image.heic diff --git a/Tella/Scenes/HomeView/Views/FileListView/Add files/AddFileYellowButton.swift b/Tella/Scenes/HomeView/Views/FileListView/Add files/AddFileYellowButton.swift index 7975b2a5d..95365fe64 100644 --- a/Tella/Scenes/HomeView/Views/FileListView/Add files/AddFileYellowButton.swift +++ b/Tella/Scenes/HomeView/Views/FileListView/Add files/AddFileYellowButton.swift @@ -17,7 +17,7 @@ struct AddFileYellowButton: View { Button(action: { self.action() }) { - Image("home.add") + Image("file.add") } } }.padding(EdgeInsets(top: 0, leading: 0, bottom: 16, trailing: 16)) diff --git a/Tella/Scenes/HomeView/Views/FileListView/Add files/LimitedAccessPhotoView/LimitedAccessPhotoView.swift b/Tella/Scenes/HomeView/Views/FileListView/Add files/LimitedAccessPhotoView/LimitedAccessPhotoView.swift index 73ece05dd..97e1d3460 100644 --- a/Tella/Scenes/HomeView/Views/FileListView/Add files/LimitedAccessPhotoView/LimitedAccessPhotoView.swift +++ b/Tella/Scenes/HomeView/Views/FileListView/Add files/LimitedAccessPhotoView/LimitedAccessPhotoView.swift @@ -64,7 +64,7 @@ struct LimitedAccessPhotoView: View { DividerView() - TellaButtonView (title: LocalizableVault.limitedPhotoLibraryImport.localized, + TellaButtonView(title: LocalizableVault.limitedPhotoLibraryImport.localized, nextButtonAction: .action, buttonType: .clear, isValid: $limitedAccessPhotoViewModel.shouldEnableButton) { diff --git a/Tella/Scenes/HomeView/Views/FileListView/File list/FileListView.swift b/Tella/Scenes/HomeView/Views/FileListView/File list/FileListView.swift index c07913714..72772be8c 100644 --- a/Tella/Scenes/HomeView/Views/FileListView/File list/FileListView.swift +++ b/Tella/Scenes/HomeView/Views/FileListView/File list/FileListView.swift @@ -1,5 +1,5 @@ // -// Copyright © 2021 HORIZONTAL. +// Copyright © 2021 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -92,11 +92,22 @@ struct FileListView: View { if !fileListViewModel.shouldHideNavigationBar { NavigationHeaderView(title: title, + backButtonAction: {backButtonAction()}, rightButtonType: fileListViewModel.shouldShowSelectButton ? .validate : .none, rightButtonAction: { attachFiles()}) } } + private func backButtonAction() { + + switch fileListViewModel.fileListType { + case .nearbySharing: + popToRoot() + default: + self.presentationMode.wrappedValue.dismiss() + } + } + func attachFiles() { fileListViewModel.attachFiles() presentationMode.wrappedValue.dismiss() diff --git a/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift b/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift index 0e3b1e7ab..1a7b1cfa6 100644 --- a/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift +++ b/Tella/Scenes/HomeView/Views/Home/Connections/ConnectionsView.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -14,16 +14,14 @@ struct ConnectionsView: View { var body: some View { - if homeViewModel.serverDataItemArray.count > 0 { - VStack(alignment: .leading, spacing: 16) { - Text("Connections") - .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) - .foregroundColor(.white) - - serversView - } - .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0)) + VStack(alignment: .leading, spacing: 16) { + Text("Connections") + .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) + .foregroundColor(.white) + + serversView } + .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0)) } var serversView: some View { @@ -34,7 +32,13 @@ struct ConnectionsView: View { @ViewBuilder var serverItems : some View { - HStack(spacing: 7) { + HStack(spacing: 12) { + + if homeViewModel.appModel.settings.nearbySharing { + ConnectionsItemView(title: LocalizableNearbySharing.nearbySharingAppBar.localized, + image: "nearby-sharing.home", + destination: nearbySharingMainView) + } ForEach(homeViewModel.serverDataItemArray, id: \.self) { server in switch server.serverType { @@ -68,6 +72,9 @@ struct ConnectionsView: View { destination: dropboxMainView) } } + + addButton + Spacer() }.padding(.trailing, 17) } @@ -85,7 +92,23 @@ struct ConnectionsView: View { } var dropboxMainView: DropboxReportMainView { - DropboxReportMainView(reportsMainViewModel: DropboxViewModel(mainAppModel: homeViewModel.appModel, dropboxRepository: DropboxRepository())) + DropboxReportMainView(reportsMainViewModel: DropboxViewModel(mainAppModel: homeViewModel.appModel, + dropboxRepository: DropboxRepository())) + } + + var nearbySharingMainView: NearbySharingMainView { + NearbySharingMainView(mainAppModel: homeViewModel.appModel) + } + + var addButton: some View { + Button { + // Show Connections View() + let viewModel = ServersViewModel(mainAppModel: homeViewModel.appModel) + navigateTo(destination: ServersListView(serversViewModel: viewModel)) + } label: { + Image("home.add") + .frame(width: 92,height: 92) + } } } diff --git a/Tella/Scenes/NearbySharing/ConnectToDeviceView.swift b/Tella/Scenes/NearbySharing/ConnectToDeviceView.swift new file mode 100644 index 000000000..700627f00 --- /dev/null +++ b/Tella/Scenes/NearbySharing/ConnectToDeviceView.swift @@ -0,0 +1,52 @@ +// +// ConnectToDeviceView.swift +// Tella +// +// Created by RIMA on 05.02.25. +// Copyright © 2025 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct ConnectToDeviceView: View { + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + VStack { + RegularText(LocalizablePeerToPeer.scanCode.localized, size: 18) + .padding(.top, 74) + + qrCodeView.padding(.bottom, 40) + RegularText(LocalizablePeerToPeer.havingTrouble.localized) + connectManuallyButton + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + } + } + var navigationBarView: some View { + NavigationHeaderView(title: LocalizablePeerToPeer.connectToDevice.localized, + navigationBarType: .inline, + backButtonAction: {self.popToRoot()}, + rightButtonType: .none) + } + var qrCodeView: some View { + ResizableImage("qrCode.icon").frame(width: 248, height: 248) + //TODO: Add a QRCode reader + } + + var connectManuallyButton: some View { + TellaButtonView(title: LocalizablePeerToPeer.connectManually.localized.uppercased(), + nextButtonAction: .destination, + destination: TellaWebServerLoginView(), + isValid: .constant(true), + buttonRole: .secondary) + .padding([.leading, .trailing], 80) + } +} + +#Preview { + ConnectToDeviceView() +} diff --git a/Tella/Scenes/NearbySharing/FileTransfer/FileTransferVM.swift b/Tella/Scenes/NearbySharing/FileTransfer/FileTransferVM.swift new file mode 100644 index 000000000..47c985b65 --- /dev/null +++ b/Tella/Scenes/NearbySharing/FileTransfer/FileTransferVM.swift @@ -0,0 +1,142 @@ +// +// FileTransferVM.swift +// Tella +// +// Created by Dhekra Rouatbi on 7/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine + +enum TransferViewAction: Equatable { + case none + case shouldShowResults +} + +class FileTransferVM: ObservableObject { + + var mainAppModel: MainAppModel + + @Published var progressViewModel : ProgressViewModel? + @Published var isLoading: Bool = false + @Published var viewAction: TransferViewAction = .none + + var title: String + var bottomSheetTitle: String + var bottomSheetMessage: String + + var transferredFiles: [NearbySharingTransferredFile] = [] + + init(mainAppModel: MainAppModel, + title: String, + bottomSheetTitle: String, + bottomSheetMessage: String) { + + self.mainAppModel = mainAppModel + self.title = title + self.bottomSheetTitle = bottomSheetTitle + self.bottomSheetMessage = bottomSheetMessage + self.bottomSheetMessage = bottomSheetMessage + } + + func initProgress(session: NearbySharingSession) { + let title = session.title ?? "" + let totalSize = transferredFiles.reduce(0) { $0 + ($1.file.size ?? 0) } + let progressItems = transferredFiles.map(makeProgressItem) + + progressViewModel = ProgressViewModel( + title: title, + percentTransferredText: formatPercentage(0), + transferredFilesSummary: makeTransferredSummary(receivedBytes: 0, totalBytes: totalSize), + percentTransferred: 0, + progressFileItems: progressItems + ) + } + + func updateProgress(with file: NearbySharingTransferredFile) { + Task { + let totalBytesReceived = transferredFiles.reduce(0) { $0 + $1.bytesReceived } + let totalBytes = transferredFiles.reduce(0) { $0 + ($1.file.size ?? 0) } + + guard totalBytes > 0 else { return } + + let ratio = Double(totalBytesReceived) / Double(totalBytes) + guard ratio <= 1 else { return } + + let percentText = formatPercentage(Int(ratio * 100)) + let summaryText = makeTransferredSummary( + receivedBytes: totalBytesReceived, + totalBytes: totalBytes + ) + + let itemToUpdate = progressViewModel?.progressFileItems.first(where: { + $0.vaultFile.id == file.vaultFile.id + }) + + let receivedFormatted = file.bytesReceived.getFormattedFileSize().getFileSizeWithoutUnit() + let totalFormatted = (file.file.size ?? 0).getFormattedFileSize() + let newTransferSummary = "\(receivedFormatted)/\(totalFormatted)" + + await MainActor.run { [weak self] in + guard let self = self else { return } + + self.progressViewModel?.percentTransferred = ratio + self.progressViewModel?.percentTransferredText = percentText + self.progressViewModel?.transferredFilesSummary = summaryText + + if let item = itemToUpdate { + item.transferSummary = newTransferSummary + item.fileStatus = file.status + } + } + } + } + func updateStatus(with file: NearbySharingTransferredFile) { + Task { + let itemToUpdate = progressViewModel?.progressFileItems.first(where: { + $0.vaultFile.id == file.vaultFile.id + }) + + if let item = itemToUpdate { + await MainActor.run { + item.fileStatus = file.status + } + } + } + } + + // MARK: - Helpers + + func makeTransferredSummary(receivedBytes: Int, totalBytes: Int) -> String { + return "" + } + + private func makeProgressItem(from file: NearbySharingTransferredFile) -> ProgressFileItemViewModel { + let size = (file.file.size ?? 0).getFormattedFileSize() + let summary = "0/\(size)" + return ProgressFileItemViewModel( + vaultFile: file.vaultFile, + transferSummary: summary, + transferProgress: 0, + fileStatus: file.status + ) + } + + func formatPercentage(_ percent: Int) -> String { + return "" + } + + func stopTask() { + + } +} + +extension FileTransferVM { + static func stub() -> FileTransferVM { + return FileTransferVM(mainAppModel: MainAppModel.stub(), title: "Title", bottomSheetTitle: "Title", bottomSheetMessage: "Message") + } +} + + + diff --git a/Tella/Scenes/NearbySharing/FileTransfer/FileTransferView.swift b/Tella/Scenes/NearbySharing/FileTransfer/FileTransferView.swift new file mode 100644 index 000000000..991051ec0 --- /dev/null +++ b/Tella/Scenes/NearbySharing/FileTransfer/FileTransferView.swift @@ -0,0 +1,92 @@ +// +// FileTransferView.swift +// Tella +// +// Created by Dhekra Rouatbi on 15/5/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct FileTransferView: View { + + @ObservedObject var viewModel: FileTransferVM + + @State private var isBottomSheetShown : Bool = false + private let delayTimeInSecond = 0.1 + + var body: some View { + + ZStack { + + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + + if viewModel.isLoading { + CircularActivityIndicatory() + } + } + } + + var contentView : some View { + ZStack { + if let progressViewModel = viewModel.progressViewModel { + TransferProgressView(viewModel: progressViewModel) + } + + if viewModel.viewAction == .none { + buttonView + } + } + } + + var navigationBarView: some View { + NavigationHeaderView(title: viewModel.title, + backButtonAction: {handleBackAction()}) + } + + private var buttonView :some View { + VStack { + Spacer() + + HStack { + Spacer() + TellaButtonView(title: LocalizableNearbySharing.cancel.localized.uppercased(), + nextButtonAction: .action, + isValid: .constant(true), + buttonRole: .secondary) { + showCancelUploadConfirmationView() + }.frame(width: 132) + .padding(.trailing, 22) + } + } + } + + private func handleBackAction() { + self.showCancelUploadConfirmationView() + } + + private func showCancelUploadConfirmationView() { + isBottomSheetShown = true + let content = ConfirmBottomSheet(titleText: viewModel.bottomSheetTitle, + msgText: viewModel.bottomSheetMessage, + cancelText: LocalizableNearbySharing.continueSharing.localized.uppercased(), + actionText:LocalizableNearbySharing.stopSharing.localized.uppercased(), + shouldHideSheet: false, + didConfirmAction: { + self.dismiss() + viewModel.stopTask() + }, didCancelAction: { + self.dismiss() + }) + self.showBottomSheetView(content: content, modalHeight: 192, isShown: $isBottomSheetShown) + } +} + +#Preview { + FileTransferView(viewModel: SenderFileTransferVM.stub()) +} diff --git a/Tella/Scenes/NearbySharing/FileTransfer/ProgressViewModel.swift b/Tella/Scenes/NearbySharing/FileTransfer/ProgressViewModel.swift new file mode 100644 index 000000000..e9fa0d9b9 --- /dev/null +++ b/Tella/Scenes/NearbySharing/FileTransfer/ProgressViewModel.swift @@ -0,0 +1,52 @@ +// +// ProgressViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 9/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine +import Foundation + +class ProgressViewModel: ObservableObject { + + // MARK: - Published Properties + @Published var title: String + @Published var description: String + @Published var percentTransferredText : String + @Published var transferredFilesSummary: String + @Published var percentTransferred: Double + @Published var progressFileItems: [ProgressFileItemViewModel] + + // MARK: - Initializer + init( + title: String = "", + description: String = "", + percentTransferredText: String = "", + transferredFilesSummary: String = "", + percentTransferred: Double = 0.0, + progressFileItems: [ProgressFileItemViewModel] = [] + ) { + self.title = title + self.description = description + self.percentTransferredText = percentTransferredText + self.transferredFilesSummary = transferredFilesSummary + self.percentTransferred = percentTransferred + self.progressFileItems = progressFileItems + } +} + +extension ProgressViewModel { + static func stub() -> ProgressViewModel { + ProgressViewModel(title: "Title", + description: "description", + percentTransferredText: "73% sent", + transferredFilesSummary: "11 files, 67/89 MB sent", + percentTransferred: 0.4, + progressFileItems:[ProgressFileItemViewModel(vaultFile: VaultFileDB.stub(), + transferSummary: "0/4.5MB")] + ) + } +} diff --git a/Tella/Scenes/NearbySharing/FileTransfer/TransferProgressView.swift b/Tella/Scenes/NearbySharing/FileTransfer/TransferProgressView.swift new file mode 100644 index 000000000..2952bf87e --- /dev/null +++ b/Tella/Scenes/NearbySharing/FileTransfer/TransferProgressView.swift @@ -0,0 +1,83 @@ +// +// FileTransfertView.swift +// Tella +// +// Created by Dhekra Rouatbi on 4/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI +import Combine + +struct TransferProgressView: View { + + @ObservedObject var viewModel : ProgressViewModel + + var body: some View { + ScrollView { + + VStack(alignment: .leading, spacing: 0) { + + reportInformations + + Spacer() + .frame(height: 16) + + itemsListView + } + }.padding(EdgeInsets(top: 20, leading: 16, bottom: 70, trailing: 16)) + } + + + private var reportInformations: some View { + Group { + + CustomText(viewModel.title, style: .subheading1Style) + + uploadProgressView + + Spacer() + .frame(height: 16) + + Divider() + .background(Color.white.opacity(0.2)) + } + } + + private var uploadProgressView : some View { + + Group { + + Spacer() + .frame(height: 8) + + CustomText(viewModel.percentTransferredText, + style: .body2ItalicStyle) + + Spacer() + .frame(height: 4) + + CustomText(viewModel.transferredFilesSummary, + style: .body2Style) + + if viewModel.percentTransferred > 0.0 { + ProgressView("", value: viewModel.percentTransferred, total: 1) + .accentColor(.green) + } + } + } + + private var itemsListView: some View { + LazyVStack(spacing: 1) { + ForEach(viewModel.progressFileItems, id: \.vaultFile.id) { file in + OutboxDetailsItemView(item: file) + .frame(height: 60) + } + } + } +} + +#Preview { + TransferProgressView(viewModel: ProgressViewModel.stub()) +} diff --git a/Tella/Scenes/NearbySharing/GetConnected/GetConnectedView.swift b/Tella/Scenes/NearbySharing/GetConnected/GetConnectedView.swift new file mode 100644 index 000000000..e2589961f --- /dev/null +++ b/Tella/Scenes/NearbySharing/GetConnected/GetConnectedView.swift @@ -0,0 +1,118 @@ +// +// WifiConnetionView.swift +// Tella +// +// Created by RIMA on 31.01.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct GetConnectedView: View { + + @StateObject var viewModel: GetConnectedViewModel + @StateObject var mainAppModel: MainAppModel + @State var isCheckboxOn = false + + struct TipsToConnectButton : IconTextButtonConfig { + var title = LocalizableNearbySharing.tipsToConnect.localized.uppercased() + var description = LocalizableNearbySharing.tipsToConnectExpl.localized + var imageName = "help.yellow" + } + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + } + + private var contentView: some View { + VStack(alignment: .center, spacing: 24) { + topView + DividerView() + sameNetworkView + Spacer() + bottomView + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding([.leading, .trailing], 20) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.getConnected.localized , + navigationBarType: .inline, + rightButtonType: .none) + } + + var lineView: some View { + Rectangle() + .frame(height: 1) + .foregroundColor(.white.opacity(0.2)) + } + + var topView: some View { + VStack(alignment: .center, spacing: 12) { + + Image("nearby-sharing.connect") + + CustomText(LocalizableNearbySharing.getConnectedSubhead.localized, + style: .heading1Style, + alignment: .center) + + CustomText(LocalizableNearbySharing.getconnectedExpl.localized, + style: .body1Style, + alignment: .leading) + .frame(maxWidth: .infinity, alignment: .leading) + + IconTextButton(buttonConfig: TipsToConnectButton(), + destination: TipsToConnectView()) + } + } + + var sameNetworkView: some View { + HStack { + CustomText(LocalizableNearbySharing.sameNetworkExpl.localized, + style: .body1Style) + Spacer() + ResizableImage(isCheckboxOn ? "checkbox.on" : "checkbox.off") + .frame(width: 24, height: 24) + .onTapGesture { + isCheckboxOn.toggle() + } + + }.cardModifier() + } + + var bottomView: some View { + NavigationBottomView(shouldActivateNext: $isCheckboxOn, + nextButtonAction: .action, + shouldHideBack: true, + nextAction: { + switch viewModel.participant { + case .sender: + let senderConnectToDeviceViewModel = SenderConnectToDeviceViewModel(nearbySharingRepository:NearbySharingRepository(), + mainAppModel: mainAppModel) + navigateTo(destination: SenderConnectToDeviceView(viewModel:senderConnectToDeviceViewModel)) + case .recipient: + let recipientConnectToDeviceViewModel = RecipientConnectToDeviceViewModel(certificateGenerator: CertificateGenerator(), + mainAppModel: mainAppModel) + navigateTo(destination: RecipientConnectToDeviceView(viewModel: recipientConnectToDeviceViewModel)) + } + }) + } + + func showTipsToConnectView() { + self.navigateTo(destination: TipsToConnectView()) + } +} + +#Preview { + GetConnectedView(viewModel: GetConnectedViewModel(participant: .recipient, + mainAppModel: MainAppModel.stub()), + mainAppModel: MainAppModel.stub()) +} + + diff --git a/Tella/Scenes/NearbySharing/GetConnected/GetConnectedViewModel.swift b/Tella/Scenes/NearbySharing/GetConnected/GetConnectedViewModel.swift new file mode 100644 index 000000000..a3464352d --- /dev/null +++ b/Tella/Scenes/NearbySharing/GetConnected/GetConnectedViewModel.swift @@ -0,0 +1,21 @@ +// +// SSIDViewModel.swift +// Tella +// +// Created by RIMA on 03.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +class GetConnectedViewModel: NSObject, ObservableObject { + + var participant: NearbySharingParticipant + private var mainAppModel:MainAppModel + + init(participant: NearbySharingParticipant, mainAppModel:MainAppModel) { + self.participant = participant + self.mainAppModel = mainAppModel + } +} diff --git a/Tella/Scenes/NearbySharing/GetConnected/TipsToConnectView.swift b/Tella/Scenes/NearbySharing/GetConnected/TipsToConnectView.swift new file mode 100644 index 000000000..1706d17fb --- /dev/null +++ b/Tella/Scenes/NearbySharing/GetConnected/TipsToConnectView.swift @@ -0,0 +1,62 @@ +// +// TipsToConnectView.swift +// Tella +// +// Created by Dhekra Rouatbi on 8/9/2025. +// Copyright © 2025 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct TipsToConnectView: View { + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + } + + private var contentView: some View { + VStack(alignment: .center, spacing: 24) { + + titleSubtitleView(title: LocalizableNearbySharing.settingUp.localized, + subtitle: LocalizableNearbySharing.settingUpExpl.localized) + + DividerView() + + titleSubtitleView(title: LocalizableNearbySharing.joining.localized, + subtitle: LocalizableNearbySharing.joiningExpl.localized) + + DividerView() + + titleSubtitleView(title: LocalizableNearbySharing.moreTips.localized, + subtitle: LocalizableNearbySharing.moreTipsExpl.localized) + + Spacer() + + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding([.leading, .trailing], 20) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.tipsToConnect.localized , + navigationBarType: .inline, + rightButtonType: .none) + } + + func titleSubtitleView(title: String, subtitle: String) -> some View { + VStack(alignment: .leading, spacing: 4) { + CustomText(title, + style: .subheading1Style) + + CustomText(subtitle, + style: .body1Style) + } + } +} + +#Preview { + TipsToConnectView() +} diff --git a/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationView.swift b/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationView.swift new file mode 100644 index 000000000..e5098fc29 --- /dev/null +++ b/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationView.swift @@ -0,0 +1,160 @@ +// +// ManuallyVerificationView.swift +// Tella +// +// Created by Dhekra Rouatbi on 15/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct ManuallyVerificationView: View { + + @ObservedObject var viewModel: ManuallyVerificationViewModel + + @State var isBottomSheetShown : Bool = false + @Environment(\.presentationMode) var presentationMode: Binding + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + .onReceive(viewModel.$senderViewAction) { action in + handleSenderViewAction(action: action) + } + .onReceive(viewModel.$recipientViewAction) { action in + handleRecipientViewAction(action: action) + } + .onAppear { viewModel.onAppear() } + .onDisappear { viewModel.onDisappear() } + } + + var contentView: some View { + VStack { + topView + infoView + Spacer() + buttonsView + }.scrollOnOverflow() + .padding([.leading, .trailing], 16) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.verificationAppBar.localized, + navigationBarType: .inline, + backButtonType: .close, + backButtonAction: {self.popTo(ViewClassType.nearbySharingMainView)}, + rightButtonType: .none) + } + + var topView: some View { + Image("device") + .padding(.bottom, 16) + } + + var infoView: some View { + participantInfoView( + text: viewModel.participant == .sender + ? LocalizableNearbySharing.verificationSender.localized + : LocalizableNearbySharing.verificationRecipient.localized + ) + } + + private func participantInfoView(text: String) -> some View { + VStack(alignment: .center, spacing: 16) { + + CustomText(viewModel.connectionInfo.certificateHash?.formatHash() ?? "", + style: .body1Style, + alignment: .center) + .frame(maxWidth: .infinity, alignment: .center) + .cardModifier() + + CustomText(text, style: .body1Style) + } + } + + var buttonsView: some View { + VStack(spacing: 17) { + confirmButton + discardButton + }.padding([.top,.bottom],16) + } + + var confirmButton: some View { + TellaButtonView(title: viewModel.confirmButtonTitle.uppercased(), + nextButtonAction: .action, + buttonType: .yellow, + isValid: $viewModel.shouldEnableConfirmButton) { + viewModel.confirmAction() + }.disabled(!viewModel.shouldEnableConfirmButton) + } + + var discardButton: some View { + TellaButtonView(title: LocalizableNearbySharing.verificationDiscard.localized.uppercased(), + nextButtonAction: .action, + isValid: .constant(true)) { + viewModel.discardAction() + } + } + + private func showBottomSheetError() { + isBottomSheetShown = true + let content = ConnectionFailedView { + isBottomSheetShown = false + popTo(ViewClassType.nearbySharingMainView) + } + self.showBottomSheetView(content: content, + modalHeight: 192, + isShown: $isBottomSheetShown, + shouldHideOnTap:false) + } + + private func handleSenderViewAction(action: SenderConnectToDeviceViewAction) { + switch action { + case .showBottomSheetError: + showBottomSheetError() + case .showSendFiles: + guard let session = viewModel.session, + let nearbySharingRepository = viewModel.nearbySharingRepository + else { + return + } + let viewModel = SenderPrepareFileTransferVM(mainAppModel: viewModel.mainAppModel, + session: session, + nearbySharingRepository:nearbySharingRepository) + self.navigateTo(destination: SenderPrepareFileTransferView(viewModel: viewModel)) + case .showToast(let message): + Toast.displayToast(message: message) + case .discardAndStartOver: + self.popTo(ViewClassType.nearbySharingMainView) + default: + break + } + } + + private func handleRecipientViewAction(action: RecipientConnectToDeviceViewAction) { + switch action { + case .showReceiveFiles: + let viewModel = RecipientPrepareFileTransferVM(mainAppModel: viewModel.mainAppModel) + self.navigateTo(destination: RecipientFileTransferView(viewModel: viewModel)) + case .errorOccured: + self.popTo(ViewClassType.nearbySharingMainView) + Toast.displayToast(message: LocalizableCommon.commonError.localized) + case .showToast(let message): + Toast.displayToast(message: message) + case .discardAndStartOver: + self.popTo(ViewClassType.nearbySharingMainView) + default: + break + } + } +} + +#Preview { + ManuallyVerificationView(viewModel: ManuallyVerificationViewModel(participant: .recipient, + connectionInfo: ConnectionInfo.stub(), + mainAppModel: MainAppModel.stub())) +} diff --git a/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationViewModel.swift b/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationViewModel.swift new file mode 100644 index 000000000..f68ccf459 --- /dev/null +++ b/Tella/Scenes/NearbySharing/ManuallyVerification/ManuallyVerificationViewModel.swift @@ -0,0 +1,140 @@ +// +// ManuallyVerificationViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 16/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +enum ManuallyVerificationState { + case initial + case waiting +} + +class ManuallyVerificationViewModel: ObservableObject { + + @Published var senderViewAction: SenderConnectToDeviceViewAction = .none + @Published var recipientViewAction: RecipientConnectToDeviceViewAction = .none + + @Published var shouldEnableConfirmButton: Bool = false + @Published var confirmButtonTitle: String = "" + + private var serverEventsCancellable: AnyCancellable? + + var participant: NearbySharingParticipant + var nearbySharingRepository: NearbySharingRepository? + var session : NearbySharingSession? + var connectionInfo: ConnectionInfo + var mainAppModel: MainAppModel + var nearbySharingServer: NearbySharingServer? + + private var subscribers = Set() + + init(participant: NearbySharingParticipant, + nearbySharingRepository: NearbySharingRepository? = nil, + connectionInfo: ConnectionInfo, + mainAppModel: MainAppModel) { + self.participant = participant + self.nearbySharingRepository = nearbySharingRepository + self.connectionInfo = connectionInfo + self.mainAppModel = mainAppModel + self.nearbySharingServer = mainAppModel.nearbySharingServer + + updateButtonsState(state: .initial) + } + + // MARK: - Observers + func onAppear() { + if serverEventsCancellable == nil { + listenToServerEvents() + } + } + + func onDisappear() { + serverEventsCancellable?.cancel() + serverEventsCancellable = nil + } + + func updateButtonsState(state: ManuallyVerificationState) { + + let result = state == .initial + + switch participant { + case .sender: + shouldEnableConfirmButton = result + confirmButtonTitle = result ? LocalizableNearbySharing.verificationConfirm.localized : LocalizableNearbySharing.verificationWaitingRecipient.localized + case .recipient: + shouldEnableConfirmButton = result + confirmButtonTitle = result ? LocalizableNearbySharing.verificationConfirm.localized: LocalizableNearbySharing.verificationWaitingSender.localized + } + } + + func confirmAction() { + participant == .recipient ? acceptRegisterRequest() : register() + } + + func discardAction() { + participant == .recipient ? discardSenderRegisterRequest() : discardRegisterRequest() + } + + private func discardSenderRegisterRequest() { + self.nearbySharingServer?.respondToRegistrationRequest(accept: false) + self.nearbySharingServer?.resetServerState() + recipientViewAction = .discardAndStartOver + } + + private func discardRegisterRequest() { + senderViewAction = .discardAndStartOver + } + + private func register() { + + let registerRequest = RegisterRequest(pin:connectionInfo.pin, nonce: UUID().uuidString ) + self.updateButtonsState(state: .waiting) + + self.nearbySharingRepository?.register(connectionInfo: connectionInfo, registerRequest: registerRequest) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { [weak self] completion in + guard let self = self else { return } + switch completion { + case .finished: + self.senderViewAction = .showSendFiles + self.senderViewAction = .showToast(message: LocalizableNearbySharing.successConnectToast.localized) + case .failure: + self.senderViewAction = .showBottomSheetError + } + }, receiveValue: { response in + if let sessionId = response.sessionId { + self.session = NearbySharingSession(sessionId: sessionId) + } + }).store(in: &self.subscribers) + } + + private func acceptRegisterRequest() { + nearbySharingServer?.respondToRegistrationRequest(accept: true) + self.updateButtonsState(state: .waiting) + } + + func listenToServerEvents() { + serverEventsCancellable = nearbySharingServer?.eventPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] event in + guard let self = self else { return } + + switch event { + case .didRegister(let success, let manual): + if manual { + self.recipientViewAction = success ? .showReceiveFiles : .errorOccured + } + case .connectionClosed: + self.recipientViewAction = .errorOccured + default: + break + } + } + } +} diff --git a/Tella/Scenes/NearbySharing/Models/ConnectionInfo.swift b/Tella/Scenes/NearbySharing/Models/ConnectionInfo.swift new file mode 100644 index 000000000..c2e2e15db --- /dev/null +++ b/Tella/Scenes/NearbySharing/Models/ConnectionInfo.swift @@ -0,0 +1,42 @@ +// +// ConnectionInfo.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/2/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation + +class ConnectionInfo : Codable, Equatable { + + var ipAddress : String + var port : Int + var certificateHash : String? + var pin : String + + enum CodingKeys: String, CodingKey { + case ipAddress = "ip_address" + case port + case certificateHash = "certificate_hash" + case pin + } + + init(ipAddress: String, port: Int, certificateHash: String?, pin: String) { + self.ipAddress = ipAddress + self.port = port + self.certificateHash = certificateHash + self.pin = pin + } + + static func == (lhs: ConnectionInfo, rhs: ConnectionInfo) -> Bool { + lhs.ipAddress == rhs.ipAddress + } +} + +extension ConnectionInfo { + static func stub() -> ConnectionInfo { + return ConnectionInfo(ipAddress: "192.1.2.6", port: 53317, certificateHash: "764357", pin: "983426") + } +} diff --git a/Tella/Scenes/NearbySharing/Models/NearbySharingParticipant.swift b/Tella/Scenes/NearbySharing/Models/NearbySharingParticipant.swift new file mode 100644 index 000000000..98b3390d1 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Models/NearbySharingParticipant.swift @@ -0,0 +1,14 @@ +// +// NearbySharingParticipant.swift +// Tella +// +// Created by RIMA on 11.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +enum NearbySharingParticipant { + case sender + case recipient +} diff --git a/Tella/Scenes/NearbySharing/NearbySharingMainView.swift b/Tella/Scenes/NearbySharing/NearbySharingMainView.swift new file mode 100644 index 000000000..724802be7 --- /dev/null +++ b/Tella/Scenes/NearbySharing/NearbySharingMainView.swift @@ -0,0 +1,95 @@ +// +// NearbySharingMainView.swift +// Tella +// +// Created by Dhekra Rouatbi on 30/1/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct NearbySharingMainView: View { + + @StateObject var mainAppModel: MainAppModel + @State var participant: NearbySharingParticipant? + + var body: some View { + + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.nearbySharingAppBar.localized) + } + + var contentView: some View { + VStack(alignment: .center, spacing: 24) { + Spacer() + headerView + nearbySharingParticipantButtons + learnMoreView + Spacer() + bottomView + } + .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)) + } + + var headerView: some View { + ServerConnectionHeaderView( + title: LocalizableNearbySharing.nearbySharingSubhead.localized, + subtitle: LocalizableNearbySharing.nearbySharingExpl.localized, + imageIconName: "nearby-sharing.main", + subtitleTextAlignment: .leading) + } + + var nearbySharingParticipantButtons: some View { + VStack(spacing: 12) { + + TellaButtonView(title: LocalizableNearbySharing.sendFiles.localized.uppercased(), + nextButtonAction: .action, + isOverlay: participant == .sender, + isValid: .constant(true), + action: { participant = .sender } + ).frame(height: 54) + TellaButtonView(title: LocalizableNearbySharing.receiveFiles.localized.uppercased(), + nextButtonAction: .action, + isOverlay: participant == .recipient, + isValid: .constant(true), + action: {participant = .recipient} + ).frame(height: 54) + } + } + + var learnMoreView: some View { + Button { + TellaUrls.nearbySharingLearnMore.url()?.open() + } label: { + CustomText(LocalizableNearbySharing.learnMore.localized, + style: .buttonDetailRegularStyle, + alignment: .center, + color: Styles.Colors.yellow) + } + } + + var bottomView: some View { + NavigationBottomView(shouldActivateNext: Binding(get: { participant != nil }, + set: { _ in }), + nextButtonAction: .action, + shouldHideBack: true, + nextAction: { + guard let participant else { return } + let wifiConnetionViewModel = GetConnectedViewModel(participant: participant, mainAppModel: mainAppModel) + navigateTo(destination: GetConnectedView(viewModel:wifiConnetionViewModel, + mainAppModel: mainAppModel)) + }) + } +} + +#Preview { + NearbySharingMainView(mainAppModel: MainAppModel.stub()) +} diff --git a/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectManuallyViewModel.swift b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectManuallyViewModel.swift new file mode 100644 index 000000000..e1c696b47 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectManuallyViewModel.swift @@ -0,0 +1,77 @@ +// +// RecipientConnectManuallyViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 20/3/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine +import UIKit + + +class RecipientConnectManuallyViewModel: ObservableObject { + + var cancellables = Set() + + @Published var ipAddress : String = "" + @Published var pin: String = "" + @Published var port: String = "" + @Published var viewState: RecipientConnectToDeviceViewAction = .none + + private var certificateGenerator : CertificateGenerator + private var serverEventsCancellable: AnyCancellable? + + var mainAppModel: MainAppModel + var nearbySharingServer: NearbySharingServer? + var connectionInfo: ConnectionInfo? + + init(certificateGenerator : CertificateGenerator, + mainAppModel:MainAppModel, + connectionInfo:ConnectionInfo?) { + + self.certificateGenerator = certificateGenerator + self.mainAppModel = mainAppModel + self.nearbySharingServer = mainAppModel.nearbySharingServer + self.connectionInfo = connectionInfo + + initParameters() + listenToServerEvents() + } + + // MARK: - Observers + func onAppear() { + if serverEventsCancellable == nil { + listenToServerEvents() + } + } + + func onDisappear() { + serverEventsCancellable?.cancel() + serverEventsCancellable = nil + } + + func initParameters() { + guard let connectionInfo else { return } + self.pin = connectionInfo.pin + self.ipAddress = connectionInfo.ipAddress + self.port = "\(connectionInfo.port)" + } + + func listenToServerEvents() { + serverEventsCancellable = nearbySharingServer?.eventPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] event in + guard let self = self else { return } + + switch event { + case .verificationRequested: + self.viewState = .showVerificationHash + default: + break + } + } + } +} diff --git a/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectToDeviceManuallyView.swift b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectToDeviceManuallyView.swift new file mode 100644 index 000000000..baf91dd89 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceManually/RecipientConnectToDeviceManuallyView.swift @@ -0,0 +1,79 @@ +// +// RecipientConnectToDeviceManuallyView.swift +// Tella +// +// Created by RIMA on 10.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct RecipientConnectToDeviceManuallyView: View { + + @StateObject var viewModel: RecipientConnectManuallyViewModel + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + + .onReceive(viewModel.$viewState) { state in + handleViewState(state: state) + } + .onAppear { viewModel.onAppear() } + .onDisappear { viewModel.onDisappear() } + } + + var contentView: some View { + VStack { + Spacer() + VStack(spacing: 24) { + topView + cardsView + } + Spacer() + } + .padding([.leading, .trailing], 16) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.connectToDevice.localized, + navigationBarType: .inline, + backButtonType: .close, + rightButtonType: .none) + } + + var topView: some View { + ServerConnectionHeaderView( + title: LocalizableNearbySharing.showDeviceInformation.localized, + subtitle: LocalizableNearbySharing.sendInputDesc.localized, + imageIconName: "device", + subtitleTextAlignment: .center) + } + + var cardsView: some View { + VStack(spacing: 8) { + CardItemView(title: LocalizableNearbySharing.ipAddress.localized, subtitle: viewModel.ipAddress) + CardItemView(title: LocalizableNearbySharing.pin.localized, subtitle: viewModel.pin) + CardItemView(title: LocalizableNearbySharing.port.localized, subtitle: viewModel.port) + } + } + + private func handleViewState(state: RecipientConnectToDeviceViewAction) { + switch state { + case .showVerificationHash: + guard let connectionInfo = viewModel.connectionInfo else { return } + let viewModel = ManuallyVerificationViewModel(participant:.recipient, + connectionInfo: connectionInfo, + mainAppModel: viewModel.mainAppModel) + self.navigateTo(destination: ManuallyVerificationView(viewModel: viewModel)) + case .showToast(let message): + Toast.displayToast(message: message) + default: + break + } + } +} diff --git a/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceView.swift b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceView.swift new file mode 100644 index 000000000..9c1335011 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceView.swift @@ -0,0 +1,123 @@ +// +// RecipientConnectToDeviceView.swift +// Tella +// +// Created by RIMA on 07.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct RecipientConnectToDeviceView: View { + + @StateObject var viewModel: RecipientConnectToDeviceViewModel + @Environment(\.presentationMode) var presentationMode: Binding + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + .onReceive(viewModel.$viewAction) { state in + handleViewState(state: state) + } + .onAppear { viewModel.onAppear() } + .onDisappear { viewModel.onDisappear() } + } + + private var contentView: some View { + VStack { + Spacer() + VStack(spacing:12) { + CustomText(LocalizableNearbySharing.showQrCode.localized, + style: .heading1Style, + alignment: .center) + + qrCodeStateView + .padding(.bottom, 28) + CustomText(LocalizableNearbySharing.havingTrouble.localized, + style: .body1Style, + alignment: .center) + connectManuallyButton + } + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.connectToDevice.localized, + navigationBarType: .inline, + backButtonAction: { + presentationMode.wrappedValue.dismiss() + viewModel.stopServerListening() + }, + rightButtonType: .none) + } + + @ViewBuilder + var qrCodeStateView: some View { + switch viewModel.qrCodeState { + case .loading: + CircularActivityIndicatory(isTransparent:true) + .frame(width: 240, height: 240) + case .loaded(let qrImage): + qrCodeImageView(qrImage: qrImage) + case .error(let error): + CustomText(error, style: .body1Style) + .frame(width: 240, height: 240) + case .none: + EmptyView() + } + } + @ViewBuilder + func qrCodeImageView(qrImage:UIImage) -> some View { + Image(uiImage: qrImage) + .resizable() + .scaledToFill() + .frame(width: 215, height: 215) + .padding(.all, 16) + .background(Color.white) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(Styles.Colors.yellow, lineWidth: 8) + ) + } + + var connectManuallyButton: some View { + + let viewModel = RecipientConnectManuallyViewModel(certificateGenerator: viewModel.certificateGenerator, + mainAppModel: viewModel.mainAppModel, + connectionInfo: viewModel.connectionInfo) + + return TellaButtonView(title: LocalizableNearbySharing.connectManually.localized.uppercased(), + nextButtonAction: .destination, + destination: RecipientConnectToDeviceManuallyView(viewModel:viewModel), + isValid: .constant(true), + buttonRole: .secondary) + .padding([.leading, .trailing], 80) + } + + private func handleViewState(state: RecipientConnectToDeviceViewAction) { + switch state { + case .showReceiveFiles: + let fileTransferVM = RecipientPrepareFileTransferVM(mainAppModel: viewModel.mainAppModel) + self.navigateTo(destination: RecipientFileTransferView(viewModel:fileTransferVM)) + case .errorOccured: + self.popTo(ViewClassType.nearbySharingMainView) + Toast.displayToast(message: LocalizableCommon.commonError.localized) + case .showToast(let message): + Toast.displayToast(message: message) + default: + break + } + } +} + +#Preview { + SenderConnectToDeviceView(viewModel: SenderConnectToDeviceViewModel(nearbySharingRepository:NearbySharingRepository(), + mainAppModel: MainAppModel.stub())) +} diff --git a/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceViewModel.swift b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceViewModel.swift new file mode 100644 index 000000000..38f5a042d --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/Connection/ConnectToDeviceQRCode/RecipientConnectToDeviceViewModel.swift @@ -0,0 +1,149 @@ +// +// RecipientConnectToDeviceViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 13/2/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import UIKit +import Combine + +enum RecipientConnectToDeviceViewAction { + case none + case showToast(message: String) + case showReceiveFiles + case showVerificationHash + case errorOccured + case discardAndStartOver +} + +class RecipientConnectToDeviceViewModel: ObservableObject { + + // MARK: - Dependencies + var mainAppModel: MainAppModel + var certificateGenerator: CertificateGenerator + var nearbySharingServer: NearbySharingServer? + var connectionInfo: ConnectionInfo? + + // MARK: - State + @Published private(set) var qrCodeState: ViewModelState = .loading + @Published private(set) var viewAction: RecipientConnectToDeviceViewAction = .none + + // MARK: - Combine + private var registrationEventsCancellable: AnyCancellable? + private var networkChangeCancellable: AnyCancellable? + + // MARK: - Config + private let port: Int = 53317 + + // MARK: - Init + init(certificateGenerator: CertificateGenerator, mainAppModel: MainAppModel) { + self.certificateGenerator = certificateGenerator + self.mainAppModel = mainAppModel + self.nearbySharingServer = mainAppModel.nearbySharingServer + + observeNetworkChanges() + generateConnectionInfo() + } + + // MARK: - Observers + func onAppear() { + // Re-subscribe if needed + if registrationEventsCancellable == nil { + listenToServerRegistrationEvents() + } + if networkChangeCancellable == nil { + observeNetworkChanges() + } + } + + func onDisappear() { + // Cancel subscriptions to pause listening + registrationEventsCancellable?.cancel() + registrationEventsCancellable = nil + + networkChangeCancellable?.cancel() + networkChangeCancellable = nil + } + + // MARK: - Public Methods + func stopServerListening() { + nearbySharingServer?.resetServerState() + } + + // MARK: - Private Methods + private func observeNetworkChanges() { + networkChangeCancellable = mainAppModel.networkMonitor.connectionDidChange + .first() + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.viewAction = .showToast(message: LocalizableNearbySharing.connectionChangedToast.localized) + } + } + + private func generateConnectionInfo() { + DispatchQueue.main.async { + let interfaceType = self.mainAppModel.networkMonitor.interfaceTypeValue + + guard let ipAddress = UIDevice.current.getIPAddress(for: interfaceType) else { + self.viewAction = .errorOccured + return + } + + guard let certificateData = self.certificateGenerator.generateP12Certificate(ipAddress: ipAddress) else { + self.viewAction = .errorOccured + return + } + + let clientIdentity = certificateData.identity + let certificateHash = certificateData.certificateHash + let pin = "\(Int.randomSixDigitPIN)" + + let connectionInfo = ConnectionInfo( + ipAddress: ipAddress, + port: self.port, + certificateHash: certificateHash, + pin: pin + ) + + self.connectionInfo = connectionInfo + + self.listenToServerRegistrationEvents() + self.nearbySharingServer?.startListening( + port: self.port, + pin: pin, + clientIdentity: clientIdentity + ) + } + } + + private func listenToServerRegistrationEvents() { + registrationEventsCancellable = nearbySharingServer?.eventPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] event in + guard let self else { return } + + switch event { + case .serverStarted: + if let connectionInfo = self.connectionInfo { + let qrImage = connectionInfo.generateQRCode(size: 215) + self.qrCodeState = .loaded(qrImage) + } + + case .serverStartFailed: + self.viewAction = .errorOccured + + case .didRegister(let success, let manual): + if !manual { + self.viewAction = success ? .showReceiveFiles : .errorOccured + } + + default: + break + } + } + } +} diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientFileTransferView.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientFileTransferView.swift new file mode 100644 index 000000000..11118d46a --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientFileTransferView.swift @@ -0,0 +1,105 @@ +// +// RecipientWaitingView.swift +// Tella +// +// Created by RIMA on 14.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct RecipientFileTransferView: View { + + @StateObject var viewModel : RecipientPrepareFileTransferVM + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + .onReceive(viewModel.$viewAction) { state in + handleViewState(state: state) + } + .onAppear { viewModel.onAppear() } + .onDisappear { viewModel.onDisappear() } + } + + var contentView: some View { + switch viewModel.viewState { + case .waitingRequest: + return AnyView(waitingView) + case .awaitingAcceptance: + return AnyView(awaitingAcceptanceView) + } + } + + var waitingView: some View { + VStack { + CustomText(LocalizableNearbySharing.waitingForSenderDesc.localized, style: .heading1Style) + ResizableImage("clock").frame(width: 48, height: 48) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.receiveFiles.localized, + navigationBarType: .inline, + backButtonAction: { + self.popTo(ViewClassType.nearbySharingMainView) + viewModel.stopServerListening() + }, + rightButtonType: .none) + } + + var awaitingAcceptanceView: some View { + VStack{ + Spacer().frame(height: 100) + ResizableImage("folders.icon").frame(width: 109, height: 109) + + CustomText(String(format: LocalizableNearbySharing.senderRequestFilesNumberDesc.localized, viewModel.files.count), style: .heading1Style) + .padding(.bottom, 16) + + CustomText(LocalizableNearbySharing.requestQuestion.localized, style: .body1Style) + + .padding(.bottom, 48) + VStack(spacing: 16) { + TellaButtonView(title: LocalizableNearbySharing.accept.localized.uppercased(), + nextButtonAction: .action, + buttonType: .yellow, + isValid: .constant(true)) { + viewModel.respondToFileUpload(acceptance: true) + } + TellaButtonView(title: LocalizableNearbySharing.reject.localized.uppercased(), + nextButtonAction: .action, + isValid: .constant(true)) { + viewModel.respondToFileUpload(acceptance: false) + } + + }.frame(height: 125) + Spacer() + } + .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)) + } + + + private func handleViewState(state: RecipientPrepareFileTransferAction) { + switch state { + case .displayFileTransferView: + guard let viewModel = ReceiverFileTransferVM(mainAppModel: self.viewModel.mainAppModel) else { return } + self.navigateTo(destination: FileReceivingView(viewModel: viewModel)) + case .showToast(let message): + Toast.displayToast(message: message) + case .errorOccured: + self.popTo(ViewClassType.nearbySharingMainView) + Toast.displayToast(message: LocalizableCommon.commonError.localized) + default: + break + } + } +} + +#Preview { + RecipientFileTransferView(viewModel: RecipientPrepareFileTransferVM.stub()) +} diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientPrepareFileTransferVM.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientPrepareFileTransferVM.swift new file mode 100644 index 000000000..e6442c6d1 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Preparing/RecipientPrepareFileTransferVM.swift @@ -0,0 +1,113 @@ +// +// RecipientWaitingViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 13/5/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine +import Foundation + +enum RecipientPrepareFileTransferAction { + case showToast(message: String) + case displayFileTransferView(files: [NearbySharingFile]) + case errorOccured + case none +} + +enum RecipientPrepareFileTransferState { + case waitingRequest + case awaitingAcceptance +} + +class RecipientPrepareFileTransferVM: ObservableObject { + + // MARK: - Properties + + var mainAppModel: MainAppModel + var nearbySharingServer: NearbySharingServer? + private var acceptance: Bool? + private var prepareUploadEventsCancellable: AnyCancellable? + + // MARK: - Published Properties + @Published var viewAction: RecipientPrepareFileTransferAction = .none + @Published var viewState: RecipientPrepareFileTransferState = .waitingRequest + @Published var files: [NearbySharingFile] = [] + + // MARK: - Initializer + + init(mainAppModel: MainAppModel) { + self.mainAppModel = mainAppModel + self.nearbySharingServer = mainAppModel.nearbySharingServer + } + + // MARK: - Observers + func onAppear() { + if prepareUploadEventsCancellable == nil { + listenToServerEvents() + } + } + + func onDisappear() { + // Cancel subscriptions to pause listening + prepareUploadEventsCancellable?.cancel() + prepareUploadEventsCancellable = nil + } + + // MARK: - Private Methods + + private func listenToServerEvents() { + prepareUploadEventsCancellable = nearbySharingServer?.eventPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] event in + guard let self = self else { return } + + switch event { + + case .prepareUploadReceived(let files): + self.files = files ?? [] + self.viewState = .awaitingAcceptance + + case .prepareUploadResponseSent(let accepted): + if accepted { + self.viewAction = .displayFileTransferView(files: self.files) + } else { + self.viewState = .waitingRequest + self.viewAction = .showToast(message: LocalizableNearbySharing.recipientFilesRejected.localized) + } + + case .connectionClosed, .errorOccured: + self.viewAction = .errorOccured + + default: + break + } + } + } + + // MARK: - Public Methods + + func respondToFileUpload(acceptance: Bool) { + self.acceptance = acceptance + nearbySharingServer?.respondToFileOffer(accept: acceptance) + } + + func stopServerListening() { + nearbySharingServer?.resetServerState() + } + +} + +// MARK: - Preview / Stub + +extension RecipientPrepareFileTransferVM { + static func stub() -> RecipientPrepareFileTransferVM { + return RecipientPrepareFileTransferVM( + mainAppModel: MainAppModel.stub() + ) + } +} + + diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/FileReceivingView.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/FileReceivingView.swift new file mode 100644 index 000000000..2d4beb05f --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/FileReceivingView.swift @@ -0,0 +1,54 @@ +// +// FileReceivingView.swift +// Tella +// +// Created by Dhekra Rouatbi on 9/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct FileReceivingView: View { + @ObservedObject var viewModel: ReceiverFileTransferVM + @EnvironmentObject var sheetManager: SheetManager + + var body: some View { + ZStack { + FileTransferView(viewModel: viewModel) + }.onReceive(viewModel.$viewAction) { action in + handleViewAction(action:action) + } + .onAppear { viewModel.onAppear() } + .onDisappear { viewModel.onDisappear() } + + } + + func handleViewAction(action: TransferViewAction) { + switch action { + + case .shouldShowResults: + let resultVM = NearbySharingResultVM(transferredFiles: viewModel.transferredFiles, + participant: .recipient) + + let resultView = NearbySharingResultView(viewModel: resultVM, buttonAction: { + navigateTo(destination: getFileListView()) + }) + navigateTo(destination: resultView) + default: + break + } + } + + private func getFileListView() -> some View { + FileListView(appModel: viewModel.mainAppModel, + rootFile: viewModel.rootFile, + filterType: .all, + title: viewModel.rootFile?.name ?? "", + fileListType: .nearbySharing) + } +} +// +//#Preview { +// FileReceivingView(viewModel: FileTransferVM.stub()) +//} diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultVM.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultVM.swift new file mode 100644 index 000000000..1ca967396 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultVM.swift @@ -0,0 +1,93 @@ +// +// NearbySharingResultVM.swift +// Tella +// +// Created by Dhekra Rouatbi on 15/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine + +final class NearbySharingResultVM: ObservableObject { + + let transferredFiles: [NearbySharingTransferredFile] + let participant: NearbySharingParticipant + + init(transferredFiles: [NearbySharingTransferredFile], participant: NearbySharingParticipant) { + self.transferredFiles = transferredFiles + self.participant = participant + } + + // MARK: - Computed Properties + + private var successStatus: NearbySharingFileStatus { + participant == .recipient ? .saved : .finished + } + + private var successSingle: LocalizableDelegate { + participant == .recipient ? LocalizableNearbySharing.successFileReceivedExpl : LocalizableNearbySharing.successFileSentExpl + } + + private var successMultiple: LocalizableDelegate { + participant == .recipient ? LocalizableNearbySharing.successFilesReceivedExpl : LocalizableNearbySharing.successFilesSentExpl + } + + private var failureSingle: LocalizableDelegate { + LocalizableNearbySharing.failureFileReceivedExpl + } + + private var failureMultiple: LocalizableDelegate { + LocalizableNearbySharing.failureFilesReceivedExpl + } + + var allFilesTransferred: Bool { + transferredFiles.allSatisfy { $0.status == successStatus } + } + + var noFilesTransferred: Bool { + transferredFiles.allSatisfy { $0.status != successStatus } + } + + var imageName: String { + allFilesTransferred ? "checked-circle" : "warning" + } + + var title: String { + allFilesTransferred ? LocalizableNearbySharing.successTitle.localized : LocalizableNearbySharing.failureTitle.localized + } + + var subTitle: String { + let total = transferredFiles.count + let successCount = transferredFiles.filter { $0.status == successStatus }.count + let failureCount = total - successCount + + switch (successCount, failureCount) { + case (let success, 0) where success == total: + let format = total == 1 ? successSingle.localized : successMultiple.localized + return String(format: format, total) + + case (0, let failure) where failure == total: + let format = total == 1 ? failureSingle.localized : failureMultiple.localized + return String(format: format, total) + + default: + let format: String + switch (successCount, failureCount) { + case (1, 1): + format = LocalizableNearbySharing.fileReceivedFileNotReceivedExpl.localized + case (1, _): + format = LocalizableNearbySharing.fileReceivedFilesNotReceivedExpl.localized + case (_, 1): + format = LocalizableNearbySharing.filesReceivedFileNotReceivedExpl.localized + default: + format = LocalizableNearbySharing.filesReceivedFilesNotReceivedExpl.localized + } + return String(format: format, successCount, failureCount) + } + } + + var buttonTitle: String? { + (noFilesTransferred || participant == .sender) ? nil : LocalizableNearbySharing.viewFilesAction.localized + } +} diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultView.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultView.swift new file mode 100644 index 000000000..dd2eda862 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/NearbySharingResultView.swift @@ -0,0 +1,48 @@ +// +// NearbySharingResultView.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct NearbySharingResultView: View { + + var viewModel: NearbySharingResultVM + var buttonAction: (()->()) = { } + + var body: some View { + + ZStack { + + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + } + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.resultsAppBar.localized , + backButtonType: .close, + backButtonAction: {self.popToRoot()}) + } + + var contentView : some View { + ResultView(imageName: viewModel.imageName, + title: viewModel.title, + subTitle: viewModel.subTitle, + buttonTitle: viewModel.buttonTitle, + buttonAction: buttonAction) + } + +} + +//#Preview { +// NearbySharingResultView() +//} + diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ReceiverFileTransferVM.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ReceiverFileTransferVM.swift new file mode 100644 index 000000000..e3457122d --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ReceiverFileTransferVM.swift @@ -0,0 +1,221 @@ +// +// ReceiverFileTransferVM.swift +// Tella +// +// Created by Dhekra Rouatbi on 7/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine +import Foundation + +final class ReceiverFileTransferVM: FileTransferVM { + + // MARK: - Properties + + private let nearbySharingServer: NearbySharingServer? + private var rootFolderTask: Task? + private var uploadEventsCancellable: AnyCancellable? + + @Published var should: Bool = false + + var rootFile: VaultFileDB? = nil + + // MARK: - Initializer + + init?(mainAppModel: MainAppModel) { + self.nearbySharingServer = mainAppModel.nearbySharingServer + guard nearbySharingServer != nil else { return nil } + + super.init( + mainAppModel: mainAppModel, + title: LocalizableNearbySharing.receivingAppBar.localized, + bottomSheetTitle: LocalizableNearbySharing.stopReceivingSheetTitle.localized, + bottomSheetMessage: LocalizableNearbySharing.stopReceivingSheetExpl.localized + ) + } + + // MARK: - Observers + func onAppear() { + if uploadEventsCancellable == nil { + observeServerSession() + } + } + + func onDisappear() { + // Cancel subscriptions to pause listening + uploadEventsCancellable?.cancel() + uploadEventsCancellable = nil + } + + // MARK: - Private Methods + + private func observeServerSession() { + Task { [weak self] in + guard let self, let server = self.nearbySharingServer else { return } + if let session = await server.state.currentSession() { + self.transferredFiles = Array(session.files.values) + self.initProgress(session: session) + self.listenToServer() + } + } + } + + private func listenToServer() { + uploadEventsCancellable = nearbySharingServer?.eventPublisher + .sink(receiveValue: { [weak self] event in + guard let self else { return } + + switch event { + case .fileTransferProgress(let file): + Task { await self.handle(file: file) } + case .connectionClosed, .errorOccured: + _ = self.transferredFiles.filter{ $0.status != .saved}.compactMap({$0.status = .failed}) + checkAllFilesAreReceived() + default: + break + } + }) + } + + private func ensureRootFolder() async -> VaultFileDB? { + if let task = rootFolderTask { return await task.value } + + let task = Task { [weak self] in + guard let self else { return nil } + return await self.saveFolder() + } + rootFolderTask = task + + let value = await task.value + if value == nil { rootFolderTask = nil } // retry on next call if creation failed + self.rootFile = value + return value + } + + // MARK: - Event handling + + private func handle(file: NearbySharingTransferredFile) async { + guard let fileID = file.vaultFile.id else { return } + + switch file.status { + case .finished: + if let transferred = transferredFiles.first(where: { $0.vaultFile.id == fileID }) { + transferred.status = .saving + self.updateStatus(with: transferred) + } + + guard + let parent = await ensureRootFolder(), + let manager = self.mainAppModel.vaultFilesManager + else { + markFailed(id: fileID) + return + } + + let imported = ImportedFile( + urlFile: file.url, + parentId: parent.id, + shouldPreserveMetadata: true, + deleteOriginal: true, + fileSource: .files, + fileId: fileID, + fileName: file.vaultFile.name + ) + + if await manager.addVaultFile(importedFile: imported) != nil { + markSaved(id: fileID) + } else { + markFailed(id: fileID) + } + + checkAllFilesAreReceived() + + case .failed: + markFailed(id: fileID) + checkAllFilesAreReceived() + + default: + self.updateProgress(with: file) + } + } + + // MARK: - UI helpers + + private func markSaved(id: String) { + guard let transferred = transferredFiles.first(where: { $0.vaultFile.id == id }) else { return } + transferred.status = .saved + updateStatus(with: transferred) + } + + private func markFailed(id: String) { + guard let transferred = transferredFiles.first(where: { $0.vaultFile.id == id }) else { return } + transferred.status = .failed + updateStatus(with: transferred) + } + + // MARK: - Folder creation + + private func saveFolder() async -> VaultFileDB? { + + guard let title = progressViewModel?.title, !title.isEmpty else { return nil } + guard let manager = mainAppModel.vaultFilesManager else { return nil } + + let result = manager.addFolderFile(name: title) + if case .success(let id) = result { + guard let id else { return nil } + return mainAppModel.vaultFilesManager?.getVaultFile(id: id) + } + return nil + } + + // MARK: - Completion check + + private func checkAllFilesAreReceived() { + Task { + // Read & act on UI state on the main actor + let allDone = self.transferredFiles.allSatisfy { $0.status == .saved || $0.status == .failed } + + if allDone { + await MainActor.run { + if self.viewAction != .shouldShowResults { + self.viewAction = .shouldShowResults + } + } + nearbySharingServer?.resetFullServerState() + } + } + } + + // MARK: - Overrides + + override func stopTask() { + // Reset server state + nearbySharingServer?.resetFullServerState() + + // Mark any in-progress files as failed + for file in transferredFiles where file.status != .saved && file.status != .failed { + file.status = .failed + } + + // Trigger UI update + viewAction = .shouldShowResults + } + + override func makeTransferredSummary(receivedBytes: Int, totalBytes: Int) -> String { + let template = transferredFiles.count > 1 + ? LocalizableNearbySharing.recipientFilesReceived.localized + : LocalizableNearbySharing.recipientFileReceived.localized + + let receivedFormatted = receivedBytes.getFormattedFileSize().getFileSizeWithoutUnit() + let totalFormatted = totalBytes.getFormattedFileSize() + + return String(format: template, transferredFiles.count, receivedFormatted, totalFormatted) + } + + override func formatPercentage(_ percent: Int) -> String { + return String(format: LocalizableNearbySharing.recipientPercentageReceived.localized, percent) + } + +} diff --git a/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ResultView.swift b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ResultView.swift new file mode 100644 index 000000000..d5bf4bf21 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Recipient/ReceiveFiles/Receiving/ResultView.swift @@ -0,0 +1,68 @@ +// +// ResultView.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct ResultView: View { + + var imageName: String + var title: String + var subTitle: String + var buttonTitle: String? + var buttonAction: (()->()) = { } + + var body: some View { + ContainerView { + + VStack { + + Spacer() + + topview + + Spacer() + .frame(height: 48) + if let buttonTitle { + TellaButtonView(title: buttonTitle, + nextButtonAction: .action, + buttonType: .yellow, + isValid: .constant(true)) { + buttonAction() + } + } + + Spacer() + + } .padding(EdgeInsets(top: 0, leading: 26, bottom: 0, trailing: 26)) + } + } + + var topview: some View { + + VStack { + Image(imageName) + + Spacer() + .frame(height: 16) + + CustomText(title, + style: .heading1Style, + alignment: .center) + Spacer() + .frame(height: 16) + CustomText(subTitle, + style: .body1Style, + alignment: .center) + } + } +} + +//#Preview { +// ResultView() +//} diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/ConnectToDeviceManuallyVM.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/ConnectToDeviceManuallyVM.swift new file mode 100644 index 000000000..f9a3406ad --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/ConnectToDeviceManuallyVM.swift @@ -0,0 +1,75 @@ +// +// ConnectToDeviceManuallyViewModel.swift +// Tella +// +// Created by RIMA on 05.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +class ConnectToDeviceManuallyVM: ObservableObject { + + var nearbySharingRepository:NearbySharingRepository + var sessionId : String? + var mainAppModel: MainAppModel + + @Published var ipAddress : String = "" + @Published var pin: String = "" + @Published var port: String = "" + @Published var publicKey: String = "" + @Published var valid : Bool = false + @Published var validFields: Bool = false + + // Fields validation + @Published var isValidIpAddress: Bool = false + @Published var isValidPin: Bool = false + @Published var isValidPort : Bool = false + + // Fields validation + @Published var shouldShowIpAddressError: Bool = false + @Published var shouldShowPinError: Bool = false + + @Published var viewState: SenderConnectToDeviceViewAction = .none + + private var subscribers = Set() + var connectionInfo : ConnectionInfo? + + init(nearbySharingRepository:NearbySharingRepository, mainAppModel:MainAppModel) { + self.nearbySharingRepository = nearbySharingRepository + self.mainAppModel = mainAppModel + + validateFields() + } + + func validateFields() { + Publishers.CombineLatest3($isValidIpAddress, $isValidPin, $isValidPort) + .map { ip, pin, port in + return ip && pin && port + } + .assign(to: \.validFields, on: self) + .store(in: &subscribers) + } + + func getHash() { + + guard let port = Int(port) else { return } + let connectionInfo = ConnectionInfo(ipAddress: ipAddress, port: port, certificateHash: nil, pin: pin) + self.connectionInfo = connectionInfo + + self.nearbySharingRepository.getHash(connectionInfo: connectionInfo) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { [weak self] completion in + debugLog(completion) + if case .failure = completion { + self?.viewState = .showBottomSheetError + } + }, receiveValue: { certificateHash in + debugLog(certificateHash) + self.connectionInfo?.certificateHash = certificateHash + self.viewState = .showVerificationHash + }).store(in: &self.subscribers) + } +} diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/SenderConnectToDeviceManuallyView.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/SenderConnectToDeviceManuallyView.swift new file mode 100644 index 000000000..9b332b312 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceManually/SenderConnectToDeviceManuallyView.swift @@ -0,0 +1,133 @@ +// +// ConnectToDeviceManuallyView.swift +// Tella +// +// Created by RIMA on 05.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct SenderConnectToDeviceManuallyView: View { + + @StateObject var viewModel: ConnectToDeviceManuallyVM + @State var isBottomSheetShown : Bool = false + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + .onReceive(viewModel.$viewState) { state in + handleViewState(state: state) + } + + } + + var contentView: some View { + VStack { + Spacer() + + VStack { + topView + Spacer() + .frame(height: 24) + textFieldsView + } + + Spacer() + bottomView + } + .padding([.leading, .trailing], 16) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.connectManually.localized, + navigationBarType: .inline, + backButtonType: .close, + rightButtonType: .none) + } + + var topView: some View { + ServerConnectionHeaderView( + title: LocalizableNearbySharing.enterDeviceInformation.localized, + imageIconName: "device", + subtitleTextAlignment: .leading) + } + + var textFieldsView: some View { + VStack(spacing:8) { + ScrollView { + ipAddressTextFieldView + pinTextFieldView + portTextFieldView + } + } + } + + var ipAddressTextFieldView: some View { + TextfieldView(fieldContent: $viewModel.ipAddress, + isValid: $viewModel.isValidIpAddress, + shouldShowError: $viewModel.shouldShowIpAddressError, + errorMessage: LocalizableNearbySharing.invalidIpAddress.localized, + fieldType: .ipAddress, + placeholder : LocalizableNearbySharing.ipAddress.localized) + .frame(height: 78) + } + + var pinTextFieldView: some View { + TextfieldView(fieldContent: $viewModel.pin, + isValid: $viewModel.isValidPin, + shouldShowError: $viewModel.shouldShowPinError , + errorMessage:LocalizableNearbySharing.invalidPin.localized, + fieldType: .pin, + placeholder : LocalizableNearbySharing.pin.localized) + .frame(height: 78) + } + + var portTextFieldView: some View { + TextfieldView(fieldContent: $viewModel.port, + isValid: $viewModel.isValidPort, + shouldShowError: .constant(false), + fieldType: .port, + placeholder : LocalizableNearbySharing.port.localized) + .frame(height: 78) + } + + var bottomView: some View { + NavigationBottomView(shouldActivateNext: $viewModel.validFields, + nextButtonAction: .action, + shouldHideBack: true, + nextAction: { + viewModel.getHash() + }) + } + + private func handleViewState(state: SenderConnectToDeviceViewAction) { + switch state { + case .showBottomSheetError: + showBottomSheetError() + case .showVerificationHash: + guard let connectionInfo = viewModel.connectionInfo else { return } + let viewModel = ManuallyVerificationViewModel(participant: .sender, + nearbySharingRepository:viewModel.nearbySharingRepository, + connectionInfo: connectionInfo, + mainAppModel: viewModel.mainAppModel) + self.navigateTo(destination: ManuallyVerificationView(viewModel: viewModel )) + + case .showToast(let message): + Toast.displayToast(message: message) + default: + break + } + } + + private func showBottomSheetError() { + isBottomSheetShown = true + let content = ConnectionFailedView() + self.showBottomSheetView(content: content, modalHeight: 192, isShown: $isBottomSheetShown) + } +} + diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/QRCodeScannerView.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/QRCodeScannerView.swift new file mode 100644 index 000000000..f8fa5ae73 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/QRCodeScannerView.swift @@ -0,0 +1,111 @@ +// +// QRCodeScannerView.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/2/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import AVFoundation +import SwiftUI +import Combine + +struct QRCodeScannerView: UIViewControllerRepresentable { + + @Environment(\.presentationMode) var presentationMode + @Binding var scannedCode: String? + var captureSession = AVCaptureSession() + + var startScanning = PassthroughSubject() + + class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate { + var parent: QRCodeScannerView + var subscribers = Set() + + init(parent: QRCodeScannerView) { + self.parent = parent + self.parent.startScanning.sink { value in + + + DispatchQueue.global(qos: .userInitiated).async { + parent.captureSession.startRunning() + } + + }.store(in: &subscribers) + } + + func metadataOutput(_ output: AVCaptureMetadataOutput, + didOutput metadataObjects: [AVMetadataObject], + from connection: AVCaptureConnection) { + if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject, + metadataObject.type == .qr, + let scannedValue = metadataObject.stringValue { + self.parent.captureSession.stopRunning() + self.parent.scannedCode = scannedValue + } + } + } + + func makeCoordinator() -> Coordinator { + return Coordinator(parent: self) + } + + func makeUIViewController(context: Context) -> UIViewController { + guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { + return UIViewController() + } + + let videoInput: AVCaptureDeviceInput + do { + videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) + } catch { + return UIViewController() + } + + if captureSession.canAddInput(videoInput) { + captureSession.addInput(videoInput) + } + + let metadataOutput = AVCaptureMetadataOutput() + if captureSession.canAddOutput(metadataOutput) { + captureSession.addOutput(metadataOutput) + metadataOutput.setMetadataObjectsDelegate(context.coordinator, queue: DispatchQueue.main) + metadataOutput.metadataObjectTypes = [.qr] + } + + let scannerView = ScannerView() + scannerView.previewLayer.session = captureSession + scannerView.previewLayer.videoGravity = .resizeAspectFill + + let viewController = UIViewController() + viewController.view.addSubview(scannerView) + scannerView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + scannerView.topAnchor.constraint(equalTo: viewController.view.topAnchor), + scannerView.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor), + scannerView.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor), + scannerView.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor) + ]) + + DispatchQueue.global(qos: .userInitiated).async { + captureSession.startRunning() + } + return viewController + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + } + + +} + +class ScannerView: UIView { + override class var layerClass: AnyClass { + return AVCaptureVideoPreviewLayer.self + } + + var previewLayer: AVCaptureVideoPreviewLayer { + return layer as! AVCaptureVideoPreviewLayer + } +} diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceView.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceView.swift new file mode 100644 index 000000000..09deebb7c --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceView.swift @@ -0,0 +1,98 @@ +// +// ConnectToDeviceView.swift +// Tella +// +// Created by RIMA on 05.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI +import Combine + +struct SenderConnectToDeviceView: View { + + @StateObject var viewModel: SenderConnectToDeviceViewModel + @State var isBottomSheetShown : Bool = false + @State var startScanning = PassthroughSubject() + + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + .onReceive(viewModel.$viewState) { state in + handleViewState(state: state) + } + } + + var contentView: some View { + VStack(alignment: .center, spacing: 12) { + Spacer() + CustomText(LocalizableNearbySharing.scanCode.localized, style: .heading1Style) + qrCodeView + .padding(.bottom, 28) + CustomText(LocalizableNearbySharing.havingTrouble.localized, style: .body1Style) + connectManuallyButton + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.connectToDevice.localized, + navigationBarType: .inline, + rightButtonType: .none) + } + + var qrCodeView: some View { + ZStack{ + QRCodeScannerView(scannedCode: $viewModel.scannedCode,startScanning: startScanning) + .cornerRadius(12) + .padding(.all,4) + ResizableImage("qrCode.icon") + + }.frame(width: 248, height: 248) + } + + var connectManuallyButton: some View { + TellaButtonView(title: LocalizableNearbySharing.connectManually.localized.uppercased(), + nextButtonAction: .destination, + destination: SenderConnectToDeviceManuallyView(viewModel: ConnectToDeviceManuallyVM(nearbySharingRepository: viewModel.nearbySharingRepository, mainAppModel: viewModel.mainAppModel)), + isValid: .constant(true), + buttonRole: .secondary) + .padding([.leading, .trailing], 80) + } + + private func showBottomSheetError() { + isBottomSheetShown = true + let content = ConnectionFailedView( tryAction: { + startScanning.send(true) + self.viewModel.observeScannedCode() + }) + self.showBottomSheetView(content: content, modalHeight: 192, isShown: $isBottomSheetShown) + } + + private func handleViewState(state: SenderConnectToDeviceViewAction) { + switch state { + case .showBottomSheetError: + showBottomSheetError() + case .showSendFiles: + guard let session = viewModel.session else { return } + let viewModel = SenderPrepareFileTransferVM(mainAppModel: viewModel.mainAppModel, + session:session, + nearbySharingRepository:viewModel.nearbySharingRepository) + self.navigateTo(destination: SenderPrepareFileTransferView(viewModel: viewModel )) + case .showToast(let message): + Toast.displayToast(message: message) + default: + break + } + } +} + +#Preview { + SenderConnectToDeviceView(viewModel: SenderConnectToDeviceViewModel(nearbySharingRepository:NearbySharingRepository(), + mainAppModel: MainAppModel.stub())) +} diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceViewModel.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceViewModel.swift new file mode 100644 index 000000000..e6b40c2e5 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectToDeviceQRCode/SenderConnectToDeviceViewModel.swift @@ -0,0 +1,86 @@ +// +// SenderConnectToDeviceViewModel.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/2/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine +import UIKit + +enum SenderConnectToDeviceViewAction { + case none + case showToast(message: String) + case showBottomSheetError + case showSendFiles + case showVerificationHash + case discardAndStartOver +} + +class SenderConnectToDeviceViewModel: NSObject, ObservableObject { + + @Published var scannedCode: String? = nil + @Published var viewState: SenderConnectToDeviceViewAction = .none + @Published var startScanning: Bool = true + + private var subscribers = Set() + + var mainAppModel: MainAppModel + var nearbySharingRepository: NearbySharingRepository + var session: NearbySharingSession? + + init(nearbySharingRepository:NearbySharingRepository, mainAppModel:MainAppModel) { + self.nearbySharingRepository = nearbySharingRepository + self.mainAppModel = mainAppModel + + super.init() + observeNetworkChanges() + observeScannedCode() + } + + func observeScannedCode() { + scannedCode = nil + self.$scannedCode + .compactMap { $0 } + .prefix(1) + .sink { [weak self] scannedCode in + let connectionInfo = scannedCode.decodeJSON(ConnectionInfo.self) + self?.register(connectionInfo: connectionInfo) + }.store(in: &subscribers) + } + + private func observeNetworkChanges() { + mainAppModel.networkMonitor.connectionDidChange + .first() + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.viewState = .showToast(message: LocalizableNearbySharing.connectionChangedToast.localized) + }.store(in: &subscribers) + } + + func register(connectionInfo:ConnectionInfo?) { + + guard let connectionInfo else { return } + + let registerRequest = RegisterRequest(pin:connectionInfo.pin, nonce: UUID().uuidString) + + self.nearbySharingRepository.register(connectionInfo: connectionInfo, registerRequest: registerRequest) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { completion in + switch completion { + case .finished: + self.viewState = .showSendFiles + self.viewState = .showToast(message: LocalizableNearbySharing.successConnectToast.localized) + case .failure: + self.viewState = .showBottomSheetError + } + }, receiveValue: { response in + if let sessionId = response.sessionId { + self.session = NearbySharingSession(sessionId: sessionId) + } + }).store(in: &self.subscribers) + } +} diff --git a/Tella/Scenes/NearbySharing/Sender/Connection/ConnectionFailedView.swift b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectionFailedView.swift new file mode 100644 index 000000000..b751914a2 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/Connection/ConnectionFailedView.swift @@ -0,0 +1,31 @@ +// +// ConnectionFailedView.swift +// Tella +// +// Created by Dhekra Rouatbi on 2/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct ConnectionFailedView: View { + + var tryAction: (()->())? + + var body: some View { + ConfirmBottomSheet(titleText: LocalizableNearbySharing.connectionFailedTitle.localized, + msgText: LocalizableNearbySharing.connectionFailedExpl.localized, + actionText:LocalizableNearbySharing.connectionFailedAction.localized, + shouldHideSheet: false, + didConfirmAction: { + self.dismiss { + tryAction?() + } + }) + } +} + +#Preview { + ConnectionFailedView(tryAction: {}) +} diff --git a/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferVM.swift b/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferVM.swift new file mode 100644 index 000000000..0bf451223 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferVM.swift @@ -0,0 +1,129 @@ +// +// SenderPrepareFileTransferVM.swift +// Tella +// +// Created by Dhekra Rouatbi on 3/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Combine +import Foundation + +enum SenderPrepareFileTransferAction { + case showToast(message: String) + case displaySendingFiles + case errorOccured + case none +} + +enum SenderPrepareFileTransferState { + case waiting + case prepareFiles +} + +class SenderPrepareFileTransferVM: ObservableObject { + + var mainAppModel: MainAppModel + var session : NearbySharingSession + var nearbySharingRepository: NearbySharingRepository + + //MARK: -AddFilesViewModel + @Published var addFilesViewModel: AddFilesViewModel + @Published var title: String = "" + @Published var validTitle: Bool = false + + @Published var viewAction: SenderPrepareFileTransferAction = .none + @Published var viewState: SenderPrepareFileTransferState = .prepareFiles + @Published var reportIsValid : Bool = false + + private var subscribers = Set() + + init(mainAppModel: MainAppModel, session : NearbySharingSession, nearbySharingRepository: NearbySharingRepository) { + self.mainAppModel = mainAppModel + self.addFilesViewModel = AddFilesViewModel(mainAppModel: mainAppModel) + self.session = session + self.nearbySharingRepository = nearbySharingRepository + validateReport() + } + + func validateReport() { + Publishers.CombineLatest($validTitle, addFilesViewModel.$files) + .map { validTitle, files in + (validTitle && !files.isEmpty) + } + .assign(to: \.reportIsValid, on: self) + .store(in: &subscribers) + } + + func prepareUpload() { + self.viewState = .waiting + session.title = title + var nearbySharingFileArray: [String: NearbySharingTransferredFile] = [:] + let sessionId = session.sessionId + + let files: [NearbySharingFile] = addFilesViewModel.files.compactMap { file in + guard let id = file.id else { + return nil + } + + let nearbySharingFile = NearbySharingFile( + id: id, + fileName: file.name, + size: file.size, + fileType: file.mimeType, + thumbnail: file.thumbnail) + + nearbySharingFileArray[id] = NearbySharingTransferredFile(vaultFile: file) + return nearbySharingFile + } + + let prepareUploadRequest = PrepareUploadRequest( + title: title, + sessionID: sessionId, + files: files + ) + + self.nearbySharingRepository.prepareUpload(prepareUpload: prepareUploadRequest) + .receive(on: DispatchQueue.main) + .sink( + receiveCompletion: { completion in + self.handlePrepareUpload(completion: completion) + }, receiveValue: { response in + + nearbySharingFileArray.values.forEach { file in + if let transmissionId = response.files?.first(where: { $0.id == file.file.id })?.transmissionID { + file.transmissionId = transmissionId + } + } + self.session.files = nearbySharingFileArray + self.viewAction = .displaySendingFiles + } + ) + .store(in: &subscribers) + } + + private func handlePrepareUpload(completion:Subscribers.Completion) { + switch completion { + case .finished: + break + case .failure(let error): + debugLog(error) + switch error { + case .httpCode(HTTPErrorCodes.forbidden.rawValue): + self.viewState = .prepareFiles + self.viewAction = .showToast(message:LocalizableNearbySharing.senderFilesRejected.localized) + default: + self.viewAction = .errorOccured + } + } + } + + func closeConnection() { + let request = CloseConnectionRequest(sessionID: session.sessionId) + nearbySharingRepository.closeConnection(closeConnectionRequest: request) + .receive(on: DispatchQueue.main) + .sink(receiveCompletion: { _ in }, receiveValue: { _ in }) + .store(in: &subscribers) + } +} diff --git a/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferView.swift b/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferView.swift new file mode 100644 index 000000000..4c0022ca7 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/SendFiles/Preparing/SenderPrepareFileTransferView.swift @@ -0,0 +1,108 @@ +// +// SenderPrepareFileTransferView.swift +// Tella +// +// Created by RIMA on 25.02.25. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI +import Combine + +struct SenderPrepareFileTransferView: View { + + @ObservedObject var viewModel: SenderPrepareFileTransferVM + + var body: some View { + ZStack { + ContainerViewWithHeader { + navigationBarView + } content: { + contentView + } + + AddFilePhotoVideoPickerView(viewModel: viewModel.addFilesViewModel) + } + .overlay(AddFileCameraView(viewModel: viewModel.addFilesViewModel)) + .overlay(AddFileRecordView(viewModel: viewModel.addFilesViewModel)) + .onReceive(viewModel.$viewAction) { state in + self.handleViewState(state: state) + } + } + + fileprivate var contentView: some View { + switch viewModel.viewState { + case .waiting: + return AnyView(waitingView) + case .prepareFiles: + return AnyView(prepareFiles) + } + } + + fileprivate var waitingView: some View { + VStack { + CustomText(LocalizableNearbySharing.senderWaitingRecipient.localized, style: .heading1Style) + ResizableImage("clock").frame(width: 48, height: 48) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + fileprivate var prepareFiles: some View { + VStack(alignment: .leading, spacing: 8) { + ScrollView { + + titleTextFieldView() + + AddFileGridView(viewModel: viewModel.addFilesViewModel, titleText: LocalizableNearbySharing.selectFilesToSend.localized) + .padding(.top, 24) + + Spacer() + } + TellaButtonView(title: LocalizableNearbySharing.sendFiles.localized.uppercased(), + nextButtonAction: .action, + buttonType: .yellow, + isValid: $viewModel.reportIsValid) { + viewModel.prepareUpload() + }.padding(.bottom, 20) + + + }.padding(16) + } + + fileprivate func titleTextFieldView() -> some View { + return TextfieldView(fieldContent: $viewModel.title, + isValid: $viewModel.validTitle, + shouldShowError: .constant(false), + fieldType: .text, + placeholder: "Title", + shouldShowTitle: true) + .frame(height: 78) + } + + fileprivate var navigationBarView: some View { + NavigationHeaderView(title: LocalizableNearbySharing.sendFiles.localized, + backButtonAction: { + self.popTo(ViewClassType.nearbySharingMainView) + self.viewModel.closeConnection() + }) + } + + private func handleViewState(state: SenderPrepareFileTransferAction) { + switch state { + case .displaySendingFiles: + let session = self.viewModel.session + let viewModel = SenderFileTransferVM(mainAppModel: self.viewModel.mainAppModel, + repository: self.viewModel.nearbySharingRepository, + session: session) + self.navigateTo(destination: FileSendingView(viewModel: viewModel)) + case .showToast(let message): + Toast.displayToast(message: message) + case .errorOccured: + self.popTo(ViewClassType.nearbySharingMainView) + Toast.displayToast(message: LocalizableCommon.commonError.localized) + default: + break + } + } +} diff --git a/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/FileSendingView.swift b/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/FileSendingView.swift new file mode 100644 index 000000000..8e441227c --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/FileSendingView.swift @@ -0,0 +1,42 @@ +// +// FileSendingView.swift +// Tella +// +// Created by Dhekra Rouatbi on 14/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct FileSendingView: View { + + @ObservedObject var viewModel: SenderFileTransferVM + + var body: some View { + ZStack { + FileTransferView(viewModel: viewModel) + }.onReceive(viewModel.$viewAction) { newAction in + handleViewAction(action: newAction) + } + } + + func handleViewAction(action: TransferViewAction) { + switch action { + + case .shouldShowResults: + + let resultVM = NearbySharingResultVM(transferredFiles: viewModel.transferredFiles, + participant: .sender) + + let resultView = NearbySharingResultView(viewModel: resultVM) + navigateTo(destination: resultView) + default: + break + } + } +} + +//#Preview { +// FileSendingView(viewModel: SenderFileTransferVM.stub()) +//} diff --git a/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/SenderFileTransferVM.swift b/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/SenderFileTransferVM.swift new file mode 100644 index 000000000..b16002962 --- /dev/null +++ b/Tella/Scenes/NearbySharing/Sender/SendFiles/Sending/SenderFileTransferVM.swift @@ -0,0 +1,109 @@ +// +// SenderFileTransferVM.swift +// Tella +// +// Created by Dhekra Rouatbi on 15/5/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import Combine + +class SenderFileTransferVM: FileTransferVM { + + var repository: NearbySharingRepository? + var session: NearbySharingSession? + private var subscribers = Set() + + init(mainAppModel: MainAppModel, + repository: NearbySharingRepository, + session: NearbySharingSession) { + + self.repository = repository + self.session = session + + super.init(mainAppModel: mainAppModel, + title: LocalizableNearbySharing.senderSendingAppBar.localized, + bottomSheetTitle: LocalizableNearbySharing.stopSharingTitle.localized, + bottomSheetMessage: LocalizableNearbySharing.stopSharingSheetExpl.localized) + transferredFiles = Array(session.files.values) + initProgress(session: session) + submitReport() + } + // MARK: - Public Methods + + func submitReport() { + + Task { + + guard let vaultfiles = session?.files.values else { return } + + for file in vaultfiles { + file.url = await self.mainAppModel.vaultManager.loadVaultFileToURLAsync(file: file.vaultFile, withSubFolder: true) + } + + vaultfiles.forEach({ file in + guard let url = file.url else { return } + let fileID = file.file.id + repository?.uploadFile(fileUploadRequest: FileUploadRequest(sessionID: session?.sessionId, + transmissionID: file.transmissionId, + fileID: fileID), + fileURL: url) + .receive(on: DispatchQueue.main) + .sink { completion in + + guard let fileID, let progressFile = self.session?.files[fileID] else {return} + + switch completion { + case .finished: + progressFile.status = .finished + case .failure: + progressFile.status = .failed + } + + self.session?.files[fileID] = progressFile + + self.checkAllFilesAreReceived() + + } receiveValue: { progress in + guard let fileID, let progressFile = self.session?.files[fileID] else {return} + progressFile.bytesReceived += progress + self.session?.files[fileID] = progressFile + self.updateProgress(with: progressFile) + }.store(in: &subscribers) + }) + } + } + + private func checkAllFilesAreReceived() { + guard let files = session?.files else { return } + let filesAreNotfinishReceiving = files.filter({$0.value.status == .transferring || $0.value.status == .queue}) + if (filesAreNotfinishReceiving.isEmpty) { + self.viewAction = .shouldShowResults + } + } + + // MARK: - Overrides + + override func stopTask() { + repository?.cancelUpload() + } + + // MARK: - Helpers + + override func makeTransferredSummary(receivedBytes: Int, totalBytes: Int) -> String { + let template = transferredFiles.count > 1 + ? LocalizableNearbySharing.recipientFilesReceived.localized + : LocalizableNearbySharing.recipientFileReceived.localized + + let receivedFormatted = receivedBytes.getFormattedFileSize().getFileSizeWithoutUnit() + let totalFormatted = totalBytes.getFormattedFileSize() + + return String(format: template, transferredFiles.count, receivedFormatted, totalFormatted) + } + + override func formatPercentage(_ percent: Int) -> String { + return String(format: LocalizableNearbySharing.recipientPercentageReceived.localized, percent) + } +} diff --git a/Tella/Scenes/NearbySharing/SenderConnectToDeviceView.swift b/Tella/Scenes/NearbySharing/SenderConnectToDeviceView.swift new file mode 100644 index 000000000..c5ed732b1 --- /dev/null +++ b/Tella/Scenes/NearbySharing/SenderConnectToDeviceView.swift @@ -0,0 +1,52 @@ +// +// ConnectToDeviceView.swift +// Tella +// +// Created by RIMA on 05.02.25. +// Copyright © 2025 HORIZONTAL. All rights reserved. +// + +import SwiftUI + +struct SenderConnectToDeviceView: View { + var body: some View { + ContainerViewWithHeader { + navigationBarView + } content: { + VStack { + RegularText(LocalizablePeerToPeer.scanCode.localized, size: 18) + .padding(.top, 74) + + qrCodeView.padding(.bottom, 40) + RegularText(LocalizablePeerToPeer.havingTrouble.localized) + connectManuallyButton + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + + } + } + var navigationBarView: some View { + NavigationHeaderView(title: LocalizablePeerToPeer.connectToDevice.localized, + navigationBarType: .inline, + backButtonAction: {self.popToRoot()}, + rightButtonType: .none) + } + var qrCodeView: some View { + ResizableImage("qrCode.icon").frame(width: 248, height: 248) + //TODO: Add a QRCode reader + } + + var connectManuallyButton: some View { + TellaButtonView(title: LocalizablePeerToPeer.connectManually.localized.uppercased(), + nextButtonAction: .destination, + destination: TellaWebServerLoginView(), + isValid: .constant(true), + buttonRole: .secondary) + .padding([.leading, .trailing], 80) + } +} + +#Preview { + SenderConnectToDeviceView() +} diff --git a/Tella/Scenes/Nextcloud/ViewModels/NextcloudDraftViewModel.swift b/Tella/Scenes/Nextcloud/ViewModels/NextcloudDraftViewModel.swift index 7c3855313..21862f15e 100644 --- a/Tella/Scenes/Nextcloud/ViewModels/NextcloudDraftViewModel.swift +++ b/Tella/Scenes/Nextcloud/ViewModels/NextcloudDraftViewModel.swift @@ -34,7 +34,7 @@ class NextcloudDraftViewModel: DraftMainViewModel { self.description = report.description ?? "" if let vaultFileResult = mainAppModel.vaultFilesManager?.getVaultFiles(ids: report.reportFiles?.compactMap{ $0.fileId } ?? []) { - self.files = Set(vaultFileResult) + addFilesViewModel.files = Set(vaultFileResult) } validateTitleAndDescription() @@ -42,7 +42,7 @@ class NextcloudDraftViewModel: DraftMainViewModel { override func saveReport() { - let reportFiles = self.files.compactMap {NextcloudReportFile(fileId: $0.id, + let reportFiles = addFilesViewModel.files.compactMap {NextcloudReportFile(fileId: $0.id, status: .notSubmitted, bytesSent: 0, createdDate: Date(), @@ -57,19 +57,7 @@ class NextcloudDraftViewModel: DraftMainViewModel { reportId == nil ? addReport(report: report) : updateReport(report: report) } - - override func deleteFile(fileId: String?) { - guard let index = files.firstIndex(where: { $0.id == fileId}) else {return } - files.remove(at: index) - } - - override func bindVaultFileTaken() { - $resultFile.sink(receiveValue: { value in - guard let value else { return } - self.files.insert(value) - }).store(in: &subscribers) - } - + private func addReport(report: NextcloudReport) { let reportId = mainAppModel.tellaData?.addNextcloudReport(report: report) diff --git a/Tella/Scenes/Reports/View Model/DraftReportVM.swift b/Tella/Scenes/Reports/View Model/DraftReportVM.swift index 6e30db469..7b6f4b066 100644 --- a/Tella/Scenes/Reports/View Model/DraftReportVM.swift +++ b/Tella/Scenes/Reports/View Model/DraftReportVM.swift @@ -10,6 +10,7 @@ import Combine import SwiftUI class DraftReportVM: DraftMainViewModel { + override init(reportId:Int? = nil, reportsMainViewModel: ReportsMainViewModel) { super.init(reportId: reportId, reportsMainViewModel: reportsMainViewModel) } @@ -18,14 +19,6 @@ class DraftReportVM: DraftMainViewModel { serverArray = mainAppModel.tellaData?.getTellaServers() ?? [] } - override func bindVaultFileTaken() { - $resultFile.sink(receiveValue: { value in - guard let value else { return } - self.files.insert(value) - self.publishUpdates() - }).store(in: &subscribers) - } - override func publishUpdates() { DispatchQueue.main.async { self.objectWillChange.send() @@ -41,7 +34,7 @@ class DraftReportVM: DraftMainViewModel { if let vaultFileResult = mainAppModel.vaultFilesManager?.getVaultFiles(ids: report.reportFiles?.compactMap{$0.fileId} ?? [] ) { let vaultFileResult = Set(vaultFileResult) - self.files = vaultFileResult + addFilesViewModel.files = vaultFileResult } } @@ -55,7 +48,7 @@ class DraftReportVM: DraftMainViewModel { description: description, status: status, server: server as? TellaServer, - vaultFiles: self.files.compactMap{ ReportFile(fileId: $0.id, + vaultFiles: addFilesViewModel.files.compactMap{ ReportFile(fileId: $0.id, status: .notSubmitted, bytesSent: 0, createdDate: Date())}, @@ -85,11 +78,6 @@ class DraftReportVM: DraftMainViewModel { } } - override func deleteFile(fileId: String?) { - guard let index = files.firstIndex(where: { $0.id == fileId}) else {return } - files.remove(at: index) - } - override func deleteReport() { mainAppModel.deleteReport(reportId: reportId) mainAppModel.deleteReport(reportId: reportId) diff --git a/Tella/Scenes/Settings/Connections/Views/AddServerURLView.swift b/Tella/Scenes/Settings/Connections/Views/AddServerURLView.swift index 6c9189f26..64476d553 100644 --- a/Tella/Scenes/Settings/Connections/Views/AddServerURLView.swift +++ b/Tella/Scenes/Settings/Connections/Views/AddServerURLView.swift @@ -36,7 +36,7 @@ struct AddServerURLView: View { fieldType: .url) Spacer() - BottomLockView(isValid: $viewModel.validURL, + NavigationBottomView(shouldActivateNext: $viewModel.validURL, nextButtonAction: .action, nextAction: { self.viewModel.checkURL() diff --git a/Tella/Scenes/Settings/Connections/Views/ServerCreateFolderView.swift b/Tella/Scenes/Settings/Connections/Views/ServerCreateFolderView.swift index a0bc3bc8e..e72b6d573 100644 --- a/Tella/Scenes/Settings/Connections/Views/ServerCreateFolderView.swift +++ b/Tella/Scenes/Settings/Connections/Views/ServerCreateFolderView.swift @@ -69,7 +69,7 @@ struct ServerCreateFolderView: View { } var bottomView: some View { - BottomLockView(isValid: $isValid, + NavigationBottomView(shouldActivateNext: $isValid, nextButtonAction: .action, shouldHideNext: createFolderViewModel.createFolderState == .loading, shouldHideBack: false, diff --git a/Tella/Scenes/Settings/Connections/Views/ServerLoginView.swift b/Tella/Scenes/Settings/Connections/Views/ServerLoginView.swift index 9dfbe2150..033a381ec 100644 --- a/Tella/Scenes/Settings/Connections/Views/ServerLoginView.swift +++ b/Tella/Scenes/Settings/Connections/Views/ServerLoginView.swift @@ -29,7 +29,7 @@ struct ServerLoginView: View { Spacer() }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) - BottomLockView(isValid: $viewModel.validPassword, + NavigationBottomView(shouldActivateNext: $viewModel.validPassword, nextButtonAction: .action, shouldHideNext: true) } @@ -66,8 +66,8 @@ struct ServerLoginView: View { .frame(height: 57) } - fileprivate func loginButtonView() -> TellaButtonView { - return TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, + fileprivate func loginButtonView() -> TellaButtonView { + return TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, nextButtonAction: .action, isValid: $viewModel.validCredentials) { UIApplication.shared.endEditing() diff --git a/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift b/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift index 8a92a9382..4aca7ce32 100644 --- a/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift +++ b/Tella/Scenes/Settings/ViewModel/ServersViewModel.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -17,19 +17,19 @@ class ServersViewModel: ObservableObject { @Published var unavailableServers: [Server] = [] @Published var shouldEnableNextButton: Bool = false @Published var selectedServerType: ServerConnectionType? - + private var subscribers = Set() let gDriveRepository : GDriveRepositoryProtocol let dropboxRepository : DropboxRepositoryProtocol - + init(mainAppModel : MainAppModel, gDriveRepository : GDriveRepositoryProtocol = GDriveRepository(), dropboxRepository : DropboxRepositoryProtocol = DropboxRepository()) { self.gDriveRepository = gDriveRepository self.dropboxRepository = dropboxRepository - + self.mainAppModel = mainAppModel self.getServers() @@ -62,7 +62,6 @@ class ServersViewModel: ObservableObject { default: break } - } func deleteAllServersConnection() { @@ -75,4 +74,11 @@ class ServersViewModel: ObservableObject { return serverConnections.filter { connection in !unavailableTypes.contains(connection.type) } - }} + } +} + +extension ServersViewModel { + static func stub() -> ServersViewModel { + return ServersViewModel(mainAppModel: MainAppModel.stub()) + } +} diff --git a/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift b/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift index 67145c83c..ee3eef548 100644 --- a/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift +++ b/Tella/Scenes/Settings/ViewModel/UwaziServerViewModel.swift @@ -123,7 +123,6 @@ class UwaziServerViewModel: ServerViewModel { self.languages.append(contentsOf: wrapper.rows ?? []) if let server = self.currentServer { let locale = server.locale - dump(locale) self.selectedLanguage = self.languages.compactMap{$0}.first(where: {$0.locale == locale}) } self.showNextSuccessLoginView = true @@ -212,7 +211,7 @@ class UwaziServerViewModel: ServerViewModel { // if the status code is 409 then 2FA is needed let httpError = HTTPErrorCodes(rawValue: code) ?? .unknown switch httpError { - case .need2FA: + case .conflict: self.showNext2FAView = true default: self.shouldShowLoginError = true diff --git a/Tella/Scenes/Settings/Views/Common/SettingToggleItem.swift b/Tella/Scenes/Settings/Views/Common/SettingToggleItem.swift index e878e0adc..a5c1f0fb9 100644 --- a/Tella/Scenes/Settings/Views/Common/SettingToggleItem.swift +++ b/Tella/Scenes/Settings/Views/Common/SettingToggleItem.swift @@ -1,5 +1,5 @@ // -// Copyright © 2021 HORIZONTAL. +// Copyright © 2021 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -10,23 +10,38 @@ struct SettingToggleItem: View { let title: String let description: String + var linkText: String? = nil + var link: String? = nil + @Binding var toggle: Bool @EnvironmentObject var appModel : MainAppModel - var isDisabled: Bool = false + var isDisabled: Bool = false var withPadding: Bool = true var onChange : (() -> ())? - + var body: some View { HStack{ - VStack(alignment: .leading){ - Text(title) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(Color.white).padding(.bottom, -5) + VStack(alignment: .leading, spacing: 2){ - Text(description) - .foregroundColor(Color.white) - .font(.custom(Styles.Fonts.regularFontName, size: 12)) - .fixedSize(horizontal: false, vertical: true) + CustomText(title, + style: .body1Style, + alignment: .leading) + + CustomText(description, + style: .buttonDetailRegularStyle, + alignment: .leading) + + if let link, + let linkText { + Button { + link.url()?.open() + } label: { + CustomText(linkText, + style: .buttonDetailRegularStyle, + alignment: .leading, + color: Styles.Colors.yellow) + } + } } Spacer() Toggle("", isOn: $toggle) diff --git a/Tella/Scenes/Settings/Views/Common/SettingsCardView.swift b/Tella/Scenes/Settings/Views/Common/SettingsCardView.swift index 8dd9b6c81..615a4e808 100644 --- a/Tella/Scenes/Settings/Views/Common/SettingsCardView.swift +++ b/Tella/Scenes/Settings/Views/Common/SettingsCardView.swift @@ -9,7 +9,7 @@ import SwiftUI struct SettingsCardView : View { - var cardViewArray : [T?] + var cardViewArray : [T] var body : some View { diff --git a/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift b/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift index 7cc2c768e..30e291339 100644 --- a/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift +++ b/Tella/Scenes/Settings/Views/Feedback/FeedbackView.swift @@ -148,7 +148,7 @@ struct FeedbackView: View { var submitButton : some View { - TellaButtonView (title: LocalizableSettings.submit.localized , + TellaButtonView(title: LocalizableSettings.submit.localized , nextButtonAction: .action, buttonType: .yellow, isValid: $feedbackViewModel.feedbackIsValid) { diff --git a/Tella/Scenes/Settings/Views/General/GeneralView.swift b/Tella/Scenes/Settings/Views/General/GeneralView.swift index a7b4bf1c8..af5427092 100644 --- a/Tella/Scenes/Settings/Views/General/GeneralView.swift +++ b/Tella/Scenes/Settings/Views/General/GeneralView.swift @@ -12,7 +12,6 @@ import SwiftUI struct GeneralView: View { @EnvironmentObject var appModel : MainAppModel - @EnvironmentObject var settingsModel : SettingsModel @EnvironmentObject var appViewState : AppViewState @State private var presentingLanguage = false diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerAccessChoiceView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerAccessChoiceView.swift index 9dd3460d9..d863f0cc2 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerAccessChoiceView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/AddServerAccessChoiceView.swift @@ -33,7 +33,7 @@ struct AddServerAccessChoiceView: View { Spacer() .frame(height: 12) - TellaButtonView (title: "NO", + TellaButtonView(title: "NO", nextButtonAction: .destination, destination: TellaWebServerLoginView().environmentObject(serversViewModel), isValid: .constant(true)) @@ -42,7 +42,7 @@ struct AddServerAccessChoiceView: View { }.padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24)) - BottomLockView(isValid: .constant(true), + NavigationBottomView(shouldActivateNext: .constant(true), nextButtonAction: .action, shouldHideNext: true, backAction: { diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift index 117d3157c..744e1a2d9 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/SettingsAddServerCardView.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -11,18 +11,28 @@ struct SettingsAddServerCardView: View { @EnvironmentObject var serversViewModel : ServersViewModel @EnvironmentObject var settingViewModel: SettingsViewModel - + var body: some View { ZStack { HStack{ - VStack(alignment: .leading) { - Text(LocalizableSettings.settConnections.localized) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(Color.white).padding(.bottom, -5) + VStack(alignment: .leading, spacing: 2) { + + CustomText(LocalizableSettings.settAddConnection.localized, + style: .body1Style, + alignment: .leading) + + CustomText(LocalizableSettings.settAddConnectionExpl.localized, + style: .buttonDetailRegularStyle, + alignment: .leading) - Text("If you work with an organization, connect to its server to send files and data. Your organization should provide you with the server details.") - .foregroundColor(Color.white) - .font(.custom(Styles.Fonts.regularFontName, size: 12)) + Button { + TellaUrls.connectionLearnMore.url()?.open() + } label: { + CustomText(LocalizableSettings.settAddConnectionLearnMore.localized, + style: .buttonDetailRegularStyle, + alignment: .leading, + color: Styles.Colors.yellow) + } } Spacer() diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessAdvancedSettingsView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessAdvancedSettingsView.swift index 0596f5ef1..2154716d7 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessAdvancedSettingsView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessAdvancedSettingsView.swift @@ -23,7 +23,7 @@ struct SuccessAdvancedSettingsView: View { Spacer() .frame(height: 48) - TellaButtonView (title: "OK", + TellaButtonView(title: "OK", nextButtonAction: .action, buttonType: .yellow, isValid: .constant(true)) { @@ -39,7 +39,7 @@ struct SuccessAdvancedSettingsView: View { var topview: some View { VStack { - Image("settings.checked-circle") + Image("checked-circle") Spacer() .frame(height: 16) diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift index d527f09eb..067b36a21 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/SuccessLoginView.swift @@ -29,7 +29,7 @@ struct SuccessLoginView: View { Spacer() .frame(height: 48) - TellaButtonView (title: type.successConnectionButtonContent, + TellaButtonView(title: type.successConnectionButtonContent, nextButtonAction: .action, buttonType: .yellow, isValid: .constant(true)) { @@ -40,7 +40,7 @@ struct SuccessLoginView: View { .frame(height: 12) if type == .tella { - TellaButtonView (title: LocalizableSettings.advancedSettings.localized, + TellaButtonView(title: LocalizableSettings.advancedSettings.localized, nextButtonAction: .destination, destination: AdvancedServerSettingsView() .environmentObject(serverViewModel), @@ -55,7 +55,7 @@ struct SuccessLoginView: View { var topview: some View { VStack { - Image("settings.checked-circle") + Image("checked-circle") Spacer() .frame(height: 16) diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebAddServerURLView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebAddServerURLView.swift index 8c0fd2aac..fa3879623 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebAddServerURLView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebAddServerURLView.swift @@ -50,7 +50,7 @@ struct TellaWebAddServerURLView: View { fieldType: .url) Spacer() - BottomLockView(isValid: $serverViewModel.validURL, + NavigationBottomView(shouldActivateNext: $serverViewModel.validURL, nextButtonAction: .action, nextAction: { // serverViewModel.checkURL() diff --git a/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebServerLoginView.swift b/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebServerLoginView.swift index 227e2410a..07f446f93 100644 --- a/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebServerLoginView.swift +++ b/Tella/Scenes/Settings/Views/Servers/AddServer/TellaWebServerLoginView.swift @@ -57,7 +57,7 @@ struct TellaWebServerLoginView: View { Spacer() .frame(height: 32) - TellaButtonView(title: "LOG IN", + TellaButtonView(title: "LOG IN", nextButtonAction: .action, isValid: $serverViewModel.validCredentials) { UIApplication.shared.endEditing() @@ -69,7 +69,7 @@ struct TellaWebServerLoginView: View { }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) - BottomLockView(isValid: $serverViewModel.validPassword, + NavigationBottomView(shouldActivateNext: $serverViewModel.validPassword, nextButtonAction: .action, shouldHideNext: true) } diff --git a/Tella/Scenes/Settings/Views/Servers/GDrive/SelectDriveConnectionView.swift b/Tella/Scenes/Settings/Views/Servers/GDrive/SelectDriveConnectionView.swift index 0ec4d50de..1b8a6c570 100644 --- a/Tella/Scenes/Settings/Views/Servers/GDrive/SelectDriveConnectionView.swift +++ b/Tella/Scenes/Settings/Views/Servers/GDrive/SelectDriveConnectionView.swift @@ -51,14 +51,14 @@ struct SelectDriveConnectionView: View { var connectionsButtons: some View { VStack(spacing: 14) { - TellaButtonView( + TellaButtonView( title: LocalizableSettings.gDriveSelectTypeShared.localized, nextButtonAction: .action, isOverlay: selectedDriveConnectionType == .shared, isValid: $gDriveServerViewModel.isSharedDriveButtonValid, action: { selectedDriveConnectionType = .shared } ) - TellaButtonView( + TellaButtonView( title: LocalizableSettings.gDriveSelectTypePersonal.localized, nextButtonAction: .action, isOverlay: selectedDriveConnectionType == .personal, @@ -80,7 +80,7 @@ struct SelectDriveConnectionView: View { } var bottomView: some View { - BottomLockView(isValid: .constant(true), + NavigationBottomView(shouldActivateNext: .constant(true), nextButtonAction: .action, shouldHideNext: selectedDriveConnectionType == .none, shouldHideBack: false, diff --git a/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift b/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift index cf5fcda2e..74b158243 100644 --- a/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift +++ b/Tella/Scenes/Settings/Views/Servers/ServerSelectionView.swift @@ -63,7 +63,7 @@ struct ServerSelectionView: View { fileprivate func buttonViews() -> some View { return Group { ForEach(serversViewModel.filterServerConnections(), id: \.type) { connection in - TellaButtonView( + TellaButtonView( title: connection.title, nextButtonAction: .action, isOverlay: serversViewModel.selectedServerType == connection.type, @@ -78,8 +78,8 @@ struct ServerSelectionView: View { } } - fileprivate func bottomView() -> BottomLockView { - return BottomLockView(isValid: $serversViewModel.shouldEnableNextButton, + fileprivate func bottomView() -> NavigationBottomView { + return NavigationBottomView(shouldActivateNext: $serversViewModel.shouldEnableNextButton, nextButtonAction: .action, shouldHideBack: true, nextAction: { @@ -138,7 +138,7 @@ struct ServerSelectionView: View { SectionTitle(text: LocalizableSettings.settServerUnavailableConnectionsTitle.localized) SectionMessage(text: LocalizableSettings.settServerUnavailableConnectionsDesc.localized) ForEach(serversViewModel.unavailableServers, id: \.serverType) { server in - TellaButtonView( + TellaButtonView( title: server.serverType?.serverTitle ?? "", nextButtonAction: .action, isValid: .constant(false) @@ -186,7 +186,7 @@ struct ServerSelectionView: View { } label: { Text(LocalizableSettings.settServerSelectionPart1Message.localized) .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(Color.yellow) + .foregroundColor(Styles.Colors.yellow) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) } diff --git a/Tella/Scenes/Settings/Views/Servers/ServersListView.swift b/Tella/Scenes/Settings/Views/Servers/ServersListView.swift index bf23e7a7e..a9903c33b 100644 --- a/Tella/Scenes/Settings/Views/Servers/ServersListView.swift +++ b/Tella/Scenes/Settings/Views/Servers/ServersListView.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -9,11 +9,8 @@ import SwiftUI struct ServersListView: View { - @EnvironmentObject var serversViewModel : ServersViewModel + @StateObject var serversViewModel : ServersViewModel @EnvironmentObject var sheetManager: SheetManager - @EnvironmentObject var mainAppModel : MainAppModel - @EnvironmentObject var settingViewModel : SettingsViewModel - @State var shouldShowEditServer : Bool = false var body: some View { @@ -24,7 +21,9 @@ struct ServersListView: View { contentView } .fullScreenCover(isPresented: $shouldShowEditServer, content: { - EditSettingsServerView(appModel: mainAppModel, isPresented: $shouldShowEditServer, server: mainAppModel.tellaData?.getTellaServer(serverId: (serversViewModel.currentServer?.id)!)) + EditSettingsServerView(appModel: serversViewModel.mainAppModel, + isPresented: $shouldShowEditServer, + server: serversViewModel.mainAppModel.tellaData?.getTellaServer(serverId: (serversViewModel.currentServer?.id)!)) }) } @@ -40,6 +39,8 @@ struct ServersListView: View { SettingsCardView (cardViewArray: serversView()) + SettingsCardView(cardViewArray: [nearbySharingView.eraseToAnyView()]) + Spacer() }.scrollOnOverflow() } @@ -56,6 +57,14 @@ struct ServersListView: View { return arrayView } + private var nearbySharingView: some View { + SettingToggleItem(title: LocalizableSettings.settNearbySharing.localized, + description: LocalizableSettings.settNearbySharingExpl.localized, + linkText:LocalizableSettings.settNearbySharingLearnMore.localized, + link:TellaUrls.nearbySharingLearnMore, + toggle: $serversViewModel.mainAppModel.settings.nearbySharing) + } + private func showServerActionBottomSheet(server:Server) { let items = serverActionItems(server: server) let modalHeight = items.count == 1 ? 140 : 175 @@ -106,7 +115,7 @@ struct ServersListView: View { } fileprivate func navigateToUwaziAddServerView(_ server: UwaziServer) { - navigateTo(destination: UwaziAddServerURLView(uwaziServerViewModel: UwaziServerViewModel(mainAppModel: mainAppModel, currentServer: server)) + navigateTo(destination: UwaziAddServerURLView(uwaziServerViewModel: UwaziServerViewModel(mainAppModel: serversViewModel.mainAppModel, currentServer: server)) .environmentObject(serversViewModel)) } private func serverActionItems(server: Server) -> [ListActionSheetItem]{ @@ -129,6 +138,6 @@ struct ServersListView: View { struct ServersListView_Previews: PreviewProvider { static var previews: some View { - ServersListView() + ServersListView(serversViewModel: ServersViewModel.stub()) } } diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift index c627027aa..e4133296a 100644 --- a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step2/UwaziServerAccessSelectionView.swift @@ -33,7 +33,7 @@ struct UwaziServerAccessSelectionView: View { buttonView() } Spacer() - BottomLockView(isValid: $isButtonValid, + NavigationBottomView(shouldActivateNext: $isButtonValid, nextButtonAction: .action, nextAction: { handleNavigation() @@ -58,13 +58,13 @@ struct UwaziServerAccessSelectionView: View { } fileprivate func buttonView() -> some View { return VStack(spacing: 12) { - TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, + TellaButtonView(title: LocalizableSettings.UwaziLogin.localized, nextButtonAction: .action, isOverlay: accessServerType == .privateServer, isValid: .constant(true),action: { accessServerType = .privateServer }) - TellaButtonView(title: LocalizableSettings.UwaziPublicInstance.localized, + TellaButtonView(title: LocalizableSettings.UwaziPublicInstance.localized, nextButtonAction: .action, isOverlay: accessServerType == .publicServer, isValid: .constant(true),action: { diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift index cb00460b9..59b027a62 100644 --- a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step4/UwaziTwoStepVerification.swift @@ -38,7 +38,7 @@ struct UwaziTwoStepVerification: View { .frame(height: 57) Spacer() .frame(height: 19) - TellaButtonView(title: LocalizableSettings.UwaziAuthenticationVerify.localized, + TellaButtonView(title: LocalizableSettings.UwaziAuthenticationVerify.localized, nextButtonAction: .action, isValid: $uwaziServerViewModel.validAuthenticationCode) { UIApplication.shared.endEditing() @@ -48,7 +48,7 @@ struct UwaziTwoStepVerification: View { } .padding(.leading, 23) .padding(.trailing,23) - BottomLockView(isValid: .constant(true), + NavigationBottomView(shouldActivateNext: .constant(true), nextButtonAction: .action, shouldHideNext: true) } diff --git a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift index 1e25fc8f1..c9e63948c 100644 --- a/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift +++ b/Tella/Scenes/Settings/Views/Servers/Uwazi/Step6/UwaziSuccessView.swift @@ -21,9 +21,9 @@ struct UwaziSuccessView: View { .foregroundColor(.white) .multilineTextAlignment(.center) Spacer().frame(height: 53) - Image("settings.checked-circle") + Image("checked-circle") Spacer() - BottomLockView(isValid: .constant(true), + NavigationBottomView(shouldActivateNext: .constant(true), nextButtonAction: .action, shouldHideNext: false, shouldHideBack: true, diff --git a/Tella/Scenes/Settings/Views/SettingsMainView.swift b/Tella/Scenes/Settings/Views/SettingsMainView.swift index d65d4bf19..473b3c049 100644 --- a/Tella/Scenes/Settings/Views/SettingsMainView.swift +++ b/Tella/Scenes/Settings/Views/SettingsMainView.swift @@ -1,6 +1,6 @@ // Tella // -// Copyright © 2022 HORIZONTAL. +// Copyright © 2022 HORIZONTAL. // Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) // @@ -63,8 +63,6 @@ struct SettingsMainView: View { GeneralView().environmentObject(settingsViewModel)) } - - var securityView: some View { SettingsItemView(imageName: "settings.lock", title: LocalizableSettings.settSecAppBar.localized, @@ -74,8 +72,7 @@ struct SettingsMainView: View { var serversView: some View { SettingsItemView(imageName: "settings.servers", title: LocalizableSettings.settConnections.localized, - destination:ServersListView() - .environmentObject(serversViewModel)) + destination:ServersListView(serversViewModel:serversViewModel)) } var helpView: some View { diff --git a/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift b/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift deleted file mode 100644 index 77c89b486..000000000 --- a/Tella/Scenes/Uwazi/Models/UwaziAttachmentsActionItems.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// UwaziAttachmentsActionItems.swift -// Tella -// -// Created by Gustavo on 02/11/2023. -// Copyright © 2023 HORIZONTAL. -// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) -// - - -import Foundation - -var addFileToDraftItems : [ListActionSheetItem] { return [ - - ListActionSheetItem(imageName: "report.camera-filled", - content: LocalizableReport.cameraFilled.localized, - type: ManageFileType.camera), - ListActionSheetItem(imageName: "report.mic-filled", - content: LocalizableReport.micFilled.localized, - type: ManageFileType.recorder), - ListActionSheetItem(imageName: "report.gallery", - content: LocalizableReport.galleryFilled.localized, - type: ManageFileType.tellaFile), - ListActionSheetItem(imageName: "report.phone", - content: LocalizableReport.phoneFilled.localized, - type: ManageFileType.fromDevice) - ]} - -var addFileToPdfItems: [ListActionSheetItem] { return [ - ListActionSheetItem(imageName: "report.gallery", - content: LocalizableReport.galleryFilled.localized, - type: ManageFileType.tellaFile), - ListActionSheetItem(imageName: "report.phone", - content: LocalizableReport.phoneFilled.localized, - type: ManageFileType.fromDevice) -]} diff --git a/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift b/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift index db8abc02a..6d9eaeeee 100644 --- a/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift +++ b/Tella/Scenes/Uwazi/ViewModel/UwaziEntityViewModel.swift @@ -20,19 +20,15 @@ class UwaziEntityViewModel: ObservableObject { var templateName: String = "" @Published var entryPrompts: [any UwaziEntryPrompt] = [] - @Published var resultFile : [VaultFileDB]? - + @Published var showingSuccessMessage : Bool = false - @Published var showingImagePicker : Bool = false - @Published var showingImportDocumentPicker : Bool = false - @Published var showingFileList : Bool = false - @Published var showingRecordView : Bool = false - @Published var showingCamera : Bool = false - + @Published var uwaziEntityParser : UwaziEntityParser? @Published var shouldHideView : Bool = false @Published var entityFetcher: UwaziEntityFetcher? = nil + //MARK: -AddFilesViewModel + @Published var addFilesViewModel: AddFilesViewModel var subscribers = Set() init(mainAppModel : MainAppModel, @@ -40,7 +36,8 @@ class UwaziEntityViewModel: ObservableObject { entityInstanceId:Int?) { self.mainAppModel = mainAppModel - + self.addFilesViewModel = AddFilesViewModel(mainAppModel: mainAppModel) + let entityInstance = getInstanceById(entityId: entityInstanceId) let templateId = templateId ?? entityInstance?.templateId @@ -102,7 +99,7 @@ class UwaziEntityViewModel: ObservableObject { } private func bindVaultFileTaken() { - $resultFile + addFilesViewModel.$resultFile .sink(receiveValue: { [weak self] files in guard let self = self, let files = files else { return } diff --git a/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift b/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift index 140bd1d12..69aa7c394 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/CreateEntityView.swift @@ -30,10 +30,10 @@ struct CreateEntityView: View { contentView } - photoVideoPickerView + AddFilePhotoVideoPickerView(viewModel: entityViewModel.addFilesViewModel) } - .overlay(cameraView) - .overlay(recordView) + .overlay(AddFileCameraView(viewModel: entityViewModel.addFilesViewModel)) + .overlay(AddFileRecordView(viewModel: entityViewModel.addFilesViewModel)) .onReceive(entityViewModel.$shouldHideView, perform: { shouldHideView in if shouldHideView { dismissViews() @@ -62,9 +62,7 @@ struct CreateEntityView: View { ScrollView { VStack(alignment: .leading) { ForEach(entityViewModel.entryPrompts, id: \.id) { prompt in - RenderPropertyComponentView(prompt: prompt) - .environmentObject(sheetManager) - .environmentObject(entityViewModel) + RenderPropertyComponentView(prompt: prompt, entityViewModel: entityViewModel) } }.padding(EdgeInsets(top: 12, leading: 16, bottom: 0, trailing: 16)) } @@ -88,30 +86,7 @@ struct CreateEntityView: View { } .buttonStyle(PlainButtonStyle()) } - - var cameraView : some View { - entityViewModel.showingCamera ? - CameraView(sourceView: SourceView.addReportFile, - showingCameraView: $entityViewModel.showingCamera, - resultFile: $entityViewModel.resultFile, - mainAppModel: entityViewModel.mainAppModel) : nil - } - - var recordView : some View { - entityViewModel.showingRecordView ? - RecordView(appModel: entityViewModel.mainAppModel, - sourceView: .addReportFile, - showingRecoredrView: $entityViewModel.showingRecordView, - resultFile: $entityViewModel.resultFile) : nil - } - - var photoVideoPickerView : some View { - PhotoVideoPickerView(showingImagePicker: $entityViewModel.showingImagePicker, - showingImportDocumentPicker: $entityViewModel.showingImportDocumentPicker, - appModel: entityViewModel.mainAppModel, - resultFile: $entityViewModel.resultFile) - } - + private func showSaveEntityConfirmationView() { sheetManager.showBottomSheet(modalHeight: modelHeight) { ConfirmBottomSheet(titleText: LocalizableUwazi.uwaziEntityExitSheetTitle.localized, diff --git a/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift b/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift index 2ec8ae5a7..12fba16f9 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/RenderPropertyComponentView.swift @@ -13,8 +13,7 @@ import SwiftUI struct RenderPropertyComponentView: View { var prompt: any UwaziEntryPrompt - @EnvironmentObject var sheetManager: SheetManager - @EnvironmentObject var entityViewModel: UwaziEntityViewModel + @ObservedObject var entityViewModel: UwaziEntityViewModel var body: some View { @@ -31,13 +30,13 @@ struct RenderPropertyComponentView: View { switch prompt.type { case .dataTypeText, .dataTypeNumeric, .dataTypeMarkdown: - UwaziTextWidget(prompt: prompt as! UwaziTextEntryPrompt) + UwaziTextWidget(prompt: prompt as! UwaziTextEntryPrompt, entityViewModel: entityViewModel) case .dataTypeSelect: - UwaziSelectWidget(prompt: prompt as! UwaziSelectEntryPrompt) + UwaziSelectWidget(prompt: prompt as! UwaziSelectEntryPrompt, entityViewModel: entityViewModel) case .dataTypeMultiFiles: - SupportingFileWidget(prompt: prompt as! UwaziFilesEntryPrompt) + SupportingFileWidget(prompt: prompt as! UwaziFilesEntryPrompt, entityViewModel: entityViewModel) case .dataTypeMultiPDFFiles: - PrimaryDocuments(prompt: prompt as! UwaziFilesEntryPrompt) + PrimaryDocuments(prompt: prompt as! UwaziFilesEntryPrompt, entityViewModel: entityViewModel) case .dataTypeDivider: UwaziDividerWidget() case .dataTypeDate: diff --git a/Tella/Scenes/Uwazi/Views/Entity/SummaryEntityView.swift b/Tella/Scenes/Uwazi/Views/Entity/SummaryEntityView.swift index d99f512cf..572ffc1ee 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/SummaryEntityView.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/SummaryEntityView.swift @@ -109,7 +109,7 @@ struct SummaryEntityView: View { HStack { - TellaButtonView (title: "SUBMIT LATER", + TellaButtonView(title: "SUBMIT LATER", nextButtonAction: .action, buttonType: .clear, isValid: .constant(true)) { @@ -120,7 +120,7 @@ struct SummaryEntityView: View { Spacer() - TellaButtonView (title: "SUBMIT", + TellaButtonView(title: "SUBMIT", nextButtonAction: .action, buttonType: .yellow, isValid: .constant(true)) { diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift deleted file mode 100644 index ae554e28a..000000000 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziFileSelector.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// UwaziFileSelector.swift -// Tella -// -// Created by Gustavo on 24/10/2023. -// Copyright © 2023 HORIZONTAL. -// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) -// - - -import SwiftUI - -struct UwaziFileSelector: View { - @ObservedObject var prompt: UwaziFilesEntryPrompt - var addFiles: () -> Void - var title: String - var body: some View { - VStack { - Spacer() - Text(prompt.helpText!) - .font(.custom(Styles.Fonts.regularFontName, size: 12)) - .foregroundColor(Color.white.opacity(0.87)) - .frame(maxWidth: .infinity, alignment: .leading) - Button { - addFiles() - } label: { - SelectFileComponent(title: title) - } - .background(Color.white.opacity(0.08)) - .cornerRadius(15) - } - } -} - -struct SelectFileComponent: View { - let title: String - - var body: some View { - HStack { - Image("uwazi.add-files") - .padding(.vertical, 20) - Text(title) - .font(.custom(Styles.Fonts.regularFontName, size: 14)) - .foregroundColor(Color.white.opacity(0.87)) - .frame(maxWidth: .infinity, alignment: .leading) - }.padding(.horizontal, 16) - } -} - -//struct UwaziFileSelector_Previews: PreviewProvider { -// static var previews: some View { -// UwaziFileSelector(addFiles: {}, title: "Title") -// } -//} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziSelectFileComponent.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziSelectFileComponent.swift new file mode 100644 index 000000000..830f402cc --- /dev/null +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/Common/UwaziSelectFileComponent.swift @@ -0,0 +1,31 @@ +// +// UwaziSelectFileComponent.swift +// Tella +// +// Created by Gustavo on 24/10/2023. +// Copyright © 2023 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import SwiftUI + +struct UwaziSelectFileComponent: View { + let title: String + + var body: some View { + HStack { + Image("uwazi.add-files") + .padding(.vertical, 20) + Text(title) + .font(.custom(Styles.Fonts.regularFontName, size: 14)) + .foregroundColor(Color.white.opacity(0.87)) + .frame(maxWidth: .infinity, alignment: .leading) + }.padding(.horizontal, 16) + } +} + +struct UwaziFileSelector_Previews: PreviewProvider { + static var previews: some View { + UwaziSelectFileComponent(title: "Title") + } +} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift index fc0664601..7f079c87a 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/PrimaryDocuments.swift @@ -12,62 +12,33 @@ import SwiftUI struct PrimaryDocuments: View { @ObservedObject var prompt: UwaziFilesEntryPrompt - @EnvironmentObject var sheetManager: SheetManager - @EnvironmentObject var entityViewModel: UwaziEntityViewModel + @ObservedObject var entityViewModel: UwaziEntityViewModel - var body: some View { - UwaziFileSelector(prompt: prompt, addFiles: { - UIApplication.shared.endEditing() - showAddFileSheet() - }, title: LocalizableUwazi.uwaziMultiFileWidgetAttachManyPDFFilesSelectTitle.localized) - FileItems(files: prompt.value) - - } - - func showAddFileSheet() { - - sheetManager.showBottomSheet( modalHeight: CGFloat(200), content: { - ActionListBottomSheet(items: addFileToPdfItems, - headerTitle: LocalizableUwazi.uwaziEntitySelectFiles.localized, - action: {item in - self.handleActions(item : item) - }) - }) - } - - func showAddPhotoVideoSheet() { - entityViewModel.showingImportDocumentPicker = true - } - - var fileListView : some View { - FileListView(appModel: entityViewModel.mainAppModel, - filterType: .documents, - title: LocalizableReport.selectFiles.localized, - fileListType: .selectFiles, - resultFile: $entityViewModel.resultFile) + init(prompt: UwaziFilesEntryPrompt, entityViewModel: UwaziEntityViewModel) { + self.prompt = prompt + self.entityViewModel = entityViewModel } - private func handleActions(item: ListActionSheetItem) { - - guard let type = item.type as? ManageFileType else { return } - - switch type { - - case .fromDevice: - showAddPhotoVideoSheet() + var body: some View { + VStack { + Spacer() + Text(prompt.helpText!) + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(Color.white.opacity(0.87)) + .frame(maxWidth: .infinity, alignment: .leading) - case .tellaFile: - sheetManager.hide() - navigateTo(destination: fileListView) + AddFileBottomSheetView(viewModel: entityViewModel.addFilesViewModel, content: { + UwaziSelectFileComponent(title: LocalizableUwazi.uwaziMultiFileWidgetAttachManyPDFFilesSelectTitle.localized) + }, moreAction: { + entityViewModel.addFilesViewModel.shouldShowDocumentsOnly = true + }) + .background(Color.white.opacity(0.08)) + .cornerRadius(15) - default: - break } + + FileItems(files: prompt.value) + } + } - -//struct PrimaryDocuments_Previews: PreviewProvider { -// static var previews: some View { -// PrimaryDocuments() -// } -//} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift index 3826a3d7e..37557d71e 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/SupportingFileWidget.swift @@ -12,73 +12,32 @@ import SwiftUI struct SupportingFileWidget: View { @ObservedObject var prompt: UwaziFilesEntryPrompt - @EnvironmentObject var sheetManager: SheetManager - @EnvironmentObject var entityViewModel: UwaziEntityViewModel + @ObservedObject var entityViewModel: UwaziEntityViewModel - var body: some View { - UwaziFileSelector(prompt: prompt, addFiles: { - UIApplication.shared.endEditing() - showAddFileSheet() - }, title: LocalizableUwazi.uwaziEntitySelectFiles.localized) - .environmentObject(prompt) - if(prompt.value.count > 0) { - FileDropdown(files: $prompt.value) - } + init(prompt: UwaziFilesEntryPrompt, entityViewModel: UwaziEntityViewModel) { + self.prompt = prompt + self.entityViewModel = entityViewModel } - func showAddFileSheet() { - - sheetManager.showBottomSheet( modalHeight: CGFloat(300), content: { - ActionListBottomSheet(items: addFileToDraftItems, - headerTitle: LocalizableUwazi.uwaziEntitySelectFiles.localized, - action: {item in - self.handleActions(item : item) - }) + var body: some View { + VStack { + Spacer() + Text(prompt.helpText!) + .font(.custom(Styles.Fonts.regularFontName, size: 12)) + .foregroundColor(Color.white.opacity(0.87)) + .frame(maxWidth: .infinity, alignment: .leading) + AddFileBottomSheetView(viewModel: entityViewModel.addFilesViewModel, content: { + UwaziSelectFileComponent(title: LocalizableUwazi.uwaziEntitySelectFiles.localized) + }, moreAction: { + entityViewModel.addFilesViewModel.shouldShowDocumentsOnly = false }) + .background(Color.white.opacity(0.08)) + .cornerRadius(15) } - - func showAddPhotoVideoSheet() { - entityViewModel.showingImagePicker = true - } - - var fileListView : some View { - FileListView(appModel: entityViewModel.mainAppModel, - filterType: .audioPhotoVideo, - title: LocalizableReport.selectFiles.localized, - fileListType: .selectFiles, - resultFile: $entityViewModel.resultFile) - } - - private func handleActions(item: ListActionSheetItem) { - - guard let type = item.type as? ManageFileType else { return } - - switch type { - - case .camera: - sheetManager.hide() - entityViewModel.showingCamera = true - - case .recorder: - sheetManager.hide() - entityViewModel.showingRecordView = true - - case .fromDevice: - showAddPhotoVideoSheet() - - case .tellaFile: - sheetManager.hide() - navigateTo(destination: fileListView) - - default: - break - } + if(prompt.value.count > 0) { + FileDropdown(files: $prompt.value) } + } } -//struct SupportingFileWidget_Previews: PreviewProvider { -// static var previews: some View { -// SupportingFileWidget() -// } -//} diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift index 42b3caae5..ddbcc543b 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziDatePicker.swift @@ -14,7 +14,6 @@ struct UwaziDatePicker: View { @State private var selectedDate: Date @State private var dateString: String @ObservedObject var prompt: UwaziTextEntryPrompt - @EnvironmentObject var entityViewModel: UwaziEntityViewModel init(prompt: UwaziTextEntryPrompt) { self.prompt = prompt diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift index f70d3ee15..dc4dfc2fd 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziSelectWidget.swift @@ -14,7 +14,8 @@ struct UwaziSelectWidget: View { @State private var shouldShowMenu : Bool = false @ObservedObject var prompt: UwaziSelectEntryPrompt - + @ObservedObject var entityViewModel: UwaziEntityViewModel + var body: some View { Button { DispatchQueue.main.async { @@ -27,7 +28,7 @@ struct UwaziSelectWidget: View { .cornerRadius(12) if shouldShowMenu { - SelectListOptions(shouldShowMenu: $shouldShowMenu, prompt: prompt) + SelectListOptions(shouldShowMenu: $shouldShowMenu, prompt: prompt, entityViewModel: entityViewModel) } } @@ -63,7 +64,8 @@ struct SelectWidgetButton: View { struct SelectListOptions: View { @Binding var shouldShowMenu: Bool var prompt: UwaziSelectEntryPrompt - + @ObservedObject var entityViewModel: UwaziEntityViewModel + var body: some View { VStack(spacing: 0) { ScrollView { @@ -72,7 +74,7 @@ struct SelectListOptions: View { SelectOptionButton( selectedOption: selectedOptions, shouldShowMenu: $shouldShowMenu, - prompt: prompt + prompt: prompt, entityViewModel: entityViewModel ) } } @@ -91,13 +93,13 @@ struct SelectOptionButton: View { let selectedOption: SelectValues @Binding var shouldShowMenu: Bool var prompt: UwaziSelectEntryPrompt - @EnvironmentObject var uwaziEntityViewModel : UwaziEntityViewModel + @ObservedObject var entityViewModel: UwaziEntityViewModel var body: some View { Button(action: { shouldShowMenu = false prompt.value = [selectedOption.id] - uwaziEntityViewModel.publishUpdates() + entityViewModel.publishUpdates() }) { Text(selectedOption.label ) .font(.custom(Styles.Fonts.regularFontName, size: 14)) diff --git a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift index 612ce8f44..474d6d6ff 100644 --- a/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift +++ b/Tella/Scenes/Uwazi/Views/Entity/Widgets/UwaziTextWidget.swift @@ -13,7 +13,7 @@ import SwiftUI struct UwaziTextWidget: View { @ObservedObject var prompt: UwaziTextEntryPrompt - @EnvironmentObject var uwaziEntityViewModel : UwaziEntityViewModel + @ObservedObject var entityViewModel: UwaziEntityViewModel var body: some View { VStack(alignment: .leading) { TextField("", text: $prompt.value) @@ -22,7 +22,7 @@ struct UwaziTextWidget: View { .frame( height: 22) .onChange(of: prompt.value, perform: { value in prompt.showClear = !value.isEmpty - uwaziEntityViewModel.publishUpdates() + entityViewModel.publishUpdates() }) Divider() diff --git a/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift b/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift index 7309a63e5..b79149532 100644 --- a/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift +++ b/Tella/Scenes/Uwazi/Views/Template/AddTemplatesView.swift @@ -58,7 +58,8 @@ struct AddTemplatesView: View { firstPart + secondPart } .onTapGesture { - navigateTo(destination: ServersListView().environmentObject(ServersViewModel(mainAppModel: uwaziTemplateViewModel.mainAppModel))) + let viewModel = ServersViewModel(mainAppModel: uwaziTemplateViewModel.mainAppModel) + navigateTo(destination: ServersListView(serversViewModel: viewModel)) } } .font(.custom(Styles.Fonts.semiBoldFontName, size: 14)) diff --git a/Tella/Supporting Files/Info.plist b/Tella/Supporting Files/Info.plist index 8f17dbb09..8e86520c4 100644 --- a/Tella/Supporting Files/Info.plist +++ b/Tella/Supporting Files/Info.plist @@ -2,122 +2,121 @@ - BGTaskSchedulerPermittedIdentifiers - - $(PRODUCT_BUNDLE_IDENTIFIER) - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Tella - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - googleDrive - CFBundleURLSchemes - - $(GOOGLE_REVERSE_CLIENT_ID) - - - - CFBundleURLSchemes - - db-$(DROPBOX_APP_KEY) - - - - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - GIDClientID - $(GOOGLE_CLIENT_ID) - DropboxAppKey - $(DROPBOX_APP_KEY) - ITSAppUsesNonExemptEncryption - - LSApplicationCategoryType - - LSApplicationQueriesSchemes - - dbapi-8-emm - dbapi-2 - - LSRequiresIPhoneOS - - NSCameraUsageDescription - Tella requires access to the camera to to take photos and videos inside the app. - NSMicrophoneUsageDescription - Tella requires access to your microphone to record audio inside the app. - NSPhotoLibraryUsageDescription - Tella requires access to your photo library to add or delete photos. - UIAppFonts - - OpenSans-Italic.ttf - OpenSans-Regular.ttf - OpenSans-Bold.ttf - OpenSans-Light.ttf - Roboto-Light.ttf - OpenSans-Semibold.ttf - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - - - - - UIBackgroundModes - - fetch - processing - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UIStatusBarStyle - UIStatusBarStyleLightContent - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSLocationWhenInUseUsageDescription - Tella requires access to the location to capture photos with location information. - - - PHPhotoLibraryPreventAutomaticLimitedAccessAlert - YES - + BGTaskSchedulerPermittedIdentifiers + + $(PRODUCT_BUNDLE_IDENTIFIER) + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Tella + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + googleDrive + CFBundleURLSchemes + + $(GOOGLE_REVERSE_CLIENT_ID) + + + + CFBundleURLSchemes + + db-$(DROPBOX_APP_KEY) + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + DropboxAppKey + $(DROPBOX_APP_KEY) + GIDClientID + $(GOOGLE_CLIENT_ID) + ITSAppUsesNonExemptEncryption + + LSApplicationCategoryType + + LSApplicationQueriesSchemes + + dbapi-8-emm + dbapi-2 + + LSRequiresIPhoneOS + + NSCameraUsageDescription + Tella requires access to the camera to to take photos and videos inside the app. + NSLocalNetworkUsageDescription + We need access to your local network for peer-to-peer sharing. + NSLocationWhenInUseUsageDescription + Tella requires access to the location to capture photos with location information. + NSMicrophoneUsageDescription + Tella requires access to your microphone to record audio inside the app. + NSPhotoLibraryUsageDescription + Tella requires access to your photo library to add or delete photos. + PHPhotoLibraryPreventAutomaticLimitedAccessAlert + YES + UIAppFonts + + OpenSans-Italic.ttf + OpenSans-Regular.ttf + OpenSans-Bold.ttf + OpenSans-Light.ttf + Roboto-Light.ttf + OpenSans-Semibold.ttf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + + + + + UIBackgroundModes + + fetch + processing + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarStyle + UIStatusBarStyleLightContent + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + diff --git a/Tella/Supporting Files/Tella-Bridging-Header.h b/Tella/Supporting Files/Tella-Bridging-Header.h new file mode 100644 index 000000000..67fd5b40e --- /dev/null +++ b/Tella/Supporting Files/Tella-Bridging-Header.h @@ -0,0 +1,15 @@ +// +// Tella-Bridging-Header.h .h +// Tella +// +// Created by Dhekra Rouatbi on 3/6/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +#ifndef Tella_Bridging_Header_h__h +#define Tella_Bridging_Header_h__h + +#include "llhttp.h" + +#endif /* Tella_Bridging_Header_h__h */ diff --git a/Tella/Supporting Files/Tella.entitlements b/Tella/Supporting Files/Tella.entitlements index 01951a240..428fa8983 100644 --- a/Tella/Supporting Files/Tella.entitlements +++ b/Tella/Supporting Files/Tella.entitlements @@ -2,6 +2,8 @@ + com.apple.developer.networking.wifi-info + com.apple.developer.default-data-protection NSFileProtectionCompleteUnlessOpen diff --git a/Tella/Supporting Files/en.lproj/Localizable.strings b/Tella/Supporting Files/en.lproj/Localizable.strings index 24b96d33e..50af404a5 100644 --- a/Tella/Supporting Files/en.lproj/Localizable.strings +++ b/Tella/Supporting Files/en.lproj/Localizable.strings @@ -248,6 +248,17 @@ If you want to take a picture or a video, proceed in the following way:\ "LockUnlock_UnlockDeleteAfterFail_Warning_RemainingAttempts" = "You have %i attempts remaining until everything in your Tella is deleted."; "LockUnlock_UnlockDeleteAfterFail_Error_ContentDeleted" = "You have exceeded the maximum number of incorrect login attempts. All your Tella data has been deleted."; "Settings_Sett_Connections" = "Connections"; + +"Settings_Sett_AddConnection" = "Add a Connection"; +"Settings_Sett_AddConnection_Expl" = "If you work with an organization, connect to its server to send files and data. You can also back up files to a personal cloud."; +"Setting_Sett_AddConnection_LearnMore" = "Learn more"; + +"Settings_Sett_NearbySharing" = "Nearby Sharing"; +"Settings_Sett_NearbySharing_Expl" = "Enable this feature to share files with devices near you, without an Internet connection."; +"Setting_Sett_NearbySharing_LearnMore" = "Learn more"; + + + "Settings_Sett_Feedback" = "Feedback"; "Settings_SettSec_DeleteAfterFail" = "Delete after failed unlock"; "Settings_SettSec_DeleteAfterFail_Off" = "Off"; @@ -549,4 +560,155 @@ If you want to add a picture or a video from the photo library, proceed in the f "Settings_SettLang_PortugueseMozambique_Expl" = "Portuguese Mozambique"; "Settings_SettLang_Tsonga_Expl" = "Tsonga"; "Settings_SettLang_Ndau_Expl" = "Ndau"; -"Settings_SettLang_Azerbaijani_Expl" = "Azerbaijani"; \ No newline at end of file +"Settings_SettLang_Azerbaijani_Expl" = "Azerbaijani"; + + +"NearbySharing_AppBar" = "Nearby sharing"; + +"NearbySharing_Main_Subhead" = "How it works"; +"NearbySharing_Main_Expl" = "Share files with nearby devices without an Internet connection \ + 1. Both devices must be connected to the same Wifi network \ + 2. Make sure you trust the person you are sharing files with"; + +"NearbySharing_Main_SendFiles" = "Send files"; +"NearbySharing_Main_ReceiveFiles" = "Receive files"; +"NearbySharing_Main_LearnMore" = "Learn more about nearby sharing"; + +"NearbySharing_Getconnected_AppBar" = "Get connected"; +"NearbySharing_Getconnected_Subhead" = "Nearby Sharing"; +"NearbySharing_Getconnected_Expl" = "Share files directly with a device near you, with or without an Internet connection. + +To get started, join the same Wi-Fi network as the recipient. Nearby Sharing will work even if the Wi-Fi network is not connected to the Internet."; +"NearbySharing_Getconnected_TipsToConnect" = "Tips to connect"; +"NearbySharing_Getconnected_TipsToConnect_Expl" = "Learn more about connecting to Wi-Fi for Nearby Sharing."; +"NearbySharing_Getconnected_SameNetwork_Expl" = "Yes, we are on the same Wi-Fi network"; + +"NearbySharing_TipsToConnect_SettingUp" = "Setting up"; +"NearbySharing_TipsToConnect_SettingUp_Expl" = "There are 2 ways to connect your devices through Wi-Fi: + 1. Using a Wi-Fi router, such as at home or work + 2. Setting up a Wi-Fi hotspot from a device, such as a phone, tablet, or computer + a. One user creates a hotspot connection by going to their device’s settings. + b. The other user connects to that hotspot."; + + +"NearbySharing_TipsToConnect_Joining" = "Joining the same network"; +"NearbySharing_TipsToConnect_Joining_Expl" = "Once the Wi-Fi network is set up, go to your devices’ settings and verify that you are both on the same Wi-Fi network."; + + +"NearbySharing_TipsToConnect_MoreTips" = "More tips"; +"NearbySharing_TipsToConnect_MoreTips_Expl" = "• The Wi-Fi network does not need to have internet access. As long as the Wi-Fi router or hotspot is powered on and functioning, it can be used for Nearby Sharing even without being online +• Make sure to disable any active VPN connections."; + +"NearbySharing_ConnectToDevice" = "Connect to device"; +"NearbySharing_ScanCode" = "Scan the recipient’s QR code"; +"NearbySharing_ShowQrCode" = "Show this QR code for the\nsender to scan"; +"NearbySharing_HavingTrouble" = "Having trouble with the QR code?"; +"NearbySharing_ConnectManually" = "Connect Manually"; +"NearbySharing_EnterDevice_Information" = "Enter the recipient’s device\n information to find it on the network"; +"NearbySharing_IpAddress" = "IP address"; +"NearbySharing_Pin" = "PIN"; +"NearbySharing_Port" = "Port"; + "NearbySharing_PublicKey" = "Public key"; +"NearbySharing_ShowDeviceInformation" = "Show your device information"; +"NearbySharing_ConnectToDevice_SenderInputDesc" = "The sender needs to input the following to\nconnect to your device."; +"NearbySharing_LocationAccess" = "Location access"; +"NearbySharing_Cancel" = "Cancel"; +"NearbySharing_Settings" = "Settings"; +"NearbySharing_NoConnection" = "No connection"; +"NearbySharing_WaitingForSenderDesc" = "Waiting for the sender to share files"; +"NearbySharing_SenderRequest_NumberOfFiles_Desc" = "The sender is trying to send you %i files"; +"NearbySharing_RequestQuestion" = "Would you like to accept them?"; +"NearbySharing_Accept" = "Accept"; +"NearbySharing_Reject" = "Reject"; +"NearbySharing_InvalidIpAddress" = "Invalid IP address"; +"NearbySharing_InvalidPin" = "Invalid PIN"; +"NearbySharing_SelectFilesToSend" = "Select files to send"; +"NearbySharing_Title" = "Title"; + +"NearbySharing_SuccessConnect_Toast" = "Success! Connected to device"; +"NearbySharing_ServerError_Toast" = "Server error"; + +"NearbySharing_ConnectionFailed_SheetTitle" = "Connection failed"; +"NearbySharing_ConnectionFailed_SheetExpl" = "Please make sure your connection details are correct and that you are on the same Wi-Fi network."; +"NearbySharing_ConnectionFailed_SheetAction" = "Try again"; + +"NearbySharing_Verification_AppBar" = "Verification"; +"NearbySharing_Verification_Sender_Expl" = "To ensure that the connection is safe, make sure that the sequence of numbers above matches what is shown on the recipient’s device. + +If the sequences do not match, the connection may not be secure and should be discarded."; + + + +"NearbySharing_Verification_Recipient_Expl" = "To ensure that the connection is safe, make sure that the sequence of numbers above matches what is shown on the other device. + +If the sequences do not match, the connection may not be secure and should be discarded."; + +"NearbySharing_Verification_Action_Confirm" = "Confirm and connect"; +"NearbySharing_Verification_Action_WaitingSender" = "Waiting for sender ..."; +"NearbySharing_Verification_Action_WaitingRecipient" = "Waiting for recipient ..."; + +"NearbySharing_Verification_Action_Discard" = "Discard and start over"; + + + +"NearbySharing_Sender_FilesRejected_Toast" = "Files rejected by recipient."; + +"NearbySharing_Recipient_FilesRejected_Toast" = "Sender's files rejected."; + +"NearbySharing_Sender_WaitingRecipient_Toast" = "Waiting for the recipient to accept files"; + + + + + +"NearbySharing_StopSharing_SheetTitle" = "Stop sharing files?"; +"NearbySharing_StopSharing_SheetExpl" = "Nearby sharing will be stopped. The recipient will not have access to files that were not fully transferred."; +"NearbySharing_StopSharing_Continue_SheetAction" = "Continue"; +"NearbySharing_StopSharing_Stop_SheetAction" = "Stop"; + + + + +"NearbySharing_Sender_PercentageSent" = "%i%% sent"; +"NearbySharing_Sender_FilesSent" = "%i files, %@/%@ sent"; +"NearbySharing_Sender_FileSent" = "%i file, %@/%@ sent"; + + +"NearbySharing_Recipient_PercentageReceived" = "%i%% received"; +"NearbySharing_Recipient_FilesReceived" = "%i files, %@/%@ received"; +"NearbySharing_Recipient_FileReceived" = "%i file, %@/%@ Received"; + + +"NearbySharing_Recipient_Receiving_AppBar" = "Receiving and encrypting files"; +"NearbySharing_StopReceiving_SheetTitle" = "Stop receiving files?"; +"NearbySharing_StopReceiving_SheetExpl" = "Nearby sharing will be stopped. You will not have access to files that were not fully transferred."; + + +"NearbySharing_Sender_Sending_AppBar" = "Sending files"; + +"NearbySharing_Results_AppBar" = "Results"; + +"NearbySharing_Results_Success_Title" = "Success"; + +"NearbySharing_Results_SuccessFilesReceived_Expl" = "You have successfully received %i files from the sender."; +"NearbySharing_Results_SuccessFileReceived_Expl" = "You have successfully received %i file from the sender."; + +"NearbySharing_Results_SuccessFilesSent_Expl" = "You have successfully sent %i files to the recipient."; +"NearbySharing_Results_SuccessFileSent_Expl" = "You have successfully sent %i file to the recipient."; + +"NearbySharing_Results_Failure_Title" = "Transfer interrupted"; + +"NearbySharing_Results_PartialFailureFilesReceived_Expl" = "Only %i files were received. %i files were not received."; + +"NearbySharing_Results_FilesReceived_FilesNotReceived_Expl" = "Only %i files were received. %i files were not received."; +"NearbySharing_Results_FileReceived_FilesNotReceived_Expl" = "Only %i file was received. %i files were not received."; +"NearbySharing_Results_FilesReceived_FileNotReceived_Expl" = "Only %i files were received. %i file was not received."; +"NearbySharing_Results_FileReceived_FileNotReceived_Expl" = "Only %i file was received. %i file was not received."; + +"NearbySharing_Results_FailureFilesReceived_Expl" = "0 files were received. %i files were not received."; +"NearbySharing_Results_FailureFileReceived_Expl" = "0 files were received. %i file were not received."; + +"NearbySharing_Results_ViewFiles_Action" = "View files"; + + +"NearbySharing_ConnectionChanged_Toast" = "Your Wi-Fi connection has changed. Please start over."; diff --git a/Tella/Utils/CertificateGenerator.swift b/Tella/Utils/CertificateGenerator.swift new file mode 100644 index 000000000..3fc652cfc --- /dev/null +++ b/Tella/Utils/CertificateGenerator.swift @@ -0,0 +1,222 @@ +// +// CertificateGenerator.swift +// Tella +// +// Created by Dhekra Rouatbi on 27/2/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import X509 +import Network + +class CertificateGenerator { + + private let commonName = "Tella iOS" + private let organization = "Tella" + + // MARK: - Main Function + + func generateP12Certificate(ipAddress: String) -> (identity: SecIdentity, certificateHash: String)? { + + // Generate RSA private key + guard let privateKey = generateRSAKey() else { + debugLog("RSA key generation failed") + return nil + } + + guard let publicKey = privateKey.getPublicKey() else { + debugLog("Failed to extract public key from private key") + return nil + } + + // Generate certificate + guard let certificate = generateSelfSignedCertificate(ipAddress: ipAddress, privateKey: privateKey, publicKey: publicKey) else { + debugLog("Failed to create certificate") + return nil + } + + let certificateData = SecCertificateCopyData(certificate) + let certificateHash = Data(certificateData as Data).sha256() + + + // Store in keychain and return identity + guard let identity = storeCertificateAndPrivateKey(certificate: certificate, privateKey: privateKey) else { + debugLog("Failed to store identity") + return nil + } + + return (identity, certificateHash) + } + + // MARK: - RSA Key Generation + + private func generateRSAKey() -> SecKey? { + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeySizeInBits as String: 2048, + kSecPrivateKeyAttrs as String: [ + kSecAttrIsPermanent as String: false + ] + ] + + var error: Unmanaged? + guard let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { + debugLog("RSA key generation error)") + return nil + } + return key + } + + // MARK: - Certificate Generation + + private func generateSelfSignedCertificate(ipAddress: String, + privateKey: SecKey, + publicKey: SecKey) -> SecCertificate? { + do { + let notBefore = Date() + let notAfter = notBefore.addYear() + + let name = try buildDistinguishedName() + let sanExtension = try createSANExtension(ipAddress: ipAddress) + + var extensions = Certificate.Extensions() + try extensions.append(sanExtension) + + let issuerPrivateKey = try Certificate.PrivateKey(privateKey) + + let certificate = try Certificate( + version: .v3, + serialNumber: .init(), + publicKey: issuerPrivateKey.publicKey, + notValidBefore: notBefore, + notValidAfter: notAfter!, + issuer: name, + subject: name, + signatureAlgorithm: .sha256WithRSAEncryption, + extensions: extensions, + issuerPrivateKey: issuerPrivateKey + ) + + return createSecCertificate(from: certificate) + + } catch { + debugLog("Certificate generation failed") + return nil + } + } + + private func buildDistinguishedName() throws -> DistinguishedName { + try DistinguishedName { + CommonName(commonName) + OrganizationName(organization) + } + } + + private func createSANExtension(ipAddress: String) throws -> Certificate.Extension { + + let ipBytes = try ipAddress.convertIPAddressToBytes() + let sanValue = createSANExtensionValue(ipBytes: ipBytes) + + return Certificate.Extension( + oid: [2, 5, 29, 17], // Subject Alternative Name + critical: false, + value: sanValue[...] + ) + } + + // Helper to create the SAN extension value + private func createSANExtensionValue(ipBytes: [UInt8]) -> [UInt8] { + // This creates a minimal SubjectAlternativeName extension containing just the IP address + // Structure: SEQUENCE -> [7] IMPLICIT OCTET STRING (ipBytes) + var bytes = [UInt8]() + + // Add the IP address (tag 7, context-specific) + bytes.append(0x87) // Context-specific tag 7 in constructed form + bytes.append(UInt8(ipBytes.count)) + bytes.append(contentsOf: ipBytes) + + // Wrap in SEQUENCE + let sequenceLength = bytes.count + var result = [UInt8]() + result.append(0x30) // SEQUENCE tag + result.append(UInt8(sequenceLength)) + result.append(contentsOf: bytes) + + return result + } + + private func createSecCertificate(from certificate: Certificate) -> SecCertificate? { + do { + let derData = try certificate.serializeAsPEM().derBytes + return SecCertificateCreateWithData(nil, Data(derData) as CFData) + } catch { + debugLog("DER serialization failed") + return nil + } + } + + // MARK: - Keychain Storage + + private func storeCertificateAndPrivateKey(certificate: SecCertificate, privateKey: SecKey) -> SecIdentity? { + let tag = "tempkey-\(UUID().uuidString)" + let tagData = tag.data(using: .utf8)! + + let privateKeyAttrs: [String: Any] = [ + kSecClass as String: kSecClassKey, + kSecAttrApplicationTag as String: tagData, + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, + kSecValueRef as String: privateKey, + kSecReturnPersistentRef as String: false + ] + + SecItemDelete(privateKeyAttrs as CFDictionary) + guard SecItemAdd(privateKeyAttrs as CFDictionary, nil) == errSecSuccess else { + debugLog("Failed to add private key to Keychain") + return nil + } + + let certAttrs: [String: Any] = [ + kSecClass as String: kSecClassCertificate, + kSecValueRef as String: certificate, + kSecReturnPersistentRef as String: false + ] + + SecItemDelete(certAttrs as CFDictionary) + guard SecItemAdd(certAttrs as CFDictionary, nil) == errSecSuccess else { + debugLog("Failed to add certificate to Keychain") + return nil + } + + let identityQuery: [String: Any] = [ + kSecClass as String: kSecClassIdentity, + kSecAttrApplicationTag as String: tagData, + kSecReturnRef as String: true + ] + + var identityRef: CFTypeRef? + let status = SecItemCopyMatching(identityQuery as CFDictionary, &identityRef) + + guard status == errSecSuccess else { + debugLog("Failed to retrieve identity from Keychain") + return nil + } + + guard let identityRef else { + debugLog("identityRef is nil") + return nil + } + + // Convert to CFTypeRef safely + let identityCF = identityRef as CFTypeRef + + guard CFGetTypeID(identityCF) == SecIdentityGetTypeID() else { + debugLog("Imported item is not a SecIdentity") + return nil + } + + return (identityCF as! SecIdentity) + } +} diff --git a/Tella/Utils/Constants.swift b/Tella/Utils/Constants.swift index 84e610a7e..9570e8fc2 100644 --- a/Tella/Utils/Constants.swift +++ b/Tella/Utils/Constants.swift @@ -14,8 +14,7 @@ struct TellaUrls { static let faq = "https://tella-app.org/faq" static let tutorial = "https://tella-app.org/get-started-ios/" static let connectionLearnMore = "https://tella-app.org/for-organizations" - - + static let nearbySharingLearnMore = "https://tella-app.org/nearby-sharing" } struct GoogleAuthConstants { diff --git a/Tella/Utils/Extensions/ArrayExtension.swift b/Tella/Utils/Extensions/ArrayExtension.swift index b842e5785..7477b4bae 100644 --- a/Tella/Utils/Extensions/ArrayExtension.swift +++ b/Tella/Utils/Extensions/ArrayExtension.swift @@ -29,6 +29,18 @@ extension Array { } return items } - - +} + +extension Array { + // Chunk Array into subarrays of fixed size + func chunked(into size: Int) -> [[Element]] { + var result: [[Element]] = [] + var index = 0 + while index < count { + let chunk = Array(self[index.. String? { + return String(data: self, encoding: .utf8) + } + mutating func extract(size: Int?) -> Data? { guard let size, self.count > size else { @@ -38,7 +45,7 @@ extension Data { // Return the new copy of data return self } - + /// This function takes the image data and metadata as parameter and returns the image data with metadata /// - Parameters: /// - data: The original image data @@ -49,7 +56,7 @@ extension Data { let uti: CFString = CGImageSourceGetType(imageRef)! let dataWithEXIF: NSMutableData = NSMutableData(data: self) let destination: CGImageDestination = CGImageDestinationCreateWithData((dataWithEXIF as CFMutableData), uti, 1, nil)! - + CGImageDestinationAddImageFromSource(destination, imageRef, 0, (properties as CFDictionary)) CGImageDestinationFinalize(destination) return dataWithEXIF @@ -61,32 +68,39 @@ extension Data { { return nil } - + let count = CGImageSourceGetCount(source) let mutableData = NSMutableData() guard let destination = CGImageDestinationCreateWithData(mutableData, type, count, nil) else { return nil } - + let exifToRemove: CFDictionary = [ kCGImagePropertyExifDictionary: kCFNull, kCGImagePropertyGPSDictionary: kCFNull, kCGImagePropertyTIFFDictionary : kCFNull ] as CFDictionary - + for index in 0 ..< count { CGImageDestinationAddImageFromSource(destination, source, index, exifToRemove) if !CGImageDestinationFinalize(destination) { debugLog("Failed to finalize") } } - + return mutableData as Data } - + func fileExtension(vaultManager:VaultManager) -> String? { let fileTypeHelper = FileTypeHelper(data: self).getFileInformation() return fileTypeHelper?.fileExtension } + + // Function to compute SHA-256 hash + func sha256() -> String { + let hashBytes = SHA256.hash(data: self) + // Convert to hex string array: each element is "%02x" + return hashBytes.map { String(format: "%02x", $0) }.joined() + } } diff --git a/Tella/Utils/Extensions/DateExtension.swift b/Tella/Utils/Extensions/DateExtension.swift index 012b76a8f..541c0d232 100644 --- a/Tella/Utils/Extensions/DateExtension.swift +++ b/Tella/Utils/Extensions/DateExtension.swift @@ -100,6 +100,9 @@ extension Date{ private func getDateComponent(component : Calendar.Component) -> DateComponents { return Calendar.current.dateComponents([component], from: self, to: Date()) } - + + func addYear() -> Date? { + return Calendar.current.date(byAdding: .year, value: 1, to: self) + } } diff --git a/Tella/Utils/Extensions/EncodableExtension.swift b/Tella/Utils/Extensions/EncodableExtension.swift index a268e7c56..b72b82469 100644 --- a/Tella/Utils/Extensions/EncodableExtension.swift +++ b/Tella/Utils/Extensions/EncodableExtension.swift @@ -9,6 +9,7 @@ import Foundation +import UIKit extension Encodable { @@ -47,4 +48,31 @@ extension Encodable { return [[:]] } } + + var jsonData: Data? { + if let jsonData = try? JSONEncoder().encode(self){ + return jsonData + } else { + return nil + } + } + + func generateQRCode(size: CGFloat) -> UIImage { + let data = self.jsonString?.data(using: .utf8) + + guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return UIImage() } + filter.setValue(data, forKey: "inputMessage") + filter.setValue("H", forKey: "inputCorrectionLevel") // High error correction + + guard let ciImage = filter.outputImage else { return UIImage() } + + let transform = CGAffineTransform(scaleX: size / ciImage.extent.width, y: size / ciImage.extent.height) + let scaledImage = ciImage.transformed(by: transform) + + let context = CIContext() + guard let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) else { return UIImage() } + + return UIImage(cgImage: cgImage) + } + } diff --git a/Tella/Utils/Extensions/FileManagerExtension.swift b/Tella/Utils/Extensions/FileManagerExtension.swift index fbd955490..25536bf00 100644 --- a/Tella/Utils/Extensions/FileManagerExtension.swift +++ b/Tella/Utils/Extensions/FileManagerExtension.swift @@ -28,4 +28,10 @@ extension FileManager { } } + class func tempDirectory(withFileName fileName: String) -> URL { + return self.default + .temporaryDirectory + .appendingPathComponent(fileName) + } } + diff --git a/Tella/Utils/Extensions/IntExtension.swift b/Tella/Utils/Extensions/IntExtension.swift index bdcd99ef9..de7b4be5b 100644 --- a/Tella/Utils/Extensions/IntExtension.swift +++ b/Tella/Utils/Extensions/IntExtension.swift @@ -42,3 +42,9 @@ extension Int { var degreesToRadians: CGFloat { return CGFloat(self) * .pi / 180 } } + +extension Int { + static var randomSixDigitPIN: String { + return String(Int.random(in: 100_000...999_999)) + } +} diff --git a/Tella/Utils/Extensions/NWConnectionExtension.swift b/Tella/Utils/Extensions/NWConnectionExtension.swift new file mode 100644 index 000000000..c9f8fcb70 --- /dev/null +++ b/Tella/Utils/Extensions/NWConnectionExtension.swift @@ -0,0 +1,20 @@ +// +// NWConnectionExtension.swift +// Tella +// +// Created by Dhekra Rouatbi on 23/7/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Network + +extension NWConnection { + var id: ObjectIdentifier { + return ObjectIdentifier(self) + } + + /// Convenience for naming connection queues + var idHash: Int { ObjectIdentifier(self).hashValue } + +} diff --git a/Tella/Utils/Extensions/SecKeyExtension.swift b/Tella/Utils/Extensions/SecKeyExtension.swift new file mode 100644 index 000000000..2df37c607 --- /dev/null +++ b/Tella/Utils/Extensions/SecKeyExtension.swift @@ -0,0 +1,34 @@ +// +// SecKeyExtension.swift +// Tella +// +// Created by Dhekra Rouatbi on 15/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Security +import Foundation + +extension SecKey { + func getString() -> String? { + guard let data = getData() else { + return nil + } + return data.base64EncodedString() + } + + func getData() -> Data? { + var error:Unmanaged? + guard let cfdata = SecKeyCopyExternalRepresentation(self, &error) else { + return nil + } + let data:Data = cfdata as Data + return data + } + + func getPublicKey() -> SecKey? { + return SecKeyCopyPublicKey(self) + } +} + diff --git a/Tella/Utils/Extensions/StringExtension.swift b/Tella/Utils/Extensions/StringExtension.swift index 08c9f8d8e..5ac7b41db 100644 --- a/Tella/Utils/Extensions/StringExtension.swift +++ b/Tella/Utils/Extensions/StringExtension.swift @@ -8,6 +8,7 @@ import MobileCoreServices import UniformTypeIdentifiers import UIKit +import Network extension String { func getDate() -> Date? { @@ -105,9 +106,11 @@ extension String { return fileExtension as String } - - + func fileExtensionFromMimeType() -> String? { + UTType(mimeType: self)?.preferredFilenameExtension + } + var tellaFileType: TellaFileType { guard let type = UTType(mimeType: self) else { @@ -184,7 +187,7 @@ extension String { func decode(_ type: T.Type) throws -> T { - let data = try JSONSerialization.data(withJSONObject: self) + let data = try? JSONSerialization.data(withJSONObject: self) return try JSONDecoder().decode (type, from: data) } @@ -200,7 +203,18 @@ extension String { return nil } } + + func convertIPAddressToBytes() throws -> [UInt8] { + if let ipv4 = IPv4Address(self) { + return Array(ipv4.rawValue) + } else if let ipv6 = IPv6Address(self) { + return Array(ipv6.rawValue) + } else { + throw CertificateError.invalidIPAddress + } + } } + extension String: @retroactive Identifiable { public var id: String { self } } @@ -209,4 +223,36 @@ extension String { func url() -> URL? { return URL(string: self) } + + func asFileURL() -> URL? { + guard !self.isEmpty else { return nil } + return URL(fileURLWithPath: self) + } + +} + + +enum CertificateError: Error { + case invalidIPAddress +} + +extension String { + func formatHash() -> String { + let trimmed = String(self.prefix(64)) + let chunks = self.chunked(into: 4) + let lines = chunks.chunked(into: 4) + return lines.map { $0.joined(separator: " ") }.joined(separator: "\n") + } + + // Chunk String into substrings of fixed size + func chunked(into size: Int) -> [String] { + var result: [String] = [] + var start = startIndex + while start < endIndex { + let end = index(start, offsetBy: size, limitedBy: endIndex) ?? endIndex + result.append(String(self[start.. UIViewController? { - - if let nav = base as? UINavigationController { - return getTopViewController(base: nav.visibleViewController) - - } - - else if let tab = base as? UITabBarController, let selected = tab.selectedViewController { - return getTopViewController(base: selected) - - } - - else if let presented = base?.presentedViewController { - return getTopViewController(base: presented) + class func getTopViewController(base: UIViewController? = topRootViewController()) -> UIViewController? { + if let nav = base as? UINavigationController { + return getTopViewController(base: nav.visibleViewController) + } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController { + return getTopViewController(base: selected) + } else if let presented = base?.presentedViewController { + return getTopViewController(base: presented) + } + return base } - return base - } - + + private class func topRootViewController() -> UIViewController? { + // For multi-scene apps + guard let scene = UIApplication.shared.connectedScenes + .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else { + return nil + } + + return scene.windows + .first(where: { $0.isKeyWindow })?.rootViewController + } + func setupApperance(with backgroundColor: UIColor = Styles.uiColor.backgroundMain) { UITabBar.appearance().unselectedItemTintColor = UIColor.white.withAlphaComponent(0.38) diff --git a/Tella/Utils/Extensions/UIDeviceExtension.swift b/Tella/Utils/Extensions/UIDeviceExtension.swift index e6097d4c9..da0c76e98 100644 --- a/Tella/Utils/Extensions/UIDeviceExtension.swift +++ b/Tella/Utils/Extensions/UIDeviceExtension.swift @@ -5,9 +5,11 @@ import UIKit +import SystemConfiguration.CaptiveNetwork +import Network class DiskStatus: NSObject { - + func MBFormatter(_ bytes: Int64) -> String { let formatter = ByteCountFormatter() formatter.allowedUnits = [.useBytes,.useKB, .useMB, .useGB] @@ -56,5 +58,45 @@ class DiskStatus: NSObject { } return freeSize.int64Value } +} + +extension UIDevice { + func getIPAddress(for type: NWInterface.InterfaceType?) -> String? { + + var ifaddr: UnsafeMutablePointer? + + guard getifaddrs(&ifaddr) == 0 else { return nil } + defer { freeifaddrs(ifaddr) } + + var ptr = ifaddr + while ptr != nil { + defer { ptr = ptr?.pointee.ifa_next } + + let interface = ptr?.pointee + let addrFamily = interface?.ifa_addr.pointee.sa_family + + if addrFamily == UInt8(AF_INET), let name = interface?.ifa_name { + let interfaceName = String(cString: name) + + if (type == .wifi && interfaceName == "en0") || (type == .cellular && interfaceName == "bridge100") { + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + + getnameinfo( + interface?.ifa_addr, + socklen_t((interface?.ifa_addr.pointee.sa_len)!), + &hostname, + socklen_t(hostname.count), + nil, + 0, + NI_NUMERICHOST + ) + + return String(cString: hostname) + } + } + } + + return nil + } } diff --git a/Tella/Utils/Extensions/URLExtension.swift b/Tella/Utils/Extensions/URLExtension.swift index 97b060e55..e2590e7ad 100644 --- a/Tella/Utils/Extensions/URLExtension.swift +++ b/Tella/Utils/Extensions/URLExtension.swift @@ -251,4 +251,22 @@ extension URL { func open() { UIApplication.shared.open(self, options: [:], completionHandler: nil) } + + func remove() { + let fileManager = FileManager.default + let accessed = self.startAccessingSecurityScopedResource() + defer { + if accessed { + self.stopAccessingSecurityScopedResource() + } + } + + if fileManager.fileExists(atPath: self.path) { + do { + try fileManager.removeItem(at: self) + } catch { + debugLog("Error removing item: \(error.localizedDescription)") + } + } + } } diff --git a/Tella/Utils/Extensions/ViewExtension.swift b/Tella/Utils/Extensions/ViewExtension.swift index 52f8599b0..15a359edd 100644 --- a/Tella/Utils/Extensions/ViewExtension.swift +++ b/Tella/Utils/Extensions/ViewExtension.swift @@ -122,8 +122,14 @@ extension View { self.present(style: .overCurrentContext, transitionStyle: .crossDissolve, builder: {viewToShow}) } - func showBottomSheetView(content : Content, modalHeight:CGFloat, isShown: Binding = .constant(true)) { - let viewToShow = DragView(modalHeight: modalHeight, isShown: isShown, content: {content}) + func showBottomSheetView(content : Content, + modalHeight:CGFloat, + isShown: Binding = .constant(true), + shouldHideOnTap:Bool = true) { + let viewToShow = DragView(modalHeight: modalHeight, + shouldHideOnTap: shouldHideOnTap, + isShown: isShown, + content: {content}) self.present(style: .overCurrentContext, transitionStyle: .crossDissolve, builder: {viewToShow}) } } diff --git a/Tella/Utils/Localizable/LocalizableNearbySharing.swift b/Tella/Utils/Localizable/LocalizableNearbySharing.swift new file mode 100644 index 000000000..07a7023e3 --- /dev/null +++ b/Tella/Utils/Localizable/LocalizableNearbySharing.swift @@ -0,0 +1,136 @@ +// +// LocalizableNearbySharing.swift +// Tella +// +// Created by Dhekra Rouatbi on 30/1/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + + + +enum LocalizableNearbySharing: String, LocalizableDelegate { + + case nearbySharingAppBar = "NearbySharing_AppBar" + case nearbySharingSubhead = "NearbySharing_Main_Subhead" + case nearbySharingExpl = "NearbySharing_Main_Expl" + + case sendFiles = "NearbySharing_Main_SendFiles" + case receiveFiles = "NearbySharing_Main_ReceiveFiles" + + case learnMore = "NearbySharing_Main_LearnMore" + + case getConnected = "NearbySharing_Getconnected_AppBar" + + + case getConnectedSubhead = "NearbySharing_Getconnected_Subhead" + case getconnectedExpl = "NearbySharing_Getconnected_Expl" + case tipsToConnect = "NearbySharing_Getconnected_TipsToConnect" + case tipsToConnectExpl = "NearbySharing_Getconnected_TipsToConnect_Expl" + case sameNetworkExpl = "NearbySharing_Getconnected_SameNetwork_Expl" + + case settingUp = "NearbySharing_TipsToConnect_SettingUp" + case settingUpExpl = "NearbySharing_TipsToConnect_SettingUp_Expl" + + case joining = "NearbySharing_TipsToConnect_Joining" + case joiningExpl = "NearbySharing_TipsToConnect_Joining_Expl" + + case moreTips = "NearbySharing_TipsToConnect_MoreTips" + case moreTipsExpl = "NearbySharing_TipsToConnect_MoreTips_Expl" + + + case connectToDevice = "NearbySharing_ConnectToDevice" + case scanCode = "NearbySharing_ScanCode" + case showQrCode = "NearbySharing_ShowQrCode" + case havingTrouble = "NearbySharing_HavingTrouble" + case connectManually = "NearbySharing_ConnectManually" + case enterDeviceInformation = "NearbySharing_EnterDevice_Information" + case ipAddress = "NearbySharing_IpAddress" + case pin = "NearbySharing_Pin" + case port = "NearbySharing_Port" + case publicKey = "NearbySharing_PublicKey" + case showDeviceInformation = "NearbySharing_ShowDeviceInformation" + case sendInputDesc = "NearbySharing_ConnectToDevice_SenderInputDesc" + case locationAccess = "NearbySharing_LocationAccess" + case cancel = "NearbySharing_Cancel" + case settings = "NearbySharing_Settings" + case noConnection = "NearbySharing_NoConnection" + case waitingForSenderDesc = "NearbySharing_WaitingForSenderDesc" + + + + case senderRequestFilesNumberDesc = "NearbySharing_SenderRequest_NumberOfFiles_Desc" + case requestQuestion = "NearbySharing_RequestQuestion" + case accept = "NearbySharing_Accept" + case reject = "NearbySharing_Reject" + case invalidIpAddress = "NearbySharing_InvalidIpAddress" + case invalidPin = "NearbySharing_InvalidPin" + case selectFilesToSend = "NearbySharing_SelectFilesToSend" + case title = "NearbySharing_Title" + case successConnectToast = "NearbySharing_SuccessConnect_Toast" + case serverErrorToast = "NearbySharing_ServerError_Toast" + + case connectionFailedTitle = "NearbySharing_ConnectionFailed_SheetTitle" + case connectionFailedExpl = "NearbySharing_ConnectionFailed_SheetExpl" + case connectionFailedAction = "NearbySharing_ConnectionFailed_SheetAction" + + case verificationAppBar = "NearbySharing_Verification_AppBar" + + case verificationSender = "NearbySharing_Verification_Sender_Expl" + + case verificationRecipient = "NearbySharing_Verification_Recipient_Expl" + + case verificationConfirm = "NearbySharing_Verification_Action_Confirm" + case verificationWaitingSender = "NearbySharing_Verification_Action_WaitingSender" + case verificationWaitingRecipient = "NearbySharing_Verification_Action_WaitingRecipient" + + case verificationDiscard = "NearbySharing_Verification_Action_Discard" + + case senderFilesRejected = "NearbySharing_Sender_FilesRejected_Toast" + + case recipientFilesRejected = "NearbySharing_Recipient_FilesRejected_Toast" + case senderWaitingRecipient = "NearbySharing_Sender_WaitingRecipient_Toast" + + + case stopSharingTitle = "NearbySharing_StopSharing_SheetTitle" + case stopSharingSheetExpl = "NearbySharing_StopSharing_SheetExpl" + case continueSharing = "NearbySharing_StopSharing_Continue_SheetAction" + case stopSharing = "NearbySharing_StopSharing_Stop_SheetAction" + + case senderPercentageSent = "NearbySharing_Sender_PercentageSent" + case senderFilesSent = "NearbySharing_Sender_FilesSent" + case senderFileSent = "NearbySharing_Sender_FileSent" + + case recipientPercentageReceived = "NearbySharing_Recipient_PercentageReceived" + case recipientFilesReceived = "NearbySharing_Recipient_FilesReceived" + case recipientFileReceived = "NearbySharing_Recipient_FileReceived" + + case receivingAppBar = "NearbySharing_Recipient_Receiving_AppBar" + case stopReceivingSheetTitle = "NearbySharing_StopReceiving_SheetTitle" + case stopReceivingSheetExpl = "NearbySharing_StopReceiving_SheetExpl" + + case senderSendingAppBar = "NearbySharing_Sender_Sending_AppBar" + + case resultsAppBar = "NearbySharing_Results_AppBar" + case successTitle = "NearbySharing_Results_Success_Title" + + case successFilesReceivedExpl = "NearbySharing_Results_SuccessFilesReceived_Expl" + case successFileReceivedExpl = "NearbySharing_Results_SuccessFileReceived_Expl" + + case successFilesSentExpl = "NearbySharing_Results_SuccessFilesSent_Expl" + case successFileSentExpl = "NearbySharing_Results_SuccessFileSent_Expl" + + case failureTitle = "NearbySharing_Results_Failure_Title" + + case filesReceivedFilesNotReceivedExpl = "NearbySharing_Results_FilesReceived_FilesNotReceived_Expl" + case fileReceivedFilesNotReceivedExpl = "NearbySharing_Results_FileReceived_FilesNotReceived_Expl" + case filesReceivedFileNotReceivedExpl = "NearbySharing_Results_FilesReceived_FileNotReceived_Expl" + case fileReceivedFileNotReceivedExpl = "NearbySharing_Results_FileReceived_FileNotReceived_Expl" + + case failureFilesReceivedExpl = "NearbySharing_Results_FailureFilesReceived_Expl" + case failureFileReceivedExpl = "NearbySharing_Results_FailureFileReceived_Expl" + + case viewFilesAction = "NearbySharing_Results_ViewFiles_Action" + + case connectionChangedToast = "NearbySharing_ConnectionChanged_Toast" +} diff --git a/Tella/Utils/Localizable/LocalizableSettings.swift b/Tella/Utils/Localizable/LocalizableSettings.swift index c3f8df023..6716ed6a6 100644 --- a/Tella/Utils/Localizable/LocalizableSettings.swift +++ b/Tella/Utils/Localizable/LocalizableSettings.swift @@ -182,7 +182,15 @@ enum LocalizableSettings: String, LocalizableDelegate { //Dropbox case settServerDropboxSuccessMessage = "Settings_Server_Dropbox_Success_Message" case settServerDropbox = "Setting_SettServer_Dropbox" - } + + case settAddConnection = "Settings_Sett_AddConnection" + case settAddConnectionExpl = "Settings_Sett_AddConnection_Expl" + case settAddConnectionLearnMore = "Setting_Sett_AddConnection_LearnMore" + + case settNearbySharing = "Settings_Sett_NearbySharing" + case settNearbySharingExpl = "Settings_Sett_NearbySharing_Expl" + case settNearbySharingLearnMore = "Setting_Sett_NearbySharing_LearnMore" +} protocol LocalizableDelegate { diff --git a/Tella/Utils/NetworkMonitor.swift b/Tella/Utils/NetworkMonitor.swift index 48644a4ed..50f36245b 100644 --- a/Tella/Utils/NetworkMonitor.swift +++ b/Tella/Utils/NetworkMonitor.swift @@ -12,6 +12,8 @@ class NetworkMonitor: ObservableObject { var connectionDidChange = PassthroughSubject() var isConnected : Bool = true + var interfaceType = PassthroughSubject() + var interfaceTypeValue : NWInterface.InterfaceType? = nil private let monitor = NWPathMonitor() private let queue = DispatchQueue(label: "Monitor") @@ -24,10 +26,28 @@ class NetworkMonitor: ObservableObject { let newStatus = path.status == .satisfied self.isConnected = newStatus self.connectionDidChange.send(newStatus) + self.updateInterfaceType(for: path) } } monitor.start(queue: queue) } + + private func updateInterfaceType(for path: NWPath) { + if path.usesInterfaceType(.wifi) { + debugLog("Connected via Wi-Fi") + interfaceTypeValue = .wifi + interfaceType.send(.wifi) + } else if path.usesInterfaceType(.cellular) { + debugLog("Connected via Cellular") + interfaceTypeValue = .cellular + interfaceType.send(.cellular) + } else { + debugLog("No active network connection") + interfaceTypeValue = nil + interfaceType.send(nil) + } + } + deinit { stopNetworkMonitoring() } diff --git a/Tella/Utils/TextStyleType.swift b/Tella/Utils/TextStyleType.swift new file mode 100644 index 000000000..e6f67e3eb --- /dev/null +++ b/Tella/Utils/TextStyleType.swift @@ -0,0 +1,130 @@ +// +// TypographyStyle.swift +// Tella +// +// Created by Dhekra Rouatbi on 23/4/2025. +// Copyright © 2025 HORIZONTAL. All rights reserved. +// + +import Foundation +import UIKit + +enum TypographyStyle { + + case heading1Font + case heading2Font + case heading3Font + case subheading1Font + case subheading2Font + case body1Font + case body2Font + case body3Font + + case buttonLStyle + case buttonSStyle + case buttonDetailRegularStyle + case buttonDetailBoldStyle + + case link1Style + + var fontSize: CGFloat { + switch self { + + case .heading1Font: + return 18 + case .heading2Font: + return 16 + case .heading3Font: + return 16 + case .subheading1Font: + return 14 + case .subheading2Font: + return 10 + case .body1Font: + return 14 + case .body2Font: + return 12 + case .body3Font: + return 10 + case .buttonLStyle: + return 16 + case .buttonSStyle: + return 14 + case .buttonDetailRegularStyle: + return 11 + case .buttonDetailBoldStyle: + return 11 + case .link1Style: + return 16 + } + } + + var name: String { + switch self { + case .heading1Font: + return Styles.Fonts.semiBoldFontName + case .heading2Font: + return Styles.Fonts.semiBoldFontName + case .heading3Font: + return Styles.Fonts.boldFontName + case .subheading1Font: + return Styles.Fonts.semiBoldFontName + case .subheading2Font: + return Styles.Fonts.boldFontName + case .body1Font: + return Styles.Fonts.regularFontName + case .body2Font: + return Styles.Fonts.regularFontName + case .body3Font: + return Styles.Fonts.regularFontName + case .buttonLStyle: + return Styles.Fonts.boldFontName + case .buttonSStyle: + return Styles.Fonts.semiBoldFontName + case .buttonDetailRegularStyle: + return Styles.Fonts.regularFontName + case .buttonDetailBoldStyle: + return Styles.Fonts.regularFontName + case .link1Style: + return Styles.Fonts.regularFontName + } + } + + var characterSpacing: CGFloat { + switch self { + case .subheading2Font, .body1Font, .buttonLStyle: + return 0.5 + case .buttonSStyle: + return 0.3 + default: + return 0 + } + } + + var lineHeight: CGFloat { + + var lineHeightMultiplier: CGFloat + + switch self { + case .subheading2Font: + lineHeightMultiplier = 1.23 + + case .body1Font: + lineHeightMultiplier = 1.5 + + default: + lineHeightMultiplier = 1 + + } + return fontSize * lineHeightMultiplier + } + + var lineSpacing: CGFloat { + return lineHeight - font.lineHeight + } + + var font : UIFont { + return UIFont(name: name, size: fontSize) ?? .systemFont(ofSize: 12) + } + +} diff --git a/Tella/Utils/TypographyStyle.swift b/Tella/Utils/TypographyStyle.swift new file mode 100644 index 000000000..aad8c2717 --- /dev/null +++ b/Tella/Utils/TypographyStyle.swift @@ -0,0 +1,136 @@ +// +// TypographyStyle.swift +// Tella +// +// Created by Dhekra Rouatbi on 23/4/2025. +// Copyright © 2025 HORIZONTAL. +// Licensed under MIT (https://github.com/Horizontal-org/Tella-iOS/blob/develop/LICENSE) +// + +import Foundation +import UIKit + +enum TypographyStyle { + + case heading1Style + case heading2Style + case heading3Style + case subheading1Style + case subheading2Style + case body1Style + case body2Style + case body2ItalicStyle + case body3Style + + case buttonLStyle + case buttonSStyle + case buttonDetailRegularStyle + case buttonDetailBoldStyle + + case link1Style + + var fontSize: CGFloat { + switch self { + + case .heading1Style: + return 18 + case .heading2Style: + return 16 + case .heading3Style: + return 16 + case .subheading1Style: + return 14 + case .subheading2Style: + return 10 + case .body1Style: + return 14 + case .body2Style: + return 12 + case .body2ItalicStyle: + return 12 + case .body3Style: + return 10 + case .buttonLStyle: + return 16 + case .buttonSStyle: + return 14 + case .buttonDetailRegularStyle: + return 11 + case .buttonDetailBoldStyle: + return 11 + case .link1Style: + return 16 + } + } + + var name: String { + switch self { + case .heading1Style: + return Styles.Fonts.semiBoldFontName + case .heading2Style: + return Styles.Fonts.semiBoldFontName + case .heading3Style: + return Styles.Fonts.boldFontName + case .subheading1Style: + return Styles.Fonts.semiBoldFontName + case .subheading2Style: + return Styles.Fonts.boldFontName + case .body1Style: + return Styles.Fonts.regularFontName + case .body2Style: + return Styles.Fonts.regularFontName + case .body2ItalicStyle: + return Styles.Fonts.italicRobotoFontName + case .body3Style: + return Styles.Fonts.regularFontName + case .buttonLStyle: + return Styles.Fonts.boldFontName + case .buttonSStyle: + return Styles.Fonts.semiBoldFontName + case .buttonDetailRegularStyle: + return Styles.Fonts.regularFontName + case .buttonDetailBoldStyle: + return Styles.Fonts.regularFontName + case .link1Style: + return Styles.Fonts.regularFontName + } + } + + var characterSpacing: CGFloat { + switch self { + case .subheading2Style, .body1Style, .buttonLStyle: + return 0.5 + case .buttonSStyle: + return 0.3 + default: + return 0 + } + } + + var lineHeight: CGFloat { + + var lineHeightMultiplier: CGFloat + + switch self { + case .subheading2Style: + lineHeightMultiplier = 1.23 + + case .body1Style: + lineHeightMultiplier = 1.5 + + default: + lineHeightMultiplier = 1 + + } + return fontSize * lineHeightMultiplier + } + + var lineSpacing: CGFloat { + return lineHeight - font.lineHeight + } + + var font : UIFont { + return UIFont(name: name, size: fontSize) ?? .systemFont(ofSize: 12) + } + +} diff --git a/Tella/Utils/Validator.swift b/Tella/Utils/Validator.swift index 68b93a766..74c3816f0 100644 --- a/Tella/Utils/Validator.swift +++ b/Tella/Utils/Validator.swift @@ -24,9 +24,12 @@ struct Regex { static let usernameRegex = "^.{3,}" static let urlRegex = #"^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$"# static let codeRegex = "^[0-9]{6,6}$" + static let ipAddressRegex = #"^(?:\d{1,3}\.){3}\d{1,3}$"# + static let pinRegex = "^[0-9]{6}$" + static let portRegex = "^[0-9]{5}$" } -func validateRegex(value: String, pattern:String) -> Bool { + func validateRegex(value: String, pattern:String) -> Bool { do { let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive) return !regex.notMatchedIn(value: value) @@ -94,5 +97,36 @@ extension String { } return true } + func ipAddressValidator() -> Bool { + guard !self.isEmpty else { + return false + } + guard validateRegex(value: self, pattern: Regex.ipAddressRegex) else { + return false + } + return true + } + + func pinValidator() -> Bool { + guard !self.isEmpty else { + return false + } + guard validateRegex(value: self, pattern: Regex.pinRegex) else { + return false + } + return true + } + + func portValidator() -> Bool { + guard !self.isEmpty else { + return false + } + guard validateRegex(value: self, pattern: Regex.portRegex) else { + return false + } + return true + } + + } diff --git a/Tella/Utils/ViewClassType.swift b/Tella/Utils/ViewClassType.swift index 1b106b8cc..002360a9c 100644 --- a/Tella/Utils/ViewClassType.swift +++ b/Tella/Utils/ViewClassType.swift @@ -21,5 +21,6 @@ struct ViewClassType { static let tellaServerReportMainView : AnyClass = UIHostingController.self static let dropboxReportMainView: AnyClass = UIHostingController.self static let fileListView: AnyClass = UIHostingController.self + static let nearbySharingMainView: AnyClass = UIHostingController.self } diff --git a/llhttp/api.c b/llhttp/api.c new file mode 100644 index 000000000..024525417 --- /dev/null +++ b/llhttp/api.c @@ -0,0 +1,509 @@ +#include +#include +#include + +#include "llhttp.h" + +#define CALLBACK_MAYBE(PARSER, NAME) \ + do { \ + const llhttp_settings_t* settings; \ + settings = (const llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER)); \ + } while (0) + +#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ + do { \ + const llhttp_settings_t* settings; \ + settings = (const llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER), (START), (LEN)); \ + if (err == -1) { \ + err = HPE_USER; \ + llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \ + } \ + } while (0) + +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings) { + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; +} + + +#if defined(__wasm__) + +extern int wasm_on_message_begin(llhttp_t * p); +extern int wasm_on_url(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_status(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_headers_complete(llhttp_t * p, int status_code, + uint8_t upgrade, int should_keep_alive); +extern int wasm_on_body(llhttp_t* p, const char* at, size_t length); +extern int wasm_on_message_complete(llhttp_t * p); + +static int wasm_on_headers_complete_wrap(llhttp_t* p) { + return wasm_on_headers_complete(p, p->status_code, p->upgrade, + llhttp_should_keep_alive(p)); +} + +const llhttp_settings_t wasm_settings = { + .on_message_begin = wasm_on_message_begin, + .on_url = wasm_on_url, + .on_status = wasm_on_status, + .on_header_field = wasm_on_header_field, + .on_header_value = wasm_on_header_value, + .on_headers_complete = wasm_on_headers_complete_wrap, + .on_body = wasm_on_body, + .on_message_complete = wasm_on_message_complete, +}; + + +llhttp_t* llhttp_alloc(llhttp_type_t type) { + llhttp_t* parser = malloc(sizeof(llhttp_t)); + llhttp_init(parser, type, &wasm_settings); + return parser; +} + +void llhttp_free(llhttp_t* parser) { + free(parser); +} + +#endif // defined(__wasm__) + +/* Some getters required to get stuff from the parser */ + +uint8_t llhttp_get_type(llhttp_t* parser) { + return parser->type; +} + +uint8_t llhttp_get_http_major(llhttp_t* parser) { + return parser->http_major; +} + +uint8_t llhttp_get_http_minor(llhttp_t* parser) { + return parser->http_minor; +} + +uint8_t llhttp_get_method(llhttp_t* parser) { + return parser->method; +} + +int llhttp_get_status_code(llhttp_t* parser) { + return parser->status_code; +} + +uint8_t llhttp_get_upgrade(llhttp_t* parser) { + return parser->upgrade; +} + + +void llhttp_reset(llhttp_t* parser) { + llhttp_type_t type = parser->type; + const llhttp_settings_t* settings = parser->settings; + void* data = parser->data; + uint16_t lenient_flags = parser->lenient_flags; + + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; + parser->data = data; + parser->lenient_flags = lenient_flags; +} + + +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { + return llhttp__internal_execute(parser, data, data + len); +} + + +void llhttp_settings_init(llhttp_settings_t* settings) { + memset(settings, 0, sizeof(*settings)); +} + + +llhttp_errno_t llhttp_finish(llhttp_t* parser) { + int err; + + /* We're in an error state. Don't bother doing anything. */ + if (parser->error != 0) { + return 0; + } + + switch (parser->finish) { + case HTTP_FINISH_SAFE_WITH_CB: + CALLBACK_MAYBE(parser, on_message_complete); + if (err != HPE_OK) return err; + + /* FALLTHROUGH */ + case HTTP_FINISH_SAFE: + return HPE_OK; + case HTTP_FINISH_UNSAFE: + parser->reason = "Invalid EOF state"; + return HPE_INVALID_EOF_STATE; + default: + abort(); + } +} + + +void llhttp_pause(llhttp_t* parser) { + if (parser->error != HPE_OK) { + return; + } + + parser->error = HPE_PAUSED; + parser->reason = "Paused"; +} + + +void llhttp_resume(llhttp_t* parser) { + if (parser->error != HPE_PAUSED) { + return; + } + + parser->error = 0; +} + + +void llhttp_resume_after_upgrade(llhttp_t* parser) { + if (parser->error != HPE_PAUSED_UPGRADE) { + return; + } + + parser->error = 0; +} + + +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { + return parser->error; +} + + +const char* llhttp_get_error_reason(const llhttp_t* parser) { + return parser->reason; +} + + +void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { + parser->reason = reason; +} + + +const char* llhttp_get_error_pos(const llhttp_t* parser) { + return parser->error_pos; +} + + +const char* llhttp_errno_name(llhttp_errno_t err) { +#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; + switch (err) { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + default: abort(); + } +#undef HTTP_ERRNO_GEN +} + + +const char* llhttp_method_name(llhttp_method_t method) { +#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; + switch (method) { + HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN) + default: abort(); + } +#undef HTTP_METHOD_GEN +} + +const char* llhttp_status_name(llhttp_status_t status) { +#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING; + switch (status) { + HTTP_STATUS_MAP(HTTP_STATUS_GEN) + default: abort(); + } +#undef HTTP_STATUS_GEN +} + + +void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_HEADERS; + } else { + parser->lenient_flags &= ~LENIENT_HEADERS; + } +} + + +void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_CHUNKED_LENGTH; + } else { + parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH; + } +} + + +void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_KEEP_ALIVE; + } else { + parser->lenient_flags &= ~LENIENT_KEEP_ALIVE; + } +} + +void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_TRANSFER_ENCODING; + } else { + parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING; + } +} + +void llhttp_set_lenient_version(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_VERSION; + } else { + parser->lenient_flags &= ~LENIENT_VERSION; + } +} + +void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE; + } else { + parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE; + } +} + +void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR; + } +} + +void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK; + } +} + +void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF; + } else { + parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF; + } +} + +void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) { + if (enabled) { + parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE; + } else { + parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE; + } +} + +/* Callbacks */ + + +int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_begin); + return err; +} + + +int llhttp__on_protocol(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_protocol, p, endp - p); + return err; +} + + +int llhttp__on_protocol_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_protocol_complete); + return err; +} + + +int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p); + return err; +} + + +int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_url_complete); + return err; +} + + +int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p); + return err; +} + + +int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_status_complete); + return err; +} + + +int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p); + return err; +} + + +int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_method_complete); + return err; +} + + +int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p); + return err; +} + + +int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_version_complete); + return err; +} + + +int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p); + return err; +} + + +int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_field_complete); + return err; +} + + +int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p); + return err; +} + + +int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_value_complete); + return err; +} + + +int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_headers_complete); + return err; +} + + +int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_complete); + return err; +} + + +int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p); + return err; +} + + +int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_header); + return err; +} + + +int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p); + return err; +} + + +int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_extension_name_complete); + return err; +} + + +int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p); + return err; +} + + +int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_extension_value_complete); + return err; +} + + +int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_complete); + return err; +} + + +int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_reset); + return err; +} + + +/* Private */ + + +void llhttp__debug(llhttp_t* s, const char* p, const char* endp, + const char* msg) { + if (p == endp) { + fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, + s->flags, msg); + } else { + fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, + s->type, s->flags, *p, msg); + } +} diff --git a/llhttp/http.c b/llhttp/http.c new file mode 100644 index 000000000..1ab91a557 --- /dev/null +++ b/llhttp/http.c @@ -0,0 +1,170 @@ +#include +#ifndef LLHTTP__TEST +# include "llhttp.h" +#else +# define llhttp_t llparse_t +#endif /* */ + +int llhttp_message_needs_eof(const llhttp_t* parser); +int llhttp_should_keep_alive(const llhttp_t* parser); + +int llhttp__before_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + return 0; +} + + +/* Return values: + * 0 - No body, `restart`, message_complete + * 1 - CONNECT request, `restart`, message_complete, and pause + * 2 - chunk_size_start + * 3 - body_identity + * 4 - body_identity_eof + * 5 - invalid transfer-encoding for request + */ +int llhttp__after_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + int hasBody; + + hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; + if ( + (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) || + /* See RFC 2616 section 4.4 - 1xx e.g. Continue */ + (parser->type == HTTP_RESPONSE && parser->status_code == 101) + ) { + /* Exit, the rest of the message is in a different protocol. */ + return 1; + } + + if (parser->type == HTTP_RESPONSE && parser->status_code == 100) { + /* No body, restart as the message is complete */ + return 0; + } + + /* See RFC 2616 section 4.4 */ + if ( + parser->flags & F_SKIPBODY || /* response to a HEAD request */ + ( + parser->type == HTTP_RESPONSE && ( + parser->status_code == 102 || /* Processing */ + parser->status_code == 103 || /* Early Hints */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 /* Not Modified */ + ) + ) + ) { + return 0; + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header, prepare for a chunk */ + return 2; + } else if (parser->flags & F_TRANSFER_ENCODING) { + if (parser->type == HTTP_REQUEST && + (parser->lenient_flags & LENIENT_CHUNKED_LENGTH) == 0 && + (parser->lenient_flags & LENIENT_TRANSFER_ENCODING) == 0) { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field + * is present in a request and the chunked transfer coding is not + * the final encoding, the message body length cannot be determined + * reliably; the server MUST respond with the 400 (Bad Request) + * status code and then close the connection. + */ + return 5; + } else { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field is present in a response and + * the chunked transfer coding is not the final encoding, the + * message body length is determined by reading the connection until + * it is closed by the server. + */ + return 4; + } + } else { + if (!(parser->flags & F_CONTENT_LENGTH)) { + if (!llhttp_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + return 0; + } else { + /* Read body until EOF */ + return 4; + } + } else if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + return 0; + } else { + /* Content-Length header given and non-zero */ + return 3; + } + } +} + + +int llhttp__after_message_complete(llhttp_t* parser, const char* p, + const char* endp) { + int should_keep_alive; + + should_keep_alive = llhttp_should_keep_alive(parser); + parser->finish = HTTP_FINISH_SAFE; + parser->flags = 0; + + /* NOTE: this is ignored in loose parsing mode */ + return should_keep_alive; +} + + +int llhttp_message_needs_eof(const llhttp_t* parser) { + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ + return 0; + } + + /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ + if ((parser->flags & F_TRANSFER_ENCODING) && + (parser->flags & F_CHUNKED) == 0) { + return 1; + } + + if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { + return 0; + } + + return 1; +} + + +int llhttp_should_keep_alive(const llhttp_t* parser) { + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !llhttp_message_needs_eof(parser); +} diff --git a/llhttp/llhttp.c b/llhttp/llhttp.c new file mode 100644 index 000000000..aa4c46820 --- /dev/null +++ b/llhttp/llhttp.c @@ -0,0 +1,10099 @@ +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef __ARM_NEON__ + #include +#endif /* __ARM_NEON__ */ + +#ifdef __wasm__ + #include +#endif /* __wasm__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) + #define UNREACHABLE __assume(0) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) + #define UNREACHABLE __builtin_unreachable() +#endif /* _MSC_VER */ + +#include "llhttp.h" + +typedef int (*llhttp__internal__span_cb)( + llhttp__internal_t*, const char*, const char*); + +static const unsigned char llparse_blob0[] = { + 'o', 'n' +}; +static const unsigned char llparse_blob1[] = { + 'e', 'c', 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob2[] = { + 'l', 'o', 's', 'e' +}; +static const unsigned char llparse_blob3[] = { + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' +}; +static const unsigned char llparse_blob4[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob5[] = { + 'c', 'h', 'u', 'n', 'k', 'e', 'd' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob6[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob7[] = { + '!', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob8[] = { + '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob9[] = { + 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' +}; +static const unsigned char llparse_blob10[] = { + 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', + 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob11[] = { + 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', + 'o', 'd', 'i', 'n', 'g' +}; +static const unsigned char llparse_blob12[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob13[] = { + 'T', 'T', 'P' +}; +static const unsigned char llparse_blob14[] = { + 0xd, 0xa, 0xd, 0xa, 'S', 'M', 0xd, 0xa, 0xd, 0xa +}; +static const unsigned char llparse_blob15[] = { + 'C', 'E' +}; +static const unsigned char llparse_blob16[] = { + 'T', 'S', 'P' +}; +static const unsigned char llparse_blob17[] = { + 'N', 'O', 'U', 'N', 'C', 'E' +}; +static const unsigned char llparse_blob18[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob19[] = { + 'E', 'C', 'K', 'O', 'U', 'T' +}; +static const unsigned char llparse_blob20[] = { + 'N', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob21[] = { + 'E', 'T', 'E' +}; +static const unsigned char llparse_blob22[] = { + 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob23[] = { + 'L', 'U', 'S', 'H' +}; +static const unsigned char llparse_blob24[] = { + 'E', 'T' +}; +static const unsigned char llparse_blob25[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob26[] = { + 'E', 'A', 'D' +}; +static const unsigned char llparse_blob27[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob28[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob29[] = { + 'S', 'E', 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob30[] = { + 'R', 'G', 'E' +}; +static const unsigned char llparse_blob31[] = { + 'C', 'T', 'I', 'V', 'I', 'T', 'Y' +}; +static const unsigned char llparse_blob32[] = { + 'L', 'E', 'N', 'D', 'A', 'R' +}; +static const unsigned char llparse_blob33[] = { + 'V', 'E' +}; +static const unsigned char llparse_blob34[] = { + 'O', 'T', 'I', 'F', 'Y' +}; +static const unsigned char llparse_blob35[] = { + 'P', 'T', 'I', 'O', 'N', 'S' +}; +static const unsigned char llparse_blob36[] = { + 'C', 'H' +}; +static const unsigned char llparse_blob37[] = { + 'S', 'E' +}; +static const unsigned char llparse_blob38[] = { + 'A', 'Y' +}; +static const unsigned char llparse_blob39[] = { + 'S', 'T' +}; +static const unsigned char llparse_blob40[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob41[] = { + 'A', 'T', 'C', 'H' +}; +static const unsigned char llparse_blob42[] = { + 'G', 'E' +}; +static const unsigned char llparse_blob43[] = { + 'U', 'E', 'R', 'Y' +}; +static const unsigned char llparse_blob44[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob45[] = { + 'O', 'R', 'D' +}; +static const unsigned char llparse_blob46[] = { + 'I', 'R', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob47[] = { + 'O', 'R', 'T' +}; +static const unsigned char llparse_blob48[] = { + 'R', 'C', 'H' +}; +static const unsigned char llparse_blob49[] = { + 'P', 'A', 'R', 'A', 'M', 'E', 'T', 'E', 'R' +}; +static const unsigned char llparse_blob50[] = { + 'U', 'R', 'C', 'E' +}; +static const unsigned char llparse_blob51[] = { + 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob52[] = { + 'A', 'R', 'D', 'O', 'W', 'N' +}; +static const unsigned char llparse_blob53[] = { + 'A', 'C', 'E' +}; +static const unsigned char llparse_blob54[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob55[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob56[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob57[] = { + 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob58[] = { + 'T', 'T', 'P' +}; +static const unsigned char llparse_blob59[] = { + 'C', 'E' +}; +static const unsigned char llparse_blob60[] = { + 'T', 'S', 'P' +}; +static const unsigned char llparse_blob61[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob62[] = { + 'T', 'P', '/' +}; + +enum llparse_match_status_e { + kMatchComplete, + kMatchPause, + kMatchMismatch +}; +typedef enum llparse_match_status_e llparse_match_status_t; + +struct llparse_match_s { + llparse_match_status_t status; + const unsigned char* current; +}; +typedef struct llparse_match_s llparse_match_t; + +static llparse_match_t llparse__match_sequence_to_lower( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p)); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_to_lower_unsafe( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) | 0x20); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_id( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = *p; + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +enum llparse_state_e { + s_error, + s_n_llhttp__internal__n_closed, + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, + s_n_llhttp__internal__n_pause_1, + s_n_llhttp__internal__n_invoke_is_equal_upgrade, + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, + s_n_llhttp__internal__n_chunk_data_almost_done_1, + s_n_llhttp__internal__n_chunk_data_almost_done, + s_n_llhttp__internal__n_consume_content_length, + s_n_llhttp__internal__n_span_start_llhttp__on_body, + s_n_llhttp__internal__n_invoke_is_equal_content_length, + s_n_llhttp__internal__n_chunk_size_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_9, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2, + s_n_llhttp__internal__n_invoke_test_lenient_flags_10, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1, + s_n_llhttp__internal__n_chunk_extension_quoted_value_done, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2, + s_n_llhttp__internal__n_error_30, + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair, + s_n_llhttp__internal__n_error_31, + s_n_llhttp__internal__n_chunk_extension_quoted_value, + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3, + s_n_llhttp__internal__n_error_33, + s_n_llhttp__internal__n_chunk_extension_value, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value, + s_n_llhttp__internal__n_error_34, + s_n_llhttp__internal__n_chunk_extension_name, + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name, + s_n_llhttp__internal__n_chunk_extensions, + s_n_llhttp__internal__n_chunk_size_otherwise, + s_n_llhttp__internal__n_chunk_size, + s_n_llhttp__internal__n_chunk_size_digit, + s_n_llhttp__internal__n_invoke_update_content_length_1, + s_n_llhttp__internal__n_consume_content_length_1, + s_n_llhttp__internal__n_span_start_llhttp__on_body_1, + s_n_llhttp__internal__n_eof, + s_n_llhttp__internal__n_span_start_llhttp__on_body_2, + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, + s_n_llhttp__internal__n_error_5, + s_n_llhttp__internal__n_headers_almost_done, + s_n_llhttp__internal__n_header_field_colon_discard_ws, + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value, + s_n_llhttp__internal__n_header_value_discard_lws, + s_n_llhttp__internal__n_header_value_discard_ws_almost_done, + s_n_llhttp__internal__n_header_value_lws, + s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_17, + s_n_llhttp__internal__n_header_value_lenient, + s_n_llhttp__internal__n_error_54, + s_n_llhttp__internal__n_header_value_otherwise, + s_n_llhttp__internal__n_header_value_connection_token, + s_n_llhttp__internal__n_header_value_connection_ws, + s_n_llhttp__internal__n_header_value_connection_1, + s_n_llhttp__internal__n_header_value_connection_2, + s_n_llhttp__internal__n_header_value_connection_3, + s_n_llhttp__internal__n_header_value_connection, + s_n_llhttp__internal__n_error_56, + s_n_llhttp__internal__n_error_57, + s_n_llhttp__internal__n_header_value_content_length_ws, + s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_error_59, + s_n_llhttp__internal__n_error_58, + s_n_llhttp__internal__n_header_value_te_token_ows, + s_n_llhttp__internal__n_header_value, + s_n_llhttp__internal__n_header_value_te_token, + s_n_llhttp__internal__n_header_value_te_chunked_last, + s_n_llhttp__internal__n_header_value_te_chunked, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, + s_n_llhttp__internal__n_header_value_discard_ws, + s_n_llhttp__internal__n_invoke_load_header_state, + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete, + s_n_llhttp__internal__n_header_field_general_otherwise, + s_n_llhttp__internal__n_header_field_general, + s_n_llhttp__internal__n_header_field_colon, + s_n_llhttp__internal__n_header_field_3, + s_n_llhttp__internal__n_header_field_4, + s_n_llhttp__internal__n_header_field_2, + s_n_llhttp__internal__n_header_field_1, + s_n_llhttp__internal__n_header_field_5, + s_n_llhttp__internal__n_header_field_6, + s_n_llhttp__internal__n_header_field_7, + s_n_llhttp__internal__n_header_field, + s_n_llhttp__internal__n_span_start_llhttp__on_header_field, + s_n_llhttp__internal__n_header_field_start, + s_n_llhttp__internal__n_headers_start, + s_n_llhttp__internal__n_url_to_http_09, + s_n_llhttp__internal__n_url_skip_to_http09, + s_n_llhttp__internal__n_url_skip_lf_to_http09_1, + s_n_llhttp__internal__n_url_skip_lf_to_http09, + s_n_llhttp__internal__n_req_pri_upgrade, + s_n_llhttp__internal__n_req_http_complete_crlf, + s_n_llhttp__internal__n_req_http_complete, + s_n_llhttp__internal__n_invoke_load_method_1, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete, + s_n_llhttp__internal__n_error_67, + s_n_llhttp__internal__n_error_74, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_error_75, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_error_76, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version, + s_n_llhttp__internal__n_req_after_protocol, + s_n_llhttp__internal__n_invoke_load_method, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete, + s_n_llhttp__internal__n_error_82, + s_n_llhttp__internal__n_req_after_http_start_1, + s_n_llhttp__internal__n_invoke_load_method_2, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1, + s_n_llhttp__internal__n_req_after_http_start_2, + s_n_llhttp__internal__n_invoke_load_method_3, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2, + s_n_llhttp__internal__n_req_after_http_start_3, + s_n_llhttp__internal__n_req_after_http_start, + s_n_llhttp__internal__n_span_start_llhttp__on_protocol, + s_n_llhttp__internal__n_req_http_start, + s_n_llhttp__internal__n_url_to_http, + s_n_llhttp__internal__n_url_skip_to_http, + s_n_llhttp__internal__n_url_fragment, + s_n_llhttp__internal__n_span_end_stub_query_3, + s_n_llhttp__internal__n_url_query, + s_n_llhttp__internal__n_url_query_or_fragment, + s_n_llhttp__internal__n_url_path, + s_n_llhttp__internal__n_span_start_stub_path_2, + s_n_llhttp__internal__n_span_start_stub_path, + s_n_llhttp__internal__n_span_start_stub_path_1, + s_n_llhttp__internal__n_url_server_with_at, + s_n_llhttp__internal__n_url_server, + s_n_llhttp__internal__n_url_schema_delim_1, + s_n_llhttp__internal__n_url_schema_delim, + s_n_llhttp__internal__n_span_end_stub_schema, + s_n_llhttp__internal__n_url_schema, + s_n_llhttp__internal__n_url_start, + s_n_llhttp__internal__n_span_start_llhttp__on_url_1, + s_n_llhttp__internal__n_url_entry_normal, + s_n_llhttp__internal__n_span_start_llhttp__on_url, + s_n_llhttp__internal__n_url_entry_connect, + s_n_llhttp__internal__n_req_spaces_before_url, + s_n_llhttp__internal__n_req_first_space_before_url, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1, + s_n_llhttp__internal__n_after_start_req_2, + s_n_llhttp__internal__n_after_start_req_3, + s_n_llhttp__internal__n_after_start_req_1, + s_n_llhttp__internal__n_after_start_req_4, + s_n_llhttp__internal__n_after_start_req_6, + s_n_llhttp__internal__n_after_start_req_8, + s_n_llhttp__internal__n_after_start_req_9, + s_n_llhttp__internal__n_after_start_req_7, + s_n_llhttp__internal__n_after_start_req_5, + s_n_llhttp__internal__n_after_start_req_12, + s_n_llhttp__internal__n_after_start_req_13, + s_n_llhttp__internal__n_after_start_req_11, + s_n_llhttp__internal__n_after_start_req_10, + s_n_llhttp__internal__n_after_start_req_14, + s_n_llhttp__internal__n_after_start_req_17, + s_n_llhttp__internal__n_after_start_req_16, + s_n_llhttp__internal__n_after_start_req_15, + s_n_llhttp__internal__n_after_start_req_18, + s_n_llhttp__internal__n_after_start_req_20, + s_n_llhttp__internal__n_after_start_req_21, + s_n_llhttp__internal__n_after_start_req_19, + s_n_llhttp__internal__n_after_start_req_23, + s_n_llhttp__internal__n_after_start_req_24, + s_n_llhttp__internal__n_after_start_req_26, + s_n_llhttp__internal__n_after_start_req_28, + s_n_llhttp__internal__n_after_start_req_29, + s_n_llhttp__internal__n_after_start_req_27, + s_n_llhttp__internal__n_after_start_req_25, + s_n_llhttp__internal__n_after_start_req_30, + s_n_llhttp__internal__n_after_start_req_22, + s_n_llhttp__internal__n_after_start_req_31, + s_n_llhttp__internal__n_after_start_req_32, + s_n_llhttp__internal__n_after_start_req_35, + s_n_llhttp__internal__n_after_start_req_36, + s_n_llhttp__internal__n_after_start_req_34, + s_n_llhttp__internal__n_after_start_req_37, + s_n_llhttp__internal__n_after_start_req_38, + s_n_llhttp__internal__n_after_start_req_42, + s_n_llhttp__internal__n_after_start_req_43, + s_n_llhttp__internal__n_after_start_req_41, + s_n_llhttp__internal__n_after_start_req_40, + s_n_llhttp__internal__n_after_start_req_39, + s_n_llhttp__internal__n_after_start_req_45, + s_n_llhttp__internal__n_after_start_req_44, + s_n_llhttp__internal__n_after_start_req_33, + s_n_llhttp__internal__n_after_start_req_46, + s_n_llhttp__internal__n_after_start_req_49, + s_n_llhttp__internal__n_after_start_req_50, + s_n_llhttp__internal__n_after_start_req_51, + s_n_llhttp__internal__n_after_start_req_52, + s_n_llhttp__internal__n_after_start_req_48, + s_n_llhttp__internal__n_after_start_req_47, + s_n_llhttp__internal__n_after_start_req_55, + s_n_llhttp__internal__n_after_start_req_57, + s_n_llhttp__internal__n_after_start_req_58, + s_n_llhttp__internal__n_after_start_req_56, + s_n_llhttp__internal__n_after_start_req_54, + s_n_llhttp__internal__n_after_start_req_59, + s_n_llhttp__internal__n_after_start_req_60, + s_n_llhttp__internal__n_after_start_req_53, + s_n_llhttp__internal__n_after_start_req_62, + s_n_llhttp__internal__n_after_start_req_63, + s_n_llhttp__internal__n_after_start_req_61, + s_n_llhttp__internal__n_after_start_req_66, + s_n_llhttp__internal__n_after_start_req_68, + s_n_llhttp__internal__n_after_start_req_69, + s_n_llhttp__internal__n_after_start_req_67, + s_n_llhttp__internal__n_after_start_req_70, + s_n_llhttp__internal__n_after_start_req_65, + s_n_llhttp__internal__n_after_start_req_64, + s_n_llhttp__internal__n_after_start_req, + s_n_llhttp__internal__n_span_start_llhttp__on_method_1, + s_n_llhttp__internal__n_res_line_almost_done, + s_n_llhttp__internal__n_invoke_test_lenient_flags_30, + s_n_llhttp__internal__n_res_status, + s_n_llhttp__internal__n_span_start_llhttp__on_status, + s_n_llhttp__internal__n_res_status_code_otherwise, + s_n_llhttp__internal__n_res_status_code_digit_3, + s_n_llhttp__internal__n_res_status_code_digit_2, + s_n_llhttp__internal__n_res_status_code_digit_1, + s_n_llhttp__internal__n_res_after_version, + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1, + s_n_llhttp__internal__n_error_93, + s_n_llhttp__internal__n_error_107, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_error_108, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_error_109, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_span_start_llhttp__on_version_1, + s_n_llhttp__internal__n_res_after_protocol, + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3, + s_n_llhttp__internal__n_error_115, + s_n_llhttp__internal__n_res_after_start_1, + s_n_llhttp__internal__n_res_after_start_2, + s_n_llhttp__internal__n_res_after_start_3, + s_n_llhttp__internal__n_res_after_start, + s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1, + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete, + s_n_llhttp__internal__n_req_or_res_method_2, + s_n_llhttp__internal__n_invoke_update_type_1, + s_n_llhttp__internal__n_req_or_res_method_3, + s_n_llhttp__internal__n_req_or_res_method_1, + s_n_llhttp__internal__n_req_or_res_method, + s_n_llhttp__internal__n_span_start_llhttp__on_method, + s_n_llhttp__internal__n_start_req_or_res, + s_n_llhttp__internal__n_invoke_load_type, + s_n_llhttp__internal__n_invoke_update_finish, + s_n_llhttp__internal__n_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llhttp__on_method( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_url( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_protocol( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_version( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_field( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_body( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_name( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_status( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->initial_message_completed; +} + +int llhttp__on_reset( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 2; + return 0; +} + +int llhttp__on_message_begin( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->type; +} + +int llhttp__internal__c_store_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->method = match; + return 0; +} + +int llhttp__on_method_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 5; +} + +int llhttp__internal__c_update_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_major = 0; + return 0; +} + +int llhttp__internal__c_update_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_minor = 9; + return 0; +} + +int llhttp__on_url_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_test_lenient_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 1) == 1; +} + +int llhttp__internal__c_test_lenient_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 256) == 256; +} + +int llhttp__internal__c_test_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 128) == 128; +} + +int llhttp__on_chunk_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->upgrade == 1; +} + +int llhttp__after_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->content_length = 0; + return 0; +} + +int llhttp__internal__c_update_initial_message_completed( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->initial_message_completed = 1; + return 0; +} + +int llhttp__internal__c_update_finish_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 0; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 4) == 4; +} + +int llhttp__internal__c_test_lenient_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 32) == 32; +} + +int llhttp__before_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__after_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_mul_add_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 16) { + return 1; + } + + state->content_length *= 16; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 512) == 512; +} + +int llhttp__on_chunk_header( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->content_length == 0; +} + +int llhttp__internal__c_test_lenient_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 128) == 128; +} + +int llhttp__internal__c_or_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 128; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 64) == 64; +} + +int llhttp__on_chunk_extension_name_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_chunk_extension_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 1; + return 0; +} + +int llhttp__internal__c_or_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 64; + return 0; +} + +int llhttp__internal__c_update_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->upgrade = 1; + return 0; +} + +int llhttp__internal__c_store_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->header_state = match; + return 0; +} + +int llhttp__on_header_field_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->header_state; +} + +int llhttp__internal__c_test_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 512) == 512; +} + +int llhttp__internal__c_test_lenient_flags_22( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 2) == 2; +} + +int llhttp__internal__c_or_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 1; + return 0; +} + +int llhttp__internal__c_update_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 1; + return 0; +} + +int llhttp__on_header_value_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_or_flags_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 2; + return 0; +} + +int llhttp__internal__c_or_flags_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 4; + return 0; +} + +int llhttp__internal__c_or_flags_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 8; + return 0; +} + +int llhttp__internal__c_update_header_state_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 6; + return 0; +} + +int llhttp__internal__c_update_header_state_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 0; + return 0; +} + +int llhttp__internal__c_update_header_state_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 5; + return 0; +} + +int llhttp__internal__c_update_header_state_7( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 7; + return 0; +} + +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 32) == 32; +} + +int llhttp__internal__c_mul_add_content_length_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 10) { + return 1; + } + + state->content_length *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_or_flags_17( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 32; + return 0; +} + +int llhttp__internal__c_test_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 8) == 8; +} + +int llhttp__internal__c_test_lenient_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 8) == 8; +} + +int llhttp__internal__c_or_flags_18( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 512; + return 0; +} + +int llhttp__internal__c_and_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags &= -9; + return 0; +} + +int llhttp__internal__c_update_header_state_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 8; + return 0; +} + +int llhttp__internal__c_or_flags_20( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 16; + return 0; +} + +int llhttp__on_protocol_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method; +} + +int llhttp__internal__c_store_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_major = match; + return 0; +} + +int llhttp__internal__c_store_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_minor = match; + return 0; +} + +int llhttp__internal__c_test_lenient_flags_24( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->lenient_flags & 16) == 16; +} + +int llhttp__on_version_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_major; +} + +int llhttp__internal__c_load_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->http_minor; +} + +int llhttp__internal__c_update_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->status_code = 0; + return 0; +} + +int llhttp__internal__c_mul_add_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->status_code > 0xffff / 10) { + return 1; + } + + state->status_code *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->status_code > 0xffff - match) { + return 1; + } + } else { + if (state->status_code < 0 - match) { + return 1; + } + } + state->status_code += match; + return 0; +} + +int llhttp__on_status_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 1; + return 0; +} + +int llhttp__internal__c_update_type_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 2; + return 0; +} + +int llhttp__internal_init(llhttp__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; + return 0; +} + +static llparse_state_t llhttp__internal__run( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llhttp__internal__n_closed: + s_n_llhttp__internal__n_closed: { + if (p == endp) { + return s_n_llhttp__internal__n_closed; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_closed; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_closed; + } + default: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { + switch (llhttp__after_message_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_content_length; + default: + goto s_n_llhttp__internal__n_invoke_update_finish_1; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_pause_1: + s_n_llhttp__internal__n_pause_1: { + state->error = 0x16; + state->reason = "Pause on CONNECT/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_is_equal_upgrade: + s_n_llhttp__internal__n_invoke_is_equal_upgrade: { + switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + default: + goto s_n_llhttp__internal__n_pause_1; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_13; + default: + goto s_n_llhttp__internal__n_error_38; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_data_almost_done_1: + s_n_llhttp__internal__n_chunk_data_almost_done_1: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_data_almost_done: + s_n_llhttp__internal__n_chunk_data_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_6; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_data_almost_done_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_7; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_consume_content_length: + s_n_llhttp__internal__n_consume_content_length: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length; + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body: + s_n_llhttp__internal__n_span_start_llhttp__on_body: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length; + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_is_equal_content_length: + s_n_llhttp__internal__n_invoke_is_equal_content_length: { + switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body; + default: + goto s_n_llhttp__internal__n_invoke_or_flags; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_size_almost_done: + s_n_llhttp__internal__n_chunk_size_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_8; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_9: + s_n_llhttp__internal__n_invoke_test_lenient_flags_9: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_20; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + case 21: + goto s_n_llhttp__internal__n_pause_5; + default: + goto s_n_llhttp__internal__n_error_19; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_6; + default: + goto s_n_llhttp__internal__n_error_21; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_7; + default: + goto s_n_llhttp__internal__n_error_22; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_10: + s_n_llhttp__internal__n_invoke_test_lenient_flags_10: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_25; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + case 21: + goto s_n_llhttp__internal__n_pause_8; + default: + goto s_n_llhttp__internal__n_error_24; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + case 21: + goto s_n_llhttp__internal__n_pause_9; + default: + goto s_n_llhttp__internal__n_error_26; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_done: + s_n_llhttp__internal__n_chunk_extension_quoted_value_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_11; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_29; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + case 21: + goto s_n_llhttp__internal__n_pause_10; + default: + goto s_n_llhttp__internal__n_error_27; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_30: + s_n_llhttp__internal__n_error_30: { + state->error = 0x2; + state->reason = "Invalid quoted-pair in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: + s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_31: + s_n_llhttp__internal__n_error_31: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quoted value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extension_quoted_value: + s_n_llhttp__internal__n_chunk_extension_quoted_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value_quoted_pair; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3: { + switch (llhttp__on_chunk_extension_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extensions; + case 21: + goto s_n_llhttp__internal__n_pause_11; + default: + goto s_n_llhttp__internal__n_error_32; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_33: + s_n_llhttp__internal__n_error_33: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extension_value: + s_n_llhttp__internal__n_chunk_extension_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 4, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 5, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_value; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_value; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_quoted_value; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_value; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3; + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_34: + s_n_llhttp__internal__n_error_34: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extension_name: + s_n_llhttp__internal__n_chunk_extension_name: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 0, 5, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extension_name; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_chunk_extension_name; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: + s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_chunk_extension_name; + goto s_n_llhttp__internal__n_chunk_extension_name; + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_extensions: + s_n_llhttp__internal__n_chunk_extensions: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_extensions; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_error_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_18; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_name; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_size_otherwise: + s_n_llhttp__internal__n_chunk_size_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_otherwise; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_5; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_4; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_extensions; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_size: + s_n_llhttp__internal__n_chunk_size: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_chunk_size_otherwise; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_chunk_size_digit: + s_n_llhttp__internal__n_chunk_size_digit: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_digit; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_error_37; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_update_content_length_1: + s_n_llhttp__internal__n_invoke_update_content_length_1: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_chunk_size_digit; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_consume_content_length_1: + s_n_llhttp__internal__n_consume_content_length_1: { + size_t avail; + uint64_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length_1; + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: + s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length_1; + UNREACHABLE; + } + case s_n_llhttp__internal__n_eof: + s_n_llhttp__internal__n_eof: { + if (p == endp) { + return s_n_llhttp__internal__n_eof; + } + p++; + goto s_n_llhttp__internal__n_eof; + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: + s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_eof; + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { + switch (llhttp__after_headers_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + case 4: + goto s_n_llhttp__internal__n_invoke_update_finish_3; + case 5: + goto s_n_llhttp__internal__n_error_39; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_5: + s_n_llhttp__internal__n_error_5: { + state->error = 0xa; + state->reason = "Invalid header field char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_headers_almost_done: + s_n_llhttp__internal__n_headers_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_flags_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_12; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_colon_discard_ws: + s_n_llhttp__internal__n_header_field_colon_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_header_field_colon; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete: { + switch (llhttp__on_header_value_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_field_start; + case 21: + goto s_n_llhttp__internal__n_pause_18; + default: + goto s_n_llhttp__internal__n_error_48; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_discard_lws: + s_n_llhttp__internal__n_header_value_discard_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_lws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_15; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_1; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: + s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_16; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_lws: + s_n_llhttp__internal__n_header_value_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lws; + } + switch (*p) { + case 9: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_18; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_almost_done: + s_n_llhttp__internal__n_header_value_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_lws; + } + default: { + goto s_n_llhttp__internal__n_error_53; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_17: + s_n_llhttp__internal__n_invoke_test_lenient_flags_17: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_almost_done; + default: + goto s_n_llhttp__internal__n_error_51; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_54: + s_n_llhttp__internal__n_error_54: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_otherwise: + s_n_llhttp__internal__n_header_value_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_19; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection_token: + s_n_llhttp__internal__n_header_value_connection_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_connection_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection_ws: + s_n_llhttp__internal__n_header_value_connection_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + case ',': { + p++; + goto s_n_llhttp__internal__n_invoke_load_header_state_6; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection_1: + s_n_llhttp__internal__n_header_value_connection_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob2, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection_2: + s_n_llhttp__internal__n_header_value_connection_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_2; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob3, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_6; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection_3: + s_n_llhttp__internal__n_header_value_connection_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob4, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_connection: + s_n_llhttp__internal__n_header_value_connection: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_1; + } + case 'k': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_2; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_3; + } + default: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_56: + s_n_llhttp__internal__n_error_56: { + state->error = 0xb; + state->reason = "Content-Length overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_57: + s_n_llhttp__internal__n_error_57: { + state->error = 0xb; + state->reason = "Invalid character in Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_content_length_ws: + s_n_llhttp__internal__n_header_value_content_length_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_or_flags_17; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_content_length: + s_n_llhttp__internal__n_header_value_content_length: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_59: + s_n_llhttp__internal__n_error_59: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_58: + s_n_llhttp__internal__n_error_58: { + state->error = 0xf; + state->reason = "Invalid `Transfer-Encoding` header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_te_token_ows: + s_n_llhttp__internal__n_header_value_te_token_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token_ows; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value: + s_n_llhttp__internal__n_header_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob6); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ + #ifdef __ARM_NEON__ + while (endp - p >= 16) { + uint8x16_t input; + uint8x16_t single; + uint8x16_t mask; + uint8x8_t narrow; + uint64_t match_mask; + int match_len; + + /* Load input */ + input = vld1q_u8(p); + /* Find first character that does not match `ranges` */ + single = vceqq_u8(input, vdupq_n_u8(0x9)); + mask = single; + single = vandq_u16( + vcgeq_u8(input, vdupq_n_u8(' ')), + vcleq_u8(input, vdupq_n_u8('~')) + ); + mask = vorrq_u16(mask, single); + single = vandq_u16( + vcgeq_u8(input, vdupq_n_u8(0x80)), + vcleq_u8(input, vdupq_n_u8(0xff)) + ); + mask = vorrq_u16(mask, single); + narrow = vshrn_n_u16(mask, 4); + match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0); + match_len = __builtin_ctzll(match_mask) >> 2; + if (match_len != 16) { + p += match_len; + goto s_n_llhttp__internal__n_header_value_otherwise; + } + p += 16; + } + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #endif /* __ARM_NEON__ */ + #ifdef __wasm_simd128__ + while (endp - p >= 16) { + v128_t input; + v128_t mask; + v128_t single; + int match_len; + + /* Load input */ + input = wasm_v128_load(p); + /* Find first character that does not match `ranges` */ + single = wasm_i8x16_eq(input, wasm_u8x16_const_splat(0x9)); + mask = single; + single = wasm_v128_and( + wasm_i8x16_ge(input, wasm_u8x16_const_splat(' ')), + wasm_i8x16_le(input, wasm_u8x16_const_splat('~')) + ); + mask = wasm_v128_or(mask, single); + single = wasm_v128_and( + wasm_i8x16_ge(input, wasm_u8x16_const_splat(0x80)), + wasm_i8x16_le(input, wasm_u8x16_const_splat(0xff)) + ); + mask = wasm_v128_or(mask, single); + match_len = __builtin_ctz( + ~wasm_i8x16_bitmask(mask) + ); + if (match_len != 16) { + p += match_len; + goto s_n_llhttp__internal__n_header_value_otherwise; + } + p += 16; + } + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #endif /* __wasm_simd128__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_te_token: + s_n_llhttp__internal__n_header_value_te_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_te_chunked_last: + s_n_llhttp__internal__n_header_value_te_chunked_last: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked_last; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case ',': { + goto s_n_llhttp__internal__n_invoke_load_type_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_te_chunked: + s_n_llhttp__internal__n_header_value_te_chunked: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_invoke_load_header_state_3; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_value_discard_ws: + s_n_llhttp__internal__n_header_value_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_14; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_header_state: + s_n_llhttp__internal__n_invoke_load_header_state: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_4; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_5; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete: { + switch (llhttp__on_header_field_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_header_state; + case 21: + goto s_n_llhttp__internal__n_pause_19; + default: + goto s_n_llhttp__internal__n_error_45; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_general_otherwise: + s_n_llhttp__internal__n_header_field_general_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general_otherwise; + } + switch (*p) { + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2; + } + default: { + goto s_n_llhttp__internal__n_error_62; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_general: + s_n_llhttp__internal__n_header_field_general: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob8); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_field_general; + } + default: { + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_colon: + s_n_llhttp__internal__n_header_field_colon: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon; + } + switch (*p) { + case ' ': { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_13; + } + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_3: + s_n_llhttp__internal__n_header_field_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_3; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob1, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_4: + s_n_llhttp__internal__n_header_field_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_4; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob9, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_2: + s_n_llhttp__internal__n_header_field_2: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_2; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'n': { + p++; + goto s_n_llhttp__internal__n_header_field_3; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_1: + s_n_llhttp__internal__n_header_field_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_1; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob0, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_field_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_5: + s_n_llhttp__internal__n_header_field_5: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_5; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob10, 15); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_5; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_6: + s_n_llhttp__internal__n_header_field_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_6; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob11, 16); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_7: + s_n_llhttp__internal__n_header_field_7: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_7; + } + match_seq = llparse__match_sequence_to_lower(state, p, endp, llparse_blob12, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_7; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field: + s_n_llhttp__internal__n_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field; + } + switch (((*p) >= 'A' && (*p) <= 'Z' ? (*p | 0x20) : (*p))) { + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_field_1; + } + case 'p': { + p++; + goto s_n_llhttp__internal__n_header_field_5; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_6; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_field_7; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_11; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: + s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_field; + goto s_n_llhttp__internal__n_header_field; + UNREACHABLE; + } + case s_n_llhttp__internal__n_header_field_start: + s_n_llhttp__internal__n_header_field_start: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_1; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_headers_almost_done; + } + case ':': { + goto s_n_llhttp__internal__n_error_44; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_headers_start: + s_n_llhttp__internal__n_headers_start: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags; + } + default: { + goto s_n_llhttp__internal__n_header_field_start; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_to_http_09: + s_n_llhttp__internal__n_url_to_http_09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http_09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_http_major; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_skip_to_http09: + s_n_llhttp__internal__n_url_skip_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09_1: + s_n_llhttp__internal__n_url_skip_lf_to_http09_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_url_to_http_09; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09: + s_n_llhttp__internal__n_url_skip_lf_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_url_skip_lf_to_http09_1; + } + default: { + goto s_n_llhttp__internal__n_error_63; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_pri_upgrade: + s_n_llhttp__internal__n_req_pri_upgrade: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_error_72; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_pri_upgrade; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_73; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_complete_crlf: + s_n_llhttp__internal__n_req_http_complete_crlf: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete_crlf; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_headers_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_26; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_complete: + s_n_llhttp__internal__n_req_http_complete: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_complete; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_25; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_req_http_complete_crlf; + } + default: { + goto s_n_llhttp__internal__n_error_71; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method_1: + s_n_llhttp__internal__n_invoke_load_method_1: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 34: + goto s_n_llhttp__internal__n_req_pri_upgrade; + default: + goto s_n_llhttp__internal__n_req_http_complete; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_1; + case 21: + goto s_n_llhttp__internal__n_pause_21; + default: + goto s_n_llhttp__internal__n_error_68; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_67: + s_n_llhttp__internal__n_error_67: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_74: + s_n_llhttp__internal__n_error_74: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_minor: + s_n_llhttp__internal__n_req_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_2; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_75: + s_n_llhttp__internal__n_error_75: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_dot: + s_n_llhttp__internal__n_req_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_req_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_76: + s_n_llhttp__internal__n_error_76: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_major: + s_n_llhttp__internal__n_req_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_4; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version: + s_n_llhttp__internal__n_span_start_llhttp__on_version: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_req_http_major; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_protocol: + s_n_llhttp__internal__n_req_after_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_req_after_protocol; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version; + } + default: { + goto s_n_llhttp__internal__n_error_77; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method: + s_n_llhttp__internal__n_invoke_load_method: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_after_protocol; + case 1: + goto s_n_llhttp__internal__n_req_after_protocol; + case 2: + goto s_n_llhttp__internal__n_req_after_protocol; + case 3: + goto s_n_llhttp__internal__n_req_after_protocol; + case 4: + goto s_n_llhttp__internal__n_req_after_protocol; + case 5: + goto s_n_llhttp__internal__n_req_after_protocol; + case 6: + goto s_n_llhttp__internal__n_req_after_protocol; + case 7: + goto s_n_llhttp__internal__n_req_after_protocol; + case 8: + goto s_n_llhttp__internal__n_req_after_protocol; + case 9: + goto s_n_llhttp__internal__n_req_after_protocol; + case 10: + goto s_n_llhttp__internal__n_req_after_protocol; + case 11: + goto s_n_llhttp__internal__n_req_after_protocol; + case 12: + goto s_n_llhttp__internal__n_req_after_protocol; + case 13: + goto s_n_llhttp__internal__n_req_after_protocol; + case 14: + goto s_n_llhttp__internal__n_req_after_protocol; + case 15: + goto s_n_llhttp__internal__n_req_after_protocol; + case 16: + goto s_n_llhttp__internal__n_req_after_protocol; + case 17: + goto s_n_llhttp__internal__n_req_after_protocol; + case 18: + goto s_n_llhttp__internal__n_req_after_protocol; + case 19: + goto s_n_llhttp__internal__n_req_after_protocol; + case 20: + goto s_n_llhttp__internal__n_req_after_protocol; + case 21: + goto s_n_llhttp__internal__n_req_after_protocol; + case 22: + goto s_n_llhttp__internal__n_req_after_protocol; + case 23: + goto s_n_llhttp__internal__n_req_after_protocol; + case 24: + goto s_n_llhttp__internal__n_req_after_protocol; + case 25: + goto s_n_llhttp__internal__n_req_after_protocol; + case 26: + goto s_n_llhttp__internal__n_req_after_protocol; + case 27: + goto s_n_llhttp__internal__n_req_after_protocol; + case 28: + goto s_n_llhttp__internal__n_req_after_protocol; + case 29: + goto s_n_llhttp__internal__n_req_after_protocol; + case 30: + goto s_n_llhttp__internal__n_req_after_protocol; + case 31: + goto s_n_llhttp__internal__n_req_after_protocol; + case 32: + goto s_n_llhttp__internal__n_req_after_protocol; + case 33: + goto s_n_llhttp__internal__n_req_after_protocol; + case 34: + goto s_n_llhttp__internal__n_req_after_protocol; + case 46: + goto s_n_llhttp__internal__n_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_66; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method; + case 21: + goto s_n_llhttp__internal__n_pause_22; + default: + goto s_n_llhttp__internal__n_error_65; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_82: + s_n_llhttp__internal__n_error_82: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_1: + s_n_llhttp__internal__n_req_after_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method_2: + s_n_llhttp__internal__n_invoke_load_method_2: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 33: + goto s_n_llhttp__internal__n_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_79; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_2; + case 21: + goto s_n_llhttp__internal__n_pause_23; + default: + goto s_n_llhttp__internal__n_error_78; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_2: + s_n_llhttp__internal__n_req_after_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_method_3: + s_n_llhttp__internal__n_invoke_load_method_3: { + switch (llhttp__internal__c_load_method(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_req_after_protocol; + case 3: + goto s_n_llhttp__internal__n_req_after_protocol; + case 6: + goto s_n_llhttp__internal__n_req_after_protocol; + case 35: + goto s_n_llhttp__internal__n_req_after_protocol; + case 36: + goto s_n_llhttp__internal__n_req_after_protocol; + case 37: + goto s_n_llhttp__internal__n_req_after_protocol; + case 38: + goto s_n_llhttp__internal__n_req_after_protocol; + case 39: + goto s_n_llhttp__internal__n_req_after_protocol; + case 40: + goto s_n_llhttp__internal__n_req_after_protocol; + case 41: + goto s_n_llhttp__internal__n_req_after_protocol; + case 42: + goto s_n_llhttp__internal__n_req_after_protocol; + case 43: + goto s_n_llhttp__internal__n_req_after_protocol; + case 44: + goto s_n_llhttp__internal__n_req_after_protocol; + case 45: + goto s_n_llhttp__internal__n_req_after_protocol; + default: + goto s_n_llhttp__internal__n_error_81; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_method_3; + case 21: + goto s_n_llhttp__internal__n_pause_24; + default: + goto s_n_llhttp__internal__n_error_80; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start_3: + s_n_llhttp__internal__n_req_after_http_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_after_http_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_after_http_start: + s_n_llhttp__internal__n_req_after_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_after_http_start; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_req_after_http_start_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_protocol: + s_n_llhttp__internal__n_span_start_llhttp__on_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_protocol; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_protocol; + goto s_n_llhttp__internal__n_req_after_http_start; + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_http_start: + s_n_llhttp__internal__n_req_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_http_start; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_to_http: + s_n_llhttp__internal__n_url_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_skip_to_http: + s_n_llhttp__internal__n_url_skip_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + p++; + goto s_n_llhttp__internal__n_url_to_http; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_fragment: + s_n_llhttp__internal__n_url_fragment: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_fragment; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + default: { + goto s_n_llhttp__internal__n_error_83; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_end_stub_query_3: + s_n_llhttp__internal__n_span_end_stub_query_3: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_query_3; + } + p++; + goto s_n_llhttp__internal__n_url_fragment; + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_query: + s_n_llhttp__internal__n_url_query: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_query; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 6: { + goto s_n_llhttp__internal__n_span_end_stub_query_3; + } + default: { + goto s_n_llhttp__internal__n_error_84; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_query_or_fragment: + s_n_llhttp__internal__n_url_query_or_fragment: { + if (p == endp) { + return s_n_llhttp__internal__n_url_query_or_fragment; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; + } + case ' ': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; + } + case '#': { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case '?': { + p++; + goto s_n_llhttp__internal__n_url_query; + } + default: { + goto s_n_llhttp__internal__n_error_85; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_path: + s_n_llhttp__internal__n_url_path: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_path; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_url_path; + } + default: { + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_stub_path_2: + s_n_llhttp__internal__n_span_start_stub_path_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_2; + } + p++; + goto s_n_llhttp__internal__n_url_path; + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_stub_path: + s_n_llhttp__internal__n_span_start_stub_path: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path; + } + p++; + goto s_n_llhttp__internal__n_url_path; + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_stub_path_1: + s_n_llhttp__internal__n_span_start_stub_path_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_1; + } + p++; + goto s_n_llhttp__internal__n_url_path; + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_server_with_at: + s_n_llhttp__internal__n_url_server_with_at: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server_with_at; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path_1; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_error_86; + } + default: { + goto s_n_llhttp__internal__n_error_87; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_server: + s_n_llhttp__internal__n_url_server: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 7, + 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 0, 5, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; + } + case 5: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 6: { + goto s_n_llhttp__internal__n_span_start_stub_path; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 8: { + p++; + goto s_n_llhttp__internal__n_url_server_with_at; + } + default: { + goto s_n_llhttp__internal__n_error_88; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_schema_delim_1: + s_n_llhttp__internal__n_url_schema_delim_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim_1; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_url_server; + } + default: { + goto s_n_llhttp__internal__n_error_89; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_schema_delim: + s_n_llhttp__internal__n_url_schema_delim: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case '/': { + p++; + goto s_n_llhttp__internal__n_url_schema_delim_1; + } + default: { + goto s_n_llhttp__internal__n_error_89; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_end_stub_schema: + s_n_llhttp__internal__n_span_end_stub_schema: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_schema; + } + p++; + goto s_n_llhttp__internal__n_url_schema_delim; + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_schema: + s_n_llhttp__internal__n_url_schema: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_schema; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_stub_schema; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_90; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_start: + s_n_llhttp__internal__n_url_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 2: { + goto s_n_llhttp__internal__n_span_start_stub_path_2; + } + case 3: { + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_91; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: + s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_start; + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_entry_normal: + s_n_llhttp__internal__n_url_entry_normal: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_normal; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url: + s_n_llhttp__internal__n_span_start_llhttp__on_url: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_server; + UNREACHABLE; + } + case s_n_llhttp__internal__n_url_entry_connect: + s_n_llhttp__internal__n_url_entry_connect: { + if (p == endp) { + return s_n_llhttp__internal__n_url_entry_connect; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + case 12: { + p++; + goto s_n_llhttp__internal__n_error_2; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_spaces_before_url: + s_n_llhttp__internal__n_req_spaces_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_spaces_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_invoke_is_equal_method; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_first_space_before_url: + s_n_llhttp__internal__n_req_first_space_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_first_space_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_error_92; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_29; + default: + goto s_n_llhttp__internal__n_error_111; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_2: + s_n_llhttp__internal__n_after_start_req_2: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_2; + } + switch (*p) { + case 'L': { + p++; + match = 19; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_3: + s_n_llhttp__internal__n_after_start_req_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 36; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_1: + s_n_llhttp__internal__n_after_start_req_1: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_1; + } + switch (*p) { + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_2; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_3; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_4: + s_n_llhttp__internal__n_after_start_req_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_4; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 16; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_6: + s_n_llhttp__internal__n_after_start_req_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_6; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 22; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_8: + s_n_llhttp__internal__n_after_start_req_8: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_8; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_8; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_9: + s_n_llhttp__internal__n_after_start_req_9: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_9; + } + switch (*p) { + case 'Y': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_7: + s_n_llhttp__internal__n_after_start_req_7: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_7; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_8; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_9; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_5: + s_n_llhttp__internal__n_after_start_req_5: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_5; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_6; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_7; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_12: + s_n_llhttp__internal__n_after_start_req_12: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_12; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_12; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_13: + s_n_llhttp__internal__n_after_start_req_13: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_13; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 35; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_13; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_11: + s_n_llhttp__internal__n_after_start_req_11: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_11; + } + switch (*p) { + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_12; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_13; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_10: + s_n_llhttp__internal__n_after_start_req_10: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_10; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_11; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_14: + s_n_llhttp__internal__n_after_start_req_14: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_14; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 45; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_14; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_17: + s_n_llhttp__internal__n_after_start_req_17: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_17; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 41; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_17; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_16: + s_n_llhttp__internal__n_after_start_req_16: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_16; + } + switch (*p) { + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_17; + } + default: { + match = 1; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_15: + s_n_llhttp__internal__n_after_start_req_15: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_15; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_after_start_req_16; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_15; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_18: + s_n_llhttp__internal__n_after_start_req_18: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_18; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_18; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_20: + s_n_llhttp__internal__n_after_start_req_20: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_20; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 31; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_20; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_21: + s_n_llhttp__internal__n_after_start_req_21: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_21; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_21; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_19: + s_n_llhttp__internal__n_after_start_req_19: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_19; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_20; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_21; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_23: + s_n_llhttp__internal__n_after_start_req_23: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_23; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 24; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_23; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_24: + s_n_llhttp__internal__n_after_start_req_24: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_24; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 23; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_24; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_26: + s_n_llhttp__internal__n_after_start_req_26: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_26; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 21; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_26; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_28: + s_n_llhttp__internal__n_after_start_req_28: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_28; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 30; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_28; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_29: + s_n_llhttp__internal__n_after_start_req_29: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_29; + } + switch (*p) { + case 'L': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_27: + s_n_llhttp__internal__n_after_start_req_27: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_27; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_28; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_29; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_25: + s_n_llhttp__internal__n_after_start_req_25: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_25; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_26; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_27; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_30: + s_n_llhttp__internal__n_after_start_req_30: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_30; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_30; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_22: + s_n_llhttp__internal__n_after_start_req_22: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_22; + } + switch (*p) { + case '-': { + p++; + goto s_n_llhttp__internal__n_after_start_req_23; + } + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_24; + } + case 'K': { + p++; + goto s_n_llhttp__internal__n_after_start_req_25; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_30; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_31: + s_n_llhttp__internal__n_after_start_req_31: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_31; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 25; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_31; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_32: + s_n_llhttp__internal__n_after_start_req_32: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_32; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_32; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_35: + s_n_llhttp__internal__n_after_start_req_35: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_35; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 28; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_35; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_36: + s_n_llhttp__internal__n_after_start_req_36: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_36; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 39; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_36; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_34: + s_n_llhttp__internal__n_after_start_req_34: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_34; + } + switch (*p) { + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_35; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_36; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_37: + s_n_llhttp__internal__n_after_start_req_37: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_37; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 38; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_37; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_38: + s_n_llhttp__internal__n_after_start_req_38: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_38; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_38; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_42: + s_n_llhttp__internal__n_after_start_req_42: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_42; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_42; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_43: + s_n_llhttp__internal__n_after_start_req_43: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_43; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_43; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_41: + s_n_llhttp__internal__n_after_start_req_41: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_41; + } + switch (*p) { + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_42; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_43; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_40: + s_n_llhttp__internal__n_after_start_req_40: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_40; + } + switch (*p) { + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_41; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_39: + s_n_llhttp__internal__n_after_start_req_39: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_39; + } + switch (*p) { + case 'I': { + p++; + match = 34; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_40; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_45: + s_n_llhttp__internal__n_after_start_req_45: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_45; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 29; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_45; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_44: + s_n_llhttp__internal__n_after_start_req_44: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_44; + } + switch (*p) { + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_45; + } + case 'T': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_33: + s_n_llhttp__internal__n_after_start_req_33: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_33; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_34; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_37; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_38; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_39; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_44; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_46: + s_n_llhttp__internal__n_after_start_req_46: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_46; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 46; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_46; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_49: + s_n_llhttp__internal__n_after_start_req_49: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_49; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 17; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_49; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_50: + s_n_llhttp__internal__n_after_start_req_50: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_50; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 44; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_50; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_51: + s_n_llhttp__internal__n_after_start_req_51: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_51; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 43; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_51; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_52: + s_n_llhttp__internal__n_after_start_req_52: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_52; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 20; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_52; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_48: + s_n_llhttp__internal__n_after_start_req_48: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_48; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_49; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_50; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_51; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_52; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_47: + s_n_llhttp__internal__n_after_start_req_47: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_47; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_48; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_55: + s_n_llhttp__internal__n_after_start_req_55: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_55; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_55; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_57: + s_n_llhttp__internal__n_after_start_req_57: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_57; + } + switch (*p) { + case 'P': { + p++; + match = 37; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_58: + s_n_llhttp__internal__n_after_start_req_58: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_58; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 42; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_58; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_56: + s_n_llhttp__internal__n_after_start_req_56: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_56; + } + switch (*p) { + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_57; + } + case '_': { + p++; + goto s_n_llhttp__internal__n_after_start_req_58; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_54: + s_n_llhttp__internal__n_after_start_req_54: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_54; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_55; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_56; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_59: + s_n_llhttp__internal__n_after_start_req_59: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_59; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 33; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_59; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_60: + s_n_llhttp__internal__n_after_start_req_60: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_60; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 26; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_60; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_53: + s_n_llhttp__internal__n_after_start_req_53: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_53; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_54; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_59; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_60; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_62: + s_n_llhttp__internal__n_after_start_req_62: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_62; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob52, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 40; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_62; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_63: + s_n_llhttp__internal__n_after_start_req_63: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_63; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob53, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_63; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_61: + s_n_llhttp__internal__n_after_start_req_61: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_61; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_after_start_req_62; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_63; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_66: + s_n_llhttp__internal__n_after_start_req_66: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_66; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob54, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 18; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_66; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_68: + s_n_llhttp__internal__n_after_start_req_68: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_68; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob55, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 32; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_68; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_69: + s_n_llhttp__internal__n_after_start_req_69: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_69; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob56, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_69; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_67: + s_n_llhttp__internal__n_after_start_req_67: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_67; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_after_start_req_68; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_69; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_70: + s_n_llhttp__internal__n_after_start_req_70: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_70; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob57, 8); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 27; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_after_start_req_70; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_65: + s_n_llhttp__internal__n_after_start_req_65: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_65; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_66; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_67; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_70; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req_64: + s_n_llhttp__internal__n_after_start_req_64: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req_64; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_65; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_after_start_req: + s_n_llhttp__internal__n_after_start_req: { + if (p == endp) { + return s_n_llhttp__internal__n_after_start_req; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_after_start_req_1; + } + case 'B': { + p++; + goto s_n_llhttp__internal__n_after_start_req_4; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_after_start_req_5; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_after_start_req_10; + } + case 'F': { + p++; + goto s_n_llhttp__internal__n_after_start_req_14; + } + case 'G': { + p++; + goto s_n_llhttp__internal__n_after_start_req_15; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_after_start_req_18; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_after_start_req_19; + } + case 'M': { + p++; + goto s_n_llhttp__internal__n_after_start_req_22; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_after_start_req_31; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_after_start_req_32; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_after_start_req_33; + } + case 'Q': { + p++; + goto s_n_llhttp__internal__n_after_start_req_46; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_after_start_req_47; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_after_start_req_53; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_after_start_req_61; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_after_start_req_64; + } + default: { + goto s_n_llhttp__internal__n_error_112; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method_1: + s_n_llhttp__internal__n_span_start_llhttp__on_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_after_start_req; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_line_almost_done: + s_n_llhttp__internal__n_res_line_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_res_line_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_29; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_test_lenient_flags_30: + s_n_llhttp__internal__n_invoke_test_lenient_flags_30: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_98; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_status: + s_n_llhttp__internal__n_res_status: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; + } + default: { + p++; + goto s_n_llhttp__internal__n_res_status; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_status: + s_n_llhttp__internal__n_span_start_llhttp__on_status: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_status; + goto s_n_llhttp__internal__n_res_status; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_status_code_otherwise: + s_n_llhttp__internal__n_res_status_code_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_otherwise; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_28; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + default: { + goto s_n_llhttp__internal__n_error_99; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_status_code_digit_3: + s_n_llhttp__internal__n_res_status_code_digit_3: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_3; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_2; + } + default: { + goto s_n_llhttp__internal__n_error_101; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_status_code_digit_2: + s_n_llhttp__internal__n_res_status_code_digit_2: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_2; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code_1; + } + default: { + goto s_n_llhttp__internal__n_error_103; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_status_code_digit_1: + s_n_llhttp__internal__n_res_status_code_digit_1: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_digit_1; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_105; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_version: + s_n_llhttp__internal__n_res_after_version: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_version; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_update_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_106; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: + s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1: { + switch (llhttp__on_version_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_res_after_version; + case 21: + goto s_n_llhttp__internal__n_pause_28; + default: + goto s_n_llhttp__internal__n_error_94; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_93: + s_n_llhttp__internal__n_error_93: { + state->error = 0x9; + state->reason = "Invalid HTTP version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_107: + s_n_llhttp__internal__n_error_107: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_http_minor: + s_n_llhttp__internal__n_res_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_7; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_108: + s_n_llhttp__internal__n_error_108: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_http_dot: + s_n_llhttp__internal__n_res_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_res_http_minor; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_8; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_109: + s_n_llhttp__internal__n_error_109: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_http_major: + s_n_llhttp__internal__n_res_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_9; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_version_1: + s_n_llhttp__internal__n_span_start_llhttp__on_version_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_version; + goto s_n_llhttp__internal__n_res_http_major; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_protocol: + s_n_llhttp__internal__n_res_after_protocol: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_protocol; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + default: { + goto s_n_llhttp__internal__n_error_114; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: + s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3: { + switch (llhttp__on_protocol_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_res_after_protocol; + case 21: + goto s_n_llhttp__internal__n_pause_30; + default: + goto s_n_llhttp__internal__n_error_113; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_error_115: + s_n_llhttp__internal__n_error_115: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_1: + s_n_llhttp__internal__n_res_after_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob58, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_2: + s_n_llhttp__internal__n_res_after_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob59, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start_3: + s_n_llhttp__internal__n_res_after_start_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob60, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4; + } + case kMatchPause: { + return s_n_llhttp__internal__n_res_after_start_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_res_after_start: + s_n_llhttp__internal__n_res_after_start: { + if (p == endp) { + return s_n_llhttp__internal__n_res_after_start; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_res_after_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_res_after_start_2; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_res_after_start_3; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: + s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_protocol; + goto s_n_llhttp__internal__n_res_after_start; + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: + s_n_llhttp__internal__n_invoke_llhttp__on_method_complete: { + switch (llhttp__on_method_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_first_space_before_url; + case 21: + goto s_n_llhttp__internal__n_pause_26; + default: + goto s_n_llhttp__internal__n_error_1; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_or_res_method_2: + s_n_llhttp__internal__n_req_or_res_method_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob61, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_110; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_update_type_1: + s_n_llhttp__internal__n_invoke_update_type_1: { + switch (llhttp__internal__c_update_type_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_version_1; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_or_res_method_3: + s_n_llhttp__internal__n_req_or_res_method_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob62, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_110; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_or_res_method_1: + s_n_llhttp__internal__n_req_or_res_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_1; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_2; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_3; + } + default: { + goto s_n_llhttp__internal__n_error_110; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_req_or_res_method: + s_n_llhttp__internal__n_req_or_res_method: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_110; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_span_start_llhttp__on_method: + s_n_llhttp__internal__n_span_start_llhttp__on_method: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_method; + goto s_n_llhttp__internal__n_req_or_res_method; + UNREACHABLE; + } + case s_n_llhttp__internal__n_start_req_or_res: + s_n_llhttp__internal__n_start_req_or_res: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_or_res; + } + switch (*p) { + case 'H': { + goto s_n_llhttp__internal__n_span_start_llhttp__on_method; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_type_2; + } + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_load_type: + s_n_llhttp__internal__n_invoke_load_type: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + case 2: + goto s_n_llhttp__internal__n_span_start_llhttp__on_protocol_1; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_invoke_update_finish: + s_n_llhttp__internal__n_invoke_update_finish: { + switch (llhttp__internal__c_update_finish(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; + } + UNREACHABLE; + } + case s_n_llhttp__internal__n_start: + s_n_llhttp__internal__n_start: { + if (p == endp) { + return s_n_llhttp__internal__n_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_initial_message_completed; + } + } + UNREACHABLE; + } + default: + UNREACHABLE; + } + s_n_llhttp__internal__n_error_2: { + state->error = 0x7; + state->reason = "Invalid characters in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_finish_2: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_initial_message_completed: { + switch (llhttp__internal__c_update_initial_message_completed(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_finish_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_content_length: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_8: { + state->error = 0x5; + state->reason = "Data after `Connection: close`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_3: { + switch (llhttp__internal__c_test_lenient_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_closed; + default: + goto s_n_llhttp__internal__n_error_8; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_2: { + switch (llhttp__internal__c_test_lenient_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_update_initial_message_completed; + default: + goto s_n_llhttp__internal__n_closed; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_finish_1: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_13: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_38: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_15: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_40: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_15; + default: + goto s_n_llhttp__internal__n_error_40; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_2: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_9: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_pause_1; + case 21: + goto s_n_llhttp__internal__n_pause_2; + default: + goto s_n_llhttp__internal__n_error_9; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_36: { + state->error = 0xc; + state->reason = "Chunk size overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_10: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_4: { + switch (llhttp__internal__c_test_lenient_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_otherwise; + default: + goto s_n_llhttp__internal__n_error_10; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_3: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length_1; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_14: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_content_length_1; + case 21: + goto s_n_llhttp__internal__n_pause_3; + default: + goto s_n_llhttp__internal__n_error_14; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_13: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_6: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_13; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_15: { + state->error = 0x2; + state->reason = "Expected LF after chunk data"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_7: { + switch (llhttp__internal__c_test_lenient_flags_7(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + default: + goto s_n_llhttp__internal__n_error_15; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_body: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_chunk_data_almost_done; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags: { + switch (llhttp__internal__c_or_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_4: { + state->error = 0x15; + state->reason = "on_chunk_header pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_12: { + state->error = 0x13; + state->reason = "`on_chunk_header` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { + switch (llhttp__on_chunk_header(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_4; + default: + goto s_n_llhttp__internal__n_error_12; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_16: { + state->error = 0x2; + state->reason = "Expected LF after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_8: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + default: + goto s_n_llhttp__internal__n_error_16; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_11: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_5: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_11; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_17: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_18: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_20: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension name"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_5: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_9; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_19: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_6: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_21: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_7: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_22: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_2; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_25: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_8: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_10; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_24: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_9: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_size_almost_done; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_26: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_28: { + state->error = 0x19; + state->reason = "Missing expected CR after chunk extension value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_11: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_chunk_size_almost_done; + default: + goto s_n_llhttp__internal__n_error_28; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_29: { + state->error = 0x2; + state->reason = "Invalid character in chunk extensions quote value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_10: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_quoted_value_done; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_27: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_2; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_30; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_31; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_31; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_11: { + state->error = 0x15; + state->reason = "on_chunk_extension_value pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extensions; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_32: { + state->error = 0x23; + state->reason = "`on_chunk_extension_value` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_value_complete_3; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_33; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_33; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_12: { + state->error = 0x15; + state->reason = "on_chunk_extension_name pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_extension_value; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_23: { + state->error = 0x22; + state->reason = "`on_chunk_extension_name` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_extension_name_complete_3: { + switch (llhttp__on_chunk_extension_name_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_chunk_extension_value; + case 21: + goto s_n_llhttp__internal__n_pause_12; + default: + goto s_n_llhttp__internal__n_error_23; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_span_start_llhttp__on_chunk_extension_value; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_chunk_extension_name_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_chunk_extension_name(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_34; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_34; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_35: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_mul_add_content_length: { + switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_36; + default: + goto s_n_llhttp__internal__n_chunk_size; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_37: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_finish_3: { + switch (llhttp__internal__c_update_finish_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_39: { + state->error = 0xf; + state->reason = "Request has invalid `Transfer-Encoding`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_7: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + case 21: + goto s_n_llhttp__internal__n_pause; + default: + goto s_n_llhttp__internal__n_error_7; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_1: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_2: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_upgrade: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_14: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_6: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_14; + default: + goto s_n_llhttp__internal__n_error_6; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_1: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags; + default: + goto s_n_llhttp__internal__n_error_5; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_17: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_42: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_17; + default: + goto s_n_llhttp__internal__n_error_42; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_3: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_4: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_upgrade_1: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_4; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_16: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_41: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_3; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade_1; + case 21: + goto s_n_llhttp__internal__n_pause_16; + default: + goto s_n_llhttp__internal__n_error_41; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags_1: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_2; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_43: { + state->error = 0x2; + state->reason = "Expected LF after headers"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_12: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_1; + default: + goto s_n_llhttp__internal__n_error_43; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_44: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_5; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_5; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_13: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_colon_discard_ws; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_60: { + state->error = 0xb; + state->reason = "Content-Length can't be present with Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_47: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_15: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_ws; + default: + goto s_n_llhttp__internal__n_error_47; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_49: { + state->error = 0xb; + state->reason = "Empty Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_18: { + state->error = 0x15; + state->reason = "on_header_value_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_48: { + state->error = 0x1d; + state->reason = "`on_header_value_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_5: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_6: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_7: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_8: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_2: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_5; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_6; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_7; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_8; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_1: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_error_49; + default: + goto s_n_llhttp__internal__n_invoke_load_header_state_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_46: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_14: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_46; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_50: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_16: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_discard_lws; + default: + goto s_n_llhttp__internal__n_error_50; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_1: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_4: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 8: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_52: { + state->error = 0xa; + state->reason = "Unexpected whitespace after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_18: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_header_state_4; + default: + goto s_n_llhttp__internal__n_error_52; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_2: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_9: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_10: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_11: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_12: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_5: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_9; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_10; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_11; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_12; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_value_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_53: { + state->error = 0x3; + state->reason = "Missing expected LF after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_51: { + state->error = 0x19; + state->reason = "Missing expected CR after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_17; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_header_value_almost_done; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_54; + return s_error; + } + goto s_n_llhttp__internal__n_error_54; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_19: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_4: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_13: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_14: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_15: { + switch (llhttp__internal__c_or_flags_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_16: { + switch (llhttp__internal__c_or_flags_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_6: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_13; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_14; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_15; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_16; + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_5: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_token; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_3: { + switch (llhttp__internal__c_update_header_state_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_6: { + switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_7: { + switch (llhttp__internal__c_update_header_state_7(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_56; + return s_error; + } + goto s_n_llhttp__internal__n_error_56; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { + switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_6; + default: + goto s_n_llhttp__internal__n_header_value_content_length; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_17: { + switch (llhttp__internal__c_or_flags_17(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_57; + return s_error; + } + goto s_n_llhttp__internal__n_error_57; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_55: { + state->error = 0x4; + state->reason = "Duplicate Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_value_content_length; + default: + goto s_n_llhttp__internal__n_error_55; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_59; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_59; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_8: { + switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_58; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_error_58; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_20: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_8; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_type_1: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_20; + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_9: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_and_flags: { + switch (llhttp__internal__c_and_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_19: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_21: { + switch (llhttp__internal__c_test_lenient_flags_20(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_9; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_type_2: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_21; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_19; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_18: { + switch (llhttp__internal__c_or_flags_18(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_and_flags; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_load_type_2; + default: + goto s_n_llhttp__internal__n_invoke_or_flags_18; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_or_flags_20: { + switch (llhttp__internal__c_or_flags_20(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_header_state_3: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_connection; + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_2; + case 3: + goto s_n_llhttp__internal__n_invoke_test_flags_3; + case 4: + goto s_n_llhttp__internal__n_invoke_or_flags_20; + default: + goto s_n_llhttp__internal__n_header_value; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_22: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_60; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags_4: { + switch (llhttp__internal__c_test_flags_4(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_22; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_61: { + state->error = 0xf; + state->reason = "Transfer-Encoding can't be present with Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_23: { + switch (llhttp__internal__c_test_lenient_flags_22(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_61; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_flags_5: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_23; + default: + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_19: { + state->error = 0x15; + state->reason = "on_header_field_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_header_state; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_45: { + state->error = 0x1c; + state->reason = "`on_header_field_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_header_field_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_62: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_10: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_header_state: { + switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_header_field_colon; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_header_state_11: { + switch (llhttp__internal__c_update_header_state_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_4: { + state->error = 0x1e; + state->reason = "Unexpected space after start line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags: { + switch (llhttp__internal__c_test_lenient_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_field_start; + default: + goto s_n_llhttp__internal__n_error_4; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_20: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_3: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_20; + default: + goto s_n_llhttp__internal__n_error_3; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_http_minor: { + switch (llhttp__internal__c_update_http_minor(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_url_complete; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_http_major: { + switch (llhttp__internal__c_update_http_major(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_http_minor; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_63: { + state->error = 0x7; + state->reason = "Expected CRLF"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_72: { + state->error = 0x17; + state->reason = "Pause on PRI/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_73: { + state->error = 0x9; + state->reason = "Expected HTTP/2 Connection Preface"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_70: { + state->error = 0x2; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_26: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_headers_start; + default: + goto s_n_llhttp__internal__n_error_70; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_69: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_25: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_req_http_complete_crlf; + default: + goto s_n_llhttp__internal__n_error_69; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_71: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_21: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_1; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_68: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_67; + return s_error; + } + goto s_n_llhttp__internal__n_error_67; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor_1: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor_2: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_major: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_1; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_2; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_24: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_1; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_http_minor: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_24; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_74; + return s_error; + } + goto s_n_llhttp__internal__n_error_74; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_75; + return s_error; + } + goto s_n_llhttp__internal__n_error_75; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_http_major: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_dot; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_76; + return s_error; + } + goto s_n_llhttp__internal__n_error_76; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_77: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_66: { + state->error = 0x8; + state->reason = "Invalid method for HTTP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_22: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_65: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_82; + return s_error; + } + goto s_n_llhttp__internal__n_error_82; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_79: { + state->error = 0x8; + state->reason = "Expected SOURCE method for ICE/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_23: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_2; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_78: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_81: { + state->error = 0x8; + state->reason = "Invalid method for RTSP/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_24: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_method_3; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_80: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_2; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_25: { + state->error = 0x15; + state->reason = "on_url_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_http_start; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_64: { + state->error = 0x1a; + state->reason = "`on_url_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_url_complete_1: { + switch (llhttp__on_url_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_req_http_start; + case 21: + goto s_n_llhttp__internal__n_pause_25; + default: + goto s_n_llhttp__internal__n_error_64; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_83: { + state->error = 0x7; + state->reason = "Invalid char in url fragment start"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_84: { + state->error = 0x7; + state->reason = "Invalid char in url query"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_85: { + state->error = 0x7; + state->reason = "Invalid char in url path"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_86: { + state->error = 0x7; + state->reason = "Double @ in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_87: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_88: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_89: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_90: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_91: { + state->error = 0x7; + state->reason = "Unexpected start char in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_is_equal_method: { + switch (llhttp__internal__c_is_equal_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_url_entry_normal; + default: + goto s_n_llhttp__internal__n_url_entry_connect; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_92: { + state->error = 0x6; + state->reason = "Expected space after method"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_29: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_111: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_method_1: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_112: { + state->error = 0x6; + state->reason = "Invalid method encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_104: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_102: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_100: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_27: { + state->error = 0x15; + state->reason = "on_status_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_headers_start; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_96: { + state->error = 0x1b; + state->reason = "`on_status_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_status_complete: { + switch (llhttp__on_status_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_headers_start; + case 21: + goto s_n_llhttp__internal__n_pause_27; + default: + goto s_n_llhttp__internal__n_error_96; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_95: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_28: { + switch (llhttp__internal__c_test_lenient_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_95; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_97: { + state->error = 0x2; + state->reason = "Expected LF after CR"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_29: { + switch (llhttp__internal__c_test_lenient_flags_8(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_status_complete; + default: + goto s_n_llhttp__internal__n_error_97; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_98: { + state->error = 0x19; + state->reason = "Missing expected CR after response line"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_status: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_30; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_99: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_2: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_100; + default: + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_101: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_mul_add_status_code_1: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_102; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_3; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_103: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_mul_add_status_code: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_104; + default: + goto s_n_llhttp__internal__n_res_status_code_digit_2; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_105: { + state->error = 0xd; + state->reason = "Invalid status code"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_status_code: { + switch (llhttp__internal__c_update_status_code(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_status_code_digit_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_106: { + state->error = 0x9; + state->reason = "Expected space after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_28: { + state->error = 0x15; + state->reason = "on_version_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_version; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_94: { + state->error = 0x21; + state->reason = "`on_version_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_version_complete_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_93; + return s_error; + } + goto s_n_llhttp__internal__n_error_93; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor_3: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 9: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor_4: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_minor_5: { + switch (llhttp__internal__c_load_http_minor(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_http_major_1: { + switch (llhttp__internal__c_load_http_major(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_http_minor_3; + case 1: + goto s_n_llhttp__internal__n_invoke_load_http_minor_4; + case 2: + goto s_n_llhttp__internal__n_invoke_load_http_minor_5; + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_5; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_test_lenient_flags_27: { + switch (llhttp__internal__c_test_lenient_flags_24(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_version_6; + default: + goto s_n_llhttp__internal__n_invoke_load_http_major_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_http_minor_1: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_test_lenient_flags_27; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_107; + return s_error; + } + goto s_n_llhttp__internal__n_error_107; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_108; + return s_error; + } + goto s_n_llhttp__internal__n_error_108; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_http_major_1: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_dot; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_version_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_version(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_109; + return s_error; + } + goto s_n_llhttp__internal__n_error_109; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_114: { + state->error = 0x8; + state->reason = "Expected HTTP/, RTSP/ or ICE/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_30: { + state->error = 0x15; + state->reason = "on_protocol_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_after_protocol; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_113: { + state->error = 0x26; + state->reason = "`on_protocol_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_protocol_complete_3; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_protocol_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_protocol(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_115; + return s_error; + } + goto s_n_llhttp__internal__n_error_115; + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_26: { + state->error = 0x15; + state->reason = "on_method_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_req_first_space_before_url; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_1: { + state->error = 0x20; + state->reason = "`on_method_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_method: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_method_complete; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_type: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_end_llhttp__on_method; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_store_method: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_update_type; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_error_110: { + state->error = 0x8; + state->reason = "Invalid word encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_span_end_llhttp__on_method_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_method(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_type_1; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_update_type_1; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_update_type_2: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_method_1; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_31: { + state->error = 0x15; + state->reason = "on_message_begin pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error: { + state->error = 0x10; + state->reason = "`on_message_begin` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { + switch (llhttp__on_message_begin(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_type; + case 21: + goto s_n_llhttp__internal__n_pause_31; + default: + goto s_n_llhttp__internal__n_error; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_pause_32: { + state->error = 0x15; + state->reason = "on_reset pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_finish; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_error_116: { + state->error = 0x1f; + state->reason = "`on_reset` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_llhttp__on_reset: { + switch (llhttp__on_reset(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_finish; + case 21: + goto s_n_llhttp__internal__n_pause_32; + default: + goto s_n_llhttp__internal__n_error_116; + } + UNREACHABLE; + } + s_n_llhttp__internal__n_invoke_load_initial_message_completed: { + switch (llhttp__internal__c_load_initial_message_completed(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_reset; + default: + goto s_n_llhttp__internal__n_invoke_update_finish; + } + UNREACHABLE; + } +} + +int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} \ No newline at end of file diff --git a/llhttp/llhttp.h b/llhttp/llhttp.h new file mode 100644 index 000000000..60544596a --- /dev/null +++ b/llhttp/llhttp.h @@ -0,0 +1,907 @@ + +#ifndef INCLUDE_LLHTTP_H_ +#define INCLUDE_LLHTTP_H_ + +#define LLHTTP_VERSION_MAJOR 9 +#define LLHTTP_VERSION_MINOR 3 +#define LLHTTP_VERSION_PATCH 0 + +#ifndef INCLUDE_LLHTTP_ITSELF_H_ +#define INCLUDE_LLHTTP_ITSELF_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct llhttp__internal_s llhttp__internal_t; +struct llhttp__internal_s { + int32_t _index; + void* _span_pos0; + void* _span_cb0; + int32_t error; + const char* reason; + const char* error_pos; + void* data; + void* _current; + uint64_t content_length; + uint8_t type; + uint8_t method; + uint8_t http_major; + uint8_t http_minor; + uint8_t header_state; + uint16_t lenient_flags; + uint8_t upgrade; + uint8_t finish; + uint16_t flags; + uint16_t status_code; + uint8_t initial_message_completed; + void* settings; +}; + +int llhttp__internal_init(llhttp__internal_t* s); +int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_ITSELF_H_ */ + + +#ifndef LLLLHTTP_C_HEADERS_ +#define LLLLHTTP_C_HEADERS_ +#ifdef __cplusplus +extern "C" { +#endif + +enum llhttp_errno { + HPE_OK = 0, + HPE_INTERNAL = 1, + HPE_STRICT = 2, + HPE_CR_EXPECTED = 25, + HPE_LF_EXPECTED = 3, + HPE_UNEXPECTED_CONTENT_LENGTH = 4, + HPE_UNEXPECTED_SPACE = 30, + HPE_CLOSED_CONNECTION = 5, + HPE_INVALID_METHOD = 6, + HPE_INVALID_URL = 7, + HPE_INVALID_CONSTANT = 8, + HPE_INVALID_VERSION = 9, + HPE_INVALID_HEADER_TOKEN = 10, + HPE_INVALID_CONTENT_LENGTH = 11, + HPE_INVALID_CHUNK_SIZE = 12, + HPE_INVALID_STATUS = 13, + HPE_INVALID_EOF_STATE = 14, + HPE_INVALID_TRANSFER_ENCODING = 15, + HPE_CB_MESSAGE_BEGIN = 16, + HPE_CB_HEADERS_COMPLETE = 17, + HPE_CB_MESSAGE_COMPLETE = 18, + HPE_CB_CHUNK_HEADER = 19, + HPE_CB_CHUNK_COMPLETE = 20, + HPE_PAUSED = 21, + HPE_PAUSED_UPGRADE = 22, + HPE_PAUSED_H2_UPGRADE = 23, + HPE_USER = 24, + HPE_CB_URL_COMPLETE = 26, + HPE_CB_STATUS_COMPLETE = 27, + HPE_CB_METHOD_COMPLETE = 32, + HPE_CB_VERSION_COMPLETE = 33, + HPE_CB_HEADER_FIELD_COMPLETE = 28, + HPE_CB_HEADER_VALUE_COMPLETE = 29, + HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34, + HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35, + HPE_CB_RESET = 31, + HPE_CB_PROTOCOL_COMPLETE = 38 +}; +typedef enum llhttp_errno llhttp_errno_t; + +enum llhttp_flags { + F_CONNECTION_KEEP_ALIVE = 0x1, + F_CONNECTION_CLOSE = 0x2, + F_CONNECTION_UPGRADE = 0x4, + F_CHUNKED = 0x8, + F_UPGRADE = 0x10, + F_CONTENT_LENGTH = 0x20, + F_SKIPBODY = 0x40, + F_TRAILING = 0x80, + F_TRANSFER_ENCODING = 0x200 +}; +typedef enum llhttp_flags llhttp_flags_t; + +enum llhttp_lenient_flags { + LENIENT_HEADERS = 0x1, + LENIENT_CHUNKED_LENGTH = 0x2, + LENIENT_KEEP_ALIVE = 0x4, + LENIENT_TRANSFER_ENCODING = 0x8, + LENIENT_VERSION = 0x10, + LENIENT_DATA_AFTER_CLOSE = 0x20, + LENIENT_OPTIONAL_LF_AFTER_CR = 0x40, + LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80, + LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100, + LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200 +}; +typedef enum llhttp_lenient_flags llhttp_lenient_flags_t; + +enum llhttp_type { + HTTP_BOTH = 0, + HTTP_REQUEST = 1, + HTTP_RESPONSE = 2 +}; +typedef enum llhttp_type llhttp_type_t; + +enum llhttp_finish { + HTTP_FINISH_SAFE = 0, + HTTP_FINISH_SAFE_WITH_CB = 1, + HTTP_FINISH_UNSAFE = 2 +}; +typedef enum llhttp_finish llhttp_finish_t; + +enum llhttp_method { + HTTP_DELETE = 0, + HTTP_GET = 1, + HTTP_HEAD = 2, + HTTP_POST = 3, + HTTP_PUT = 4, + HTTP_CONNECT = 5, + HTTP_OPTIONS = 6, + HTTP_TRACE = 7, + HTTP_COPY = 8, + HTTP_LOCK = 9, + HTTP_MKCOL = 10, + HTTP_MOVE = 11, + HTTP_PROPFIND = 12, + HTTP_PROPPATCH = 13, + HTTP_SEARCH = 14, + HTTP_UNLOCK = 15, + HTTP_BIND = 16, + HTTP_REBIND = 17, + HTTP_UNBIND = 18, + HTTP_ACL = 19, + HTTP_REPORT = 20, + HTTP_MKACTIVITY = 21, + HTTP_CHECKOUT = 22, + HTTP_MERGE = 23, + HTTP_MSEARCH = 24, + HTTP_NOTIFY = 25, + HTTP_SUBSCRIBE = 26, + HTTP_UNSUBSCRIBE = 27, + HTTP_PATCH = 28, + HTTP_PURGE = 29, + HTTP_MKCALENDAR = 30, + HTTP_LINK = 31, + HTTP_UNLINK = 32, + HTTP_SOURCE = 33, + HTTP_PRI = 34, + HTTP_DESCRIBE = 35, + HTTP_ANNOUNCE = 36, + HTTP_SETUP = 37, + HTTP_PLAY = 38, + HTTP_PAUSE = 39, + HTTP_TEARDOWN = 40, + HTTP_GET_PARAMETER = 41, + HTTP_SET_PARAMETER = 42, + HTTP_REDIRECT = 43, + HTTP_RECORD = 44, + HTTP_FLUSH = 45, + HTTP_QUERY = 46 +}; +typedef enum llhttp_method llhttp_method_t; + +enum llhttp_status { + HTTP_STATUS_CONTINUE = 100, + HTTP_STATUS_SWITCHING_PROTOCOLS = 101, + HTTP_STATUS_PROCESSING = 102, + HTTP_STATUS_EARLY_HINTS = 103, + HTTP_STATUS_RESPONSE_IS_STALE = 110, + HTTP_STATUS_REVALIDATION_FAILED = 111, + HTTP_STATUS_DISCONNECTED_OPERATION = 112, + HTTP_STATUS_HEURISTIC_EXPIRATION = 113, + HTTP_STATUS_MISCELLANEOUS_WARNING = 199, + HTTP_STATUS_OK = 200, + HTTP_STATUS_CREATED = 201, + HTTP_STATUS_ACCEPTED = 202, + HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_RESET_CONTENT = 205, + HTTP_STATUS_PARTIAL_CONTENT = 206, + HTTP_STATUS_MULTI_STATUS = 207, + HTTP_STATUS_ALREADY_REPORTED = 208, + HTTP_STATUS_TRANSFORMATION_APPLIED = 214, + HTTP_STATUS_IM_USED = 226, + HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299, + HTTP_STATUS_MULTIPLE_CHOICES = 300, + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + HTTP_STATUS_USE_PROXY = 305, + HTTP_STATUS_SWITCH_PROXY = 306, + HTTP_STATUS_TEMPORARY_REDIRECT = 307, + HTTP_STATUS_PERMANENT_REDIRECT = 308, + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED = 401, + HTTP_STATUS_PAYMENT_REQUIRED = 402, + HTTP_STATUS_FORBIDDEN = 403, + HTTP_STATUS_NOT_FOUND = 404, + HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HTTP_STATUS_NOT_ACCEPTABLE = 406, + HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_STATUS_REQUEST_TIMEOUT = 408, + HTTP_STATUS_CONFLICT = 409, + HTTP_STATUS_GONE = 410, + HTTP_STATUS_LENGTH_REQUIRED = 411, + HTTP_STATUS_PRECONDITION_FAILED = 412, + HTTP_STATUS_PAYLOAD_TOO_LARGE = 413, + HTTP_STATUS_URI_TOO_LONG = 414, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416, + HTTP_STATUS_EXPECTATION_FAILED = 417, + HTTP_STATUS_IM_A_TEAPOT = 418, + HTTP_STATUS_PAGE_EXPIRED = 419, + HTTP_STATUS_ENHANCE_YOUR_CALM = 420, + HTTP_STATUS_MISDIRECTED_REQUEST = 421, + HTTP_STATUS_UNPROCESSABLE_ENTITY = 422, + HTTP_STATUS_LOCKED = 423, + HTTP_STATUS_FAILED_DEPENDENCY = 424, + HTTP_STATUS_TOO_EARLY = 425, + HTTP_STATUS_UPGRADE_REQUIRED = 426, + HTTP_STATUS_PRECONDITION_REQUIRED = 428, + HTTP_STATUS_TOO_MANY_REQUESTS = 429, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_STATUS_LOGIN_TIMEOUT = 440, + HTTP_STATUS_NO_RESPONSE = 444, + HTTP_STATUS_RETRY_WITH = 449, + HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450, + HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460, + HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463, + HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494, + HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495, + HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496, + HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497, + HTTP_STATUS_INVALID_TOKEN = 498, + HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499, + HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HTTP_STATUS_NOT_IMPLEMENTED = 501, + HTTP_STATUS_BAD_GATEWAY = 502, + HTTP_STATUS_SERVICE_UNAVAILABLE = 503, + HTTP_STATUS_GATEWAY_TIMEOUT = 504, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506, + HTTP_STATUS_INSUFFICIENT_STORAGE = 507, + HTTP_STATUS_LOOP_DETECTED = 508, + HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509, + HTTP_STATUS_NOT_EXTENDED = 510, + HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511, + HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520, + HTTP_STATUS_WEB_SERVER_IS_DOWN = 521, + HTTP_STATUS_CONNECTION_TIMEOUT = 522, + HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523, + HTTP_STATUS_TIMEOUT_OCCURED = 524, + HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525, + HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526, + HTTP_STATUS_RAILGUN_ERROR = 527, + HTTP_STATUS_SITE_IS_OVERLOADED = 529, + HTTP_STATUS_SITE_IS_FROZEN = 530, + HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561, + HTTP_STATUS_NETWORK_READ_TIMEOUT = 598, + HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599 +}; +typedef enum llhttp_status llhttp_status_t; + +#define HTTP_ERRNO_MAP(XX) \ + XX(0, OK, OK) \ + XX(1, INTERNAL, INTERNAL) \ + XX(2, STRICT, STRICT) \ + XX(25, CR_EXPECTED, CR_EXPECTED) \ + XX(3, LF_EXPECTED, LF_EXPECTED) \ + XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ + XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \ + XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ + XX(6, INVALID_METHOD, INVALID_METHOD) \ + XX(7, INVALID_URL, INVALID_URL) \ + XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ + XX(9, INVALID_VERSION, INVALID_VERSION) \ + XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ + XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ + XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ + XX(13, INVALID_STATUS, INVALID_STATUS) \ + XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ + XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ + XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ + XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ + XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ + XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ + XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ + XX(21, PAUSED, PAUSED) \ + XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ + XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \ + XX(24, USER, USER) \ + XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \ + XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \ + XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \ + XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \ + XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \ + XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \ + XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \ + XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \ + XX(31, CB_RESET, CB_RESET) \ + XX(38, CB_PROTOCOL_COMPLETE, CB_PROTOCOL_COMPLETE) \ + + +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(46, QUERY, QUERY) \ + + +#define RTSP_METHOD_MAP(XX) \ + XX(1, GET, GET) \ + XX(3, POST, POST) \ + XX(6, OPTIONS, OPTIONS) \ + XX(35, DESCRIBE, DESCRIBE) \ + XX(36, ANNOUNCE, ANNOUNCE) \ + XX(37, SETUP, SETUP) \ + XX(38, PLAY, PLAY) \ + XX(39, PAUSE, PAUSE) \ + XX(40, TEARDOWN, TEARDOWN) \ + XX(41, GET_PARAMETER, GET_PARAMETER) \ + XX(42, SET_PARAMETER, SET_PARAMETER) \ + XX(43, REDIRECT, REDIRECT) \ + XX(44, RECORD, RECORD) \ + XX(45, FLUSH, FLUSH) \ + + +#define HTTP_ALL_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(34, PRI, PRI) \ + XX(35, DESCRIBE, DESCRIBE) \ + XX(36, ANNOUNCE, ANNOUNCE) \ + XX(37, SETUP, SETUP) \ + XX(38, PLAY, PLAY) \ + XX(39, PAUSE, PAUSE) \ + XX(40, TEARDOWN, TEARDOWN) \ + XX(41, GET_PARAMETER, GET_PARAMETER) \ + XX(42, SET_PARAMETER, SET_PARAMETER) \ + XX(43, REDIRECT, REDIRECT) \ + XX(44, RECORD, RECORD) \ + XX(45, FLUSH, FLUSH) \ + XX(46, QUERY, QUERY) \ + + +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, CONTINUE) \ + XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \ + XX(102, PROCESSING, PROCESSING) \ + XX(103, EARLY_HINTS, EARLY_HINTS) \ + XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \ + XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \ + XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \ + XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \ + XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \ + XX(200, OK, OK) \ + XX(201, CREATED, CREATED) \ + XX(202, ACCEPTED, ACCEPTED) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \ + XX(204, NO_CONTENT, NO_CONTENT) \ + XX(205, RESET_CONTENT, RESET_CONTENT) \ + XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \ + XX(207, MULTI_STATUS, MULTI_STATUS) \ + XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \ + XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \ + XX(226, IM_USED, IM_USED) \ + XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \ + XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \ + XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \ + XX(302, FOUND, FOUND) \ + XX(303, SEE_OTHER, SEE_OTHER) \ + XX(304, NOT_MODIFIED, NOT_MODIFIED) \ + XX(305, USE_PROXY, USE_PROXY) \ + XX(306, SWITCH_PROXY, SWITCH_PROXY) \ + XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \ + XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \ + XX(400, BAD_REQUEST, BAD_REQUEST) \ + XX(401, UNAUTHORIZED, UNAUTHORIZED) \ + XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \ + XX(403, FORBIDDEN, FORBIDDEN) \ + XX(404, NOT_FOUND, NOT_FOUND) \ + XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \ + XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \ + XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \ + XX(409, CONFLICT, CONFLICT) \ + XX(410, GONE, GONE) \ + XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \ + XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \ + XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \ + XX(414, URI_TOO_LONG, URI_TOO_LONG) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \ + XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \ + XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \ + XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \ + XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \ + XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \ + XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \ + XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \ + XX(423, LOCKED, LOCKED) \ + XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \ + XX(425, TOO_EARLY, TOO_EARLY) \ + XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \ + XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \ + XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \ + XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \ + XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \ + XX(444, NO_RESPONSE, NO_RESPONSE) \ + XX(449, RETRY_WITH, RETRY_WITH) \ + XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \ + XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \ + XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \ + XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \ + XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \ + XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \ + XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \ + XX(498, INVALID_TOKEN, INVALID_TOKEN) \ + XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \ + XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \ + XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \ + XX(502, BAD_GATEWAY, BAD_GATEWAY) \ + XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \ + XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \ + XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \ + XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \ + XX(508, LOOP_DETECTED, LOOP_DETECTED) \ + XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \ + XX(510, NOT_EXTENDED, NOT_EXTENDED) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \ + XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \ + XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \ + XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \ + XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \ + XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \ + XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \ + XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \ + XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \ + XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \ + XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \ + XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \ + XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \ + XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* LLLLHTTP_C_HEADERS_ */ + + +#ifndef INCLUDE_LLHTTP_API_H_ +#define INCLUDE_LLHTTP_API_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include + +#if defined(__wasm__) +#define LLHTTP_EXPORT __attribute__((visibility("default"))) +#elif defined(_WIN32) +#define LLHTTP_EXPORT __declspec(dllexport) +#else +#define LLHTTP_EXPORT +#endif + +typedef llhttp__internal_t llhttp_t; +typedef struct llhttp_settings_s llhttp_settings_t; + +typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); +typedef int (*llhttp_cb)(llhttp_t*); + +struct llhttp_settings_s { + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_begin; + + /* Possible return values 0, -1, HPE_USER */ + llhttp_data_cb on_protocol; + llhttp_data_cb on_url; + llhttp_data_cb on_status; + llhttp_data_cb on_method; + llhttp_data_cb on_version; + llhttp_data_cb on_header_field; + llhttp_data_cb on_header_value; + llhttp_data_cb on_chunk_extension_name; + llhttp_data_cb on_chunk_extension_value; + + /* Possible return values: + * 0 - Proceed normally + * 1 - Assume that request/response has no body, and proceed to parsing the + * next message + * 2 - Assume absence of body (as above) and make `llhttp_execute()` return + * `HPE_PAUSED_UPGRADE` + * -1 - Error + * `HPE_PAUSED` + */ + llhttp_cb on_headers_complete; + + /* Possible return values 0, -1, HPE_USER */ + llhttp_data_cb on_body; + + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_complete; + llhttp_cb on_protocol_complete; + llhttp_cb on_url_complete; + llhttp_cb on_status_complete; + llhttp_cb on_method_complete; + llhttp_cb on_version_complete; + llhttp_cb on_header_field_complete; + llhttp_cb on_header_value_complete; + llhttp_cb on_chunk_extension_name_complete; + llhttp_cb on_chunk_extension_value_complete; + + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ + llhttp_cb on_chunk_header; + llhttp_cb on_chunk_complete; + llhttp_cb on_reset; +}; + +/* Initialize the parser with specific type and user settings. + * + * NOTE: lifetime of `settings` has to be at least the same as the lifetime of + * the `parser` here. In practice, `settings` has to be either a static + * variable or be allocated with `malloc`, `new`, etc. + */ +LLHTTP_EXPORT +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings); + +LLHTTP_EXPORT +llhttp_t* llhttp_alloc(llhttp_type_t type); + +LLHTTP_EXPORT +void llhttp_free(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_type(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_http_major(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_http_minor(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_method(llhttp_t* parser); + +LLHTTP_EXPORT +int llhttp_get_status_code(llhttp_t* parser); + +LLHTTP_EXPORT +uint8_t llhttp_get_upgrade(llhttp_t* parser); + +/* Reset an already initialized parser back to the start state, preserving the + * existing parser type, callback settings, user data, and lenient flags. + */ +LLHTTP_EXPORT +void llhttp_reset(llhttp_t* parser); + +/* Initialize the settings object */ +LLHTTP_EXPORT +void llhttp_settings_init(llhttp_settings_t* settings); + +/* Parse full or partial request/response, invoking user callbacks along the + * way. + * + * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing + * interrupts, and such errno is returned from `llhttp_execute()`. If + * `HPE_PAUSED` was used as a errno, the execution can be resumed with + * `llhttp_resume()` call. + * + * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` + * is returned after fully parsing the request/response. If the user wishes to + * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. + * + * NOTE: if this function ever returns a non-pause type error, it will continue + * to return the same error upon each successive call up until `llhttp_init()` + * is called. + */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); + +/* This method should be called when the other side has no further bytes to + * send (e.g. shutdown of readable side of the TCP connection.) + * + * Requests without `Content-Length` and other messages might require treating + * all incoming bytes as the part of the body, up to the last byte of the + * connection. This method will invoke `on_message_complete()` callback if the + * request was terminated safely. Otherwise a error code would be returned. + */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_finish(llhttp_t* parser); + +/* Returns `1` if the incoming message is parsed until the last byte, and has + * to be completed by calling `llhttp_finish()` on EOF + */ +LLHTTP_EXPORT +int llhttp_message_needs_eof(const llhttp_t* parser); + +/* Returns `1` if there might be any other messages following the last that was + * successfully parsed. + */ +LLHTTP_EXPORT +int llhttp_should_keep_alive(const llhttp_t* parser); + +/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set + * appropriate error reason. + * + * Important: do not call this from user callbacks! User callbacks must return + * `HPE_PAUSED` if pausing is required. + */ +LLHTTP_EXPORT +void llhttp_pause(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. + */ +LLHTTP_EXPORT +void llhttp_resume(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` + */ +LLHTTP_EXPORT +void llhttp_resume_after_upgrade(llhttp_t* parser); + +/* Returns the latest return error */ +LLHTTP_EXPORT +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); + +/* Returns the verbal explanation of the latest returned error. + * + * Note: User callback should set error reason when returning the error. See + * `llhttp_set_error_reason()` for details. + */ +LLHTTP_EXPORT +const char* llhttp_get_error_reason(const llhttp_t* parser); + +/* Assign verbal description to the returned error. Must be called in user + * callbacks right before returning the errno. + * + * Note: `HPE_USER` error code might be useful in user callbacks. + */ +LLHTTP_EXPORT +void llhttp_set_error_reason(llhttp_t* parser, const char* reason); + +/* Returns the pointer to the last parsed byte before the returned error. The + * pointer is relative to the `data` argument of `llhttp_execute()`. + * + * Note: this method might be useful for counting the number of parsed bytes. + */ +LLHTTP_EXPORT +const char* llhttp_get_error_pos(const llhttp_t* parser); + +/* Returns textual name of error code */ +LLHTTP_EXPORT +const char* llhttp_errno_name(llhttp_errno_t err); + +/* Returns textual name of HTTP method */ +LLHTTP_EXPORT +const char* llhttp_method_name(llhttp_method_t method); + +/* Returns textual name of HTTP status */ +LLHTTP_EXPORT +const char* llhttp_status_name(llhttp_status_t status); + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_headers(llhttp_t* parser, int enabled); + + +/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and + * `Content-Length` headers (disabled by default). + * + * Normally `llhttp` would error when `Transfer-Encoding` is present in + * conjunction with `Content-Length`. This error is important to prevent HTTP + * request smuggling, but may be less desirable for small number of cases + * involving legacy servers. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled); + + +/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 + * requests responses. + * + * Normally `llhttp` would error on (in strict mode) or discard (in loose mode) + * the HTTP request/response after the request/response with `Connection: close` + * and `Content-Length`. This is important to prevent cache poisoning attacks, + * but might interact badly with outdated and insecure clients. With this flag + * the extra request/response will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * poisoning attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of `Transfer-Encoding` header. + * + * Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value + * and another value after it (either in a single header or in multiple + * headers whose value are internally joined using `, `). + * This is mandated by the spec to reliably determine request body size and thus + * avoid request smuggling. + * With this flag the extra value will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of HTTP version. + * + * Normally `llhttp` would error when the HTTP version in the request or status line + * is not `0.9`, `1.0`, `1.1` or `2.0`. + * With this flag the invalid value will be parsed normally. + * + * **Enabling this flag can pose a security issue since you will allow unsupported + * HTTP versions. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_version(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of additional data received after a message ends + * and keep-alive is disabled. + * + * Normally `llhttp` would error when additional unexpected data is received if the message + * contains the `Connection` header with `close` value. + * With this flag the extra data will discarded without throwing an error. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * poisoning attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of incomplete CRLF sequences. + * + * Normally `llhttp` would error when a CR is not followed by LF when terminating the + * request line, the status line, the headers or a chunk header. + * With this flag only a CR is required to terminate such sections. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled); + +/* + * Enables/disables lenient handling of line separators. + * + * Normally `llhttp` would error when a LF is not preceded by CR when terminating the + * request line, the status line, the headers, a chunk header or a chunk data. + * With this flag only a LF is required to terminate such sections. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of chunks not separated via CRLF. + * + * Normally `llhttp` would error when after a chunk data a CRLF is missing before + * starting a new chunk. + * With this flag the new chunk can start immediately after the previous one. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled); + +/* Enables/disables lenient handling of spaces after chunk size. + * + * Normally `llhttp` would error when after a chunk size is followed by one or more + * spaces are present instead of a CRLF or `;`. + * With this flag this check is disabled. + * + * **Enabling this flag can pose a security issue since you will be exposed to + * request smuggling attacks. USE WITH CAUTION!** + */ +LLHTTP_EXPORT +void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_API_H_ */ + + +#endif /* INCLUDE_LLHTTP_H_ */