Skip to content

Commit

Permalink
Revert "Merge pull request #4 from iMac0de/master"
Browse files Browse the repository at this point in the history
This reverts commit d89afe5, reversing
changes made to 2936e5a.
  • Loading branch information
RockfordWei committed Dec 28, 2017
1 parent d89afe5 commit 5f54cb7
Showing 1 changed file with 140 additions and 183 deletions.
323 changes: 140 additions & 183 deletions Sources/PerfectAWS/PerfectAWS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,192 +6,149 @@ import PerfectLib
import PerfectThread

open class AWS {

public static var debug = false

open class Access {
var key = ""
var secret = ""
var timestamp = ""
var date = ""

public func update() {
let fmt = DateFormatter()
//fmt.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z"
fmt.timeZone = TimeZone(identifier: "UTC")
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
timestamp = fmt.string(from: Date())

fmt.dateFormat = "yyyyMMdd"
date = fmt.string(from: Date())
}
public init(accessKey: String, accessSecret: String) {
key = accessKey
secret = accessSecret
update()
}

public func signV4(_ string: String, _ key: HMACKey) -> String {
var bytes = string.sign(.sha256, key: key)?.encode(.base64)
bytes?.append(0)
if let b = bytes {
return String(cString: b)
} else {
return ""
}
}

public static var debug = false

open class Access {
var key = ""
var secret = ""
var timestamp = ""

public func update() {
let fmt = DateFormatter()
fmt.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z"
timestamp = fmt.string(from: Date())
}

public enum Exception: Error {
case UnknownHost
case InvalidFile
case InvalidHeader
public init(accessKey: String, accessSecret: String) {
key = accessKey
secret = accessSecret
update()
}

open class S3 {
public static let hosts: [String: String] = [
"us-east-1": "s3.amazonaws.com",
"us-east-2": "s3.us-east-2.amazonaws.com",
"us-west-1": "s3-us-west-1.amazonaws.com",
"us-west-2": "s3-us-west-2.amazonaws.com",
"eu-west-1": "s3-eu-west-1.amazonaws.com",
"eu-west-3": "s3-eu-west-3.amazonaws.com",
"eu-central-1": "s3.eu-central-1.amazonaws.com",
"ap-south-1": "s3.ap-south-1.amazonaws.com",
"ap-southeast-1": "s3-ap-southeast-1.amazonaws.com",
"ap-southeast-2": "s3-ap-southeast-2.amazonaws.com",
"ap-northeast-1": "s3-ap-northeast-1.amazonaws.com",
"ap-northeast-2": "s3.ap-northeast-2.amazonaws.com",
"sa-east-1": "s3-sa-east-1.amazonaws.com"
]

private static func prepare(_ access: Access, method: String, bucket: String, region: String, file: String, contentType: String) throws -> (CURL, UnsafeMutablePointer<curl_slist>) {
guard let host = hosts[region] else {
throw Exception.UnknownHost
}

access.update()

let header = """
\(method)
/\(file)
host:\(bucket).\(host)
x-amz-date:\(access.timestamp)
host;x-amz-date
UNSIGNED-PAYLOAD
"""

guard let headerDigest = header.digest(.sha256)?.encode(.hex) else {
throw Exception.CannotSign
}

let headerDigestString = String(cString: headerDigest)

let stringToSign = """
AWS4-HMAC-SHA256
\(access.timestamp)
\(access.date)/\(region)/s3/aws4_request
\(headerDigestString)
"""

let awsSecretKey = "AWS4"+access.secret

guard let kDate = access.date.sign(.sha256, key: HMACKey(awsSecretKey)),
let kRegion = region.sign(.sha256, key: HMACKey(kDate)),
let kService = "s3".sign(.sha256, key: HMACKey(kRegion)),
let kSigning = "aws4_request".sign(.sha256, key: HMACKey(kService)),
let signature = stringToSign.sign(.sha256, key: HMACKey(kSigning)),
let signatureBytes = signature.encode(.hex) else {
throw Exception.CannotSign
}

let signatureString = String(cString: signatureBytes)
let authorization = "AWS4-HMAC-SHA256 Credential=\(access.key)/\(access.date)/\(region)/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=\(signatureString)"
let url = "https://\(bucket).\(host)/\(file)"
let curl = CURL(url: url)

if AWS.debug {
_ = curl.setOption(CURLOPT_VERBOSE, int: 1)
_ = curl.setOption(CURLOPT_STDERR, v: stdout)
}

var headers: UnsafeMutablePointer<curl_slist>? = nil
headers = curl_slist_append(headers, "Host: \(bucket).\(host)")
headers = curl_slist_append(headers, "Date: \(access.timestamp)")
headers = curl_slist_append(headers, "Content-Type: \(contentType)")
headers = curl_slist_append(headers, "x-amz-content-sha256: UNSIGNED-PAYLOAD")
headers = curl_slist_append(headers, "x-amz-date: \(access.timestamp)")
headers = curl_slist_append(headers, "Authorization: \(authorization)")

_ = curl.setOption(CURLOPT_FOLLOWLOCATION, int: 1)
guard let list = headers else {
throw Exception.InvalidHeader
}
_ = curl.setOption(CURLOPT_HTTPHEADER, v: list)
return (curl, list)
}

public static func delete(_ access: Access, bucket: String, region: String, file: String, contentType: String) throws {

let (curl, headers) = try prepare(access, method: "DELETE", bucket: bucket, region: region, file: file, contentType: contentType)

_ = curl.setOption(CURLOPT_CUSTOMREQUEST, s: "DELETE")
let (code, _, _) = curl.performFully()
guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)
}

public static func download(_ access: Access, bucket: String, region: String, file: String, contentType: String) throws -> [UInt8] {

let (curl, headers) = try prepare(access, method: "GET", bucket: bucket, region: region, file: file, contentType: contentType)

_ = curl.setOption(CURLOPT_HTTPGET, int: 1)

let (code, _, body) = curl.performFully()
guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)
return body
}

public static func upload(_ access: Access, bucket: String, region: String, file: String, destinationPath: String, contentType: String) throws {

var fileInfo = stat()
stat(file, &fileInfo)

guard fileInfo.st_size > 0,
let fpointer = fopen(file, "rb") else {
throw Exception.InvalidFile
}

let (curl, headers) = try prepare(access, method: "PUT", bucket: bucket, region: region, file: destinationPath, contentType: contentType)

_ = curl.setOption(CURLOPT_INFILESIZE_LARGE, int: fileInfo.st_size)
_ = curl.setOption(CURLOPT_READDATA, v: fpointer)
_ = curl.setOption(CURLOPT_UPLOAD, int: 1)
_ = curl.setOption(CURLOPT_PUT, int: 1)
_ = curl.setOption(CURLOPT_READFUNCTION, f: { ptr, size, nitems, stream in
if let fstream = stream {
let f = fstream.assumingMemoryBound(to: FILE.self)
return fread(ptr, size, nitems, f)
} else {
return 0
}
})

let (code, _, _) = curl.performFully()

guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)

public func signV4(_ string: String) -> String {
var bytes = string.sign(.sha1, key: HMACKey(secret))?.encode(.base64)
bytes?.append(0)
if let b = bytes {
return String(cString: b)
} else {
return ""
}
}
}

public enum Exception: Error {
case UnknownHost
case InvalidFile
case InvalidHeader
}

open class S3 {
public static let hosts: [String: String] = [
"us-east-1": "s3.amazonaws.com",
"us-east-2": "s3.us-east-2.amazonaws.com",
"us-west-1": "s3-us-west-1.amazonaws.com",
"us-west-2": "s3-us-west-2.amazonaws.com",
"eu-west-1": "s3-eu-west-1.amazonaws.com",
"eu-west-2": "s3-eu-west-2.amazonaws.com",
"eu-west-3": "s3-eu-west-3.amazonaws.com",
"eu-central-1": "s3.eu-central-1.amazonaws.com",
"ap-south-1": "s3.ap-south-1.amazonaws.com",
"ap-southeast-1": "s3-ap-southeast-1.amazonaws.com",
"ap-southeast-2": "s3-ap-southeast-2.amazonaws.com",
"ap-northeast-1": "s3-ap-northeast-1.amazonaws.com",
"ap-northeast-2": "s3.ap-northeast-2.amazonaws.com",
"sa-east-1": "s3-sa-east-1.amazonaws.com"
]

private static func prepare(_ access: Access, method: String, bucket: String, region: String, file: String, contentType: String) throws -> (CURL, UnsafeMutablePointer<curl_slist>) {
guard let host = hosts[region] else {
throw Exception.UnknownHost
}


access.update()
let resource = "/\(bucket)/\(file)"
let stringToSign = "\(method)\n\n\(contentType)\n\(access.timestamp)\n\(resource)"
let signature = access.signV4(stringToSign)
let url = "https://\(bucket).\(host)/\(file)"
let curl = CURL(url: url)

if AWS.debug {
_ = curl.setOption(CURLOPT_VERBOSE, int: 1)
_ = curl.setOption(CURLOPT_STDERR, v: stdout)
}

var headers: UnsafeMutablePointer<curl_slist>? = nil
headers = curl_slist_append(headers, "Host: \(bucket).\(host)")
headers = curl_slist_append(headers, "Date: \(access.timestamp)")
headers = curl_slist_append(headers, "Content-Type: \(contentType)")
headers = curl_slist_append(headers, "Authorization: AWS \(access.key):\(signature)")

_ = curl.setOption(CURLOPT_FOLLOWLOCATION, int: 1)
guard let list = headers else {
throw Exception.InvalidHeader
}
_ = curl.setOption(CURLOPT_HTTPHEADER, v: list)
return (curl, list)
}

public static func delete(_ access: Access, bucket: String, region: String, file: String, contentType: String) throws {

let (curl, headers) = try prepare(access, method: "DELETE", bucket: bucket, region: region, file: file, contentType: contentType)

_ = curl.setOption(CURLOPT_CUSTOMREQUEST, s: "DELETE")
let (code, _, _) = curl.performFully()
guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)
}

public static func download(_ access: Access, bucket: String, region: String, file: String, contentType: String) throws -> [UInt8] {

let (curl, headers) = try prepare(access, method: "GET", bucket: bucket, region: region, file: file, contentType: contentType)

_ = curl.setOption(CURLOPT_HTTPGET, int: 1)

let (code, _, body) = curl.performFully()
guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)
return body
}

public static func upload(_ access: Access, bucket: String, region: String, file: String, contentType: String) throws {

var fileInfo = stat()
stat(file, &fileInfo)

guard fileInfo.st_size > 0,
let fpointer = fopen(file, "rb") else {
throw Exception.InvalidFile
}

let (curl, headers) = try prepare(access, method: "PUT", bucket: bucket, region: region, file: file, contentType: contentType)

_ = curl.setOption(CURLOPT_INFILESIZE_LARGE, int: fileInfo.st_size)
_ = curl.setOption(CURLOPT_READDATA, v: fpointer)
_ = curl.setOption(CURLOPT_UPLOAD, int: 1)
_ = curl.setOption(CURLOPT_PUT, int: 1)
_ = curl.setOption(CURLOPT_READFUNCTION, f: { ptr, size, nitems, stream in
if let fstream = stream {
let f = fstream.assumingMemoryBound(to: FILE.self)
return fread(ptr, size, nitems, f)
} else {
return 0
}
})

let (code, _, _) = curl.performFully()
guard code == 0 else {
throw Exception.InvalidFile
}
curl_slist_free_all(headers)
}
}
}

0 comments on commit 5f54cb7

Please sign in to comment.