/
curlicue
executable file
·137 lines (113 loc) · 3.94 KB
/
curlicue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/bin/sh
# Curlicue - an OAuth wrapper for curl
#
# Copyright © 2010 Decklin Foster <decklin@red-bean.com>
# Please see README for usage information and LICENSE for license.
# Because HTTP responses from the OAuth "dance" will be URL-encoded, and
# we want to round-trip this data, we require that credentials files are
# also URL-encoded. Therefore, no decoding is done here. $1 is the name
# of another function that processes each pair (as two args).
load_cred_file() {
foreach_query_pair parse_cred "$(cat "$1" 2>/dev/null)"
}
foreach_query_pair() {
local IFS='&'
for i in $2; do
$1 "${i%%=*}" "${i#*=}"
done
}
# This list is tiring, but we can't just let random files set any old
# variable. The ones that don't start with oauth_ are extensions from
# one provider or another.
parse_cred() {
case "$1" in
oauth_consumer_key) oauth_consumer_key="$2";;
oauth_consumer_secret) oauth_consumer_secret="$2";;
oauth_token) oauth_token="$2";;
oauth_token_secret) oauth_token_secret="$2";;
user_id) user_id="$2";;
screen_name) screen_name="$2";;
application_name) application_name="$2";;
esac
}
quote_vals() {
sed 's/=\(.*\)/="\1"/'
}
echo_pair() {
echo "$1=$2"
}
join_params() {
paste -s -d "$1" -
}
# The timestamp/nonce are generated in this script, so they need to be
# URL-encoded. The key/token are read in from already URL-encoded files,
# so they should *not* be encoded again. This, along with the sort,
# means we cannot factor out the call to curl-encode.
mk_params() {
for i in \
oauth_version="$(curl-encode "=1.0")" \
oauth_signature_method="$(curl-encode "=HMAC-SHA1")" \
oauth_timestamp="$(curl-encode "=$oauth_timestamp")" \
oauth_nonce="$(curl-encode "=$oauth_nonce")" \
oauth_consumer_key="$oauth_consumer_key" \
${oauth_token:+oauth_token="$oauth_token"} \
$(foreach_query_pair echo_pair "$extra_params") \
$(foreach_query_pair echo_pair "$1")
do
echo "$i"
done | sort
}
# This is bad; it leaks the secret on the command line. The right thing
# would be to use -passin, but it doesn't seem to affect -hmac.
hmac_sha1() {
printf '%s' "$2" | openssl dgst -sha1 -hmac "$1" -binary | openssl base64
}
# Here's where we start.
method=GET
oauth_timestamp="$(date +%s)"
oauth_nonce="$(openssl rand -base64 12)"
while getopts 'e:f:Pp:vu:' opt; do
case "$opt" in
e) eval "echo \"$OPTARG\""; exit 0;;
f) load_cred_file "$OPTARG"; loaded=1;;
P) data_is_not_params=1;;
p) extra_params="$OPTARG";;
v) verbose=1;;
*) echo "Unknown option: $opt"; exit 2;;
esac
done; shift $(($OPTIND-1))
# The remaining args in $@ go directly to curl. Fools that we are, we
# attempt to parse them here. Only one URL is supported.
for i; do
case "$prev" in
-d|--data|--data-raw) test -z "$data_is_not_params" && url_params="$i";;
-X|--request) method="$i";;
esac
case "$i" in
-d|--data|--data-raw) method=POST;;
http*\?*) url="${i%%\?*}"; url_params="${i#*\?}";;
http*) url="$i";;
esac
prev="$i"
done
if test -z "$loaded"; then
cropped_url="${url#*://}"
host="${cropped_url%%/*}"
load_cred_file "$HOME/.curlicue/$host"
fi
if test -z "$oauth_consumer_key"; then
echo "Couldn't load a consumer key! Exiting." 1>&2
exit 1
fi
# This is where the magic happens.
params="$(mk_params "$url_params" | join_params '&')"
base_string="$(curl-encode "=$method" "=$url" "=$params")"
signing_key="$oauth_consumer_secret&$oauth_token_secret"
oauth_signature="$(hmac_sha1 "$signing_key" "$base_string")"
sig_params="$(curl-encode "oauth_signature=$oauth_signature")"
auth_header="$(mk_params "$sig_params" | quote_vals | join_params ',')"
if test -n "$verbose"; then
echo "Base string: $base_string" 1>&2
echo "Authorization: OAuth $auth_header" 1>&2
fi
curl -H "Authorization: OAuth $auth_header" "$@"