uxplayer/lib/fcup_request.h
2025-05-03 17:09:23 +08:00

113 lines
4.9 KiB
C

/*
* Copyright (c) 2022 fduncanh
* All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
/* this file is part of raop.c via http_handlers.h and should not be included in any other file */
//produces the fcup request plist in xml format as a null-terminated string
char *create_fcup_request(const char *url, int request_id, const char *client_session_id, int *datalen) {
char *plist_xml = NULL;
/* values taken from apsdk-public; */
/* these seem to be arbitrary choices */
const int sessionID = 1;
const int FCUP_Response_ClientInfo = 1;
const int FCUP_Response_ClientRef = 40030004;
/* taken from a working AppleTV? */
const char User_Agent[] = "AppleCoreMedia/1.0.0.11B554a (Apple TV; U; CPU OS 7_0_4 like Mac OS X; en_us";
plist_t req_root_node = plist_new_dict();
plist_t session_id_node = plist_new_uint((int64_t) sessionID);
plist_dict_set_item(req_root_node, "sessionID", session_id_node);
plist_t type_node = plist_new_string("unhandledURLRequest");
plist_dict_set_item(req_root_node, "type", type_node);
plist_t fcup_request_node = plist_new_dict();
plist_t client_info_node = plist_new_uint(FCUP_Response_ClientInfo);
plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientInfo", client_info_node);
plist_t client_ref_node = plist_new_uint((int64_t) FCUP_Response_ClientRef);
plist_dict_set_item(fcup_request_node, "FCUP_Response_ClientRef", client_ref_node);
plist_t request_id_node = plist_new_uint((int64_t) request_id);
plist_dict_set_item(fcup_request_node, "FCUP_Response_RequestID", request_id_node);
plist_t url_node = plist_new_string(url);
plist_dict_set_item(fcup_request_node, "FCUP_Response_URL", url_node);
plist_t session_id1_node = plist_new_uint((int64_t) sessionID);
plist_dict_set_item(fcup_request_node, "sessionID", session_id1_node);
plist_t fcup_response_header_node = plist_new_dict();
plist_t playback_session_id_node = plist_new_string(client_session_id);
plist_dict_set_item(fcup_response_header_node, "X-Playback-Session-Id", playback_session_id_node);
plist_t user_agent_node = plist_new_string(User_Agent);
plist_dict_set_item(fcup_response_header_node, "User-Agent", user_agent_node);
plist_dict_set_item(fcup_request_node, "FCUP_Response_Headers", fcup_response_header_node);
plist_dict_set_item(req_root_node, "request", fcup_request_node);
uint32_t uint_val;
plist_to_xml(req_root_node, &plist_xml, &uint_val);
*datalen = (int) uint_val;
plist_free(req_root_node);
assert(plist_xml[*datalen] == '\0');
return plist_xml; //needs to be freed after use
}
int fcup_request(void *conn_opaque, const char *media_url, const char *client_session_id, int request_id) {
raop_conn_t *conn = (raop_conn_t *) conn_opaque;
int datalen = 0;
int requestlen;
int socket_fd = httpd_get_connection_socket_by_type(conn->raop->httpd, CONNECTION_TYPE_PTTH, 1);
logger_log(conn->raop->logger, LOGGER_DEBUG, "fcup_request send socket = %d", socket_fd);
/* create xml plist request data */
char *plist_xml = create_fcup_request(media_url, request_id, client_session_id, &datalen);
/* use http_response tools for creating the reverse http request */
http_response_t *request = http_response_create();
http_response_reverse_request_init(request, "POST", "/event", "HTTP/1.1");
http_response_add_header(request, "X-Apple-Session-ID", client_session_id);
http_response_add_header(request, "Content-Type", "text/x-apple-plist+xml");
http_response_finish(request, plist_xml, datalen);
free(plist_xml);
const char *http_request = http_response_get_data(request, &requestlen);
int send_len = send(socket_fd, http_request, requestlen, 0);
if (send_len < 0) {
int sock_err = SOCKET_GET_ERROR();
logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request: send error %d:%s\n",
sock_err, strerror(sock_err));
http_response_destroy(request);
/* shut down connection? */
return -1;
}
if (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG) {
char *request_str = utils_data_to_text(http_request, requestlen);
logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", request_str);
free (request_str);
}
http_response_destroy(request);
logger_log(conn->raop->logger, LOGGER_DEBUG,"fcup_request: send sent Request of %d bytes from socket %d\n",
send_len, socket_fd);
return 0;
}