LoginSignup
7
6

More than 3 years have passed since last update.

C言語のSOCKETとopenSSLでTwitter APIを利用する方法

Last updated at Posted at 2020-01-06

Twitter APIをC言語で利用する方法

C言語でTwitterAPIを使う人はまずいないでしょう。使う人がいればかなりの熟練者か奇特な趣味の持ち主です。

この記事のプログラムは学習目的で作ったので実用性はありません。日本語にも対応していません。実用性を求める場合は、WinInetというAPIを使うほうが便利です。もっと簡単に、CurlというAPIを使うほうがより現実的です。

このプログラムは、最もベーシックなソケット通信を行い、TwitterのAPIサーバーに接続します。さらに実践的なSSL/TLS通信にも挑戦しています。httpからhttpsが主流になった現在、SSL通信は必須の技術です。ですが、特殊な知識は必要ありません。次にあげる必須項目を準備しておくだけです。

OpenSSLをPCに導入済み、Windows7以降でVisual StudioをIDEとして使用している、そして、TwitterAPIを利用申請して利用可能な状態、つまり以下の四つの秘密鍵をtwitterから発行されている方が利用可能です。

Consumer_API_keys
API_secret_key
Access_token
Access_token_secret

Visual Studioの設定は、OpenSSLのインクルードディレクトリ、ライブラリーディレクトリの追加。リンカーとリソースファイルにOpensslの各libファイルを追加する必要があります。ここのところまでは各自で行ってください。Webにはそれらのやり方が書かれたサイトが多く存在します。

OpenSSLをWindowsで使用するには、インストーラー版が出ているのでこちらのサイトからダウンロードしてインストールすると便利です。Win32 OpenSSL v1.1.1dが最新です。

SSL/TLS通信を開始する

ソケットでSSLを利用するにはまず第一番目にソケットを作成し、サーバーに接続することから始めます。この構文は通常のソケット通信と変わりません。コード内コメントにて解説します。

socket
WSADATA wsaData;
DWORD dwRetval;
struct addrinfo* result = NULL;
struct addrinfo* ptr = NULL;
struct addrinfo hints;
SOCKET connectSocket;

// Winsock DLLの利用を宣言します。成功すると0が返ります。
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

//ホストアドレス用構造体を作成します。
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

// hostAdressには"api.twitter.com"を入れ、ポートはSSLの443番を使います。
iResult = getaddrinfo(hostAddress, httpsPort, &hints, &result);

ptr = result;

// ソケットを作成します
connectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

// サーバーに接続します。成功すると0が返ります。
iResult = connect(connectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);

サーバーに接続したら、SSL通信に切り替えます。OpenSSLを利用するため、CTXとSSLオブジェクトを作成して、イニシャライズします。

CTXを作るとき接続方法として、TLS_client_method()を指示します。Twitter APIはTLS1.2を利用しているからです。古いOpenSSLはTLS1.2に対応していない場合もあるので、最新のものをPCに導入しておく必要があります。最新のOpensslでサポートするプロトコルは、SSLv3, TLSv1, TLSv1.1, TLSv1.2.です。

このほか、TLSv1_2_method(),TLSv1_1_method()などがありますが、これらはプロトコルのバージョンが限られるのであえてこれらを指定する必要性はなくなっています。


// SSLライブラリー,エラーメッセージを初期化。
SSL_library_init();
SSL_load_error_strings();

// CTXオブジェクトの作成。
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());

// SSLオブジェクトの作成
SSL* ssl = SSL_new(ctx);

// ソケットのファイルディスクリプタをSSL構造体に適用します。
SSL_set_fd(ssl, connectSocket);

// SSLで接続します。成功すると1が、失敗すると0かそれ以下の数字が返ります。
iResult = SSL_connect(ssl);

SSL接続に成功したら、OAuth認証をヘッダーに加えたHTTPリクエストをtwitter APIのサーバーに送信します。OAuth認証は各種パラーメーターを集めて作成します。次の項目で詳しく説明します。

OAuth認証情報の作成

Twitter APIの核心部分です。正しい認証情報でHTTPヘッダーを作成しないと、リクエストを送っても、サーバーから「400 Bad Request」か「401 Unauthorized」が返ってきます。

Twitter APIではGETもしくはPOSTを記述したHTTPリクエスト文の下に、OAuth情報を付加します。リクエストは次のように記述します。

http_request

GET /1.1/statuses/user_timeline.json?screen_name=twitterapi&count=2 HTTP/1.1
Host : api.twitter.com
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_consumer_key="<CONSUMER_API_KEY>", oauth_nonce="OAUTH_NONCE", oauth_signature="<OAUTH_SIGNATURE>", oauth_signature_method="HMAC-SHA1", oauth_timestamp="<OAUTH_TIMESTAMP>", oauth_token="<ACCESS_TOKEN>", oauth_version="1.0"



このリクエスト文は特定ユーザーのタイムラインを表示すJSONを要求します。OAuth認証を通すため、次の認証キーを送っています。それぞれ","で区切っているので、改行は入れません。

oauth_consumer_key=与えられたconsumer_key
oauth_nonce=任意の文字列
oauth_signature=認証するための署名
oauth_signature_method=署名メソッド
oauth_timestamp=タイムスタンプ。UNIX表示。
oauth_token=与えられたアクセス・トークン
oauth_version=OAuthバージョン。1.0固定。

oauth_consumerとoauth_tokenはそれぞれ各人に与えられたキーを記述します。
oauth_nonceは、適当な文字列です。ただし、記号などは入れてはいけません。
oauth_timestampは、送信するときのUNIX時間を求めます。この時間が大きくずれていると認証されません。おおよそ1日以内は大丈夫かと思われますが、正確に測ったわけではないのでリクエストを送信するたびに更新したほうがいいでしょう。
oauth_signature_methodとoauth_versionは、それぞれ"HMAC-SHA1"と"1.0"です。HMAC-SHA1という暗号化方法でOauth signatureを作成しているという意味になります。signatureの作成については、後述します。

すでに与えられたキー以外について、C言語でどのように求めるか書きます。まずはoauth_nonceです。と言いたいところですが、その前に、OauthではBase64に変換することが必須ですので、Base64変換から書きます。

次のコードは文字列からBase64に変換します。ヘッダーにwincrypt.hを追加します。マイクロソフトのライブラリなので、VisualStudio以外のIDEでは使用できないと思います。
では、次の関数を見てみす。

TextToBase64
int cryptTextToBase64(char* src, char* dest, size_t size) {

    // CryptBinaryToString function <wincrypt.h> convert TEXT to base64.

    // Calculate size of buffer
    DWORD dwPcc = 0;
    BOOL bRet = CryptBinaryToString((BYTE*)src, strlen(src), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &dwPcc); 
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    // Allocate buffer size as dwPcc
    char* cryptedBase64 = (char*)malloc(dwPcc);
    bRet = CryptBinaryToString((BYTE*)src, strlen(src), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, cryptedBase64, &dwPcc);
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    strcpy_s(dest, size, cryptedBase64);

    free(cryptedBase64);

    return 0;
}

上のコードを見ると、CryptBinaryToString が二度出てきます。最初は、Base64に変換した後のサイズを求めています。そのサイズをもとにバッファーを作成します。二度目にそのバッファーに変換したBASE64の文字列をコピーします。第二引数はsizeofではなくstrlenを使うことに注意します。

oauth_nonceはBase64に変換しないといけないので上記の関数を使用します。oauth_nonceは、ランダムな文字をBase64に変換します。変換後、文字列以外の記号("=","+","/")を省きます。ちなみにBase64ではこの3つの記号しかありません。

では、その工程を下に記します。関数にしてあります。

generate_oauth_nonce
char* generateNonce() {

    char alphabetSet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 62 charactors

    ZeroMemory(randCharSet, sizeof(randCharSet));
    unsigned int number = 0;
    unsigned int iRow = 0;
    errno_t err;

    for (int i = 0; i < 32; i++) {

        err = rand_s(&number);
        if (err != 0) {
            printf_s("The rand_s function failed!\n");
        }

        iRow = (unsigned int)((double)number / ((double)UINT_MAX + 1) * strlen(alphabetSet));
        char temp[2];
        temp[0]= alphabetSet[iRow];
        temp[1] = '\0';
        strcat_s(randCharSet, sizeof(randCharSet), temp);

    }

    printf("\n<Generated random charactor>\n%s\n\n",randCharSet);
    cryptTextToBase64(randCharSet, nonceSub, sizeof(nonceSub));
    printf("<Oauth nonce converted base64>\n%s\n\n", nonceSub);

    // Non-charactors must be eliminated in twitter api
    for (int i = 0; i < strlen(nonceSub); i++) {
        if (nonceSub[i] == '=') {
            if (nonceSub[i + 1] == '=') {
                nonceSub[i + 1] = '\0';
                nonceSub[i] = '\0';
                i--;
            }
            else {
                nonceSub[i] = nonceSub[i + 1];
                i--;
            }
        }
        else if (nonceSub[i] == '+') {
            nonceSub[i] = nonceSub[i + 1];
            i--;
        }
        else if (nonceSub[i] == '/') {
            nonceSub[i] = nonceSub[i + 1];
            i--;
        }
    }

    printf("<oauth nonce non-charactor removed>\n%s\n\n", nonceSub);
    strcpy_s(header_nonce, sizeof(header_nonce), nonceSub);
    printf("<header_nonce>\n%s\n\n", header_nonce);

    return nonceSub;
}

alphabetSetにアルファベットの大文字小文字と数字の6②文字からなる配列を用意します。
作成するランダム文字は32文字とします。

rand_s()はランダムな数字を生成します。0~31の数字をランダムに発生させ、文字列の配列から該当する文字を抽出してstrcatで連結していきます。Base64は文字列の最後は"="か"=="になるので、代わりに"\0"で埋めて完了させます。

次はtimestampです。使用言語が違えどもUnix時刻を求める関数は、それぞれの言語にあるようです。今回はC言語です。headerにtime.hを加えます。下に記述しましたが、これは簡単なので詳しい説明は省きます。

GetUnixTime

    int iUnixTime = time(NULL);
    char unixT[128];
    sprintf_s(unixT, sizeof(unixT), "%d", iUnixTime);

次は、signatureの作成です。これが一番難しく、Bad Requestが返る多くの要因になっています。手順を間違わずに解いていけば正解のsignatureが必ず作成されるはずです。

Oauth_signatureの作成

signatureを作成する際、最初にやることはParameter Stringを組み立てることです。Parameter Stringとはどのようなものでしょうか?下の参考例を見てください。

Oauth Parameter String:

key1=value1&key2=value2&oauth_consumer_key=d308e3ccg59e&oauth_nonce=CqWLVz8GkaL&
oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272026745&oauth_token=abcdefghi&
oauth_token_secret=jklmnopqrstu&oauth_version=1.0&opensocial_app_id=1&
opensocial_owner_id=0123456&opensocial_viewer_id=0123456

"=(イコール)"を挟んで左にキー名、右が値(Value)です。それぞれ&(アンパーサンド)で連結してBase Stringを完成させています。&で改行を入れてみると下のようになります。

key1=value1&
key2=value2&
oauth_consumer_key=d308e3ccg59e&
oauth_nonce=CqWLVz8GkaL&
oauth_signature_method=HMAC-SHA1&
oauth_timestamp=1272026745&
oauth_token=abcdefghi&
oauth_token_secret=jklmnopqrstu&
oauth_version=1.0&
opensocial_app_id=1&
opensocial_owner_id=0123456&
opensocial_viewer_id=0123456

上から順番にアルファベット並びになっているのがわかると思います。キー名がアルファベット順でないとサーバーから認証エラーが返ります。

oauth_***はOAuth認証に必須なパラメータです。そのほかは、GetまたはPOSTのリクエストを送る際のクエリパラメーターです。要求先サーバによってパラメーターは違ってきます。Twitter Apiの場合、パラメーターの解説がホームページで公開されているので参考にしてください。

次にtwitter apiのパラメーターストリング例を挙げます。

include_entities=true&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog&oauth_nonce=kYjzVB>B8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg&oauth_signature_method=HMAC->SHA1&oauth_timestamp=1318622958&oauth_token=370773112->GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb&oauth_version=1.0&status=Hello%20Ladies%2>0%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21

このparameter Stringは、ツイートするときにヘッダーにくっつけるOAuth認証に使われるものです。statusのvalue値はURLエンコードされたツイートの内容です。include_entities=trueは、オプションのクエリパラメーターです。ポストするときリクエストラインは次のようになります。

POST /1.1/statuses/update.json?include_entities=true HTTP/1.1

?以降をクエリーパラメーターと言います。parameter stringを作る際、include_entities=trueはアルファベット順にすると先頭になることがわかります。

OAuth認証用のparameterを含めて&で区切ったらキー名、値をパーセントエンコードします。この方法はOAuthプロトコルで決められているものです。すると以下のようになります。

include_entities=true&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog&oauth_nonce=kYjzVB>B8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg&oauth_signature_method=HMAC->SHA1&oauth_timestamp=1318622958&oauth_token=370773112->GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb&oauth_version=1.0&status=Hello%20Ladies%2>0%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21

OAuthのキー名はパーセントエンコードしても変更点はありません。なのでこの部分のパーセントエンコードは必要ないといえます。下にパーセントエンコード処理のサンプルコードを書きます。

percentEncoding

char* percentEncoding(char* src) {

    int ilen = strlen(src);
    char dest[1024];
    ZeroMemory(dest, sizeof(dest));

    for (int i = 0; i < ilen; i++) {

        char c[2];
        c[0] = src[i];
        c[1] = '\0';

        switch (src[i])
        {
        case ':':
        {
            strcat_s(dest, sizeof(dest), "%3A");
            break;
        }
        case '/':
        {
            strcat_s(dest, sizeof(dest), "%2F");
            break;
        }
        case '?':
        {
            strcat_s(dest, sizeof(dest), "%3F");
            break;
        }
        case '#':
        {
            strcat_s(dest, sizeof(dest), "%23");
            break;
        }
        case '[':
        {
            strcat_s(dest, sizeof(dest), "%5B");
            break;
        }
        case ']':
        {
            strcat_s(dest, sizeof(dest), "%5D");
            break;
        }
        case '@':
        {
            strcat_s(dest, sizeof(dest), "%40");
            break;
        }
        case '!':
        {
            strcat_s(dest, sizeof(dest), "%21");
            break;
        }
        case '$':
        {
            strcat_s(dest, sizeof(dest), "%24");
            break;
        }
        case '&':
        {
            strcat_s(dest, sizeof(dest), "%26");
            break;
        }
        case '\'':
        {
            strcat_s(dest, sizeof(dest), "%27");
            break;
        }
        case '(':
        {
            strcat_s(dest, sizeof(dest), "%28");
            break;
        }
        case ')':
        {
            strcat_s(dest, sizeof(dest), "%29");
            break;
        }
        case '*':
        {
            strcat_s(dest, sizeof(dest), "%2A");
            break;
        }
        case '+':
        {
            strcat_s(dest, sizeof(dest), "%2B");
            break;
        }
        case ',':
        {
            strcat_s(dest, sizeof(dest), "%2C");
            break;
        }
        case ';':
        {
            strcat_s(dest, sizeof(dest), "%3B");
            break;
        }
        case '=':
        {
            strcat_s(dest, sizeof(dest), "%3D");
            break;
        }
        case '%':
        {
            strcat_s(dest, sizeof(dest), "%25");
            break;
        }
        case ' ':
        {
            strcat_s(dest, sizeof(dest), "%20");
            break;
        }
        case '\"':
        {
            strcat_s(dest, sizeof(dest), "%22");
            break;
        }
        default:

            strcat_s(dest, sizeof(dest), c);
            break;
        }

    }

    return dest;

}

パーセントエンコードが終了したら、次にOAuth認証 Base Stringを作成します。次のように作成します。

  1. 大文字にしたPOSTまたはGETを先頭にして、そのあとに&を連結する。例:POST&
  2. URL部分をパーセントエンコードして&の後に連結する。例:POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&
  3. 先ほど作成したPrameter Stringをパーセントエンコードして&の後に連結する。

そうすると次のようになります。

POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities>%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZF>abxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC->SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112->GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%25>20Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521

&(アンパーサンド)はかならず2つ付加しているか確認します。

これが終わったら次は、Parameter Base stringから認証キーを作成します。認証キーはツイッターから発行してもらったconsumer secretOAuth token secretを&で連結したものです。

consumer_secret&OAuth_token_secret

プロトコルではいづれもパーセントエンコードしなくてはいけませんが、ツイッターの場合、変換される文字は含まれないので必要ないといえます。下に例を挙げます。

kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw&LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE

&を挟んで、左がconsumer secretで右がOAuth token secretです。

認証キーとParameter Base Stringを使て、HMAC-SHA方式で暗号化します。暗号化はOpenSSLのHMAC関数を使用します。

CreatingSignature
    unsigned char* digdest;
    size_t md_len;
    digdest = HMAC(EVP_sha1(), signingKey, strlen(signingKey), (unsigned char*)paramString, strlen(paramString), NULL, &md_len);

    mdString = (char*)malloc(md_len * 2);
    for (int i = 0; i < md_len; i++) {

        sprintf_s(&mdString[i * 2], md_len, "%02x", (unsigned int)digdest[i]);

    }

結果は以下のように16進数文字列として作成されます。
84 2B 52 99 88 7E 88 7602 12 A0 56 AC 4E C2 EE 16 26 B5 49
これをBase64に変換します。16進数からBASE64に変換する場合、以下のような処理をします。

  1. 16進数文字列をバイナリーに変換したときの文字列サイズを求める。
  2. 求められたサイズのバッファーを作成する。
  3. 16進数からバイナリーに変換し、バッファーに入れる。
  4. バイナリーから文字列に変換したときのサイズを求める。
  5. 求められたサイズのバッファーを作成する。
  6. バイナリーから文字列に変換し、バッファーに入れる。

下に関数化したものを書きます。

HextoBase64

int cryptHexToBase64(char* src, char* dest, size_t size) {

    // First of all, Hexadecimal string convert Hexadecimal raw data and it set to byte array. 
    DWORD dwDest = 0;
    CryptStringToBinary(src, strlen(src), CRYPT_STRING_HEXRAW ,NULL, &dwDest, NULL, NULL);
    BYTE* hexVal = (BYTE*)malloc(dwDest);
    CryptStringToBinary(src, strlen(src), CRYPT_STRING_HEXRAW, hexVal, &dwDest, NULL, NULL);

    printf("<hex-string converted HEX RAW data. Set it to byte array>\nSize = %d\n\n",  dwDest);

    // CryptBinaryToString with <wincrypt.h> convert hexadecimal binary data to base64 string.
    DWORD dwPcc = 0;
    BOOL bRet = CryptBinaryToString(hexVal, dwDest, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &dwPcc);
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    char* cryptedBase64 = (char*)malloc(dwPcc);
    CryptBinaryToString(hexVal, dwDest, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, cryptedBase64, &dwPcc);

    printf("<Win32 <wincrypt.h> encoder convert hex array to base64>\nSize = %d, %s\n\n",dwPcc, cryptedBase64);

    strcpy_s(dest, size, cryptedBase64);

    free(cryptedBase64);
    free(hexVal);

    return 0;
}

signatureキーとBase StringをHMAC-SHA方式で暗号化すると、最終的に以下の結果が得られます。
hCtSmYh+iHYCEqBWrE7C7hYmtUk=
これがOAuth認証に必要なOAuth_Signatureとなるものです。これをPOSTするヘッダーに追加します。

リクエストヘッダーの作成

ではこれまでの作業で得られたOAuth_Signatureを使って、リクエストヘッダーを作成します。最初は、リクエストラインです。

ツイートを投稿する場合、以下のようになります。
POST /1.1/statuses/update.json?include_entities=true HTTP/1.1
もしくは、
POST /1.1/statuses/update.json?include_entities=true&status=yourTweet HTTP/1.1

上の場合、Bodyにツイート内容を追加します。下はリクエストラインにツイート内容を追加しています。両方ツイートは可能です。

リクエストラインの下一行はHost名を追加します。その下にURLエンコードを宣言する。
Host: api.twitter.com
Content-Type : application/x-www-form-urlencoded

その下にOAuth認証パラメーターを追加する。
合わせると以下のようなリクエストヘッダーとなる。

RequestHeader

POST /1.1/statuses/update.json?include_entities=true&status=yourTweet HTTP/1.1
Host: api.twitter.com
Content-Type : application/x-www-form-urlencoded
Authorization: OAuth oauth_consumer_key="****",oauth_nonce="*****",oauth_signature="****",oauth_>signat>ure_method="HMAC-SHA1",oauth_timestamp="1578231186",oauth_token="****",oauth_version="1.0"

ヘッダーが完成したらデーターを送信します。SSL_writeを使い、リクエストヘッダーをChar型で送ります。

SSL_write(ssl, requestHeader,sizeof(requestHeader));

データーを送信したら、サーバーからの返答が返ってくるので受け取ります。
下にそのコードを書きます。受け取りは1バイトずつ読み込みます。基本はBYTE型で受け取りますが、twitter apiの場合はサーバーからJSONが返ってくるだけなので、Char型でも問題ありません。SSL/TLS通信の場合、ソケットのrecvの代わりに、SSL_readを使い、その返り値が0になるまでループさせます。

reading_response

    // Receive data
    BYTE readByte[1];
    BYTE outputByte[800000];
    ZeroMemory(readByte, sizeof(readByte));
    ZeroMemory(outputByte, sizeof(outputByte));
    int iApend = -1, iCount = 0;

    do {

        iResult = SSL_read(ssl, readByte, sizeof(readByte));
        if (iResult == 0) {
            break;
        }

        iApend += iResult;
        memcpy_s(outputByte + iApend, sizeof(outputByte), readByte, iResult);

        iCount++;

    } while (iResult > 0);


データーを受け取ったらソケットを閉じます。まずSSLから閉じます。最後にソケット通信を完了させます。

clean_up_socket_ssl

    // cleanup ssl
    iResult = SSL_shutdown(ssl);
    SSL_free(ssl);
    SSL_CTX_free(ctx);

    // cleanup socket
    iResult = closesocket(connectSocket);
    iResult = WSACleanup();

プログラムと解説

今回はツイッターの関数として以下の4つを作成しました。

  1. ツイートする(アルファベット・数字のみ。日本語はUTF-8で)。createTwitterTweet。
  2. 特定ユーザーのツイートのタイムラインを表示。createTwitterUserTimeline。
  3. キーワード検索。createTwitterSearchWord。
  4. twitter apiのサーバーに、リクエスト・トークンとトークン・シークレットを要求する。createTwitterRequestToken。

日本語の場合、UTF-8に変換する必要ひがあります。別のソフトにてUTF-8に変換した文字列でサーバーにリクエストをします。サーバーに「日本」という文字を送る場合、次のように変換します。
%E6%97%A5%E6%9C%AC

サーバーからのレスポンスも日本語は、ユニコードのコードポイントで返ってくるので、これも何らかの日本語化処理が必要です。ここら辺の処理はC言語の不便さがあります。Cで日本語化する場合、オープンソースのライブラリー「ICU(International Components for Unicode)」を使うという方法があります。

TwitterからのレスポンスはすべてJSONのテキストで返ってきますので、見やすくパースするプログラムを自作したり、ライブラリーを使用するといいと思います。

このプログラムはヘッダーファイルとソースファイルを分けてあります。

header.h

//How to tweet by Twitter API in C. Old basic socket programming with modern SSL.

#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#define _CRT_RAND_S // for rand_s fucntion
#pragma warning(disable : 4996)

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <time.h>
#include <string>
#include <stdlib.h>
#include <atlenc.h>
#include <wincrypt.h> // for base64 encrypt
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/crypto.h> // Option
#include <openssl/x509.h> // Option
#include <openssl/pem.h> // Option
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "crypt32.lib")

#define HTTP_GET "GET"
#define HTTP_POST "POST"

// Declare and initialize variables

WSADATA wsaData;
DWORD dwRetval;
struct addrinfo* result = NULL;
struct addrinfo* ptr = NULL;
struct addrinfo hints;
SOCKET connectSocket;

int iResult;
const int recvBufLen = 512;
char recvbuf[recvBufLen];
char randCharSet[33];
char oauth_signature[512];
char headerString[1024];
char recievedStrings[900000];
char* mdString;
char oauth_consumer_key[256];
char oauth_nonce[256];
char header_nonce[256];
char nonceSub[256];
char oauth_signature_method[256];
char oauth_timestamp[256];
char header_timestamp[256];
char oauth_token[256];
char oauth_version[256];
char requestHeader[2048];
char query[256];
char queryPart[128][256];
char oa_query_count[256];
char oa_query_scName[256];
char oa_query_status[256];
char oa_query_keyWord[256];
char oa_query_callback[256];

const char Consumer_API_keys[] = "";
const char API_secret_key[] = "";
const char Access_token[] = "";
const char Access_token_secret[] = "";

const char httpsPort[] = "443";
const char hostAddress[] = "api.twitter.com";
const char CRLF[] = "\r\n";
char yourTweet[] = "this is my tweet"; // UTF-8
char screenN[] = "realDonaldTrump"; // twitter screen Name. <account name @screen_name>
char searchKeyWord[] = "Japan";

enum ApiFunction {
    twitterUserTimeline = 1001,
    twitterTweet,
    twitterSearchWord,
    twitterRequestToken
};

char* generateNonce();
char* generate_oauth_signature(const char* httpMeth, char* headerStr, char query[][256], ApiFunction apiFunc);
char* percentEncoding(char* src);
char* UrlEncoding(char* src);
int cryptHexToBase64(char* src, char* dest, size_t size);
int cryptTextToBase64(char* src, char* dest, size_t size);
int createTwitterRequestToken(char* dest, size_t destSize);
int createTwitterUserTimeline(char* dest, size_t destSize, char* scName, int count);
int createTwitterTweet(char* reqHeader, size_t destSize, char* srcTweet);
int createTwitterSearchWord(char* reqheader, size_t destSize, char* keyWord, int count);
char* generateUnixtTime();

main.cpp

#include "header.h"

int main() {

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address and port
    iResult = getaddrinfo(hostAddress, httpsPort, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed: %d\n", iResult);
        WSACleanup();
        return 1;
    }

    connectSocket = INVALID_SOCKET;
    ptr = result;

    connectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    if (connectSocket == INVALID_SOCKET) {
        printf("Error at socket(): %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }

    // Connect to server.
    iResult = connect(connectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        closesocket(connectSocket);
        connectSocket = INVALID_SOCKET;
    }

    freeaddrinfo(result);

    if (connectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }

    //SSL Initialize
    SSL_library_init();
    SSL_load_error_strings();
    int iSslErr;

    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    if (!ctx) {
        printf("Error creating the context.\n");
        return 1;
    }

    // Create new ssl object
    SSL* ssl = SSL_new(ctx);
    if (!ssl) {
        printf("Error creating SSL structure.\n");
        return 1;
    }

    SSL_set_fd(ssl, connectSocket);
    iResult = SSL_connect(ssl);
    if (iResult < 1) {
        iSslErr = SSL_get_error(ssl, iResult);
        printf("SSL error #%d in accept, program terminated\n", iSslErr);

        closesocket(connectSocket);
        SSL_free(ssl);
        SSL_CTX_free(ctx);
        return 1;
    }

    printf("SSL connection on Twitter api server. socket %x, Version: %s, Cipher: %s\n\n", connectSocket, SSL_get_version(ssl), SSL_get_cipher(ssl));

     createTwitterTweet(requestHeader, sizeof(requestHeader), yourTweet); // 1. Tweet
    // createTwitterUserTimeline(requestHeader, sizeof(requestHeader), screenN, 5); // 2. Search user timeline. 
    // createTwitterRequestToken(requestHeader, sizeof(requestHeader)); // 3. Request a Token and a token secret.
    // createTwitterSearchWord(requestHeader, sizeof(requestHeader), searchKeyWord, 5); // 4.Search key word tweet.

    printf("<Generated HTTP HEADER>\n%s\n\n", requestHeader);

    // Send an initial buffer using SSL communication.
    iResult = SSL_write(ssl, requestHeader,sizeof(requestHeader));

    // Check for error in write.
    if (iResult < 1) {
        iSslErr = SSL_get_error(ssl, iResult);
        printf("Error #%d in write,program terminated\n", iSslErr);
        // If iSslErr=6 it means the server issued an SSL_shutdown. 
        // You must respond with a shutdown to complete a graceful shutdown.
     if (iSslErr == 6)
        SSL_shutdown(ssl);
        SSL_free(ssl);
        closesocket(connectSocket);
        SSL_CTX_free(ctx);
        return 1;
    }

    printf("The application sent %d byte data.\n\n", iResult);

    // Receive data
    BYTE readByte[1];
    BYTE outputByte[800000];
    ZeroMemory(readByte, sizeof(readByte));
    ZeroMemory(outputByte, sizeof(outputByte));
    int iApend = -1, iCount = 0;

    do {

        iResult = SSL_read(ssl, readByte, sizeof(readByte));
        if (iResult == 0) {
            break;
        }

        iApend += iResult;
        memcpy_s(outputByte + iApend, sizeof(outputByte), readByte, iResult);

        iCount++;

    } while (iResult > 0);


    FILE* fpr;
    fopen_s(&fpr, "./textSrc.txt", "w+");
    fwrite(outputByte, sizeof(BYTE), iCount + 1, fpr);
    fclose(fpr);

    printf("%s\n\n", outputByte);

    // cleanup ssl
    int s = 0;
    iResult= SSL_shutdown(ssl);
    if (iResult == 0) {

        printf("The SSL shutdown is not yet finished. Now, retrying...\n");
        while (iResult == 0) {
            Sleep(1000);
            iResult = SSL_shutdown(ssl);
            s++;
            if (s == 3) {
                return 1;
            }
        }
    }
    else if (iResult < 0) {
        SSL_get_error(ssl, iResult);
        printf("The shutdown was not successful because a fatal error occurred. Code : %d\n", iResult);
        return 1;
    }

    printf("The SSL shutdown was successfully completed. \n");

    SSL_free(ssl);
    SSL_CTX_free(ctx);

    // cleanup socket
    iResult = closesocket(connectSocket);
    if (iResult!= 0) {
        printf("SOCKET_ERROR is returned in closesocket, value : %d\n", WSAGetLastError());
    }

    iResult = WSACleanup();

    return 0;
}

char* generateNonce() {

    char alphabetSet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // 62 charactors

    ZeroMemory(randCharSet, sizeof(randCharSet));
    unsigned int number = 0;
    unsigned int iRow = 0;
    errno_t err;

    for (int i = 0; i < 32; i++) {

        err = rand_s(&number);
        if (err != 0) {
            printf_s("The rand_s function failed!\n");
        }

        iRow = (unsigned int)((double)number / ((double)UINT_MAX + 1) * strlen(alphabetSet));
        char temp[2];
        temp[0]= alphabetSet[iRow];
        temp[1] = '\0';
        strcat_s(randCharSet, sizeof(randCharSet), temp);

    }

    printf("\n<Generated random charactor>\n%s\n\n",randCharSet);
    cryptTextToBase64(randCharSet, nonceSub, sizeof(nonceSub));
    printf("<Oauth nonce converted base64>\n%s\n\n", nonceSub);

    // Non-charactors must be eliminated in twitter api
    for (int i = 0; i < strlen(nonceSub); i++) {
        if (nonceSub[i] == '=') {
            if (nonceSub[i + 1] == '=') {
                nonceSub[i + 1] = '\0';
                nonceSub[i] = '\0';
                i--;
            }
            else {
                nonceSub[i] = nonceSub[i + 1];
                i--;
            }
        }
        else if (nonceSub[i] == '+') {
            nonceSub[i] = nonceSub[i + 1];
            i--;
        }
        else if (nonceSub[i] == '/') {
            nonceSub[i] = nonceSub[i + 1];
            i--;
        }
    }

    printf("<oauth nonce non-charactor removed>\n%s\n\n", nonceSub);
    strcpy_s(header_nonce, sizeof(header_nonce), nonceSub);
    printf("<header_nonce>\n%s\n\n", header_nonce);

    return nonceSub;
}

char* generate_oauth_signature(const char* httpMeth, char* URL, char queryDest[][256], ApiFunction apiFunc) {

    // Creating the signature base string
    // Collecting parametors for creating signature
    switch (apiFunc)
    {
    case twitterUserTimeline:
    {
        strcpy_s(oa_query_count, sizeof(oa_query_count), queryDest[0]);
        strcat_s(oa_query_count, sizeof(oa_query_count), "&");

        strcpy_s(oa_query_scName, sizeof(oa_query_scName), "&");
        strcat_s(oa_query_scName, sizeof(oa_query_scName), queryDest[1]);
    }
    case twitterTweet:
    {
        strcpy_s(oa_query_status, sizeof(oa_query_status), "&status=");
        strcat_s(oa_query_status, sizeof(oa_query_status), queryDest[0]);
    }
    case twitterSearchWord:
    {
        strcpy_s(oa_query_count, sizeof(oa_query_count), queryDest[0]);
        strcat_s(oa_query_count, sizeof(oa_query_count), "&");

        strcpy_s(oa_query_keyWord, sizeof(oa_query_keyWord), "&");
        strcat_s(oa_query_keyWord, sizeof(oa_query_keyWord), UrlEncoding(queryDest[1]));
    }
    case twitterRequestToken:
    {
        // Do something.
    }
    default:
        break;
    }

    strcpy_s(oauth_consumer_key, sizeof(oauth_consumer_key), "oauth_consumer_key=");
    strcat_s(oauth_consumer_key, sizeof(oauth_consumer_key), Consumer_API_keys);
    strcat_s(oauth_consumer_key, sizeof(oauth_consumer_key), "&");
    strcpy_s(oauth_nonce, sizeof(oauth_nonce), "oauth_nonce=");
    strcat_s(oauth_nonce, sizeof(oauth_nonce), generateNonce());
    strcat_s(oauth_nonce, sizeof(oauth_nonce), "&");
    strcpy_s(oauth_signature_method, sizeof(oauth_signature_method), "oauth_signature_method=HMAC-SHA1&");
    strcpy_s(oauth_timestamp, sizeof(oauth_timestamp), "oauth_timestamp=");
    strcat_s(oauth_timestamp, sizeof(oauth_timestamp), generateUnixtTime());
    strcat_s(oauth_timestamp, sizeof(oauth_timestamp), "&");
    strcpy_s(oauth_token, sizeof(oauth_token), "oauth_token=");
    strcat_s(oauth_token, sizeof(oauth_token), Access_token);
    strcat_s(oauth_token, sizeof(oauth_token), "&");
    strcpy_s(oauth_version, sizeof(oauth_version), "oauth_version=1.0");

    char paramString[1024];
    char testParam[1024];
    ZeroMemory(paramString, sizeof(paramString));
    ZeroMemory(testParam, sizeof(testParam));

    if (httpMeth == HTTP_GET) {
        strcat_s(paramString, sizeof(paramString), HTTP_GET);
        strcat_s(testParam, sizeof(testParam), HTTP_GET);
    }
    else if (httpMeth == HTTP_POST) {
        strcat_s(paramString, sizeof(paramString), HTTP_POST);
        strcat_s(testParam, sizeof(testParam), HTTP_POST);
    }

    char baseAdress[] = "https://api.twitter.com";

    strcat_s(paramString, sizeof(paramString), "&");
    strcat_s(paramString, sizeof(paramString), percentEncoding(baseAdress));
    strcat_s(paramString, sizeof(paramString), percentEncoding(URL));
    strcat_s(paramString, sizeof(paramString), "&");

    if (apiFunc == twitterUserTimeline) {
        strcat_s(paramString, sizeof(paramString), percentEncoding(oa_query_count));
    }
    else if(apiFunc == twitterSearchWord){
        strcat_s(paramString, sizeof(paramString), percentEncoding(oa_query_count));
    }

    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_consumer_key));
    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_nonce));
    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_signature_method));
    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_timestamp));
    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_token));
    strcat_s(paramString, sizeof(paramString), percentEncoding(oauth_version));
    if (apiFunc == twitterUserTimeline) {
        strcat_s(paramString, sizeof(paramString), percentEncoding(oa_query_scName));
    }
    else if (apiFunc == twitterTweet) {
        strcat_s(paramString, sizeof(paramString), percentEncoding(oa_query_status));
    }else if (apiFunc == twitterSearchWord) {
        strcat_s(paramString, sizeof(paramString), percentEncoding(oa_query_keyWord));
    }

#ifdef _DEBUG

    strcat_s(testParam, sizeof(testParam), "&");
    strcat_s(testParam, sizeof(testParam), baseAdress);
    strcat_s(testParam, sizeof(testParam), URL);
    strcat_s(testParam, sizeof(testParam), "&");
    switch (apiFunc)
    {
    case twitterUserTimeline:
    {
        strcpy_s(oa_query_count, sizeof(oa_query_count), queryDest[0]);
        strcat_s(oa_query_count, sizeof(oa_query_count), "&");

        strcpy_s(oa_query_scName, sizeof(oa_query_scName), "&");
        strcat_s(oa_query_scName, sizeof(oa_query_scName), queryDest[1]);
    }
    case twitterTweet:
    {
        strcpy_s(oa_query_status, sizeof(oa_query_status), "&status=");
        strcat_s(oa_query_status, sizeof(oa_query_status), queryDest[0]);
    }
    case twitterSearchWord:
    {

        strcpy_s(oa_query_count, sizeof(oa_query_count), queryDest[0]);
        strcat_s(oa_query_count, sizeof(oa_query_count), "&");
        strcat_s(testParam, sizeof(testParam), oa_query_count);

        strcpy_s(oa_query_keyWord, sizeof(oa_query_keyWord), "&");
        strcat_s(oa_query_keyWord, sizeof(oa_query_keyWord), queryDest[1]);

    }
    default:
        break;
    }
    strcat_s(testParam, sizeof(testParam), oauth_consumer_key);
    strcat_s(testParam, sizeof(testParam), oauth_nonce);
    strcat_s(testParam, sizeof(testParam), oauth_signature_method);
    strcat_s(testParam, sizeof(testParam), oauth_timestamp);
    strcat_s(testParam, sizeof(testParam), oauth_token);
    strcat_s(testParam, sizeof(testParam), oauth_version);
    if (apiFunc == twitterUserTimeline) {
        strcat_s(testParam, sizeof(testParam),oa_query_scName);
    }
    else if (apiFunc == twitterTweet) {
        strcat_s(testParam, sizeof(testParam), oa_query_status);
    }
    else if (apiFunc == twitterSearchWord) {
        strcat_s(testParam, sizeof(testParam), oa_query_keyWord);
    }

    printf("<Base parameter string>\n%s\n\n", testParam);

#endif // !_DEBUG

    printf("<Generated paramemter string>\n%s\n\n", paramString);

    // Getting a signing key
    char signingKey[256];
    strcpy_s(signingKey, sizeof(signingKey), API_secret_key);
    strcat_s(signingKey, sizeof(signingKey), "&");
    strcat_s(signingKey, sizeof(signingKey), Access_token_secret);

    unsigned char* digdest;
    size_t md_len;
    digdest = HMAC(EVP_sha1(), signingKey, strlen(signingKey), (unsigned char*)paramString, strlen(paramString), NULL, &md_len);

    mdString = (char*)malloc(md_len * 2);
    for (int i = 0; i < md_len; i++) {

        sprintf_s(&mdString[i * 2], md_len, "%02x", (unsigned int)digdest[i]);

    }

    printf("<md_len size, calculated signature>\nSize = %d, %s\n\n", md_len, mdString);

    // Create oauth signature
    cryptHexToBase64(mdString, oauth_signature, sizeof(oauth_signature));

    printf("<Oauth signature>\n%s\n\n", oauth_signature);
    printf("<Percent encoded Oauth signature>\n%s\n\n", percentEncoding(oauth_signature));

    return percentEncoding(oauth_signature);
}

char* percentEncoding(char* src) {

    int ilen = strlen(src);
    char dest[1024];
    ZeroMemory(dest, sizeof(dest));

    for (int i = 0; i < ilen; i++) {

        char c[2];
        c[0] = src[i];
        c[1] = '\0';

        switch (src[i])
        {
        case ':':
        {
            strcat_s(dest, sizeof(dest), "%3A");
            break;
        }
        case '/':
        {
            strcat_s(dest, sizeof(dest), "%2F");
            break;
        }
        case '?':
        {
            strcat_s(dest, sizeof(dest), "%3F");
            break;
        }
        case '#':
        {
            strcat_s(dest, sizeof(dest), "%23");
            break;
        }
        case '[':
        {
            strcat_s(dest, sizeof(dest), "%5B");
            break;
        }
        case ']':
        {
            strcat_s(dest, sizeof(dest), "%5D");
            break;
        }
        case '@':
        {
            strcat_s(dest, sizeof(dest), "%40");
            break;
        }
        case '!':
        {
            strcat_s(dest, sizeof(dest), "%21");
            break;
        }
        case '$':
        {
            strcat_s(dest, sizeof(dest), "%24");
            break;
        }
        case '&':
        {
            strcat_s(dest, sizeof(dest), "%26");
            break;
        }
        case '\'':
        {
            strcat_s(dest, sizeof(dest), "%27");
            break;
        }
        case '(':
        {
            strcat_s(dest, sizeof(dest), "%28");
            break;
        }
        case ')':
        {
            strcat_s(dest, sizeof(dest), "%29");
            break;
        }
        case '*':
        {
            strcat_s(dest, sizeof(dest), "%2A");
            break;
        }
        case '+':
        {
            strcat_s(dest, sizeof(dest), "%2B");
            break;
        }
        case ',':
        {
            strcat_s(dest, sizeof(dest), "%2C");
            break;
        }
        case ';':
        {
            strcat_s(dest, sizeof(dest), "%3B");
            break;
        }
        case '=':
        {
            strcat_s(dest, sizeof(dest), "%3D");
            break;
        }
        case '%':
        {
            strcat_s(dest, sizeof(dest), "%25");
            break;
        }
        case ' ':
        {
            strcat_s(dest, sizeof(dest), "%20");
            break;
        }
        case '\"':
        {
            strcat_s(dest, sizeof(dest), "%22");
            break;
        }
        default:

            strcat_s(dest, sizeof(dest), c);
            break;
        }

    }

    return dest;

}


int cryptHexToBase64(char* src, char* dest, size_t size) {

    // First of all, Hexadecimal string convert Hexadecimal raw data and it set to byte array. 
    DWORD dwDest = 0;
    CryptStringToBinary(src, strlen(src), CRYPT_STRING_HEXRAW ,NULL, &dwDest, NULL, NULL);
    BYTE* hexVal = (BYTE*)malloc(dwDest);
    CryptStringToBinary(src, strlen(src), CRYPT_STRING_HEXRAW, hexVal, &dwDest, NULL, NULL);

    printf("<hex-string converted HEX RAW data. Set it to byte array>\nSize = %d\n\n",  dwDest);

    // CryptBinaryToString with <wincrypt.h> convert hexadecimal binary data to base64 string.
    DWORD dwPcc = 0;
    BOOL bRet = CryptBinaryToString(hexVal, dwDest, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &dwPcc);
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    char* cryptedBase64 = (char*)malloc(dwPcc);
    CryptBinaryToString(hexVal, dwDest, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, cryptedBase64, &dwPcc);

    printf("<Win32 <wincrypt.h> encoder convert hex array to base64>\nSize = %d, %s\n\n",dwPcc, cryptedBase64);

    strcpy_s(dest, size, cryptedBase64);

    free(cryptedBase64);
    free(hexVal);

    return 0;
}

int cryptTextToBase64(char* src, char* dest, size_t size) {

    // CryptBinaryToString function <wincrypt.h> convert TEXT to base64.

    // Calculate size of buffer
    DWORD dwPcc = 0;
    BOOL bRet = CryptBinaryToString((BYTE*)src, strlen(src), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &dwPcc); 
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    // Allocate buffer size as dwPcc
    char* cryptedBase64 = (char*)malloc(dwPcc);
    bRet = CryptBinaryToString((BYTE*)src, strlen(src), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, cryptedBase64, &dwPcc);
    if (!bRet) {
        printf("Crypt function failed.\n");
        return 1;
    }

    strcpy_s(dest, size, cryptedBase64);

    free(cryptedBase64);

    return 0;
}

int createTwitterTweet(char* reqHeader, size_t destSize, char* srcTweet) {

    char URL[] = "/1.1/statuses/update.json";
    char requestLine[256];
    char urlQue[256];
    strcpy_s(urlQue, sizeof(urlQue), "?");
    strcat_s(urlQue, sizeof(urlQue), "status=");
    strcat_s(urlQue, sizeof(urlQue), srcTweet);

    strcpy_s(queryPart[0], sizeof(queryPart[0]), percentEncoding(srcTweet));

    strcpy_s(requestLine, sizeof(requestLine), "POST ");
    strcat_s(requestLine, sizeof(requestLine), URL);
    strcat_s(requestLine, sizeof(requestLine), UrlEncoding(urlQue));
    strcat_s(requestLine, sizeof(requestLine), " HTTP/1.1");

    printf("<URL for request>\n%s%s\n\n",URL, urlQue);

    char accept[] = "Accept: */*";
    char connection[] = "cache-control: no-cache";
    char contentType[] = "Content-Type : application/x-www-form-urlencoded";
    char authorization[] = "Authorization: ";
    char oa_consKey[] = "OAuth oauth_consumer_key=\"";
    char oa_Nonce[] = "oauth_nonce=\"";
    char oa_signature[256];
    strcpy_s(oa_signature, sizeof(oa_signature), "oauth_signature=\"");
    char oa_sigMethod[] = "oauth_signature_method=\"HMAC-SHA1\",";
    char oa_timeStamp[] = "oauth_timestamp=\"";
    char oa_accessToken[] = "oauth_token=\"";
    char oa_version[] = "oauth_version=\"1.0\"";
    char contentLen[] = "Content-Length: 0";
    char hostAd[] = "Host: api.twitter.com";

    strcat_s(oa_signature, sizeof(oa_signature), generate_oauth_signature(HTTP_POST, URL, queryPart, twitterTweet));

    ZeroMemory(reqHeader, destSize);

    strcat_s(reqHeader, destSize, requestLine);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, hostAd);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, accept);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, connection);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, contentLen);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, authorization);
    strcat_s(reqHeader, destSize, oa_consKey);
    strcat_s(reqHeader, destSize, Consumer_API_keys);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_Nonce);
    strcat_s(reqHeader, destSize, header_nonce);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_signature);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_sigMethod);
    strcat_s(reqHeader, destSize, oa_timeStamp);
    strcat_s(reqHeader, destSize, header_timestamp);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_accessToken);
    strcat_s(reqHeader, destSize, Access_token);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_version);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, CRLF);

    return 0;

};

int createTwitterRequestToken(char* reqHeader, size_t destSize) {

    char URL[] = "/oauth/request_token";
    char query[] = "";
    char requestLine[256];
    strcpy_s(requestLine, sizeof(requestLine), "POST ");
    strcat_s(requestLine, sizeof(requestLine), URL);
    strcat_s(requestLine, sizeof(requestLine), " HTTP/1.1");

    char authorization[] = "Authorization: ";
    char oa_consKey[] = "OAuth oauth_consumer_key=\"";
    char oa_Nonce[] = "oauth_nonce=\"";
    char oa_signature[256];
    strcpy_s(oa_signature, sizeof(oa_signature), "oauth_signature=\"");
    char oa_sigMethod[] = "oauth_signature_method=\"HMAC-SHA1\",";
    char oa_timeStamp[] = "oauth_timestamp=\"";
    char oa_accessToken[] = "oauth_token=\"";
    char oa_version[] = "oauth_version=\"1.0\"";
    char contentType[] = "Content-Type: application/x-www-form-urlencoded";
    char hostAd[] = "Host: api.twitter.com";

    strcat_s(oa_signature, sizeof(oa_signature), generate_oauth_signature(HTTP_POST, URL, NULL, twitterRequestToken));

    ZeroMemory(reqHeader, destSize);

    ZeroMemory(reqHeader, destSize);
    strcat_s(reqHeader, destSize, requestLine);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, hostAd);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, contentType);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, authorization);
    strcat_s(reqHeader, destSize, oa_consKey);
    strcat_s(reqHeader, destSize, Consumer_API_keys);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_Nonce);
    strcat_s(reqHeader, destSize, header_nonce);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_signature);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_sigMethod);
    strcat_s(reqHeader, destSize, oa_timeStamp);
    strcat_s(reqHeader, destSize, header_timestamp);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_accessToken);
    strcat_s(reqHeader, destSize, Access_token);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_version);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, CRLF);

    return 0;
}

char* generateUnixtTime() {

    //This value should be the number of seconds since the Unix epoch at the point the request is generated

    int iUnixTime = time(NULL);
    char unixT[128];
    sprintf_s(unixT, sizeof(unixT), "%d", iUnixTime);
    sprintf_s(header_timestamp, sizeof(header_timestamp), "%d", iUnixTime);

    return unixT;
}

int createTwitterUserTimeline(char* reqHeader, size_t destSize, char* scName, int count) {

    printf("Twitter user timeline function started.\n\n");
    char URL[] = "/1.1/statuses/user_timeline.json";
    char requestLine[256];
    if (count < 1) {
        count = 2;
    }

    char chCount[5];
    _itoa_s(count, chCount, sizeof(chCount), 10);

    ZeroMemory(queryPart, sizeof(queryPart));

    strcpy_s(queryPart[0], sizeof(queryPart[0]), "count=");
    strcat_s(queryPart[0], sizeof(queryPart[0]), chCount);
    strcpy_s(queryPart[1], sizeof(queryPart[1]), "screen_name=");
    strcat_s(queryPart[1], sizeof(queryPart[1]), scName);

    printf("<query part>\nquery1: %s\nquery2: %s\n\n", queryPart[0], queryPart[1]);

    // Creating request line
    strcpy_s(requestLine, sizeof(requestLine), "GET ");
    strcat_s(requestLine, sizeof(requestLine), URL);
    strcat_s(requestLine, sizeof(requestLine), "?");
    strcat_s(requestLine, sizeof(requestLine), UrlEncoding(queryPart[0]));
    strcat_s(requestLine, sizeof(requestLine), "&");
    strcat_s(requestLine, sizeof(requestLine), UrlEncoding(queryPart[1]));
    strcat_s(requestLine, sizeof(requestLine), " HTTP/1.1");

    // Parameter key 
    char authorization[] = "Authorization: ";
    char oa_consKey[] = "OAuth oauth_consumer_key=\"";
    char oa_Nonce[] = "oauth_nonce=\"";
    char oa_signature[256];
    strcpy_s(oa_signature, sizeof(oa_signature), "oauth_signature=\"");
    char oa_sigMethod[] = "oauth_signature_method=\"HMAC-SHA1\",";
    char oa_timeStamp[] = "oauth_timestamp=\"";
    char oa_accessToken[] = "oauth_token=\"";
    char oa_version[] = "oauth_version=\"1.0\"";
    char contentType[] = "Content-Type: application/x-www-form-urlencoded";
    char hostAd[] = "Host: api.twitter.com";

    // Generate Oauth signature
    strcat_s(oa_signature, sizeof(oa_signature), generate_oauth_signature(HTTP_GET, URL, queryPart, twitterUserTimeline));

    // Create Http header
    ZeroMemory(reqHeader, destSize);
    strcat_s(reqHeader, destSize, requestLine);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, hostAd);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, contentType);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, authorization);
    strcat_s(reqHeader, destSize, oa_consKey);
    strcat_s(reqHeader, destSize, Consumer_API_keys);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_Nonce);
    strcat_s(reqHeader, destSize, header_nonce);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_signature);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_sigMethod);
    strcat_s(reqHeader, destSize, oa_timeStamp);
    strcat_s(reqHeader, destSize, header_timestamp);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_accessToken);
    strcat_s(reqHeader, destSize, Access_token);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_version);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, CRLF);


    return 0;
}

int createTwitterSearchWord(char* reqHeader, size_t destSize, char* keyWord, int count) {

    printf("Twitter key word search started.\n\n");
    char URL[] = "/1.1/search/tweets.json";
    char requestLine[256];
    if (count < 1) {
        count = 15;
    }
    else if(count > 100) {

        printf("The number of tweets to return per page, up to a maximum of 100. Defaults to 15.\n");
        return 1;
    }

    char chCount[4];
    _itoa_s(count, chCount, sizeof(chCount), 10);

    ZeroMemory(queryPart, sizeof(queryPart));

    strcat_s(queryPart[0], sizeof(queryPart[0]), "count=");
    strcat_s(queryPart[0], sizeof(queryPart[0]), chCount);
    strcpy_s(queryPart[1], sizeof(queryPart[1]), "q=");
    strcat_s(queryPart[1], sizeof(queryPart[1]), keyWord);

    printf("<query part>\nquery1: %s\nquery2: %s\n\n", queryPart[0], queryPart[1]);

    // Creating reqest line
    strcpy_s(requestLine, sizeof(requestLine), "GET ");
    strcat_s(requestLine, sizeof(requestLine), URL);
    strcat_s(requestLine, sizeof(requestLine), "?");
    strcat_s(requestLine, sizeof(requestLine), UrlEncoding(queryPart[0]));
    strcat_s(requestLine, sizeof(requestLine), "&");
    strcat_s(requestLine, sizeof(requestLine), UrlEncoding(queryPart[1]));
    strcat_s(requestLine, sizeof(requestLine), " HTTP/1.1");


    // Parameter key name
    char authorization[] = "Authorization: ";
    char oa_consKey[] = "OAuth oauth_consumer_key=\"";
    char oa_Nonce[] = "oauth_nonce=\"";
    char oa_signature[256];
    strcpy_s(oa_signature, sizeof(oa_signature), "oauth_signature=\"");
    char oa_sigMethod[] = "oauth_signature_method=\"HMAC-SHA1\",";
    char oa_timeStamp[] = "oauth_timestamp=\"";
    char oa_accessToken[] = "oauth_token=\"";
    char oa_version[] = "oauth_version=\"1.0\"";
    char contentType[] = "Content-Type: application/x-www-form-urlencoded";
    char hostAd[] = "Host: api.twitter.com";

    // Generate Oauth signature
    strcat_s(oa_signature, sizeof(oa_signature), generate_oauth_signature(HTTP_GET, URL, queryPart, twitterSearchWord));

    // Create http header
    ZeroMemory(reqHeader, destSize);
    strcat_s(reqHeader, destSize, requestLine);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, hostAd);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, contentType);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, authorization);
    strcat_s(reqHeader, destSize, oa_consKey);
    strcat_s(reqHeader, destSize, Consumer_API_keys);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_Nonce);
    strcat_s(reqHeader, destSize, header_nonce);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_signature);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_sigMethod);
    strcat_s(reqHeader, destSize, oa_timeStamp);
    strcat_s(reqHeader, destSize, header_timestamp);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_accessToken);
    strcat_s(reqHeader, destSize, Access_token);
    strcat_s(reqHeader, destSize, "\",");
    strcat_s(reqHeader, destSize, oa_version);
    strcat_s(reqHeader, destSize, CRLF);
    strcat_s(reqHeader, destSize, CRLF);

    return 0;
}



char* UrlEncoding(char* src) {

    int ilen = strlen(src);
    char dest[1024];
    ZeroMemory(dest, sizeof(dest));

    for (int i = 0; i < ilen; i++) {

        char c[2];
        c[0] = src[i];
        c[1] = '\0';

        switch (src[i])
        {
        case ':':
        {
            strcat_s(dest, sizeof(dest), "%3A");
            break;
        }
        case '/':
        {
            strcat_s(dest, sizeof(dest), "%2F");
            break;
        }
        /*case '?':
        {
            strcat_s(dest, sizeof(dest), "%3F");
            break;
        }*/
        case '#':
        {
            strcat_s(dest, sizeof(dest), "%23");
            break;
        }
        case '[':
        {
            strcat_s(dest, sizeof(dest), "%5B");
            break;
        }
        case ']':
        {
            strcat_s(dest, sizeof(dest), "%5D");
            break;
        }
        case '@':
        {
            strcat_s(dest, sizeof(dest), "%40");
            break;
        }
        case '!':
        {
            strcat_s(dest, sizeof(dest), "%21");
            break;
        }
        case '$':
        {
            strcat_s(dest, sizeof(dest), "%24");
            break;
        }
        /*case '&':
        {
            strcat_s(dest, sizeof(dest), "&amp;");
            break;
        }*/
        case '\'':
        {
            strcat_s(dest, sizeof(dest), "%27");
            break;
        }
        case '(':
        {
            strcat_s(dest, sizeof(dest), "%28");
            break;
        }
        case ')':
        {
            strcat_s(dest, sizeof(dest), "%29");
            break;
        }
        case '*':
        {
            strcat_s(dest, sizeof(dest), "%2A");
            break;
        }
        //case '+':
        //{
        //  strcat_s(dest, sizeof(dest), "%2B");
        //  break;
        //}
        case ',':
        {
            strcat_s(dest, sizeof(dest), "%2C");
            break;
        }
        case ';':
        {
            strcat_s(dest, sizeof(dest), "%3B");
            break;
        }
        case '%':
        {
            strcat_s(dest, sizeof(dest), "%25");
            break;
        }
        case ' ':
        {
            strcat_s(dest, sizeof(dest), "%20");
            break;
        }
        /*case '=':
        {
            strcat_s(dest, sizeof(dest), "%3D");
            break;
        }*/
        case '\"':
        {
            strcat_s(dest, sizeof(dest), "%22");
            break;
        }
        default:

            strcat_s(dest, sizeof(dest), c);
            break;
        }

    }

    return dest;


}
7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6