REBOL [
    Title: "OAuth Scheme"
    Date: 27-Sep-2012
    Author: "Christopher Ross-Gill"
]

do http://reb4.me/r/altwebform

unless in system/schemes 'oauth [
    system/schemes: make system/schemes [oauth: none]
]

system/schemes/oauth: make system/standard/port [
    scheme: 'oauth
    port-id: 0
    handler: none
    key: context [
        consumer-key: consumer-secret: "ABCD"
    ]
    user: context [
        token: secret: "ABCD"
    ]
    passive: none
    cache-size: 5
    proxy: make object! [host: port-id: user: pass: type: bypass: #[none]]
    access: context [
        oauth_callback: none
        oauth_consumer_key: none
        oauth_token: oauth_nonce: none
        oauth_signature_method: "HMAC-SHA1"
        oauth_timestamp: none
        oauth_version: 1.0
        oauth_verifier: oauth_signature: none
    ]
]

system/schemes/oauth/handler: context [
    port-flags: system/standard/port-flags/pass-thru

    init: func [port url /local spec][
        unless all [
            block? url
            url? port/url
            parse/all port/url ["http" opt "s" "://" some skip]
        ][
            make error! "Not a valid OAuth Specification"
        ]

        unless find [get post put delete] port/target [
            make error! "No HTTP Method Specified"
        ]

        port/timeout: enbase/base checksum/secure join now/precise port/key/consumer-key 64

        any [date? port/date port/date: now]
        port/date: form any [
            attempt [to integer! difference port/date 1-Jan-1970/0:0:0]
            port/date - 1-Jan-1970/0:0:0 * 86400.0
        ]
        clear find/last port/date "."

        port/sub-port: make port! port/url
    ]

    sign: func [port [port!]][
        port/pass: system/words/copy ""

        port/access: make port/access [
            oauth_consumer_key: port/key/consumer-key
            oauth_token: port/user/token
            oauth_nonce: port/timeout
            oauth_timestamp: port/date
        ]

        port/user-data: make port/access any [port/user-data []]
        port/user-data: sort/skip third port/user-data 2

        port/access/oauth_signature: enbase/base checksum/secure/key rejoin [
            uppercase form port/target "&" url-encode form port/url "&"
            url-encode replace/all to-webform port/user-data "+" "%20"
        ] rejoin [
            port/key/consumer-secret "&" any [port/user/secret ""]
        ] 64

        foreach [header value] third port/access [
            if value [
                repend port/pass [", " form header {="} url-encode form value {"}]
            ]
        ]

        join "OAuth" next port/pass
    ]

    open: func [port][
        if object? port/user-data [port/user-data: third port/user-data]

        switch port/target [
            put delete [
                port/access: compose [port/target: (uppercase form port/target) (any [port/user-data []])]
                port/access: 'post
            ]
        ]

        switch port/target [
            get [
                port/target: compose/deep [
                    header [Authorization: (sign port)]
                ]
                if port/user-data [
                    port/user-data: context sort/skip port/user-data 2
                    port/sub-port/target: append any [port/sub-port/target ""] to-webform/prefix port/user-data
                ]
            ]
            post put delete [
                port/target: compose/deep [
                    (port/target) (either port/user-data [to-webform port/user-data][""]) [
                        Authorization: (sign port)
                        Content-Type: "application/x-www-form-urlencoded"
                    ]
                ]
            ]
        ]

        system/words/open/custom port/sub-port port/target

        port/state/index: 0
        port/state/flags: port/state/flags or port-flags
    ]

    copy: func [port [port!]][
        port/awake system/words/copy port/sub-port
    ]

    close: func [port][
        system/words/close port/sub-port
    ]
]