[go: nahoru, domu]

ARTSPConnection.cpp revision e681b91c27439907f216cb6c88426929bc5194bf
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ARTSPConnection"
19#include <utils/Log.h>
20
21#include "ARTSPConnection.h"
22
23#include <cutils/properties.h>
24
25#include <media/stagefright/foundation/ABuffer.h>
26#include <media/stagefright/foundation/ADebug.h>
27#include <media/stagefright/foundation/AMessage.h>
28#include <media/stagefright/foundation/base64.h>
29#include <media/stagefright/MediaErrors.h>
30
31#include <arpa/inet.h>
32#include <fcntl.h>
33#include <netdb.h>
34#include <openssl/md5.h>
35#include <sys/socket.h>
36
37namespace android {
38
39// static
40const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
41
42ARTSPConnection::ARTSPConnection()
43    : mState(DISCONNECTED),
44      mAuthType(NONE),
45      mSocket(-1),
46      mConnectionID(0),
47      mNextCSeq(0),
48      mReceiveResponseEventPending(false) {
49    MakeUserAgent(&mUserAgent);
50}
51
52ARTSPConnection::~ARTSPConnection() {
53    if (mSocket >= 0) {
54        LOGE("Connection is still open, closing the socket.");
55        close(mSocket);
56        mSocket = -1;
57    }
58}
59
60void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) {
61    sp<AMessage> msg = new AMessage(kWhatConnect, id());
62    msg->setString("url", url);
63    msg->setMessage("reply", reply);
64    msg->post();
65}
66
67void ARTSPConnection::disconnect(const sp<AMessage> &reply) {
68    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
69    msg->setMessage("reply", reply);
70    msg->post();
71}
72
73void ARTSPConnection::sendRequest(
74        const char *request, const sp<AMessage> &reply) {
75    sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
76    msg->setString("request", request);
77    msg->setMessage("reply", reply);
78    msg->post();
79}
80
81void ARTSPConnection::observeBinaryData(const sp<AMessage> &reply) {
82    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
83    msg->setMessage("reply", reply);
84    msg->post();
85}
86
87void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
88    switch (msg->what()) {
89        case kWhatConnect:
90            onConnect(msg);
91            break;
92
93        case kWhatDisconnect:
94            onDisconnect(msg);
95            break;
96
97        case kWhatCompleteConnection:
98            onCompleteConnection(msg);
99            break;
100
101        case kWhatSendRequest:
102            onSendRequest(msg);
103            break;
104
105        case kWhatReceiveResponse:
106            onReceiveResponse();
107            break;
108
109        case kWhatObserveBinaryData:
110        {
111            CHECK(msg->findMessage("reply", &mObserveBinaryMessage));
112            break;
113        }
114
115        default:
116            TRESPASS();
117            break;
118    }
119}
120
121// static
122bool ARTSPConnection::ParseURL(
123        const char *url, AString *host, unsigned *port, AString *path,
124        AString *user, AString *pass) {
125    host->clear();
126    *port = 0;
127    path->clear();
128    user->clear();
129    pass->clear();
130
131    if (strncasecmp("rtsp://", url, 7)) {
132        return false;
133    }
134
135    const char *slashPos = strchr(&url[7], '/');
136
137    if (slashPos == NULL) {
138        host->setTo(&url[7]);
139        path->setTo("/");
140    } else {
141        host->setTo(&url[7], slashPos - &url[7]);
142        path->setTo(slashPos);
143    }
144
145    ssize_t atPos = host->find("@");
146
147    if (atPos >= 0) {
148        // Split of user:pass@ from hostname.
149
150        AString userPass(*host, 0, atPos);
151        host->erase(0, atPos + 1);
152
153        ssize_t colonPos = userPass.find(":");
154
155        if (colonPos < 0) {
156            *user = userPass;
157        } else {
158            user->setTo(userPass, 0, colonPos);
159            pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
160        }
161    }
162
163    const char *colonPos = strchr(host->c_str(), ':');
164
165    if (colonPos != NULL) {
166        unsigned long x;
167        if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) {
168            return false;
169        }
170
171        *port = x;
172
173        size_t colonOffset = colonPos - host->c_str();
174        size_t trailing = host->size() - colonOffset;
175        host->erase(colonOffset, trailing);
176    } else {
177        *port = 554;
178    }
179
180    return true;
181}
182
183static void MakeSocketBlocking(int s, bool blocking) {
184    // Make socket non-blocking.
185    int flags = fcntl(s, F_GETFL, 0);
186    CHECK_NE(flags, -1);
187
188    if (blocking) {
189        flags &= ~O_NONBLOCK;
190    } else {
191        flags |= O_NONBLOCK;
192    }
193
194    CHECK_NE(fcntl(s, F_SETFL, flags), -1);
195}
196
197void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
198    ++mConnectionID;
199
200    if (mState != DISCONNECTED) {
201        close(mSocket);
202        mSocket = -1;
203
204        flushPendingRequests();
205    }
206
207    mState = CONNECTING;
208
209    AString url;
210    CHECK(msg->findString("url", &url));
211
212    sp<AMessage> reply;
213    CHECK(msg->findMessage("reply", &reply));
214
215    AString host, path;
216    unsigned port;
217    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
218            || (mUser.size() > 0 && mPass.size() == 0)) {
219        // If we have a user name but no password we have to give up
220        // right here, since we currently have no way of asking the user
221        // for this information.
222
223        LOGE("Malformed rtsp url %s", url.c_str());
224
225        reply->setInt32("result", ERROR_MALFORMED);
226        reply->post();
227
228        mState = DISCONNECTED;
229        return;
230    }
231
232    if (mUser.size() > 0) {
233        LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
234    }
235
236    struct hostent *ent = gethostbyname(host.c_str());
237    if (ent == NULL) {
238        LOGE("Unknown host %s", host.c_str());
239
240        reply->setInt32("result", -ENOENT);
241        reply->post();
242
243        mState = DISCONNECTED;
244        return;
245    }
246
247    mSocket = socket(AF_INET, SOCK_STREAM, 0);
248
249    MakeSocketBlocking(mSocket, false);
250
251    struct sockaddr_in remote;
252    memset(remote.sin_zero, 0, sizeof(remote.sin_zero));
253    remote.sin_family = AF_INET;
254    remote.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
255    remote.sin_port = htons(port);
256
257    int err = ::connect(
258            mSocket, (const struct sockaddr *)&remote, sizeof(remote));
259
260    reply->setInt32("server-ip", ntohl(remote.sin_addr.s_addr));
261
262    if (err < 0) {
263        if (errno == EINPROGRESS) {
264            sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
265            msg->setMessage("reply", reply);
266            msg->setInt32("connection-id", mConnectionID);
267            msg->post();
268            return;
269        }
270
271        reply->setInt32("result", -errno);
272        mState = DISCONNECTED;
273
274        close(mSocket);
275        mSocket = -1;
276    } else {
277        reply->setInt32("result", OK);
278        mState = CONNECTED;
279        mNextCSeq = 1;
280
281        postReceiveReponseEvent();
282    }
283
284    reply->post();
285}
286
287void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
288    if (mState == CONNECTED || mState == CONNECTING) {
289        close(mSocket);
290        mSocket = -1;
291
292        flushPendingRequests();
293    }
294
295    sp<AMessage> reply;
296    CHECK(msg->findMessage("reply", &reply));
297
298    reply->setInt32("result", OK);
299    mState = DISCONNECTED;
300
301    mUser.clear();
302    mPass.clear();
303    mAuthType = NONE;
304    mNonce.clear();
305
306    reply->post();
307}
308
309void ARTSPConnection::onCompleteConnection(const sp<AMessage> &msg) {
310    sp<AMessage> reply;
311    CHECK(msg->findMessage("reply", &reply));
312
313    int32_t connectionID;
314    CHECK(msg->findInt32("connection-id", &connectionID));
315
316    if ((connectionID != mConnectionID) || mState != CONNECTING) {
317        // While we were attempting to connect, the attempt was
318        // cancelled.
319        reply->setInt32("result", -ECONNABORTED);
320        reply->post();
321        return;
322    }
323
324    struct timeval tv;
325    tv.tv_sec = 0;
326    tv.tv_usec = kSelectTimeoutUs;
327
328    fd_set ws;
329    FD_ZERO(&ws);
330    FD_SET(mSocket, &ws);
331
332    int res = select(mSocket + 1, NULL, &ws, NULL, &tv);
333    CHECK_GE(res, 0);
334
335    if (res == 0) {
336        // Timed out. Not yet connected.
337
338        msg->post();
339        return;
340    }
341
342    int err;
343    socklen_t optionLen = sizeof(err);
344    CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
345    CHECK_EQ(optionLen, (socklen_t)sizeof(err));
346
347    if (err != 0) {
348        LOGE("err = %d (%s)", err, strerror(err));
349
350        reply->setInt32("result", -err);
351
352        mState = DISCONNECTED;
353        close(mSocket);
354        mSocket = -1;
355    } else {
356        reply->setInt32("result", OK);
357        mState = CONNECTED;
358        mNextCSeq = 1;
359
360        postReceiveReponseEvent();
361    }
362
363    reply->post();
364}
365
366void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
367    sp<AMessage> reply;
368    CHECK(msg->findMessage("reply", &reply));
369
370    if (mState != CONNECTED) {
371        reply->setInt32("result", -ENOTCONN);
372        reply->post();
373        return;
374    }
375
376    AString request;
377    CHECK(msg->findString("request", &request));
378
379    // Just in case we need to re-issue the request with proper authentication
380    // later, stash it away.
381    reply->setString("original-request", request.c_str(), request.size());
382
383    addAuthentication(&request);
384    addUserAgent(&request);
385
386    // Find the boundary between headers and the body.
387    ssize_t i = request.find("\r\n\r\n");
388    CHECK_GE(i, 0);
389
390    int32_t cseq = mNextCSeq++;
391
392    AString cseqHeader = "CSeq: ";
393    cseqHeader.append(cseq);
394    cseqHeader.append("\r\n");
395
396    request.insert(cseqHeader, i + 2);
397
398    LOGV("request: '%s'", request.c_str());
399
400    size_t numBytesSent = 0;
401    while (numBytesSent < request.size()) {
402        ssize_t n =
403            send(mSocket, request.c_str() + numBytesSent,
404                 request.size() - numBytesSent, 0);
405
406        if (n == 0) {
407            // Server closed the connection.
408            LOGE("Server unexpectedly closed the connection.");
409
410            reply->setInt32("result", ERROR_IO);
411            reply->post();
412            return;
413        } else if (n < 0) {
414            if (errno == EINTR) {
415                continue;
416            }
417
418            LOGE("Error sending rtsp request.");
419            reply->setInt32("result", -errno);
420            reply->post();
421            return;
422        }
423
424        numBytesSent += (size_t)n;
425    }
426
427    mPendingRequests.add(cseq, reply);
428}
429
430void ARTSPConnection::onReceiveResponse() {
431    mReceiveResponseEventPending = false;
432
433    if (mState != CONNECTED) {
434        return;
435    }
436
437    struct timeval tv;
438    tv.tv_sec = 0;
439    tv.tv_usec = kSelectTimeoutUs;
440
441    fd_set rs;
442    FD_ZERO(&rs);
443    FD_SET(mSocket, &rs);
444
445    int res = select(mSocket + 1, &rs, NULL, NULL, &tv);
446    CHECK_GE(res, 0);
447
448    if (res == 1) {
449        MakeSocketBlocking(mSocket, true);
450
451        bool success = receiveRTSPReponse();
452
453        MakeSocketBlocking(mSocket, false);
454
455        if (!success) {
456            // Something horrible, irreparable has happened.
457            flushPendingRequests();
458            return;
459        }
460    }
461
462    postReceiveReponseEvent();
463}
464
465void ARTSPConnection::flushPendingRequests() {
466    for (size_t i = 0; i < mPendingRequests.size(); ++i) {
467        sp<AMessage> reply = mPendingRequests.valueAt(i);
468
469        reply->setInt32("result", -ECONNABORTED);
470        reply->post();
471    }
472
473    mPendingRequests.clear();
474}
475
476void ARTSPConnection::postReceiveReponseEvent() {
477    if (mReceiveResponseEventPending) {
478        return;
479    }
480
481    sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id());
482    msg->post();
483
484    mReceiveResponseEventPending = true;
485}
486
487status_t ARTSPConnection::receive(void *data, size_t size) {
488    size_t offset = 0;
489    while (offset < size) {
490        ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
491        if (n == 0) {
492            // Server closed the connection.
493            LOGE("Server unexpectedly closed the connection.");
494            return ERROR_IO;
495        } else if (n < 0) {
496            if (errno == EINTR) {
497                continue;
498            }
499
500            LOGE("Error reading rtsp response.");
501            return -errno;
502        }
503
504        offset += (size_t)n;
505    }
506
507    return OK;
508}
509
510bool ARTSPConnection::receiveLine(AString *line) {
511    line->clear();
512
513    bool sawCR = false;
514    for (;;) {
515        char c;
516        if (receive(&c, 1) != OK) {
517            return false;
518        }
519
520        if (sawCR && c == '\n') {
521            line->erase(line->size() - 1, 1);
522            return true;
523        }
524
525        line->append(&c, 1);
526
527        if (c == '$' && line->size() == 1) {
528            // Special-case for interleaved binary data.
529            return true;
530        }
531
532        sawCR = (c == '\r');
533    }
534}
535
536sp<ABuffer> ARTSPConnection::receiveBinaryData() {
537    uint8_t x[3];
538    if (receive(x, 3) != OK) {
539        return NULL;
540    }
541
542    sp<ABuffer> buffer = new ABuffer((x[1] << 8) | x[2]);
543    if (receive(buffer->data(), buffer->size()) != OK) {
544        return NULL;
545    }
546
547    buffer->meta()->setInt32("index", (int32_t)x[0]);
548
549    return buffer;
550}
551
552static bool IsRTSPVersion(const AString &s) {
553    return s == "RTSP/1.0";
554}
555
556bool ARTSPConnection::receiveRTSPReponse() {
557    AString statusLine;
558
559    if (!receiveLine(&statusLine)) {
560        return false;
561    }
562
563    if (statusLine == "$") {
564        sp<ABuffer> buffer = receiveBinaryData();
565
566        if (buffer == NULL) {
567            return false;
568        }
569
570        if (mObserveBinaryMessage != NULL) {
571            sp<AMessage> notify = mObserveBinaryMessage->dup();
572            notify->setObject("buffer", buffer);
573            notify->post();
574        } else {
575            LOGW("received binary data, but no one cares.");
576        }
577
578        return true;
579    }
580
581    sp<ARTSPResponse> response = new ARTSPResponse;
582    response->mStatusLine = statusLine;
583
584    LOGI("status: %s", response->mStatusLine.c_str());
585
586    ssize_t space1 = response->mStatusLine.find(" ");
587    if (space1 < 0) {
588        return false;
589    }
590    ssize_t space2 = response->mStatusLine.find(" ", space1 + 1);
591    if (space2 < 0) {
592        return false;
593    }
594
595    bool isRequest = false;
596
597    if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) {
598        CHECK(IsRTSPVersion(
599                    AString(
600                        response->mStatusLine,
601                        space2 + 1,
602                        response->mStatusLine.size() - space2 - 1)));
603
604        isRequest = true;
605
606        response->mStatusCode = 0;
607    } else {
608        AString statusCodeStr(
609                response->mStatusLine, space1 + 1, space2 - space1 - 1);
610
611        if (!ParseSingleUnsignedLong(
612                    statusCodeStr.c_str(), &response->mStatusCode)
613                || response->mStatusCode < 100 || response->mStatusCode > 999) {
614            return false;
615        }
616    }
617
618    AString line;
619    for (;;) {
620        if (!receiveLine(&line)) {
621            break;
622        }
623
624        if (line.empty()) {
625            break;
626        }
627
628        LOGV("line: %s", line.c_str());
629
630        ssize_t colonPos = line.find(":");
631        if (colonPos < 0) {
632            // Malformed header line.
633            return false;
634        }
635
636        AString key(line, 0, colonPos);
637        key.trim();
638        key.tolower();
639
640        line.erase(0, colonPos + 1);
641        line.trim();
642
643        response->mHeaders.add(key, line);
644    }
645
646    unsigned long contentLength = 0;
647
648    ssize_t i = response->mHeaders.indexOfKey("content-length");
649
650    if (i >= 0) {
651        AString value = response->mHeaders.valueAt(i);
652        if (!ParseSingleUnsignedLong(value.c_str(), &contentLength)) {
653            return false;
654        }
655    }
656
657    if (contentLength > 0) {
658        response->mContent = new ABuffer(contentLength);
659
660        size_t numBytesRead = 0;
661        while (numBytesRead < contentLength) {
662            ssize_t n = recv(
663                    mSocket, response->mContent->data() + numBytesRead,
664                    contentLength - numBytesRead, 0);
665
666            if (n == 0) {
667                // Server closed the connection.
668                TRESPASS();
669            } else if (n < 0) {
670                if (errno == EINTR) {
671                    continue;
672                }
673
674                TRESPASS();
675            }
676
677            numBytesRead += (size_t)n;
678        }
679    }
680
681    if (response->mStatusCode == 401) {
682        if (mAuthType == NONE && mUser.size() > 0
683                && parseAuthMethod(response)) {
684            ssize_t i;
685            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
686            CHECK_GE(i, 0);
687
688            sp<AMessage> reply = mPendingRequests.valueAt(i);
689            mPendingRequests.removeItemsAt(i);
690
691            AString request;
692            CHECK(reply->findString("original-request", &request));
693
694            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
695            msg->setMessage("reply", reply);
696            msg->setString("request", request.c_str(), request.size());
697
698            LOGI("re-sending request with authentication headers...");
699            onSendRequest(msg);
700
701            return true;
702        }
703    }
704
705    return isRequest
706        ? handleServerRequest(response)
707        : notifyResponseListener(response);
708}
709
710bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
711    // Implementation of server->client requests is optional for all methods
712    // but we do need to respond, even if it's just to say that we don't
713    // support the method.
714
715    ssize_t space1 = request->mStatusLine.find(" ");
716    CHECK_GE(space1, 0);
717
718    AString response;
719    response.append("RTSP/1.0 501 Not Implemented\r\n");
720
721    ssize_t i = request->mHeaders.indexOfKey("cseq");
722
723    if (i >= 0) {
724        AString value = request->mHeaders.valueAt(i);
725
726        unsigned long cseq;
727        if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
728            return false;
729        }
730
731        response.append("CSeq: ");
732        response.append(cseq);
733        response.append("\r\n");
734    }
735
736    response.append("\r\n");
737
738    size_t numBytesSent = 0;
739    while (numBytesSent < response.size()) {
740        ssize_t n =
741            send(mSocket, response.c_str() + numBytesSent,
742                 response.size() - numBytesSent, 0);
743
744        if (n == 0) {
745            // Server closed the connection.
746            LOGE("Server unexpectedly closed the connection.");
747
748            return false;
749        } else if (n < 0) {
750            if (errno == EINTR) {
751                continue;
752            }
753
754            LOGE("Error sending rtsp response.");
755            return false;
756        }
757
758        numBytesSent += (size_t)n;
759    }
760
761    return true;
762}
763
764// static
765bool ARTSPConnection::ParseSingleUnsignedLong(
766        const char *from, unsigned long *x) {
767    char *end;
768    *x = strtoul(from, &end, 10);
769
770    if (end == from || *end != '\0') {
771        return false;
772    }
773
774    return true;
775}
776
777status_t ARTSPConnection::findPendingRequest(
778        const sp<ARTSPResponse> &response, ssize_t *index) const {
779    *index = 0;
780
781    ssize_t i = response->mHeaders.indexOfKey("cseq");
782
783    if (i < 0) {
784        // This is an unsolicited server->client message.
785        return OK;
786    }
787
788    AString value = response->mHeaders.valueAt(i);
789
790    unsigned long cseq;
791    if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
792        return ERROR_MALFORMED;
793    }
794
795    i = mPendingRequests.indexOfKey(cseq);
796
797    if (i < 0) {
798        return -ENOENT;
799    }
800
801    *index = i;
802
803    return OK;
804}
805
806bool ARTSPConnection::notifyResponseListener(
807        const sp<ARTSPResponse> &response) {
808    ssize_t i;
809    status_t err = findPendingRequest(response, &i);
810
811    if (err == OK && i < 0) {
812        // An unsolicited server response is not a problem.
813        return true;
814    }
815
816    if (err != OK) {
817        return false;
818    }
819
820    sp<AMessage> reply = mPendingRequests.valueAt(i);
821    mPendingRequests.removeItemsAt(i);
822
823    reply->setInt32("result", OK);
824    reply->setObject("response", response);
825    reply->post();
826
827    return true;
828}
829
830bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
831    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
832
833    if (i < 0) {
834        return false;
835    }
836
837    AString value = response->mHeaders.valueAt(i);
838
839    if (!strncmp(value.c_str(), "Basic", 5)) {
840        mAuthType = BASIC;
841    } else {
842#if !defined(HAVE_ANDROID_OS)
843        // We don't have access to the MD5 implementation on the simulator,
844        // so we won't support digest authentication.
845        return false;
846#endif
847
848        CHECK(!strncmp(value.c_str(), "Digest", 6));
849        mAuthType = DIGEST;
850
851        i = value.find("nonce=");
852        CHECK_GE(i, 0);
853        CHECK_EQ(value.c_str()[i + 6], '\"');
854        ssize_t j = value.find("\"", i + 7);
855        CHECK_GE(j, 0);
856
857        mNonce.setTo(value, i + 7, j - i - 7);
858    }
859
860    return true;
861}
862
863#if defined(HAVE_ANDROID_OS)
864static void H(const AString &s, AString *out) {
865    out->clear();
866
867    MD5_CTX m;
868    MD5_Init(&m);
869    MD5_Update(&m, s.c_str(), s.size());
870
871    uint8_t key[16];
872    MD5_Final(key, &m);
873
874    for (size_t i = 0; i < 16; ++i) {
875        char nibble = key[i] >> 4;
876        if (nibble <= 9) {
877            nibble += '0';
878        } else {
879            nibble += 'a' - 10;
880        }
881        out->append(&nibble, 1);
882
883        nibble = key[i] & 0x0f;
884        if (nibble <= 9) {
885            nibble += '0';
886        } else {
887            nibble += 'a' - 10;
888        }
889        out->append(&nibble, 1);
890    }
891}
892#endif
893
894static void GetMethodAndURL(
895        const AString &request, AString *method, AString *url) {
896    ssize_t space1 = request.find(" ");
897    CHECK_GE(space1, 0);
898
899    ssize_t space2 = request.find(" ", space1 + 1);
900    CHECK_GE(space2, 0);
901
902    method->setTo(request, 0, space1);
903    url->setTo(request, space1 + 1, space2 - space1);
904}
905
906void ARTSPConnection::addAuthentication(AString *request) {
907    if (mAuthType == NONE) {
908        return;
909    }
910
911    // Find the boundary between headers and the body.
912    ssize_t i = request->find("\r\n\r\n");
913    CHECK_GE(i, 0);
914
915    if (mAuthType == BASIC) {
916        AString tmp;
917        tmp.append(mUser);
918        tmp.append(":");
919        tmp.append(mPass);
920
921        AString out;
922        encodeBase64(tmp.c_str(), tmp.size(), &out);
923
924        AString fragment;
925        fragment.append("Authorization: Basic ");
926        fragment.append(out);
927        fragment.append("\r\n");
928
929        request->insert(fragment, i + 2);
930
931        return;
932    }
933
934#if defined(HAVE_ANDROID_OS)
935    CHECK_EQ((int)mAuthType, (int)DIGEST);
936
937    AString method, url;
938    GetMethodAndURL(*request, &method, &url);
939
940    AString A1;
941    A1.append(mUser);
942    A1.append(":");
943    A1.append("Streaming Server");
944    A1.append(":");
945    A1.append(mPass);
946
947    AString A2;
948    A2.append(method);
949    A2.append(":");
950    A2.append(url);
951
952    AString HA1, HA2;
953    H(A1, &HA1);
954    H(A2, &HA2);
955
956    AString tmp;
957    tmp.append(HA1);
958    tmp.append(":");
959    tmp.append(mNonce);
960    tmp.append(":");
961    tmp.append(HA2);
962
963    AString digest;
964    H(tmp, &digest);
965
966    AString fragment;
967    fragment.append("Authorization: Digest ");
968    fragment.append("nonce=\"");
969    fragment.append(mNonce);
970    fragment.append("\", ");
971    fragment.append("username=\"");
972    fragment.append(mUser);
973    fragment.append("\", ");
974    fragment.append("uri=\"");
975    fragment.append(url);
976    fragment.append("\", ");
977    fragment.append("response=\"");
978    fragment.append(digest);
979    fragment.append("\"");
980    fragment.append("\r\n");
981
982    request->insert(fragment, i + 2);
983#endif
984}
985
986// static
987void ARTSPConnection::MakeUserAgent(AString *userAgent) {
988    userAgent->clear();
989    userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android ");
990
991#if (PROPERTY_VALUE_MAX < 8)
992#error "PROPERTY_VALUE_MAX must be at least 8"
993#endif
994
995    char value[PROPERTY_VALUE_MAX];
996    property_get("ro.build.version.release", value, "Unknown");
997    userAgent->append(value);
998    userAgent->append(")\r\n");
999}
1000
1001void ARTSPConnection::addUserAgent(AString *request) const {
1002    // Find the boundary between headers and the body.
1003    ssize_t i = request->find("\r\n\r\n");
1004    CHECK_GE(i, 0);
1005
1006    request->insert(mUserAgent, i + 2);
1007}
1008
1009}  // namespace android
1010