Dropbox API を curl で叩いてみる

curlDropboxREST API を叩いてみる。


Dropbox APIのドキュメントはこちら
Dropbox Mobile API Documentation


Dropbox APIのドキュメントにはOAuthに関することは何も書かれていないので以下の文書を参考にした。
OAuth Core 1.0 Revision A 日本語訳

アクセストークン/シークレットを取得

仮に、Dropboxのアカウントとアプリ用のConsumer key/secretが以下のようだったとする。

  • メールアドレス => foobar@foobar12345.com
  • パスワード => foobarpass
  • Consumer key => hogehoge
  • Consumer secret => fugafuga

まず以下のようにして /token APIからアクセストークン/シークレットを取得

curl -s 'https://api.dropbox.com/0/token?email=foobar@foobar12345.com&password=foobarpass&oauth_consumer_key=hogehoge'

レスポンスはこんな感じ。フォーマットはJSON

{"token": "piyopiyo", "secret": "puyopuyo"}
(値はダミー)

これを利用して、その他のAPIもアクセス可能となる。

API にアクセスしてみよう

試しに /account/info APIにアクセスしてみる。

URLはこれ

https://api.dropbox.com/0/account/info

このURLに署名付きリクエストを送る。

署名をするためのベース文字列を生成

OAuth Core 1.0 Revision A 日本語訳 の9.1章参照
出来上がった署名ベース文字列はこちら

GET&https%3A%2F%2Fapi.dropbox.com%2F0%2Faccount%2Finfo&oauth_consumer_key%3Dgly1ffa07jpan24%26oauth_nonce%3DM6mvq3I0d4%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1295511698%26oauth_token%3Dpiyopiyo

ベース文字列に署名をする

OAuth Core 1.0 Revision A 日本語訳 の9.2章参照
署名にはopensslを使用すればOK

echo -n 'GET&https%3A%2F%2Fapi.dropbox.com%2F0%2Faccount%2Finfo&oauth_consumer_key%3Dgly1ffa07jpan24%26oauth_nonce%3DM6mvq3I0d4%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1295511698%26oauth_token%3Dpiyopiyo' | openssl sha1 -hmac 'fugafuga&puyopuyo' -binary | openssl base64 | sed 's/\//%2F/g' | sed 's/=/%3D/g' | sed 's/+/%2B/g'

出力はこんな感じ。これが署名。

syi0AHTbMuVrFYCw5lcYNFuEOXs%3D

Authorizationヘッダの生成

OAuth Core 1.0 Revision A 日本語訳 の5.4章参照
できあがったAuthorizationヘッダがこれ

Authorization: OAuth oauth_consumer_key="hogehoge",oauth_token="piyopiyo",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1295511698",oauth_nonce="M6mvq3I0d4",oauth_signature="syi0AHTbMuVrFYCw5lcYNFuEOXs%3D"
(oauth_versionは無くても行けた)

APIにアクセス

ここまで来れば後はAuthorizationヘッダをくっつけて、APIのURLにリクエストを投げるだけ

curl -H 'Authorization: OAuth oauth_consumer_key="hogehoge",oauth_token="piyopiyo",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1295511698",oauth_nonce="M6mvq3I0d4",oauth_signature="syi0AHTbMuVrFYCw5lcYNFuEOXs%3D"' 'https://api.dropbox.com/0/account/info'

するとこのようなレスポンスが返ってくる。

{"referral_link": "https://www.dropbox.com/referrals/◯◯◯◯◯", "display_name": "foo bar", "uid": 12345678, "country": "JP", "quota_info": {"shared": 0, "quota": 2415919104, "normal": 471546692}, "email": "foobar@foobar12345.com"}

パラメータを敢えて間違えるとエラーレスポンスも体験できる。

シェルスクリプトにしてみた

んー。シェルでやるもんじゃあないなぁ(笑)
連想配列のないbashJSONは相性が悪い。


dropbox_api.sh

#!/bin/bash
function _dbg() {
    #echo $1
    return 0
}

#
# ランダム文字列生成
#
randstring()
{
  len=$1
  rndstr=""
  while [ $(echo ${rndstr} | wc -m) -le $len ]; do
    rnd=$[RANDOM % 75 + 48]
    if [ ${rnd} -ge 58 -a ${rnd} -le 64 -o ${rnd} -ge 91 -a ${rnd} -le 96 ]; then
      continue 
    fi
    rndstr=${rndstr}$(printf \\x$(printf %x ${rnd}))
  done
  echo ${rndstr}
  return 0
}

#
# 状態スタックに状態をプッシュ
#
function push_stat() {
    _STAT[${#_STAT[@]}]=$1
    _dbg NUM_STAT=${#_STAT[@]}, _STAT=${_STAT[*]}
    return 0
}

#
# 状態スタックから状態をポップ
#
function pop_stat() {
    unset _STAT[`expr ${#_STAT[@]} - 1`]
    _dbg NUM_STAT=${#_STAT[@]}, _STAT=${_STAT[*]}
    return 0
}

#
# 現在の状態を取得
#
function top_stat() {
    local num=${#_STAT[@]}
    if [ $num -gt 0 ]; then
        echo ${_STAT[(($num-1))]}
    else
        _dbg ""
    fi
    return 0
}

#
# JSON解析君
# JSONを標準入力から読み取り
#     JSON_<obj1>_<obj2>_<prop1>=<value>
# のような文字列を標準出力に出力するのでevalしてくだしあ
# 呼び出し前には unset ${!JSON_*} しておくこと
#
function json() {
    local ST_OBJ=0
    local ST_KEY=1
    local ST_STR=2
    local ST_VALUE=3

    local KEY="JSON"
    local VALUE=""
    local LINE
    local CHAR

    push_stat $ST_VALUE

    while read LINE; do
        for((i=0; i < ${#LINE}; i++)); do
            CHAR=${LINE:$i:1}
            _dbg $CHAR

            case `top_stat` in
            $ST_KEY)
                case $CHAR in
                \")
                    push_stat $ST_STR
                    _dbg "str start"
                    ;;
                \:)
                    pop_stat
                    _dbg "key end"
                    _dbg key=$VALUE
                    KEY=$VALUE
                    unset VALUE

                    push_stat $ST_VALUE
                    ;;
                esac
                ;;

            $ST_STR)
                case $CHAR in
                \")
                    pop_stat
                    _dbg "str end"
                    ;;
                " ")
                    ;;
                *)
                    VALUE=${VALUE}$CHAR
                    ;;
                esac
                ;;
            $ST_VALUE)
                case $CHAR in
                \")
                    push_stat $ST_STR
                    _dbg "str start"
                    ;;

                \,|\})
                    pop_stat
                    _dbg "value end"
                    _dbg value=$VALUE
                    if [ -n "$KEY" ]; then
                        PROP=`echo ${OBJPATH[@]} | sed -e 's/ /_/g'`
                        if [ -n $PROP ]; then
                            PROP=${PROP}_$KEY
                        fi

                        _dbg "$PROP=$VALUE"
                        echo "$PROP=$VALUE"
                        #eval "$PROP=$VALUE"
                    fi
                    unset VALUE
                    unset KEY

                    case $CHAR in
                    \,)
                        push_stat $ST_KEY
                        _dbg "key start"
                        ;;
                    \})
                        pop_stat
                        _dbg "object end"
                        unset OBJPATH[`expr ${#OBJPATH[@]} - 1`];
                        ;;
                    esac
                    ;;
                \{)
                    push_stat $ST_OBJ
                    _dbg object start

                    # obj start
                    OBJPATH[${#OBJPATH[@]}]=$KEY

                    push_stat $ST_KEY
                    _dbg key start
                    ;;
                " ")
                    ;;
                *)
                    VALUE=${VALUE}$CHAR
                    ;;
                esac
                ;;
            esac
        done
    done
}

#main
MAIL=$1
PASS=$2
CKEY=$3
CSECRET=$4

if [ -z "$CKEY" -o -z "$CSECRET" -o -z "$MAIL" -o -z "$PASS" ]; then
    echo usage: `basename $0` '<mail address> <password> <consumer key> <consumer secret>' >&2
    exit 1
fi

echo retrieve Access token/secret form Dropbox..

TOKEN_RESP=`curl -s "https://api.dropbox.com/0/token?email=$MAIL&password=$PASS&oauth_consumer_key=$CKEY"`

echo $TOKEN_RESP

unset ${!JSON_*}
eval `echo $TOKEN_RESP | json`

ATOKEN=$JSON_token
ASECRET=$JSON_secret

echo "Access token: $ATOKEN"
echo "Access token secret: $ASECRET"

if [ -z "$ATOKEN" -o -z "$ASECRET" ]; then
    echo "Can't retrieve Access token/secret" >&2
    exit 1
fi

NONCE=`randstring 10`
TIMESTAMP=`date +%s`

echo "creating OAuth Signature Base String.."

SIGN_BASE='GET&https%3A%2F%2Fapi.dropbox.com%2F0%2Faccount%2Finfo&oauth_consumer_key%3D'$CKEY'%26oauth_nonce%3D'$NONCE'%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D'$TIMESTAMP'%26oauth_token%3D'$ATOKEN 

echo "Signature Base String: $SIGN_BASE"

echo "Signing.."
SIGN=`echo -n $SIGN_BASE | openssl sha1 -hmac $CSECRET'&'$ASECRET -binary | openssl base64 | sed 's/\//%2F/g' | sed 's/=/%3D/g' | sed 's/+/%2B/g'`
echo "Signature: $SIGN"

echo "Retrieving account info..."
RESP=`curl -s -H 'Authorization: OAuth oauth_consumer_key="'$CKEY'",oauth_token="'$ATOKEN'",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'$TIMESTAMP'",oauth_nonce="'$NONCE'",oauth_signature="'$SIGN'"' 'https://api.dropbox.com/0/account/info'`
echo $RESP

unset ${!JSON_*}
eval `echo $RESP | json`

for p in ${!JSON_*}; do
    eval echo $p=\$$p
done

使い方

$ dropbox_api.sh