react-native-nitro-cookies 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroCookies.podspec +30 -0
- package/android/CMakeLists.txt +24 -0
- package/android/build.gradle +128 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/nitrocookies/NitroCookies.kt +340 -0
- package/android/src/main/java/com/margelo/nitro/nitrocookies/NitroCookiesPackage.kt +22 -0
- package/ios/NitroCookies.swift +411 -0
- package/lib/module/NitroCookies.nitro.js +4 -0
- package/lib/module/NitroCookies.nitro.js.map +1 -0
- package/lib/module/index.js +264 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +43 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NitroCookies.nitro.d.ts +89 -0
- package/lib/typescript/src/NitroCookies.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +229 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +86 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/nitro.json +17 -0
- package/nitrogen/generated/android/c++/JCookie.hpp +86 -0
- package/nitrogen/generated/android/c++/JHybridNitroCookiesSpec.cpp +224 -0
- package/nitrogen/generated/android/c++/JHybridNitroCookiesSpec.hpp +73 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/Cookie.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/HybridNitroCookiesSpec.kt +90 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocookies/nitrocookiesOnLoad.kt +35 -0
- package/nitrogen/generated/android/nitrocookies+autolinking.cmake +81 -0
- package/nitrogen/generated/android/nitrocookies+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrocookiesOnLoad.cpp +44 -0
- package/nitrogen/generated/android/nitrocookiesOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/NitroCookies+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Bridge.cpp +64 -0
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Bridge.hpp +243 -0
- package/nitrogen/generated/ios/NitroCookies-Swift-Cxx-Umbrella.hpp +50 -0
- package/nitrogen/generated/ios/NitroCookiesAutolinking.mm +33 -0
- package/nitrogen/generated/ios/NitroCookiesAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridNitroCookiesSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroCookiesSpecSwift.hpp +145 -0
- package/nitrogen/generated/ios/swift/Cookie.swift +226 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_Cookie_.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNitroCookiesSpec.swift +65 -0
- package/nitrogen/generated/ios/swift/HybridNitroCookiesSpec_cxx.swift +344 -0
- package/nitrogen/generated/shared/c++/Cookie.hpp +104 -0
- package/nitrogen/generated/shared/c++/HybridNitroCookiesSpec.cpp +29 -0
- package/nitrogen/generated/shared/c++/HybridNitroCookiesSpec.hpp +75 -0
- package/package.json +129 -0
- package/src/NitroCookies.nitro.ts +99 -0
- package/src/index.tsx +283 -0
- package/src/types.ts +99 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "NitroCookies"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/l2hyunwoo/react-native-nitro-cookies.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
s.source_files = [
|
|
18
|
+
"ios/**/*.{swift}",
|
|
19
|
+
"ios/**/*.{m,mm}",
|
|
20
|
+
"cpp/**/*.{hpp,cpp}",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
s.dependency 'React-jsi'
|
|
24
|
+
s.dependency 'React-callinvoker'
|
|
25
|
+
|
|
26
|
+
load 'nitrogen/generated/ios/NitroCookies+autolinking.rb'
|
|
27
|
+
add_nitrogen_files(s)
|
|
28
|
+
|
|
29
|
+
install_modules_dependencies(s)
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
project(nitrocookies)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME nitrocookies)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
# Define C++ library and add all sources
|
|
9
|
+
add_library(${PACKAGE_NAME} SHARED src/main/cpp/cpp-adapter.cpp)
|
|
10
|
+
|
|
11
|
+
# Add Nitrogen specs :)
|
|
12
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/nitrocookies+autolinking.cmake)
|
|
13
|
+
|
|
14
|
+
# Set up local includes
|
|
15
|
+
include_directories("src/main/cpp" "../cpp")
|
|
16
|
+
|
|
17
|
+
find_library(LOG_LIB log)
|
|
18
|
+
|
|
19
|
+
# Link all libraries together
|
|
20
|
+
target_link_libraries(
|
|
21
|
+
${PACKAGE_NAME}
|
|
22
|
+
${LOG_LIB}
|
|
23
|
+
android # <-- Android core
|
|
24
|
+
)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.getExtOrDefault = {name ->
|
|
3
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['NitroCookies_' + name]
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
repositories {
|
|
7
|
+
google()
|
|
8
|
+
mavenCentral()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
dependencies {
|
|
12
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
13
|
+
// noinspection DifferentKotlinGradleVersion
|
|
14
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def reactNativeArchitectures() {
|
|
19
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
20
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
apply plugin: "com.android.library"
|
|
24
|
+
apply plugin: "kotlin-android"
|
|
25
|
+
apply from: '../nitrogen/generated/android/nitrocookies+autolinking.gradle'
|
|
26
|
+
|
|
27
|
+
apply plugin: "com.facebook.react"
|
|
28
|
+
|
|
29
|
+
def getExtOrIntegerDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroCookies_" + name]).toInteger()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
android {
|
|
34
|
+
namespace "com.margelo.nitro.nitrocookies"
|
|
35
|
+
|
|
36
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
37
|
+
|
|
38
|
+
defaultConfig {
|
|
39
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
40
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
41
|
+
|
|
42
|
+
externalNativeBuild {
|
|
43
|
+
cmake {
|
|
44
|
+
cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
|
|
45
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
46
|
+
abiFilters (*reactNativeArchitectures())
|
|
47
|
+
|
|
48
|
+
buildTypes {
|
|
49
|
+
debug {
|
|
50
|
+
cppFlags "-O1 -g"
|
|
51
|
+
}
|
|
52
|
+
release {
|
|
53
|
+
cppFlags "-O2"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
externalNativeBuild {
|
|
61
|
+
cmake {
|
|
62
|
+
path "CMakeLists.txt"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
packagingOptions {
|
|
67
|
+
excludes = [
|
|
68
|
+
"META-INF",
|
|
69
|
+
"META-INF/**",
|
|
70
|
+
"**/libc++_shared.so",
|
|
71
|
+
"**/libfbjni.so",
|
|
72
|
+
"**/libjsi.so",
|
|
73
|
+
"**/libfolly_json.so",
|
|
74
|
+
"**/libfolly_runtime.so",
|
|
75
|
+
"**/libglog.so",
|
|
76
|
+
"**/libhermes.so",
|
|
77
|
+
"**/libhermes-executor-debug.so",
|
|
78
|
+
"**/libhermes_executor.so",
|
|
79
|
+
"**/libreactnative.so",
|
|
80
|
+
"**/libreactnativejni.so",
|
|
81
|
+
"**/libturbomodulejsijni.so",
|
|
82
|
+
"**/libreact_nativemodule_core.so",
|
|
83
|
+
"**/libjscexecutor.so"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
buildFeatures {
|
|
88
|
+
buildConfig true
|
|
89
|
+
prefab true
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
buildTypes {
|
|
93
|
+
release {
|
|
94
|
+
minifyEnabled false
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lintOptions {
|
|
99
|
+
disable "GradleCompatible"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
compileOptions {
|
|
103
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
104
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
sourceSets {
|
|
108
|
+
main {
|
|
109
|
+
java.srcDirs += [
|
|
110
|
+
"generated/java",
|
|
111
|
+
"generated/jni"
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
repositories {
|
|
118
|
+
mavenCentral()
|
|
119
|
+
google()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
123
|
+
|
|
124
|
+
dependencies {
|
|
125
|
+
implementation "com.facebook.react:react-android"
|
|
126
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
127
|
+
implementation project(":react-native-nitro-modules")
|
|
128
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrocookies
|
|
2
|
+
|
|
3
|
+
import android.webkit.CookieManager
|
|
4
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
5
|
+
import com.margelo.nitro.core.Promise
|
|
6
|
+
import java.net.HttpURLConnection
|
|
7
|
+
import java.net.URL
|
|
8
|
+
import java.text.SimpleDateFormat
|
|
9
|
+
import java.util.Locale
|
|
10
|
+
import java.util.TimeZone
|
|
11
|
+
import kotlin.coroutines.resume
|
|
12
|
+
import kotlin.coroutines.suspendCoroutine
|
|
13
|
+
|
|
14
|
+
/** HybridNitroCookies - Android implementation of cookie management */
|
|
15
|
+
@DoNotStrip
|
|
16
|
+
class NitroCookies : HybridNitroCookiesSpec() {
|
|
17
|
+
|
|
18
|
+
/** Convert Cookie struct to Set-Cookie header string */
|
|
19
|
+
private fun toRFC6265String(cookie: Cookie): String {
|
|
20
|
+
val parts = mutableListOf<String>()
|
|
21
|
+
|
|
22
|
+
// Name=Value (required)
|
|
23
|
+
parts.add("${cookie.name}=${cookie.value}")
|
|
24
|
+
|
|
25
|
+
// Path attribute
|
|
26
|
+
cookie.path?.let { parts.add("Path=$it") }
|
|
27
|
+
|
|
28
|
+
// Domain attribute
|
|
29
|
+
cookie.domain?.let { parts.add("Domain=$it") }
|
|
30
|
+
|
|
31
|
+
// Expires attribute (convert ISO 8601 to RFC 1123)
|
|
32
|
+
cookie.expires?.let { expiresISO ->
|
|
33
|
+
try {
|
|
34
|
+
val date = iso8601Formatter.parse(expiresISO)
|
|
35
|
+
date?.let {
|
|
36
|
+
val expiresRFC = rfc1123Formatter.format(it)
|
|
37
|
+
parts.add("Expires=$expiresRFC")
|
|
38
|
+
}
|
|
39
|
+
} catch (e: Exception) {
|
|
40
|
+
// Invalid date format, skip expires
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Secure flag
|
|
45
|
+
if (cookie.secure == true) {
|
|
46
|
+
parts.add("Secure")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// HttpOnly flag
|
|
50
|
+
if (cookie.httpOnly == true) {
|
|
51
|
+
parts.add("HttpOnly")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return parts.joinToString("; ")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Parse a Set-Cookie header string into Cookie struct */
|
|
58
|
+
private fun createCookieData(setCookieHeader: String, url: URL): Cookie? {
|
|
59
|
+
val parts = setCookieHeader.split(";").map { it.trim() }
|
|
60
|
+
if (parts.isEmpty()) return null
|
|
61
|
+
|
|
62
|
+
// First part is name=value
|
|
63
|
+
val nameValue = parts[0].split("=", limit = 2)
|
|
64
|
+
if (nameValue.size != 2) return null
|
|
65
|
+
|
|
66
|
+
val name = nameValue[0].trim()
|
|
67
|
+
val value = nameValue[1].trim()
|
|
68
|
+
|
|
69
|
+
var path: String? = null
|
|
70
|
+
var domain: String? = null
|
|
71
|
+
var expires: String? = null
|
|
72
|
+
var secure: Boolean? = null
|
|
73
|
+
var httpOnly: Boolean? = null
|
|
74
|
+
|
|
75
|
+
// Parse attributes
|
|
76
|
+
for (i in 1 until parts.size) {
|
|
77
|
+
val part = parts[i]
|
|
78
|
+
when {
|
|
79
|
+
part.startsWith("Path=", ignoreCase = true) -> {
|
|
80
|
+
path = part.substring(5).trim()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
part.startsWith("Domain=", ignoreCase = true) -> {
|
|
84
|
+
domain = part.substring(7).trim()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
part.startsWith("Expires=", ignoreCase = true) -> {
|
|
88
|
+
val expiresRFC = part.substring(8).trim()
|
|
89
|
+
try {
|
|
90
|
+
val date = rfc1123Formatter.parse(expiresRFC)
|
|
91
|
+
date?.let { expires = iso8601Formatter.format(it) }
|
|
92
|
+
} catch (e: Exception) {
|
|
93
|
+
// Invalid date, skip
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
part.equals("Secure", ignoreCase = true) -> {
|
|
98
|
+
secure = true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
part.equals("HttpOnly", ignoreCase = true) -> {
|
|
102
|
+
httpOnly = true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Apply defaults
|
|
108
|
+
if (path == null) path = "/"
|
|
109
|
+
if (domain == null) domain = url.host
|
|
110
|
+
|
|
111
|
+
return Cookie(
|
|
112
|
+
name = name,
|
|
113
|
+
value = value,
|
|
114
|
+
path = path,
|
|
115
|
+
domain = domain,
|
|
116
|
+
version = null,
|
|
117
|
+
expires = expires,
|
|
118
|
+
secure = secure,
|
|
119
|
+
httpOnly = httpOnly
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Check if cookie domain matches or is subdomain of URL host Similar to iOS isMatchingDomain */
|
|
124
|
+
private fun isMatchingDomain(cookieDomain: String, urlHost: String): Boolean {
|
|
125
|
+
// Exact match
|
|
126
|
+
if (cookieDomain == urlHost) {
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Wildcard match (.example.com matches api.example.com)
|
|
131
|
+
if (cookieDomain.startsWith(".")) {
|
|
132
|
+
val domain = cookieDomain.substring(1)
|
|
133
|
+
return urlHost.endsWith(domain) || urlHost == domain
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Subdomain match (example.com matches api.example.com)
|
|
137
|
+
return urlHost.endsWith(".$cookieDomain")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Validate that cookie domain matches URL host */
|
|
141
|
+
private fun validateDomain(cookie: Cookie, url: URL) {
|
|
142
|
+
val host = url.host ?: throw Exception("INVALID_URL: URL has no host")
|
|
143
|
+
val cookieDomain = cookie.domain ?: host
|
|
144
|
+
|
|
145
|
+
if (!isMatchingDomain(cookieDomain, host)) {
|
|
146
|
+
throw Exception(
|
|
147
|
+
"DOMAIN_MISMATCH: Cookie domain '$cookieDomain' does not match URL host '$host'"
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Validate URL has protocol (http:// or https://) */
|
|
153
|
+
private fun validateURL(urlString: String): URL {
|
|
154
|
+
val url =
|
|
155
|
+
try {
|
|
156
|
+
URL(urlString)
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
throw Exception(
|
|
159
|
+
"INVALID_URL: Invalid URL: '$urlString'. URLs must include protocol (http:// or https://)"
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (url.protocol != "http" && url.protocol != "https") {
|
|
164
|
+
throw Exception(
|
|
165
|
+
"INVALID_URL: Invalid URL: '$urlString'. URLs must include protocol (http:// or https://)"
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return url
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// MARK: - Main Cookie Operations
|
|
173
|
+
|
|
174
|
+
/** Set a single cookie */
|
|
175
|
+
override fun set(url: String, cookie: Cookie, useWebKit: Boolean?): Promise<Boolean> {
|
|
176
|
+
return Promise.async {
|
|
177
|
+
val urlObj = validateURL(url)
|
|
178
|
+
validateDomain(cookie, urlObj)
|
|
179
|
+
|
|
180
|
+
// Apply defaults
|
|
181
|
+
val cookieWithDefaults =
|
|
182
|
+
cookie.copy(path = cookie.path ?: "/", domain = cookie.domain ?: urlObj.host)
|
|
183
|
+
|
|
184
|
+
val cookieManager = CookieManager.getInstance()
|
|
185
|
+
cookieManager.setAcceptCookie(true)
|
|
186
|
+
|
|
187
|
+
val setCookieString = toRFC6265String(cookieWithDefaults)
|
|
188
|
+
cookieManager.setCookie(url, setCookieString)
|
|
189
|
+
|
|
190
|
+
true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Get all cookies for a URL */
|
|
195
|
+
override fun get(url: String, useWebKit: Boolean?): Promise<Array<Cookie>> {
|
|
196
|
+
return Promise.async {
|
|
197
|
+
val urlObj = validateURL(url)
|
|
198
|
+
val cookieManager = CookieManager.getInstance()
|
|
199
|
+
val cookieString = cookieManager.getCookie(url)
|
|
200
|
+
|
|
201
|
+
if (cookieString.isNullOrEmpty()) {
|
|
202
|
+
return@async emptyArray()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Parse cookie string (format: "name1=value1; name2=value2")
|
|
206
|
+
val cookies = mutableListOf<Cookie>()
|
|
207
|
+
val cookiePairs = cookieString.split(";").map { it.trim() }
|
|
208
|
+
|
|
209
|
+
for (pair in cookiePairs) {
|
|
210
|
+
val nameValue = pair.split("=", limit = 2)
|
|
211
|
+
if (nameValue.size == 2) {
|
|
212
|
+
cookies.add(
|
|
213
|
+
Cookie(
|
|
214
|
+
name = nameValue[0].trim(),
|
|
215
|
+
value = nameValue[1].trim(),
|
|
216
|
+
path = "/",
|
|
217
|
+
domain = urlObj.host,
|
|
218
|
+
version = null,
|
|
219
|
+
expires = null,
|
|
220
|
+
secure = null,
|
|
221
|
+
httpOnly = null
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
cookies.toTypedArray()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Clear all cookies */
|
|
232
|
+
override fun clearAll(useWebKit: Boolean?): Promise<Boolean> {
|
|
233
|
+
return Promise.async {
|
|
234
|
+
val cookieManager = CookieManager.getInstance()
|
|
235
|
+
suspendCoroutine { continuation ->
|
|
236
|
+
cookieManager.removeAllCookies { success -> continuation.resume(success) }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Parse and set cookies from Set-Cookie header */
|
|
242
|
+
override fun setFromResponse(url: String, value: String): Promise<Boolean> {
|
|
243
|
+
return Promise.async {
|
|
244
|
+
val urlObj = validateURL(url)
|
|
245
|
+
val cookieManager = CookieManager.getInstance()
|
|
246
|
+
cookieManager.setAcceptCookie(true)
|
|
247
|
+
|
|
248
|
+
// Set-Cookie header can contain multiple cookies
|
|
249
|
+
val setCookieHeaders = value.split("\n").map { it.trim() }
|
|
250
|
+
for (header in setCookieHeaders) {
|
|
251
|
+
if (header.isNotEmpty()) {
|
|
252
|
+
cookieManager.setCookie(url, header)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
true
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Make HTTP request and get cookies from response */
|
|
261
|
+
override fun getFromResponse(url: String): Promise<Array<Cookie>> {
|
|
262
|
+
return Promise.async {
|
|
263
|
+
try {
|
|
264
|
+
val urlObj = validateURL(url)
|
|
265
|
+
val connection = urlObj.openConnection() as HttpURLConnection
|
|
266
|
+
connection.requestMethod = "GET"
|
|
267
|
+
connection.connect()
|
|
268
|
+
|
|
269
|
+
val cookies = mutableListOf<Cookie>()
|
|
270
|
+
val setCookieHeaders = connection.headerFields["Set-Cookie"] ?: emptyList()
|
|
271
|
+
|
|
272
|
+
for (header in setCookieHeaders) {
|
|
273
|
+
val cookie = createCookieData(header, urlObj)
|
|
274
|
+
cookie?.let { cookies.add(it) }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
connection.disconnect()
|
|
278
|
+
cookies.toTypedArray()
|
|
279
|
+
} catch (e: Exception) {
|
|
280
|
+
throw Exception("NETWORK_ERROR: ${e.message}")
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Get all cookies regardless of domain (iOS only - not supported on Android) */
|
|
286
|
+
override fun getAll(useWebKit: Boolean?): Promise<Array<Cookie>> {
|
|
287
|
+
return Promise.async {
|
|
288
|
+
throw Exception("PLATFORM_UNSUPPORTED: getAll() is only available on iOS")
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Clear specific cookie by name (iOS only - not fully supported on Android) */
|
|
293
|
+
override fun clearByName(url: String, name: String, useWebKit: Boolean?): Promise<Boolean> {
|
|
294
|
+
return Promise.async {
|
|
295
|
+
val urlObj = validateURL(url)
|
|
296
|
+
val cookieManager = CookieManager.getInstance()
|
|
297
|
+
|
|
298
|
+
// Android CookieManager doesn't support removing specific cookies by name
|
|
299
|
+
// We can only expire them by setting a past expiration date
|
|
300
|
+
val expiredCookie =
|
|
301
|
+
"$name=; Path=/; Domain=${urlObj.host}; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
|
|
302
|
+
cookieManager.setCookie(url, expiredCookie)
|
|
303
|
+
|
|
304
|
+
true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Flush cookies to persistent storage (Android only) */
|
|
309
|
+
override fun flush(): Promise<Unit> {
|
|
310
|
+
return Promise.async {
|
|
311
|
+
val cookieManager = CookieManager.getInstance()
|
|
312
|
+
cookieManager.flush()
|
|
313
|
+
Unit
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Remove session cookies (Android only) */
|
|
318
|
+
override fun removeSessionCookies(): Promise<Boolean> {
|
|
319
|
+
return Promise.async {
|
|
320
|
+
val cookieManager = CookieManager.getInstance()
|
|
321
|
+
suspendCoroutine { continuation ->
|
|
322
|
+
cookieManager.removeSessionCookies { success -> continuation.resume(success) }
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
companion object {
|
|
328
|
+
/** ISO 8601 date formatter for cookie expires (yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ) */
|
|
329
|
+
private val iso8601Formatter =
|
|
330
|
+
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US).apply {
|
|
331
|
+
timeZone = TimeZone.getTimeZone("UTC")
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** RFC 1123 date formatter for Set-Cookie headers (EEE, dd MMM yyyy HH:mm:ss z) */
|
|
335
|
+
private val rfc1123Formatter =
|
|
336
|
+
SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US).apply {
|
|
337
|
+
timeZone = TimeZone.getTimeZone("GMT")
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrocookies
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
|
+
|
|
8
|
+
class NitroCookiesPackage : BaseReactPackage() {
|
|
9
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
14
|
+
return ReactModuleInfoProvider { HashMap() }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
companion object {
|
|
18
|
+
init {
|
|
19
|
+
System.loadLibrary("nitrocookies")
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|