From bf0ea2c956fd7a2ed33dd36da2a04594120e0c6b Mon Sep 17 00:00:00 2001 From: Jinkun Jang Date: Wed, 13 Mar 2013 01:55:17 +0900 Subject: [PATCH] Tizen 2.1 base --- AUTHORS | 2 + CMakeLists.txt | 23 + LICENSE.APLv2 | 202 ++ NOTICE | 3 + download-provider-service | 9 + download-provider-service.service.in | 4 + download-provider.manifest | 21 + download-provider.pc.in | 9 + packaging/download-provider.service | 10 + packaging/download-provider.spec | 416 +++++ res/images/Q02_Notification_Download_failed.png | Bin 0 -> 4493 bytes script/commit-template | 10 + src/CMakeLists.txt | 69 + src/agent/CMakeLists.txt | 86 + src/agent/download-agent-basic.c | 418 +++++ src/agent/download-agent-client-mgr.c | 634 +++++++ src/agent/download-agent-debug.c | 205 +++ src/agent/download-agent-dl-info-util.c | 500 +++++ src/agent/download-agent-dl-mgr.c | 314 ++++ src/agent/download-agent-encoding.c | 282 +++ src/agent/download-agent-file.c | 1244 +++++++++++++ src/agent/download-agent-http-mgr.c | 1796 ++++++++++++++++++ src/agent/download-agent-http-misc.c | 64 + src/agent/download-agent-http-msg-handler.c | 1343 ++++++++++++++ src/agent/download-agent-http-queue.c | 395 ++++ src/agent/download-agent-interface.c | 237 +++ src/agent/download-agent-mime-util.c | 405 ++++ src/agent/download-agent-plugin-conf.c | 167 ++ src/agent/download-agent-plugin-libsoup.c | 961 ++++++++++ src/agent/download-agent-utils-dl-id-history.c | 71 + src/agent/download-agent-utils.c | 292 +++ src/agent/include/download-agent-basic.h | 29 + src/agent/include/download-agent-client-mgr.h | 90 + src/agent/include/download-agent-debug.h | 94 + src/agent/include/download-agent-defs.h | 89 + src/agent/include/download-agent-dl-info-util.h | 254 +++ src/agent/include/download-agent-dl-mgr.h | 33 + src/agent/include/download-agent-encoding.h | 27 + src/agent/include/download-agent-file.h | 57 + src/agent/include/download-agent-http-mgr.h | 47 + src/agent/include/download-agent-http-misc.h | 47 + .../include/download-agent-http-msg-handler.h | 120 ++ src/agent/include/download-agent-http-queue.h | 125 ++ src/agent/include/download-agent-interface.h | 473 +++++ src/agent/include/download-agent-mime-util.h | 34 + src/agent/include/download-agent-plugin-conf.h | 28 + .../include/download-agent-plugin-http-interface.h | 51 + src/agent/include/download-agent-plugin-libsoup.h | 75 + src/agent/include/download-agent-pthread.h | 147 ++ src/agent/include/download-agent-type.h | 35 + .../include/download-agent-utils-dl-id-history.h | 35 + src/agent/include/download-agent-utils.h | 74 + src/download-provider-da-interface.c | 606 ++++++ src/download-provider-db.c | 1319 ++++++++++++++ src/download-provider-main.c | 320 ++++ src/download-provider-network.c | 288 +++ src/download-provider-notification.c | 437 +++++ src/download-provider-pid.c | 34 + src/download-provider-request.c | 720 ++++++++ src/download-provider-slots.c | 174 ++ src/download-provider-socket.c | 213 +++ src/download-provider-thread-queue.c | 331 ++++ src/download-provider-thread-request.c | 1926 ++++++++++++++++++++ src/include/download-provider-config.h | 45 + src/include/download-provider-da-interface.h | 29 + src/include/download-provider-db.h | 174 ++ src/include/download-provider-log.h | 63 + src/include/download-provider-network.h | 28 + src/include/download-provider-notification.h | 27 + src/include/download-provider-pthread.h | 96 + src/include/download-provider-queue.h | 23 + src/include/download-provider-request.h | 46 + src/include/download-provider-slots.h | 88 + src/include/download-provider-socket.h | 40 + src/include/download-provider.h | 154 ++ 75 files changed, 19337 insertions(+) create mode 100644 AUTHORS create mode 100755 CMakeLists.txt create mode 100644 LICENSE.APLv2 create mode 100644 NOTICE create mode 100644 download-provider-service create mode 100644 download-provider-service.service.in create mode 100755 download-provider.manifest create mode 100644 download-provider.pc.in create mode 100644 packaging/download-provider.service create mode 100755 packaging/download-provider.spec create mode 100644 res/images/Q02_Notification_Download_failed.png create mode 100644 script/commit-template create mode 100755 src/CMakeLists.txt create mode 100755 src/agent/CMakeLists.txt create mode 100755 src/agent/download-agent-basic.c create mode 100755 src/agent/download-agent-client-mgr.c create mode 100755 src/agent/download-agent-debug.c create mode 100755 src/agent/download-agent-dl-info-util.c create mode 100755 src/agent/download-agent-dl-mgr.c create mode 100755 src/agent/download-agent-encoding.c create mode 100755 src/agent/download-agent-file.c create mode 100755 src/agent/download-agent-http-mgr.c create mode 100755 src/agent/download-agent-http-misc.c create mode 100755 src/agent/download-agent-http-msg-handler.c create mode 100755 src/agent/download-agent-http-queue.c create mode 100755 src/agent/download-agent-interface.c create mode 100755 src/agent/download-agent-mime-util.c create mode 100755 src/agent/download-agent-plugin-conf.c create mode 100755 src/agent/download-agent-plugin-libsoup.c create mode 100755 src/agent/download-agent-utils-dl-id-history.c create mode 100755 src/agent/download-agent-utils.c create mode 100755 src/agent/include/download-agent-basic.h create mode 100755 src/agent/include/download-agent-client-mgr.h create mode 100755 src/agent/include/download-agent-debug.h create mode 100755 src/agent/include/download-agent-defs.h create mode 100755 src/agent/include/download-agent-dl-info-util.h create mode 100755 src/agent/include/download-agent-dl-mgr.h create mode 100755 src/agent/include/download-agent-encoding.h create mode 100755 src/agent/include/download-agent-file.h create mode 100755 src/agent/include/download-agent-http-mgr.h create mode 100755 src/agent/include/download-agent-http-misc.h create mode 100755 src/agent/include/download-agent-http-msg-handler.h create mode 100755 src/agent/include/download-agent-http-queue.h create mode 100755 src/agent/include/download-agent-interface.h create mode 100755 src/agent/include/download-agent-mime-util.h create mode 100755 src/agent/include/download-agent-plugin-conf.h create mode 100755 src/agent/include/download-agent-plugin-http-interface.h create mode 100755 src/agent/include/download-agent-plugin-libsoup.h create mode 100755 src/agent/include/download-agent-pthread.h create mode 100755 src/agent/include/download-agent-type.h create mode 100755 src/agent/include/download-agent-utils-dl-id-history.h create mode 100755 src/agent/include/download-agent-utils.h create mode 100755 src/download-provider-da-interface.c create mode 100755 src/download-provider-db.c create mode 100755 src/download-provider-main.c create mode 100755 src/download-provider-network.c create mode 100755 src/download-provider-notification.c create mode 100755 src/download-provider-pid.c create mode 100755 src/download-provider-request.c create mode 100755 src/download-provider-slots.c create mode 100755 src/download-provider-socket.c create mode 100755 src/download-provider-thread-queue.c create mode 100755 src/download-provider-thread-request.c create mode 100755 src/include/download-provider-config.h create mode 100755 src/include/download-provider-da-interface.h create mode 100755 src/include/download-provider-db.h create mode 100755 src/include/download-provider-log.h create mode 100755 src/include/download-provider-network.h create mode 100755 src/include/download-provider-notification.h create mode 100755 src/include/download-provider-pthread.h create mode 100755 src/include/download-provider-queue.h create mode 100755 src/include/download-provider-request.h create mode 100755 src/include/download-provider-slots.h create mode 100755 src/include/download-provider-socket.h create mode 100755 src/include/download-provider.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..707ec6f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Kwangmin Bang +Jungki Kwak diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..51cd43a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + + +ADD_SUBDIRECTORY(src/agent) +ADD_SUBDIRECTORY(src/) + +PROJECT(download-provider C) + +CONFIGURE_FILE(download-provider.pc.in download-provider.pc @ONLY) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-provider.pc DESTINATION lib/pkgconfig) + +INSTALL(PROGRAMS download-provider-service DESTINATION /etc/rc.d/init.d) + +CONFIGURE_FILE(download-provider-service.service.in download-provider-service.service @ONLY) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-provider-service.service DESTINATION /usr/share/dbus-1/services) + +# install images +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/res/images/ DESTINATION share/download-provider/ + FILES_MATCHING + PATTERN "*.png" + ) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.APLv2 share/license/${PROJECT_NAME}) +INSTALL(FILES share/license/${PROJECT_NAME} DESTINATION share/license) diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..6220b80 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE file for Apache License terms and conditions. diff --git a/download-provider-service b/download-provider-service new file mode 100644 index 0000000..40fa113 --- /dev/null +++ b/download-provider-service @@ -0,0 +1,9 @@ +#!/bin/sh + +### A script for running download daemon in booting time. +#dlogutil -v long -f /var/log/download-capi.log -r 100 -n 10 TIZEN_N_URL_DOWNLOAD & +#dlogutil -v long -f /var/log/download-daemon.log -r 1000 -n 10 download-provider DownloadAgent & + +if [ -x /usr/bin/download-provider ]; then +/usr/bin/download-provider & +fi diff --git a/download-provider-service.service.in b/download-provider-service.service.in new file mode 100644 index 0000000..198f496 --- /dev/null +++ b/download-provider-service.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.download-provider +Exec=/bin/sh -c 'if [ -x /usr/bin/download-provider ]; then /usr/bin/download-provider service & fi' +User=root diff --git a/download-provider.manifest b/download-provider.manifest new file mode 100755 index 0000000..b2cca84 --- /dev/null +++ b/download-provider.manifest @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/download-provider.pc.in b/download-provider.pc.in new file mode 100644 index 0000000..5ec2085 --- /dev/null +++ b/download-provider.pc.in @@ -0,0 +1,9 @@ +# Package Information (download-provider daemon style) + +prefix=/usr +includedir=${prefix}/include + +Name: Download Provider +Description: Download Provider Daemon +Version: @VERSION@ +Cflags: -I${includedir}/download-provider diff --git a/packaging/download-provider.service b/packaging/download-provider.service new file mode 100644 index 0000000..da1ea5f --- /dev/null +++ b/packaging/download-provider.service @@ -0,0 +1,10 @@ +[Unit] +Description=Download provider service + +[Service] +Type=simple +ExecStart=/usr/bin/download-provider + +[Install] +WantedBy=tizen-middleware.target + diff --git a/packaging/download-provider.spec b/packaging/download-provider.spec new file mode 100755 index 0000000..9fca5cc --- /dev/null +++ b/packaging/download-provider.spec @@ -0,0 +1,416 @@ + +Name: download-provider +Summary: download the contents in background. +Version: 1.0.2 +Release: 15 +Group: Development/Libraries +License: Apache License, Version 2.0 +Source0: %{name}-%{version}.tar.gz +Source1: download-provider.service +Requires(post): /usr/bin/sqlite3 +BuildRequires: cmake +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gobject-2.0) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(libsoup-2.4) +BuildRequires: pkgconfig(xdgmime) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(db-util) +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(bundle) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(capi-network-connection) +BuildRequires: pkgconfig(notification) +BuildRequires: pkgconfig(appsvc) +BuildRequires: pkgconfig(dbus-1) + +%description +Description: download the contents in background + +%package devel +Summary: download-provider +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Description: download the contents in background (developement files) + +%prep +%setup -q + +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} + +%build +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install +mkdir -p %{buildroot}/usr/share/license +mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc3.d +ln -s %{_sysconfdir}/rc.d/init.d/download-provider-service %{buildroot}%{_sysconfdir}/rc.d/rc3.d/S70download-provider-service +mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc5.d +ln -s %{_sysconfdir}/rc.d/init.d/download-provider-service %{buildroot}%{_sysconfdir}/rc.d/rc5.d/S70download-provider-service + +mkdir -p %{buildroot}%{_libdir}/systemd/user/tizen-middleware.target.wants +install %{SOURCE1} %{buildroot}%{_libdir}/systemd/user/ +ln -s ../download-provider.service %{buildroot}%{_libdir}/systemd/user/tizen-middleware.target.wants/ + +mkdir -p %{buildroot}/opt/data/download-provider +mkdir -p %{buildroot}/opt/usr/dbspace/ +if [ ! -f %{buildroot}/opt/usr/dbspace/.download-provider.db ]; +then +sqlite3 %{buildroot}/opt/usr/dbspace/.download-provider.db 'PRAGMA journal_mode=PERSIST; +PRAGMA foreign_keys=ON; +CREATE TABLE logging +( +id INTEGER UNIQUE PRIMARY KEY, +state INTEGER DEFAULT 0, +errorcode INTEGER DEFAULT 0, +startcount INTEGER DEFAULT 0, +packagename TEXT DEFAULT NULL, +createtime DATE, +accesstime DATE +); + +CREATE TABLE requestinfo +( +id INTEGER UNIQUE PRIMARY KEY, +auto_download BOOLEAN DEFAULT 0, +state_event BOOLEAN DEFAULT 0, +progress_event BOOLEAN DEFAULT 0, +network_type TINYINT DEFAULT 0, +filename TEXT DEFAULT NULL, +destination TEXT DEFAULT NULL, +url TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE downloadinfo +( +id INTEGER UNIQUE PRIMARY KEY, +http_status INTEGER DEFAULT 0, +content_size UNSIGNED BIG INT DEFAULT 0, +mimetype VARCHAR(64) DEFAULT NULL, +content_name TEXT DEFAULT NULL, +saved_path TEXT DEFAULT NULL, +tmp_saved_path TEXT DEFAULT NULL, +etag TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE httpheaders +( +id INTEGER NOT NULL, +header_field TEXT DEFAULT NULL, +header_data TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE notification +( +id INTEGER UNIQUE PRIMARY KEY, +noti_enable BOOLEAN DEFAULT 0, +extra_key TEXT DEFAULT NULL, +extra_data TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX requests_index ON logging (id, state, errorcode, packagename, createtime, accesstime); +' +fi + +%files +%defattr(-,root,root,-) +%dir %attr(0775,root,app) /opt/data/download-provider +%manifest download-provider.manifest +/usr/share/download-provider/*.png +%{_libdir}/libdownloadagent2.so.0.0.1 +%{_libdir}/libdownloadagent2.so +%{_libdir}/systemd/user/download-provider.service +%{_libdir}/systemd/user/tizen-middleware.target.wants/download-provider.service +%{_bindir}/download-provider +%{_sysconfdir}/rc.d/init.d/download-provider-service +%{_sysconfdir}/rc.d/rc3.d/S70download-provider-service +%{_sysconfdir}/rc.d/rc5.d/S70download-provider-service +/usr/share/license/%{name} +/usr/share/dbus-1/services/download-provider-service.service +%attr(660,root,app) /opt/usr/dbspace/.download-provider.db +%attr(660,root,app) /opt/usr/dbspace/.download-provider.db-journal + +%files devel +%defattr(-,root,root,-) +%{_libdir}/libdownloadagent2.so.0.0.1 +%{_libdir}/libdownloadagent2.so +%{_bindir}/download-provider +%{_includedir}/download-provider/download-provider.h +%{_libdir}/pkgconfig/download-provider.pc + +%changelog +* Fri Feb 01 2013 Kwangmin Bang +- [smack] manifest for booting-script +- [prevent defect] Dereference after null check + +* Thu Jan 31 2013 Kwangmin Bang +- add the state which means is connecting to remote + +* Wed Jan 30 2013 Kwangmin Bang +- [smack] labeling for booting script +- [prevent defect] Dereference before/after null check + +* Tue Jan 29 2013 Jungki Kwak +- Add to check invalid state when setting the callback +- Resolve prevent defects +- Remove the dependancy with unnecessary package +- Remove the dependancy with dbus-glib +- Modify smack label of shared library + +* Mon Jan 28 2013 Kwangmin Bang +- recognize who launch me by parameter of main function +- ready the socket first before initializing dbus service + +* Fri Jan 25 2013 Jungki Kwak +- Resolve compile error with GCC-4.7 +- Convert error value in case the download start is failed +- Add to check the user's install direcoty +- Modify the name of license and add notice file +- Call sqlite3 in install section in spec file +- Terminate Only when support DBUS-Activation + +* Fri Jan 11 2013 Kwangmin Bang +- crash when terminated after clear requests all in timeout +- Change the flow of playready download +- audo_download need start_time + +* Tue Jan 08 2013 Jungki Kwak +- Resolve prevent defects +- [Title] Fixed a bug that the icon of txt file can't be recognized and the txt file can't be opend +- Modify to check returned value of sqlite API +- Show error string in case of download CAPI failure + +* Fri Dec 21 2012 Jungki Kwak +- Send file_size from memory first +- Add error code about libsoup error message +- Change the session time-out to 60 seconds +- Remove duplicated dlog message +- Modify a bug to get mime type from db +- Modify license information at spec file +- Resolve prevent defects +- Add missed existed db file when creaing db table +- The temporary file is not used any more +- Use SQL without MUTEX LOCK +- Support ECHO command +- Reduce SQL query to prevent SQL Error +- replace snprintf to sqlite3_mprintf for prepare sqlite3_stmt + +* Fri Dec 14 2012 Kwangmin Bang +- add credential in request structure +- change to ORDER BY createtime ASC from DESC in getting old list +- Resolve prevent defects +- Add boiler plates +- Remove old source codes +- apply the authentication by packagename for all commands +- fix memory leak in error case of DB query +- Add micro seconds value for temporary file name +- apply the limit count and sorting in SQL query +- do not load PAUSED request in booting time +- Add define statement for OMA DRM +- call IPC for sending event at the tail of function +- maintain DB info in DESTROY command +- Separate DB Table, reduce the amount of memory + +* Fri Dec 07 2012 Kwangmin Bang +- fix the crash when terminating by itself + +* Thu Dec 06 2012 Kwangmin Bang +- Fix the crash when accessing the history + +* Fri Nov 30 2012 Kwangmin Bang +- support DBUS-activation +- apply int64 for file size +- most API support ID_NOT_FOUND errorcode +- fix crash and memory leak in SET_EXTRA_PARAM +- fix memory leak in HTTP_HEADER case +- remove servicedata column +- Add to update the last received size + +* Tue Nov 27 2012 Jungki Kwak +- Change to parse the content name +- Change the policy for temporary file name +- Change to get etag when download is failed +- DP_ERROR_ID_NOT_FOUND for set_url COMMAND +- new common sql function for getting/setting a column +- Add operation when registering notification +- disable flexible timeout, use 60 sec for timeout +- allow set_url even if unloaded from memory +- Add functions for notification extra param +- Block to can not re-use the request is completed +- send errorcode to client why failed to create request +- unload the request which not setted auto_download when client is terminated +- call da_agent_cancel after free slot +- Add debug message for event queue of client mgr +- Modify abort flow in case of deinit + +* Fri Nov 16 2012 Jungki Kwak +- Resolve a bug about execute option API of ongoing notification +- load all request in queue in booting time +- stay on the memory although no client +- Dead default in switch +- Enable to register notification message +- Add to add, get and remove http header values +- Change functions to pause and resume which is based on download ID +- Unchecked return value +- clear history if registered before 48 hours or total of histories is over 1000. +- stay on memory if client is connected with provider +- advance auto-download feature +- remove __thread keyword for sqlite handle +- update regdate after copy history from queue +- concede network is connected although detail get API is failed in connection state changed callback. +- read pid from client when no support SO_PEERCRED +- wake up Queue thread only when exist ready request +- Add functions to save and submit http status code to client +- detect network changes using connection callback + +* Thu Nov 08 2012 Jungki Kwak +- In Offline state, do not search queue +- Apply changed enum value of connection CAPI + +* Wed Oct 31 2012 Jungki Kwak +- change the limitaiton count can download at once +- support Cancel after Pause +- Change the installation directory according to guide +- deal as error if write() return 0 + +* Mon Oct 28 2012 Kwangmin Bang +- increase the timout of socket +- enhance socket update feature + +* Thu Oct 25 2012 Kwangmin Bang +- prevent defects +- change the limitation of length +- improve the performance logging to DB +- check packagename for resume call +- refresh FD_SET when only new socket is accepted +- Fix the socket error when a client is crashed + +* Tue Oct 23 2012 Kwangmin Bang +- terminate daemon in main socket error +- apply reading timeout for socket + +* Mon Oct 22 2012 Jungki Kwak +- Resolve the lockup when client app is crashed +- Check the state before starting download +- Support paused callback + +* Fri Oct 19 2012 Kwangmin Bang + - replace ID based download-provider to Major Process + +* Tue Oct 16 2012 Jungki Kwak +- Install LICENSE file + +* Fri Oct 12 2012 Jungki Kwak +- Install LICENSE file + +* Thu Oct 11 2012 Jungki Kwak +- Use mutex when calling xdgmime API +- Do not use fsync function + +* Mon Oct 08 2012 Kwangmin Bang +- Fix the crash when come many stop with terminating App + +* Fri Sep 28 2012 Kwangmin Bang +- Add exception code about content-dispostion +- wait to free till callback is finished + +* Mon Sep 24 2012 Jungki Kwak +- Add to define for db an library in manifest file + +* Fri Sep 21 2012 Kwangmin Bang +- prevent free slot till called notify_cb +- socket descriptor can be zero + +* Fri Sep 21 2012 Jungki Kwak +- Apply manifest file in which the domain is defined +- Change way to pass extension data + +* Fri Sep 14 2012 Kwangmin Bang +- fix the crash by assert of pthread_mutex_lock +- NULL-check before free +- guarantee instant download for pended requests +- add descriptor +- fix the memory leak in error case of ipc_receive_request_msg +- guarantee one instant download per app +- call pthread_exit to terminate the currently running thread +- Remove pthread_cancel function + +* Fri Sep 07 2012 Kwangmin Bang +- add LICENSE +- Add to search download id from history db + +* Thu Sep 06 2012 Kwangmin Bang +- start to download again even if already finished +- change thread style +- arrange the request priority +- change data type +- wait till getting the response from client + +* Mon Sep 03 2012 Kwangmin Bang +- fix timeout error + +* Mon Sep 03 2012 Kwangmin Bang +- free slot after getting event from url-download +- fix INTEGER OVERFLOW + +* Thu Aug 30 2012 Kwangmin Bang +- initialize mutex for auto-redownloading +- support Pause/Resume with new connection +- fix the memory leak + +* Mon Aug 27 2012 Kwangmin Bang +- Change the ownership of downloaded file +- Add detached option when pthread is created +- fix the failure getting history info from database +- fix first timeout takes a long time +- fix wrong checking of network status +- fix the crash by double free +- divide log level +- Resolve prevent defects for agent module +- Resolve a bug to join domain in case of playready + +* Tue Aug 23 2012 Kwangmin Bang +- event thread does not deal in some state +- fix the lockup by mutex and the crash by invaild socket event + +* Tue Aug 22 2012 Jungki Kwak +- Fix the crash when use notification +- One thread model for socket +- Fix the defects found by prevent tool +- Remove mutex lock/unlock in case of invalid id +- Support the status of download in case of getting new connection with requestid +- Clear db and register notification when stopped the download +- Update notification function +- Enable to set the defined file name by user + +* Tue Aug 17 2012 Jungki Kwak +- Enable to use destination path +- Add to handle invalid id + +* Tue Aug 16 2012 Jungki Kwak +- Change socket close timing + +* Mon Aug 13 2012 Kwangmin Bang +- Disable default dlog in launching script. + +* Tue Aug 09 2012 Jungki Kwak +- The function to init dbus glib is removed + +* Tue Aug 08 2012 Jungki Kwak +- The function to init dbus glib is added for connection network CAPI + +* Tue Aug 07 2012 Jungki Kwak +- Change the name of temp direcoty. +- When add requestinfo to slot, save it to DB. + +* Mon Aug 06 2012 Jungki Kwak +- Initial version is updated. + diff --git a/res/images/Q02_Notification_Download_failed.png b/res/images/Q02_Notification_Download_failed.png new file mode 100644 index 0000000000000000000000000000000000000000..04ec4f07c9dee5e505c92d7ade14dd5c33ce85af GIT binary patch literal 4493 zcmV;85pwQ{P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000KKNklulY8RA(rqoU|RzWKWfE!@NTe+Ofbw_u%&dQAcPfpbX00960Vxn8lAS+i8 zlgvm843ct!00RSqBN`uX(wfh}!0?`df#CyGC;-Zzk1us3GB7YCGB7ZlhYGD{U|?7P z6~pU1WJQVS8c;O-`t_>;DRnP^ruqf2K>79SR|Bc39=lC!Y-|k#QhWnNiIyx)00000 z|Nmklw+^6dy+M=UK+0MFp_l<5rdHT95?gmsE22LTTRf0#88qj>umA%CgCLp`2dMae zN@{C(b%B&M-(xXsKU5q!D^nDViBLllamY6kkjHNstnfjrUKgMl3UfpQay&xyHBhT0 zKri{Ini~I;fJwKEh?13&*pinjO$TxS00030|6&?;h42Wp2wGJb5UNTDRVl>!=sYyf zG|)QeU;w!LwX>GI}#^UXedd(^pbPdua!J5E*r;A-DX!ucvs?X9AiIz zPx!U$K{4i{&8b1-T1_^}ZZCzR{-cZYSgYU5mo#RDT6j=rN`C3$O5~k${#`6#3PLE2 z3vg6q63j^CU%>{Irs<$`2`D&S^qO+t&Vq1YKYnvRGy#Bzr*sf7^&Sev+i@{oV797$ zri-h6!Wcs#VlKVyCPZu;-~>#onWKSkPisQNe2bl`l<&L6&aHu==CAf9JzB7A`h~cB z%Z=7mhAb2vm|1jdN5=w1bxyg$o`=zgF5@9a@6KjQouw3t$)bnqkw45i1)Ko}nDk@qCt6dGz0E>ea*4VxST5gs1K z{5vKouqVu)>)GTOh=CZ0ff%TU@&Nz<|Nrcr(UF2M5QcZ|AMm>0x>5`QX+#xNZt_rcd~QSc{OK(q&7(E-FIAN;6_yLp-D{5))r56hk{fg-1P|%) za;&BT?FM5&nogfItaRFeMqYjfNWb+#!?Itb>!4g4ad;beo}{w2A0SN>nx6-ryLUC1 z-~cJn(b$pt<#fIqTe;E`xI8G8#!DV6F3-n}+Tq+wk;VC4pvje16fH+^4{?t5#c zUO-Yx1xl%lhO>Pzitq?lZg?BA15TSu+|C8-WkNvm$xh~`XUj5CA2@BWdFiP7~bx#N{Sx{t8Q2mrG{{<*Cb~dI9PyXqLnLfScKrUeGxL^safH0 z>&)&r7=s%et|Ja}8z*D-u&{(Qm$6vF;SOmkazx8lX zJToj?a1zoPOO0(ZBPSqf5J&nuY_cJugQZdr&%(0Va;m}CW`9c((b2+#$im$P7Qd?t z));OrnA%`V$px=qg;dVohA#c*_yVa?PUR$BzN8<8RTY5L8eB+6?InF`NvB#lL6?q` zPmmx$0;Db#p4+rw4@n4-=~>s1$`uj59HFjVm$}fADp + +#include "download-agent-basic.h" +#include "download-agent-debug.h" +#include "download-agent-client-mgr.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-pthread.h" +#include "download-agent-file.h" + +static void* __thread_start_download(void* data); +void __thread_clean_up_handler_for_start_download(void *arg); + +static da_result_t __make_source_info_basic_download( + stage_info *stage, + client_input_t *client_input); +static da_result_t __download_content(stage_info *stage); + +da_result_t start_download(const char *url , int *dl_id) +{ + DA_LOG_FUNC_START(Default); + return start_download_with_extension(url, dl_id, NULL); +} + +da_result_t start_download_with_extension( + const char *url, + int *dl_id, + extension_data_t *extension_data) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = 0; + const char **request_header = DA_NULL; + const char *install_path = DA_NULL; + const char *file_name = DA_NULL; + const char *etag = DA_NULL; + const char *temp_file_path = DA_NULL; + int request_header_count = 0; + void *user_data = DA_NULL; + client_input_t *client_input = DA_NULL; + client_input_basic_t *client_input_basic = DA_NULL; + download_thread_input *thread_info = DA_NULL; + pthread_attr_t thread_attr; + + DA_LOG_FUNC_START(Default); + + if (extension_data) { + request_header = extension_data->request_header; + if (extension_data->request_header_count) + request_header_count = extension_data->request_header_count; + install_path = extension_data->install_path; + file_name = extension_data->file_name; + user_data = extension_data->user_data; + etag = extension_data->etag; + temp_file_path = extension_data->temp_file_path; + } + + ret = get_available_slot_id(&slot_id); + if (DA_RESULT_OK != ret) + return ret; + + *dl_id = GET_DL_ID(slot_id); + + client_input = (client_input_t *)calloc(1, sizeof(client_input_t)); + if (!client_input) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + client_input->user_data = user_data; + if (install_path) { + int install_path_len = strlen(install_path); + if (install_path[install_path_len-1] == '/') + install_path_len--; + + client_input->install_path = (char *)calloc(install_path_len+1, sizeof(char)); + if (client_input->install_path) + snprintf(client_input->install_path, install_path_len+1, install_path); + } + + if (file_name) { + client_input->file_name = (char *)calloc(strlen(file_name)+1, sizeof(char)); + if (client_input->file_name) + strncpy(client_input->file_name, file_name, strlen(file_name)); + } + + if (etag) { + client_input->etag = (char *)calloc(strlen(etag)+1, sizeof(char)); + if (client_input->etag) + strncpy(client_input->etag, etag, strlen(etag)); + + } + + if (temp_file_path) { + client_input->temp_file_path = (char *)calloc(strlen(temp_file_path)+1, sizeof(char)); + if (client_input->temp_file_path) + strncpy(client_input->temp_file_path, temp_file_path, strlen(temp_file_path)); + } + + client_input_basic = &(client_input->client_input_basic); + client_input_basic->req_url = (char *)calloc(strlen(url)+1, sizeof(char)); + if(DA_NULL == client_input_basic->req_url) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(client_input_basic->req_url ,url,strlen(url)); + + if (request_header_count > 0) { + int i = 0; + client_input_basic->user_request_header = + (char **)calloc(1, sizeof(char *)*request_header_count); + if(DA_NULL == client_input_basic->user_request_header) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + for (i = 0; i < request_header_count; i++) + { + client_input_basic->user_request_header[i] = strdup(request_header[i]); + } + client_input_basic->user_request_header_count = request_header_count; + } + } + + thread_info = (download_thread_input *)calloc(1, sizeof(download_thread_input)); + if (!thread_info) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + thread_info->slot_id = slot_id; + thread_info->client_input = client_input; + } + if (pthread_attr_init(&thread_attr) != 0) { + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + goto ERR; + } + + if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0) { + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + goto ERR; + } + + if (pthread_create(&GET_DL_THREAD_ID(slot_id), &thread_attr, + __thread_start_download, thread_info) < 0) { + DA_LOG_ERR(Thread, "making thread failed.."); + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + } else { + if (GET_DL_THREAD_ID(slot_id) < 1) { + DA_LOG_ERR(Thread, "The thread start is failed before calling this"); +// When http resource is leaked, the thread ID is initialized at error handling section of thread_start_download() +// Because the thread ID is initialized, the ptrhead_detach should not be called. This is something like timing issue between threads. +// thread info and basic_dl_input is freed at thread_start_download(). And it should not returns error code in this case. + goto ERR; + } + } + DA_LOG_CRITICAL(Thread, "download thread create slot_id[%d] thread id[%lu]", + slot_id,GET_DL_THREAD_ID(slot_id)); + +ERR: + if (DA_RESULT_OK != ret) { + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + if (thread_info) { + free(thread_info); + thread_info = DA_NULL; + } + destroy_download_info(slot_id); + } + return ret; +} + +da_result_t __make_source_info_basic_download( + stage_info *stage, + client_input_t *client_input) +{ + da_result_t ret = DA_RESULT_OK; + client_input_basic_t *client_input_basic = DA_NULL; + source_info_t *source_info = DA_NULL; + source_info_basic_t *source_info_basic = DA_NULL; + + DA_LOG_FUNC_START(Default); + + if (!stage) { + DA_LOG_ERR(Default, "no stage; DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + client_input_basic = &(client_input->client_input_basic); + if (DA_NULL == client_input_basic->req_url) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + source_info_basic = (source_info_basic_t*)calloc(1, sizeof(source_info_basic_t)); + if (DA_NULL == source_info_basic) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + + source_info_basic->url = client_input_basic->req_url; + client_input_basic->req_url = DA_NULL; + + if (client_input_basic->user_request_header) { + source_info_basic->user_request_header = + client_input_basic->user_request_header; + source_info_basic->user_request_header_count = + client_input_basic->user_request_header_count; + client_input_basic->user_request_header = DA_NULL; + client_input_basic->user_request_header_count = 0; + } + + source_info = GET_STAGE_SOURCE_INFO(stage); + memset(source_info, 0, sizeof(source_info_t)); + + source_info->source_info_type.source_info_basic = source_info_basic; + + DA_LOG(Default, "BASIC HTTP STARTED: URL=%s", + source_info->source_info_type.source_info_basic->url); +ERR: + return ret; +} + +void __thread_clean_up_handler_for_start_download(void *arg) +{ + DA_LOG_CRITICAL(Default, "cleanup for thread id = %d", pthread_self()); +} + +static void *__thread_start_download(void *data) +{ + da_result_t ret = DA_RESULT_OK; + download_thread_input *thread_info = DA_NULL; + client_input_t *client_input = DA_NULL; + stage_info *stage = DA_NULL; + download_state_t download_state = 0; + + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(Thread); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL); + + thread_info = (download_thread_input*)data; + if (DA_NULL == thread_info) { + DA_LOG_ERR(Thread, "thread_info is NULL.."); + ret = DA_ERR_INVALID_ARGUMENT; + return DA_NULL; + } else { + slot_id = thread_info->slot_id; + client_input = thread_info->client_input; + + if(thread_info) { + free(thread_info); + thread_info = DA_NULL; + } + } + + pthread_cleanup_push(__thread_clean_up_handler_for_start_download, (void *)NULL); + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default, "Invalid Download ID"); + goto ERR; + } + + if (!client_input) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default, "Invalid client_input"); + goto ERR; + } + + stage = Add_new_download_stage(slot_id); + if (!stage) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + DA_LOG_ERR(Default, "STAGE ADDITION FAIL!"); + goto ERR; + } + DA_LOG(Default, "new added Stage : %p", stage); + + GET_DL_USER_DATA(slot_id) = client_input->user_data; + client_input->user_data = DA_NULL; + GET_DL_USER_INSTALL_PATH(slot_id) = client_input->install_path; + client_input->install_path = DA_NULL; + GET_DL_USER_FILE_NAME(slot_id) = client_input->file_name; + client_input->file_name = DA_NULL; + GET_DL_USER_ETAG(slot_id) = client_input->etag; + client_input->etag = DA_NULL; + GET_DL_USER_TEMP_FILE_PATH(slot_id) = client_input->temp_file_path; + client_input->temp_file_path = DA_NULL; + ret = __make_source_info_basic_download(stage, client_input); + /* to save memory */ + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + + if (ret == DA_RESULT_OK) + ret = __download_content(stage); + +ERR: + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + + if (DA_RESULT_OK == ret) { + char *installed_path = NULL; + char *etag = DA_NULL; + req_dl_info *request_info = NULL; + file_info *file_storage = NULL; + DA_LOG_CRITICAL(Default, "Whole download flow is finished."); + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + if (download_state == DOWNLOAD_STATE_ABORTED) { + DA_LOG(Default, "Abort case. Do not call client callback"); +#ifdef PAUSE_EXIT + } else if (download_state == DOWNLOAD_STATE_PAUSED) { + DA_LOG(Default, "Finish case from paused state"); + destroy_download_info(slot_id); +#endif + } else { + request_info = GET_STAGE_TRANSACTION_INFO(stage); + etag = GET_REQUEST_HTTP_HDR_ETAG(request_info); + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + installed_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + send_user_noti_and_finish_download_flow(slot_id, installed_path, + etag); + } + } else { + char *etag = DA_NULL; + req_dl_info *request_info = NULL; + request_info = GET_STAGE_TRANSACTION_INFO(stage); + DA_LOG_CRITICAL(Default, "Download Failed -Return = %d", ret); + if (request_info) { + etag = GET_REQUEST_HTTP_HDR_ETAG(request_info); + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + DA_NULL, etag, ret, get_http_status(slot_id)); + } + destroy_download_info(slot_id); + } + + pthread_cleanup_pop(0); + DA_LOG_CRITICAL(Thread, "=====thread_start_download - EXIT====="); + pthread_exit((void *)NULL); + return DA_NULL; +} + +da_result_t __download_content(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state = 0; + da_bool_t isDownloadComplete = DA_FALSE; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(Default); + + slot_id = GET_STAGE_DL_ID(stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD, stage); + + do { + stage = GET_DL_CURRENT_STAGE(slot_id); + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + DA_LOG(Default, "download_state to - [%d] ", download_state); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + + switch(download_state) { + case DOWNLOAD_STATE_NEW_DOWNLOAD: + ret = requesting_download(stage); + + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + if (download_state == DOWNLOAD_STATE_CANCELED || + download_state == DOWNLOAD_STATE_ABORTED || + download_state == DOWNLOAD_STATE_PAUSED) { + break; + } else { + if (DA_RESULT_OK == ret) { + ret = handle_after_download(stage); + } + } + break; + default: + isDownloadComplete = DA_TRUE; + break; + } + }while ((DA_RESULT_OK == ret) && (DA_FALSE == isDownloadComplete)); + + return ret; +} diff --git a/src/agent/download-agent-client-mgr.c b/src/agent/download-agent-client-mgr.c new file mode 100755 index 0000000..e4268ac --- /dev/null +++ b/src/agent/download-agent-client-mgr.c @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-file.h" + +#define IS_CLIENT_Q_HAVING_DATA(QUEUE) (QUEUE->having_data) + +static client_app_mgr_t client_app_mgr; + +static da_result_t __launch_client_thread(void); +static void *__thread_for_client_noti(void *data); +void __thread_clean_up_handler_for_client_thread(void *arg); +static void __pop_client_noti(client_noti_t **out_client_noti); + +void __client_q_goto_sleep_without_lock(void); +void __client_q_wake_up_without_lock(void); +void destroy_client_noti(client_noti_t *client_noti); + +da_result_t init_client_app_mgr() +{ + DA_LOG_FUNC_START(ClientNoti); + + if(client_app_mgr.is_init) + return DA_RESULT_OK; + + client_app_mgr.is_init = DA_TRUE; + client_app_mgr.client_app_info.client_user_agent = DA_NULL; + client_app_mgr.is_thread_init = DA_FALSE; + + return DA_RESULT_OK; +} + +da_bool_t is_client_app_mgr_init(void) +{ + return client_app_mgr.is_init; +} + +da_result_t reg_client_app( + da_client_cb_t *da_client_callback) +{ + da_result_t ret = DA_RESULT_OK; + client_queue_t *queue = DA_NULL; + client_noti_t *client_noti = DA_NULL; + + DA_LOG_FUNC_START(ClientNoti); + + memset(&(client_app_mgr.client_app_info.client_callback), + 0, sizeof(da_client_cb_t)); + memcpy(&(client_app_mgr.client_app_info.client_callback), + da_client_callback, sizeof(da_client_cb_t)); + + _da_thread_mutex_init(&(client_app_mgr.mutex_client_mgr), DA_NULL); + + /* If some noti is existed at queue, delete all */ + do { + __pop_client_noti(&client_noti); + destroy_client_noti(client_noti); + } while(client_noti != DA_NULL); + + queue = &(client_app_mgr.client_queue); + DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue); + _da_thread_mutex_init(&(queue->mutex_client_queue), DA_NULL); + _da_thread_cond_init(&(queue->cond_client_queue), DA_NULL); + + ret = __launch_client_thread(); + + return ret; +} + +da_result_t dereg_client_app(void) +{ + client_noti_t *client_noti = DA_NULL; + + DA_LOG_FUNC_START(ClientNoti); + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = DA_INVALID_ID; + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_TERMINATE; + client_noti->next = DA_NULL; + + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + if (client_app_mgr.is_thread_init != DA_TRUE) { + DA_LOG_CRITICAL(ClientNoti, "try to cancel client mgr thread id[%lu]", client_app_mgr.thread_id); + if (pthread_cancel(client_app_mgr.thread_id) < 0) { + DA_LOG_ERR(ClientNoti, "cancel thread is failed!!!"); + } + free(client_noti); + } else { + void *t_return = NULL; + DA_LOG_VERBOSE(ClientNoti, "pushing Q_CLIENT_NOTI_TYPE_TERMINATE"); + push_client_noti(client_noti); + DA_LOG_CRITICAL(Thread, "===try to join client mgr thread id[%lu]===", client_app_mgr.thread_id); + if (pthread_join(client_app_mgr.thread_id, &t_return) < 0) { + DA_LOG_ERR(Thread, "join client thread is failed!!!"); + } + DA_LOG_CRITICAL(Thread, "===thread join return[%d]===", (char*)t_return); + } + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + + /* ToDo: This clean up should be done at the end of client_thread. */ + if(client_app_mgr.client_app_info.client_user_agent) { + free(client_app_mgr.client_app_info.client_user_agent); + client_app_mgr.client_app_info.client_user_agent = DA_NULL; + } + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + client_app_mgr.is_thread_init = DA_FALSE; + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + _da_thread_mutex_destroy(&(client_app_mgr.mutex_client_mgr)); + return DA_RESULT_OK; +} + +da_result_t send_client_paused_info(int slot_id) +{ + client_noti_t *client_noti = DA_NULL; + user_paused_info_t *paused_info = DA_NULL; + download_state_t state = GET_DL_STATE_ON_ID(slot_id); + + DA_LOG_FUNC_START(ClientNoti); + + if (!GET_DL_ENABLE_PAUSE_UPDATE(slot_id)) { + DA_LOG(ClientNoti, "Do not call pause cb"); + return DA_RESULT_OK; + } + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_RESULT_OK; + } + + DA_LOG_VERBOSE(ClientNoti, "slot_id[%d]", slot_id); + if ((DOWNLOAD_STATE_PAUSED != state)) { + DA_LOG(ClientNoti, "The state is not paused. state:%d", state); + return DA_ERR_INVALID_STATE; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PAUSED_INFO; + client_noti->next = DA_NULL; + + paused_info = (user_paused_info_t *)&(client_noti->type.paused_info); + paused_info->download_id= GET_DL_ID(slot_id); + DA_LOG(ClientNoti, "pushing paused info. slot_id=%d, dl_id=%d", + slot_id, GET_DL_ID(slot_id)); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_update_progress_info ( + int slot_id, + int dl_id, + unsigned long int received_size + ) +{ + client_noti_t *client_noti = DA_NULL; + user_progress_info_t *progress_info = DA_NULL; + + //DA_LOG_FUNC_START(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PROGRESS_INFO; + client_noti->next = DA_NULL; + + progress_info = (user_progress_info_t *)&(client_noti->type.update_progress_info); + progress_info->download_id= dl_id; + progress_info->received_size = received_size; + + DA_LOG_VERBOSE(ClientNoti, "pushing received_size=%lu, slot_id=%d, dl_id=%d", + received_size, slot_id, dl_id); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_update_dl_info ( + int slot_id, + int dl_id, + char *file_type, + unsigned long int file_size, + char *tmp_saved_path, + char *pure_file_name, + char *etag, + char *extension) +{ + client_noti_t *client_noti = DA_NULL; + user_download_info_t *update_dl_info = DA_NULL; + int len = 0; + + DA_LOG_FUNC_START(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_STARTED_INFO; + client_noti->next = DA_NULL; + + update_dl_info = (user_download_info_t *)&(client_noti->type.update_dl_info); + update_dl_info->download_id = dl_id; + update_dl_info->file_size = file_size; + if (pure_file_name && extension) { + len = strlen(pure_file_name) + strlen(extension) + 1; + update_dl_info->content_name = (char *)calloc(len + 1, sizeof(char)); + if (!update_dl_info->content_name) { + free(client_noti); + return DA_ERR_FAIL_TO_MEMALLOC; + } + snprintf(update_dl_info->content_name, len + 1, "%s.%s", + pure_file_name, extension); + } + + /* These strings MUST be copied to detach __thread_for_client_noti from download_info */ + if (file_type) + update_dl_info->file_type = strdup(file_type); + + if (tmp_saved_path) + update_dl_info->tmp_saved_path = strdup(tmp_saved_path); + + if (etag) + update_dl_info->etag = strdup(etag); + DA_LOG(ClientNoti, "pushing file_size=%lu, slot_id=%d, dl_id=%d", + file_size, slot_id, dl_id); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_finished_info ( + int slot_id, + int dl_id, + char *saved_path, + char *etag, + int error, + int http_status + ) +{ + client_noti_t *client_noti = DA_NULL; + user_finished_info_t *finished_info = DA_NULL; + + DA_LOG_FUNC_START(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_FINISHED_INFO; + client_noti->next = DA_NULL; + + finished_info = (user_finished_info_t *)&(client_noti->type.finished_info); + finished_info->download_id = dl_id; + finished_info->err = error; + finished_info->http_status = http_status; + + if (saved_path) { + finished_info->saved_path = strdup(saved_path); + DA_LOG(ClientNoti, "saved path=%s", saved_path); + } + if (etag) { + finished_info->etag = strdup(etag); + DA_LOG(ClientNoti, "pushing finished info. etag[%s]", etag); + } + DA_LOG(ClientNoti, "user_data=%p", client_noti->user_data); + DA_LOG(ClientNoti, "http_status=%d", http_status); + DA_LOG(ClientNoti, "pushing slot_id=%d, dl_id=%d err=%d", slot_id, dl_id, error); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t __launch_client_thread(void) +{ + pthread_t thread_id = DA_NULL; + + DA_LOG_FUNC_START(Thread); + + if (pthread_create(&thread_id,DA_NULL,__thread_for_client_noti,DA_NULL) < 0) { + DA_LOG_ERR(Thread, "making thread failed.."); + return DA_ERR_FAIL_TO_CREATE_THREAD; + } + DA_LOG(Thread, "client mgr thread id[%d]", thread_id); + client_app_mgr.thread_id = thread_id; + return DA_RESULT_OK; +} + +void destroy_client_noti(client_noti_t *client_noti) +{ + if (client_noti) { + if (client_noti->noti_type == Q_CLIENT_NOTI_TYPE_STARTED_INFO) { + user_download_info_t *update_dl_info = DA_NULL; + update_dl_info = (user_download_info_t*)&(client_noti->type.update_dl_info); + if (update_dl_info->file_type) { + free(update_dl_info->file_type); + update_dl_info->file_type = DA_NULL; + } + if (update_dl_info->tmp_saved_path) { + free(update_dl_info->tmp_saved_path); + update_dl_info->tmp_saved_path = DA_NULL; + } + if (update_dl_info->etag) { + free(update_dl_info->etag); + update_dl_info->etag = DA_NULL; + } + } else if (client_noti->noti_type == + Q_CLIENT_NOTI_TYPE_FINISHED_INFO) { + user_finished_info_t *finished_info = DA_NULL; + finished_info = (user_finished_info_t*) + &(client_noti->type.finished_info); + if (finished_info->saved_path) { + free(finished_info->saved_path); + finished_info->saved_path = DA_NULL; + } + if (finished_info->etag) { + free(finished_info->etag); + finished_info->etag = DA_NULL; + } + } + free(client_noti); + } +} + + +void push_client_noti(client_noti_t *client_noti) +{ + client_queue_t *queue = DA_NULL; + client_noti_t *head = DA_NULL; + client_noti_t *pre = DA_NULL; + client_noti_t *cur = DA_NULL; + + /* DA_LOG_FUNC_START(ClientNoti); */ + + queue = &(client_app_mgr.client_queue); + _da_thread_mutex_lock (&(queue->mutex_client_queue)); + + head = queue->client_q_head; + if (!head) { + queue->client_q_head = client_noti; + } else { + cur = head; + while (cur->next) { + pre = cur; + cur = pre->next; + } +#if 0 + if (cur->noti_type == Q_CLIENT_NOTI_TYPE_PROGRESS_INFO) { + /* For UI performance. If the update noti info is existed at queue, + replace it with new update noti info */ + if (cur->slot_id == client_noti->slot_id) { + /* DA_LOG(ClientNoti, "exchange queue's tail and pushing item"); */ + if (pre == DA_NULL) + queue->client_q_head = client_noti; + else + pre->next = client_noti; + destroy_client_noti(cur); + } else { + cur->next = client_noti; + } + } else { + cur->next = client_noti; + } +#else + cur->next = client_noti; +#endif + } + + queue->having_data = DA_TRUE; + + __client_q_wake_up_without_lock(); + if (queue->client_q_head->next) { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]", + queue->client_q_head, queue->client_q_head->next); + } else { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL", + queue->client_q_head); + } + + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); +} + +void __pop_client_noti(client_noti_t **out_client_noti) +{ + client_queue_t *queue = DA_NULL; + + /* DA_LOG_FUNC_START(ClientNoti); */ + + queue = &(client_app_mgr.client_queue); + + _da_thread_mutex_lock (&(queue->mutex_client_queue)); + + if (queue->client_q_head) { + *out_client_noti = queue->client_q_head; + queue->client_q_head = queue->client_q_head->next; + if (queue->client_q_head) { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]", + *out_client_noti, queue->client_q_head); + } else { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL", + *out_client_noti); + } + } else { + *out_client_noti = DA_NULL; + } + + if (queue->client_q_head == DA_NULL) { + queue->having_data = DA_FALSE; + } + + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); +} + +void __client_q_goto_sleep_without_lock(void) +{ + client_queue_t *queue = DA_NULL; + + /* DA_LOG_FUNC_START(ClientNoti); */ + + queue = &(client_app_mgr.client_queue); + _da_thread_cond_wait(&(queue->cond_client_queue), &(queue->mutex_client_queue)); +} + +void __client_q_wake_up_without_lock(void) +{ + client_queue_t *queue = DA_NULL; + + /* DA_LOG_FUNC_START(ClientNoti); */ + + queue = &(client_app_mgr.client_queue); + _da_thread_cond_signal(&(queue->cond_client_queue)); +} + +void __thread_clean_up_handler_for_client_thread(void *arg) +{ + DA_LOG_CRITICAL(Thread, "cleanup for thread id = %d", pthread_self()); +} + +static void *__thread_for_client_noti(void *data) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t need_wait = DA_TRUE; + client_queue_t *queue = DA_NULL; + client_noti_t *client_noti = DA_NULL; + + //DA_LOG_FUNC_START(Thread); + + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + client_app_mgr.is_thread_init = DA_TRUE; + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + + queue = &(client_app_mgr.client_queue); + DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL); + pthread_cleanup_push(__thread_clean_up_handler_for_client_thread, (void *)DA_NULL); + + do { + _da_thread_mutex_lock(&(queue->mutex_client_queue)); + if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) { + DA_LOG_VERBOSE(Thread, "Sleep @ thread_for_client_noti!"); + __client_q_goto_sleep_without_lock(); + DA_LOG_VERBOSE(Thread, "Woke up @ thread_for_client_noti"); + } + _da_thread_mutex_unlock(&(queue->mutex_client_queue)); + + do { + __pop_client_noti(&client_noti); + if (client_noti == DA_NULL) { + DA_LOG_ERR(ClientNoti, "There is no data on client queue!"); + ret = DA_ERR_INVALID_STATE; + need_wait = DA_FALSE; + } else { + DA_LOG_VERBOSE(ClientNoti, "noti type[%d]", + client_noti->noti_type); + switch (client_noti->noti_type) { + case Q_CLIENT_NOTI_TYPE_STARTED_INFO: + { + user_download_info_t *update_dl_info = DA_NULL;; + update_dl_info = (user_download_info_t*)(&(client_noti->type.update_dl_info)); + if (client_app_mgr.client_app_info.client_callback.update_dl_info_cb) { + client_app_mgr.client_app_info.client_callback.update_dl_info_cb(update_dl_info, client_noti->user_data); + if (update_dl_info->etag) + DA_LOG(ClientNoti, "Etag:[%s]", update_dl_info->etag); + DA_LOG(ClientNoti, "Update download info for slot_id=%d, dl_id=%d, received size=%lu- DONE", + client_noti->slot_id, + update_dl_info->download_id, + update_dl_info->file_size + ); + } + } + break; + case Q_CLIENT_NOTI_TYPE_PROGRESS_INFO: + { + user_progress_info_t *progress_info = DA_NULL;; + progress_info = (user_progress_info_t*)(&(client_noti->type.update_progress_info)); + if (client_app_mgr.client_app_info.client_callback.update_progress_info_cb) { + client_app_mgr.client_app_info.client_callback.update_progress_info_cb(progress_info, client_noti->user_data); + DA_LOG_VERBOSE(ClientNoti, "Update downloading info for slot_id=%d, dl_id=%d, received size=%lu - DONE", + client_noti->slot_id, + progress_info->download_id, + progress_info->received_size); + } + } + break; + case Q_CLIENT_NOTI_TYPE_FINISHED_INFO: + { + user_finished_info_t *finished_info = DA_NULL;; + finished_info = (user_finished_info_t*)(&(client_noti->type.finished_info)); + if (client_app_mgr.client_app_info.client_callback.finished_info_cb) { + client_app_mgr.client_app_info.client_callback.finished_info_cb( + finished_info, client_noti->user_data); + DA_LOG(ClientNoti, "Completed info for slot_id=%d, dl_id=%d, saved_path=%s etag=%s err=%d http_state=%d user_data=%p- DONE", + client_noti->slot_id, + finished_info->download_id, + finished_info->saved_path, + finished_info->etag, + finished_info->err, + finished_info->http_status, + client_noti->user_data); + } + } + break; + case Q_CLIENT_NOTI_TYPE_PAUSED_INFO: + { + user_paused_info_t *da_paused_info = DA_NULL; + da_paused_info = (user_paused_info_t *)(&(client_noti->type.paused_info)); + + if (client_app_mgr.client_app_info.client_callback.paused_info_cb) { + DA_LOG(ClientNoti, "User Paused info for slot_id=%d, dl_id=%d - Done", + client_noti->slot_id, + da_paused_info->download_id); + client_app_mgr.client_app_info.client_callback.paused_info_cb( + da_paused_info, client_noti->user_data); + } + } + break; + case Q_CLIENT_NOTI_TYPE_TERMINATE: + DA_LOG_CRITICAL(ClientNoti, "Q_CLIENT_NOTI_TYPE_TERMINATE"); + need_wait = DA_FALSE; + break; + } + destroy_client_noti(client_noti); + } + + if(DA_TRUE == need_wait) { + _da_thread_mutex_lock(&(queue->mutex_client_queue)); + if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) { + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); + break; + } else { + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); + } + } else { + break; + } + } while (1); + } while (DA_TRUE == need_wait); + + _da_thread_mutex_destroy(&(queue->mutex_client_queue)); + _da_thread_cond_destroy(&(queue->cond_client_queue)); + + pthread_cleanup_pop(0); + DA_LOG_CRITICAL(Thread, "=====thread_for_client_noti- EXIT====="); + pthread_exit((void *)NULL); + return DA_NULL; +} + +char *get_client_user_agent_string(void) +{ + if (!client_app_mgr.is_init) + return DA_NULL; + + return client_app_mgr.client_app_info.client_user_agent; +} diff --git a/src/agent/download-agent-debug.c b/src/agent/download-agent-debug.c new file mode 100755 index 0000000..07078b3 --- /dev/null +++ b/src/agent/download-agent-debug.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "download-agent-debug.h" +#include "download-agent-utils.h" + +#define STRING_IT(x) #x +#define TURN_ON_LOG(channel) (DALogBitMap |= (0x1<<(channel))) + +int DALogBitMap; + +char *__get_log_env(void); +char **__parsing_log_env(char *in_log_env); +char *__copying_str(char *source, int length); +char *__get_channel_name_from_enum(da_log_channel channel_enum); + +da_result_t init_log_mgr(void) { + da_result_t ret = DA_RESULT_OK; + static da_bool_t did_log_mgr_init = DA_FALSE; + char *log_env = DA_NULL; + char **parsed_log_env = DA_NULL; + char **cur_parsed_log_env = DA_NULL; + int i = 0; + + if (did_log_mgr_init) + return ret; + + did_log_mgr_init = DA_TRUE; + + log_env = __get_log_env(); + if (!log_env) { + /* If no environment values are found, do behave like all logs are turned on except for Soup log */ + DALogBitMap = ~(0x1 << Soup); + return ret; + } + + TURN_ON_LOG(Default); + + parsed_log_env = __parsing_log_env(log_env); + if (parsed_log_env) { + char *channel_keyward = DA_NULL; + for (cur_parsed_log_env = parsed_log_env; *cur_parsed_log_env; cur_parsed_log_env++) { + if (!*cur_parsed_log_env) + break; + for (i = 0; i < DA_LOG_CHANNEL_MAX; i++) { + channel_keyward = __get_channel_name_from_enum(i); + if (channel_keyward && !strcmp(*cur_parsed_log_env, + channel_keyward)) { + TURN_ON_LOG(i); + break; + } + } + free(*cur_parsed_log_env); + } + free(parsed_log_env); + } + + if (log_env) + free(log_env); + + return ret; +} + +char *__get_log_env(void) { + char *log_env = DA_NULL; + + /* environment value has higher priority than configure file */ + log_env = getenv(DA_DEBUG_ENV_KEY); + if (log_env && strlen(log_env)) + return strdup(log_env); + + if (read_data_from_file(DA_DEBUG_CONFIG_FILE_PATH, &log_env)) + return log_env; + + return DA_NULL; +} + +char **__parsing_log_env(char *in_log_env) { + char **out_parsed_result = DA_NULL; + + char **temp_result_array = DA_NULL; + char **cur_temp_result_array = DA_NULL; + int how_many_item = 0; + int how_many_delimeter = 0; + + char delimiter = ','; + + char *org_str = in_log_env; + char *cur_char = org_str; + char *start = org_str; + char *end = org_str; + int target_len = 0; + + if (!org_str) + return DA_NULL; + + /* counting delimiter to know how many items should be memory allocated. + * This causes two round of loop (counting delimiter and real operation). + * But I think it is tolerable, because input parameter is from console. + * And it is also a reason why we should not use fixed length array. + * Users are hard to input very long environment, but it is possible. */ + for (cur_char = org_str; *cur_char; cur_char++) { + if (*cur_char == delimiter) + how_many_delimeter++; + } + how_many_item = how_many_delimeter + 1; + temp_result_array = (char**) calloc(1, how_many_item + 1); + if (!(temp_result_array)) + goto ERR; + + cur_temp_result_array = temp_result_array; + cur_char = org_str; + while (1) { + if (*cur_char == delimiter) { + end = cur_char; + target_len = (int) (end - start); + *cur_temp_result_array++ = __copying_str(start, + target_len); + start = ++cur_char; + continue; + } else if (!(*cur_char)) { + end = cur_char; + target_len = (int) (end - start); + *cur_temp_result_array++ = __copying_str(start, + target_len); + *cur_temp_result_array = DA_NULL; + break; + } else { + cur_char++; + } + } + out_parsed_result = temp_result_array; +ERR: + return out_parsed_result; +} + +char *__copying_str(char *source, int length) { + char *copied_str = DA_NULL; + char *cur_pos = DA_NULL; + char white_space = ' '; + char end_of_line = 10; /* ASCII for LF */ + int i = 0; + + if (!source || !(length > 0)) + return DA_NULL; + + copied_str = (char*) calloc(1, length + 1); + if (copied_str) { + cur_pos = copied_str; + for (i = 0; i < length; i++) { + if ((source[i] != white_space) && (source[i] + != end_of_line)) + *cur_pos++ = source[i]; + } + } + + return copied_str; +} + +char *__get_channel_name_from_enum(da_log_channel channel_enum) { + switch (channel_enum) { + case Soup: + return STRING_IT(Soup); + case HTTPManager: + return STRING_IT(HTTPManager); + case FileManager: + return STRING_IT(FileManager); + case DRMManager: + return STRING_IT(DRMManager); + case DownloadManager: + return STRING_IT(DownloadManager); + case ClientNoti: + return STRING_IT(ClientNoti); + case HTTPMessageHandler: + return STRING_IT(HTTPMessageHandler); + case Encoding: + return STRING_IT(Encoding); + case QueueManager: + return STRING_IT(QueueManager); + case Parsing: + return STRING_IT(Parsing); + case Thread: + return STRING_IT(Thread); + case Default: + return STRING_IT(Default); + default: + return DA_NULL; + } +} diff --git a/src/agent/download-agent-dl-info-util.c b/src/agent/download-agent-dl-info-util.c new file mode 100755 index 0000000..8e3bb78 --- /dev/null +++ b/src/agent/download-agent-dl-info-util.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "download-agent-client-mgr.h" +#include "download-agent-dl-info-util.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-file.h" +#include "download-agent-http-mgr.h" +#include "download-agent-plugin-conf.h" + +pthread_mutex_t mutex_download_state[DA_MAX_DOWNLOAD_ID]; +static pthread_mutex_t mutex_download_mgr = PTHREAD_MUTEX_INITIALIZER; +download_mgr_t download_mgr; + +void cleanup_source_info_basic_download(source_info_basic_t *source_info_basic); +void cleanup_req_dl_info_http(req_dl_info *http_download); +void destroy_file_info(file_info *file); + +da_result_t init_download_mgr() { + da_result_t ret = DA_RESULT_OK; + int i = 0; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock(&mutex_download_mgr); + + if (download_mgr.is_init == DA_FALSE) { + download_mgr.is_init = DA_TRUE; + + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + _da_thread_mutex_init(&mutex_download_state[i], DA_NULL); + init_download_info(i); + } + init_dl_id_history(&(download_mgr.dl_id_history)); + } + + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + +da_result_t deinit_download_mgr(void) { + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock(&mutex_download_mgr); + if (download_mgr.is_init == DA_TRUE) { + int i = 0; + dl_info_t *dl_info = DA_NULL; + void *t_return = NULL; + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + dl_info = &(download_mgr.dl_info[i]); + if (dl_info && dl_info->is_using) { + request_to_abort_http_download(GET_DL_CURRENT_STAGE(i)); + DA_LOG_CRITICAL(Thread, "===download id[%d] thread id[%lu] join===",i, GET_DL_THREAD_ID(i)); +/* Because the download daemon can call the deinit function, the resources of pthread are not freed + FIXME later : It is needed to change the termination flow again. + if (pthread_join(GET_DL_THREAD_ID(i), &t_return) < 0) { + DA_LOG_ERR(Thread, "join client thread is failed!!!"); + } +*/ + DA_LOG_CRITICAL(Thread, "===download id[%d] thread join return[%d]===",i, (char*)t_return); + } + } + download_mgr.is_init = DA_FALSE; + deinit_dl_id_history(&(download_mgr.dl_id_history)); + } + _da_thread_mutex_unlock(&mutex_download_mgr); + return ret; +} + +void init_download_info(int slot_id) +{ + dl_info_t *dl_info = DA_NULL; + +// DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock(&mutex_download_state[slot_id]); +// DA_LOG_VERBOSE(Default, "Init slot_id [%d] Info", slot_id); + dl_info = &(download_mgr.dl_info[slot_id]); + + dl_info->is_using = DA_FALSE; + dl_info->state = DOWNLOAD_STATE_IDLE; + dl_info->download_stage_data = DA_NULL; + dl_info->dl_id = 0; + dl_info->http_status = 0; + dl_info->enable_pause_update = DA_FALSE; + dl_info->user_install_path = DA_NULL; + dl_info->user_file_name = DA_NULL; + dl_info->user_etag = DA_NULL; + dl_info->user_temp_file_path = DA_NULL; + dl_info->user_data = DA_NULL; + + Q_init_queue(&(dl_info->queue)); + + DA_LOG_VERBOSE(Default, "Init slot_id [%d] Info END", slot_id); + _da_thread_mutex_unlock(&mutex_download_state[slot_id]); + + return; +} + +void destroy_download_info(int slot_id) +{ + dl_info_t *dl_info = DA_NULL; + + DA_LOG_FUNC_START(Default); + + DA_LOG(Default, "Destroying slot_id [%d] Info", slot_id); + + if (slot_id == DA_INVALID_ID) { + DA_LOG_ERR(Default, "invalid slot_id"); + return; + } + + dl_info = &(download_mgr.dl_info[slot_id]); + if (DA_FALSE == dl_info->is_using) { +/* DA_LOG_ERR(Default, "invalid slot_id"); */ + return; + } + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + dl_info->state = DOWNLOAD_STATE_IDLE; + DA_LOG(Default, "Changed download_state to - [%d] ", dl_info->state); + + dl_info->active_dl_thread_id = 0; + + if (dl_info->download_stage_data != DA_NULL) { + remove_download_stage(slot_id, dl_info->download_stage_data); + dl_info->download_stage_data = DA_NULL; + } + dl_info->dl_id = 0; + dl_info->enable_pause_update = DA_FALSE; + if (dl_info->user_install_path) { + free(dl_info->user_install_path); + dl_info->user_install_path = DA_NULL; + } + + if (dl_info->user_file_name) { + free(dl_info->user_file_name); + dl_info->user_file_name = DA_NULL; + } + + if (dl_info->user_etag) { + free(dl_info->user_etag); + dl_info->user_etag = DA_NULL; + } + + if (dl_info->user_temp_file_path ) { + free(dl_info->user_temp_file_path ); + dl_info->user_temp_file_path = DA_NULL; + } + + dl_info->user_data = DA_NULL; + + Q_destroy_queue(&(dl_info->queue)); + dl_info->http_status = 0; + + dl_info->is_using = DA_FALSE; + + DA_LOG(Default, "Destroying slot_id [%d] Info END", slot_id); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + return; +} + +void *Add_new_download_stage(int slot_id) +{ + stage_info *download_stage_data = NULL; + stage_info *new_download_stage_data = NULL; + + DA_LOG_FUNC_START(Default); + + new_download_stage_data = (stage_info*)calloc(1, sizeof(stage_info)); + if (!new_download_stage_data) + goto ERR; + + new_download_stage_data->dl_id = slot_id; + download_stage_data = GET_DL_CURRENT_STAGE(slot_id); + if (download_stage_data) { + while (download_stage_data->next_stage_info) { + download_stage_data + = download_stage_data->next_stage_info; + }; + download_stage_data->next_stage_info = new_download_stage_data; + } else { + GET_DL_CURRENT_STAGE(slot_id) = new_download_stage_data; + } + DA_LOG(Default, "NEW STAGE ADDED FOR DOWNLOAD ID[%d] new_stage[%p]", slot_id,new_download_stage_data); + +ERR: + return new_download_stage_data; +} + +void remove_download_stage(int slot_id, stage_info *in_stage) +{ + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_START(Default); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (DA_NULL == stage) { + DA_LOG_VERBOSE(Default, "There is no stage field on slot_id = %d", slot_id); + goto ERR; + } + + if (DA_NULL == in_stage) { + DA_LOG_VERBOSE(Default, "There is no in_stage to remove."); + goto ERR; + } + + if (in_stage == stage) { + DA_LOG_VERBOSE(Default, "Base stage will be removed. in_stage[%p]",in_stage); + DA_LOG_VERBOSE(Default, "next stage[%p]",stage->next_stage_info); + GET_DL_CURRENT_STAGE(slot_id) = stage->next_stage_info; + empty_stage_info(in_stage); + free(in_stage); + in_stage = DA_NULL; + } else { + while (in_stage != stage->next_stage_info) { + stage = stage->next_stage_info; + } + if (in_stage == stage->next_stage_info) { + stage->next_stage_info + = stage->next_stage_info->next_stage_info; + DA_LOG_VERBOSE(Default, "Stage will be removed. in_stage[%p]",in_stage); + DA_LOG_VERBOSE(Default, "next stage[%p]",stage->next_stage_info); + empty_stage_info(in_stage); + free(in_stage); + in_stage = DA_NULL; + } + } + +ERR: + return; +} + +void empty_stage_info(stage_info *in_stage) +{ + source_info_t *source_information = NULL; + req_dl_info *request_download_info = NULL; + file_info *file_information = NULL; + + DA_LOG_FUNC_START(Default); + + DA_LOG(Default, "Stage to Remove:[%p]", in_stage); + source_information = GET_STAGE_SOURCE_INFO(in_stage); + + cleanup_source_info_basic_download( + GET_SOURCE_BASIC(source_information)); + + request_download_info = GET_STAGE_TRANSACTION_INFO(in_stage); + + cleanup_req_dl_info_http(request_download_info); + + file_information = GET_STAGE_CONTENT_STORE_INFO(in_stage); + destroy_file_info(file_information); +} + +void cleanup_source_info_basic_download(source_info_basic_t *source_info_basic) +{ + if (NULL == source_info_basic) + goto ERR; + + DA_LOG_FUNC_START(Default); + + if (NULL != source_info_basic->url) { + free(source_info_basic->url); + source_info_basic->url = DA_NULL; + } + +ERR: + return; + +} + +void cleanup_req_dl_info_http(req_dl_info *http_download) +{ + DA_LOG_FUNC_START(Default); + + if (http_download->http_info.http_msg_request) { + http_msg_request_destroy( + &(http_download->http_info.http_msg_request)); + } + + if (http_download->http_info.http_msg_response) { + http_msg_response_destroy( + &(http_download->http_info.http_msg_response)); + } + + if (DA_NULL != http_download->location_url) { + free(http_download->location_url); + http_download->location_url = DA_NULL; + } + if (DA_NULL != http_download->content_type_from_header) { + free(http_download->content_type_from_header); + http_download->content_type_from_header = DA_NULL; + } + + if (DA_NULL != http_download->etag_from_header) { + free(http_download->etag_from_header); + http_download->etag_from_header = DA_NULL; + } + + http_download->invloved_transaction_id = DA_INVALID_ID; + http_download->content_len_from_header = 0; + http_download->downloaded_data_size = 0; + + _da_thread_mutex_destroy(&(http_download->mutex_http_state)); + + return; +} + +void destroy_file_info(file_info *file_information) +{ +// DA_LOG_FUNC_START(Default); + + if (!file_information) + return; + + if (file_information->file_name_final) { + free(file_information->file_name_final); + file_information->file_name_final = NULL; + } + + if (file_information->content_type) { + free(file_information->content_type); + file_information->content_type = NULL; + } + + if (file_information->pure_file_name) { + free(file_information->pure_file_name); + file_information->pure_file_name = NULL; + } + + if (file_information->extension) { + free(file_information->extension); + file_information->extension = NULL; + } + return; +} + +void clean_up_client_input_info(client_input_t *client_input) +{ + DA_LOG_FUNC_START(Default); + + if (client_input) { + client_input->user_data = NULL; + + if (client_input->install_path) { + free(client_input->install_path); + client_input->install_path = DA_NULL; + } + + if (client_input->file_name) { + free(client_input->file_name); + client_input->file_name = DA_NULL; + } + + if (client_input->etag) { + free(client_input->etag); + client_input->etag = DA_NULL; + } + + if (client_input->temp_file_path) { + free(client_input->temp_file_path); + client_input->temp_file_path = DA_NULL; + } + + client_input_basic_t *client_input_basic = + &(client_input->client_input_basic); + + if (client_input_basic && client_input_basic->req_url) { + free(client_input_basic->req_url); + client_input_basic->req_url = DA_NULL; + } + + if (client_input_basic && client_input_basic->user_request_header) { + int i = 0; + int count = client_input_basic->user_request_header_count; + for (i = 0; i < count; i++) + { + if (client_input_basic->user_request_header[i]) { + free(client_input_basic->user_request_header[i]); + client_input_basic->user_request_header[i] = DA_NULL; + } + } + + free(client_input_basic->user_request_header); + client_input_basic->user_request_header = DA_NULL; + client_input_basic->user_request_header_count = 0; + } + } else { + DA_LOG_ERR(Default, "client_input is NULL."); + } + + return; +} + +da_result_t get_slot_id_for_dl_id( + int dl_id, + int* slot_id) +{ + da_result_t ret = DA_ERR_INVALID_DL_REQ_ID; + int iter = 0; + + if (dl_id < 0) { + DA_LOG_ERR(Default, "dl_id is less than 0 - %d", dl_id); + return DA_ERR_INVALID_DL_REQ_ID; + } + + _da_thread_mutex_lock(&mutex_download_mgr); + for (iter = 0; iter < DA_MAX_DOWNLOAD_ID; iter++) { + if (download_mgr.dl_info[iter].is_using == DA_TRUE) { + if (download_mgr.dl_info[iter].dl_id == + dl_id) { + *slot_id = iter; + ret = DA_RESULT_OK; + break; + } + } + } + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + + +da_result_t get_available_slot_id(int *available_id) +{ + da_result_t ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + int i; + + _da_thread_mutex_lock(&mutex_download_mgr); + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + if (download_mgr.dl_info[i].is_using == DA_FALSE) { + init_download_info(i); + + download_mgr.dl_info[i].is_using = DA_TRUE; + + download_mgr.dl_info[i].dl_id + = get_available_dl_id(&(download_mgr.dl_id_history)); + + *available_id = i; + DA_LOG_CRITICAL(Default, "available download id = %d", *available_id); + ret = DA_RESULT_OK; + + break; + } + } + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + +da_bool_t is_valid_slot_id(int slot_id) +{ + da_bool_t ret = DA_FALSE; + + if (slot_id >= 0 && slot_id < DA_MAX_DOWNLOAD_ID) { + if (download_mgr.dl_info[slot_id].is_using == DA_TRUE) + ret = DA_TRUE; + } + + return ret; +} + +void store_http_status(int dl_id, int status) +{ + if (status < 100 || status > 599) { + DA_LOG_ERR(Default, "Invalid status code [%d]", status); + return; + } + DA_LOG_VERBOSE(Default, "store_http_status id[%d]status[%d] ",dl_id, status); + download_mgr.dl_info[dl_id].http_status = status; +} + +int get_http_status(int slot_id) +{ + if (!download_mgr.dl_info[slot_id].is_using) { + DA_LOG_ERR(Default, "Invalid slot_id [%d]", slot_id); + return 0; + } + return download_mgr.dl_info[slot_id].http_status; +} diff --git a/src/agent/download-agent-dl-mgr.c b/src/agent/download-agent-dl-mgr.c new file mode 100755 index 0000000..cf3ad52 --- /dev/null +++ b/src/agent/download-agent-dl-mgr.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-file.h" +#include "download-agent-plugin-conf.h" + + +static da_result_t __cancel_download_with_slot_id(int slot_id); +static da_result_t __suspend_download_with_slot_id(int slot_id); + + +da_result_t requesting_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_session = DA_NULL; + + DA_LOG_FUNC_START(Default); + + if (!stage) { + DA_LOG_ERR(Default, "stage is null.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = make_req_dl_info_http(stage, GET_STAGE_TRANSACTION_INFO(stage)); + if (ret != DA_RESULT_OK) + goto ERR; + + request_session = GET_STAGE_TRANSACTION_INFO(stage); + ret = request_http_download(stage); + if (DA_RESULT_OK == ret) { + DA_LOG(Default, "Http download is complete."); + } else { + DA_LOG_ERR(Default, "Http download is failed. ret = %d", ret); + goto ERR; + } +ERR: + return ret; +} + +da_result_t handle_after_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + da_mime_type_id_t mime_type = DA_MIME_TYPE_NONE; + + DA_LOG_FUNC_START(Default); + + mime_type = get_mime_type_id( + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage))); + + switch (mime_type) { + case DA_MIME_TYPE_NONE: + DA_LOG(Default, "DA_MIME_TYPE_NONE"); + ret = DA_ERR_MISMATCH_CONTENT_TYPE; + break; + default: + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_FINISH, stage); + break; + } /* end of switch */ + + return ret; +} + +static da_result_t __cancel_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + + if (download_state == DOWNLOAD_STATE_FINISH || + download_state == DOWNLOAD_STATE_CANCELED) { + DA_LOG_CRITICAL(Default, "Already download is finished. Do not send cancel request"); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + return ret; + } + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (!stage) + return DA_RESULT_OK; + + ret = request_to_cancel_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download cancel Successful for download id - %d", slot_id); +ERR: + return ret; +} + +da_result_t cancel_download(int dl_id) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + goto ERR; + } + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = __cancel_download_with_slot_id(slot_id); + +ERR: + return ret; + +} + +static da_result_t __suspend_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (!stage) + return DA_ERR_CANNOT_SUSPEND; + + ret = request_to_suspend_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download Suspend Successful for download id-%d", slot_id); +ERR: + return ret; +} + +da_result_t suspend_download(int dl_id, da_bool_t is_enable_cb) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + goto ERR; + } + GET_DL_ENABLE_PAUSE_UPDATE(slot_id) = is_enable_cb; + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = __suspend_download_with_slot_id(slot_id); + +ERR: + return ret; + +} + +static da_result_t __resume_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + + ret = request_to_resume_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download Resume Successful for download id-%d", slot_id); +ERR: + return ret; +} + +da_result_t resume_download(int dl_id) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) + goto ERR; + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_DL_REQ_ID; + goto ERR; + } + + ret = __resume_download_with_slot_id(slot_id); + +ERR: + return ret; +} + +da_result_t send_user_noti_and_finish_download_flow( + int slot_id, char *installed_path, char *etag) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state = DA_NULL; + da_bool_t need_destroy_download_info = DA_FALSE; + + DA_LOG_FUNC_START(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "state = %d", download_state); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + switch (download_state) { + case DOWNLOAD_STATE_FINISH: + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + installed_path, DA_NULL, DA_RESULT_OK, + get_http_status(slot_id)); + need_destroy_download_info = DA_TRUE; + break; + case DOWNLOAD_STATE_CANCELED: + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + installed_path, etag, DA_RESULT_USER_CANCELED, + get_http_status(slot_id)); + need_destroy_download_info = DA_TRUE; + break; +#ifdef PAUSE_EXIT + case DOWNLOAD_STATE_PAUSED: + need_destroy_download_info = DA_TRUE; + break; +#endif + default: + DA_LOG(Default, "download state = %d", download_state); + break; + } + + if (need_destroy_download_info == DA_TRUE) { + destroy_download_info(slot_id); + } else { + DA_LOG_CRITICAL(Default, "download info is not destroyed"); + } + + return ret; +} + +da_bool_t is_valid_download_id(int dl_id) +{ + + da_bool_t ret = DA_TRUE; + int slot_id = DA_INVALID_ID; + + DA_LOG_VERBOSE(Default, "[is_valid_download_id]download_id : %d", dl_id); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + ret = DA_FALSE; + goto ERR; + } else { + ret = DA_TRUE; + } + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_FALSE; + goto ERR; + } + if (GET_DL_THREAD_ID(slot_id) < 1) { + DA_LOG_ERR(Default, "Download thread is not alive"); + ret = DA_FALSE; + goto ERR; + } + +ERR: + return ret; +} diff --git a/src/agent/download-agent-encoding.c b/src/agent/download-agent-encoding.c new file mode 100755 index 0000000..ae2fdf4 --- /dev/null +++ b/src/agent/download-agent-encoding.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "download-agent-encoding.h" +#include "download-agent-debug.h" + +da_result_t _parsing_base64_encoded_str(const char *in_encoded_str, + char **out_charset_type, + char *out_encoding_type, + char **out_raw_encoded_str); + +da_bool_t is_base64_encoded_word(const char *in_str) +{ + const char *haystack = DA_NULL; + char first_needle[8] = {0,}; + char second_needle[8] = {0,}; + char *found_str = DA_NULL; + + if (!in_str) { + DA_LOG_ERR(Default, "input string is NULL"); + return DA_FALSE; + } + + haystack = in_str; + if (haystack[0] == '"') { + snprintf(first_needle, sizeof(first_needle), "%s", "\"=?"); // "=? + snprintf(second_needle, sizeof(second_needle), "%s", "?=\""); // ?=" + } else { + snprintf(first_needle, sizeof(first_needle), "%s", "=?"); // =? + snprintf(second_needle, sizeof(second_needle), "%s", "?="); // ?= + } + +// DA_LOG(Default, "needle = [%s], haystack = [%s]", first_needle, haystack); + + found_str = strstr(haystack, first_needle); + if (found_str) { + if (found_str == haystack) { +// DA_LOG(Default, "Input string is starting with %s", needle); + haystack = haystack + strlen(haystack) - strlen(second_needle); +// DA_LOG(Default, "second haystack is [%s]", haystack); + if(!strcmp(haystack, second_needle)) + return DA_TRUE; + } + } + return DA_FALSE; +} + +da_result_t decode_base64_encoded_str(const char *in_encoded_str, + char **out_decoded_ascii_str) +{ + da_result_t ret = DA_RESULT_OK; + + const char *org_str = DA_NULL; + char *charset_type = NULL; + char encoding_type = '\0'; + char *raw_encoded_str = NULL; + char *decoded_str = NULL; + const gchar *g_encoded_text = NULL; + guchar *g_decoded_text = NULL; + gsize g_decoded_text_len = 0; + + DA_LOG(Default, "input str = [%s]", in_encoded_str); + + org_str = in_encoded_str; + if(!org_str) { + DA_LOG_ERR(Default, "Input string is NULL"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = _parsing_base64_encoded_str(org_str, &charset_type, + &encoding_type, &raw_encoded_str); + if(ret != DA_RESULT_OK) { + goto ERR; + } + +// DA_LOG(Default, "charset = [%s], encoding = [%c], raw = [%s]", charset_type, encoding_type, raw_encoded_str); + + if(encoding_type != 'B') { + DA_LOG_ERR(Default, "Encoded Word is not encoded with Base64, but %c. We can only handle Base64.", encoding_type); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + /* + * on glib/gtype.h + * typedef char gchar; + * typedef unsigned char guchar; + * + */ + g_encoded_text = (const gchar*)raw_encoded_str; + g_decoded_text = g_base64_decode(g_encoded_text, &g_decoded_text_len); + + if(g_decoded_text) { + DA_LOG(Default, "g_decoded_text = [%s]", g_decoded_text); + decoded_str = (char*)calloc(1, g_decoded_text_len+1); + if(!decoded_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + memcpy(decoded_str, g_decoded_text, g_decoded_text_len); + } + } + DA_LOG(Default, "decoded_str = [%s]", decoded_str); + +ERR: + *out_decoded_ascii_str = decoded_str; + + if(charset_type) { + free(charset_type); + charset_type = NULL; + } + + if(raw_encoded_str) { + free(raw_encoded_str); + raw_encoded_str = NULL; + } + + if(g_decoded_text) { + g_free(g_decoded_text); + } + + return ret; +} + + +da_result_t _parsing_base64_encoded_str(const char *in_encoded_str, + char **out_charset_type, + char *out_encoding_type, + char **out_raw_encoded_str) +{ + da_result_t ret = DA_RESULT_OK; + + const char *org_str = DA_NULL; // e.g. =?UTF-8?B?7Jew7JWE7JmA7IKs7J6QLmpwZw==?= + char *charset_type = NULL; // e.g. UTF-8 + char encoding_type = '\0'; // e.g. B (means Base64) + char *raw_encoded_str = NULL; // e.g. 7Jew7JWE7JmA7IKs7J6QLmpwZw== + + char *haystack = DA_NULL; + char needle[8] = {0,}; + + char *wanted_str = DA_NULL; + int wanted_str_len = 0; + char *wanted_str_start = DA_NULL; + char *wanted_str_end = DA_NULL; + + org_str = in_encoded_str; + if (!org_str) { + DA_LOG_ERR(Default, "Input string is NULL"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + // strip "=?" + haystack = (char*)org_str; + snprintf(needle, sizeof(needle), "=?"); + wanted_str_end = strstr(haystack, needle); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str = wanted_str_end + strlen(needle); + DA_LOG(Default, "strip [%s]", wanted_str); + } + + // for charset + haystack = wanted_str_start = wanted_str; + needle[0] = '?'; + wanted_str_end = strchr(haystack, needle[0]); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str_len = wanted_str_end - wanted_str_start + 1; + wanted_str = (char*)calloc(1, wanted_str_len+1); + if (!wanted_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + snprintf(wanted_str, wanted_str_len+1, "%s", wanted_str_start); + charset_type = wanted_str; + wanted_str = DA_NULL; + } + + DA_LOG(Default, "charset [%s]", charset_type); + } + + + // for encoding + encoding_type = *(++wanted_str_end); + DA_LOG(Default, "encoding [%c]", encoding_type); + + // for raw encoded str + haystack = wanted_str_start = wanted_str_end + 1; + snprintf(needle, sizeof(needle), "?="); + wanted_str_end = strstr(haystack, needle); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str_len = wanted_str_end - wanted_str_start + 1; + wanted_str = (char*)calloc(1, wanted_str_len+1); + if (!wanted_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + snprintf(wanted_str, wanted_str_len+1, "%s", wanted_str_start); + raw_encoded_str = wanted_str; + wanted_str = NULL; + } + + DA_LOG(Default, "raw encoded str [%s]", raw_encoded_str); + } + +ERR: + if (ret != DA_RESULT_OK) { + if (charset_type) { + free(charset_type); + charset_type = NULL; + } + } + + *out_charset_type = charset_type; + *out_encoding_type = encoding_type; + *out_raw_encoded_str = raw_encoded_str; + + return ret; +} + +void decode_url_encoded_str(const char *in_encoded_str, char **out_str) +{ + char *in = NULL; + char *out = NULL; + *out_str = calloc(1, strlen(in_encoded_str) + 1); + if (*out_str == NULL) + return; + out = *out_str; + in = (char *)in_encoded_str; + while (*in) + { + if (*in == '%') { + int hex = 0; + in++; + if (sscanf(in, "%2x", &hex) <= 0) { + return; + } else { + *out = hex; + in++; + } + } else if (*in == '+') { + *out = ' '; + } else { + *out = *in; + } + in++; + out++; + } +} + diff --git a/src/agent/download-agent-file.c b/src/agent/download-agent-file.c new file mode 100755 index 0000000..a048c23 --- /dev/null +++ b/src/agent/download-agent-file.c @@ -0,0 +1,1244 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-file.h" +#include "download-agent-mime-util.h" +#include "download-agent-http-mgr.h" +#include "download-agent-plugin-conf.h" + +#ifdef _ENABLE_OMA_DRM +#include "download-agent-plugin-drm.h" +#endif + + +#define NO_NAME_TEMP_STR "No name" + +static da_result_t __set_file_size(stage_info *stage); +static da_result_t __saved_file_open(stage_info *stage); + +static char *__derive_extension(stage_info *stage); +static da_result_t __divide_file_name_into_pure_name_N_extesion( + const char *in_file_name, + char **out_pure_file_name, + char **out_extension); +static da_result_t __get_candidate_file_name(stage_info *stage, + char **out_pure_file_name, char **out_extension); + +static da_result_t __file_write_buf_make_buf(file_info *file_storage); +static da_result_t __file_write_buf_destroy_buf(file_info *file_storage); +static da_result_t __file_write_buf_flush_buf(stage_info *stage, + file_info *file_storage); +static da_result_t __file_write_buf_copy_to_buf(file_info *file_storage, + char *body, int body_len); +static da_result_t __file_write_buf_directly_write(stage_info *stage, + file_info *file_storage, char *body, int body_len); + + +da_result_t create_saved_dir(void) +{ + da_result_t ret = DA_RESULT_OK; + char *tmp_default_path = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + ret = get_default_install_dir(&tmp_default_path); + if (ret != DA_RESULT_OK) + goto ERR; + + if (!is_dir_exist(tmp_default_path)) { + ret = create_dir(tmp_default_path); + } + +ERR: + if (tmp_default_path) { + free(tmp_default_path); + tmp_default_path = DA_NULL; + } + return ret; +} + +da_result_t clean_files_from_dir(char *dir_path) +{ + da_result_t ret = DA_RESULT_OK; + struct dirent *d = DA_NULL; + DIR *dir; + char file_path[DA_MAX_FULL_PATH_LEN] = { 0, }; + + DA_LOG_FUNC_START(FileManager); + + if (dir_path == DA_NULL) + return DA_ERR_INVALID_ARGUMENT; + + if (is_dir_exist(dir_path)) { + dir = opendir(dir_path); + if (DA_NULL == dir) { + DA_LOG_ERR(FileManager, "opendir() for %s is failed.", dir_path); + ret = DA_ERR_INVALID_INSTALL_PATH; + } else { + while (DA_NULL != (d = readdir(dir))) { + DA_LOG(FileManager, "%s",d->d_name); + if (0 == strncmp(d->d_name, ".", strlen(".")) + || 0 == strncmp(d->d_name, + "..", + strlen(".."))) { + continue; + } + + memset(file_path, 0x00, DA_MAX_FULL_PATH_LEN); + snprintf(file_path, DA_MAX_FULL_PATH_LEN, + "%s/%s", dir_path, d->d_name); + if (remove(file_path) < 0) { + DA_LOG_ERR(FileManager, "fail to remove file"); + } + } + + closedir(dir); + if (remove(dir_path) < 0) { + DA_LOG_ERR(FileManager, "fail to remove dir"); + } + } + } + return ret; +} + +/* Priority to obtain MIME Type + * 1. HTTP response header's field + * 2. from OMA descriptor file's attribute (mandatory field) + * 3. Otherwise, leave blank for MIME Type + */ +da_result_t get_mime_type(stage_info *stage, char **out_mime_type) +{ + char *mime_type = DA_NULL; + + if (!GET_STAGE_SOURCE_INFO(stage)) + return DA_ERR_INVALID_ARGUMENT; + + /* Priority 1 */ + if (GET_REQUEST_HTTP_HDR_CONT_TYPE(GET_STAGE_TRANSACTION_INFO(stage))) { + mime_type = GET_REQUEST_HTTP_HDR_CONT_TYPE(GET_STAGE_TRANSACTION_INFO(stage)); + DA_LOG(FileManager, "content type from HTTP response header [%s]", mime_type); + } + + if (!mime_type) { + DA_LOG(FileManager, "no content type derived"); + return DA_RESULT_OK; + } + + /* FIXME really need memory allocation? */ + *out_mime_type = (char *) calloc(1, strlen(mime_type) + 1); + if (*out_mime_type) { + snprintf(*out_mime_type, strlen(mime_type) + 1, mime_type); + DA_LOG_VERBOSE(FileManager, "out_mime_type str[%s] ptr[%p] len[%d]", + *out_mime_type,*out_mime_type,strlen(*out_mime_type)); + } else { + DA_LOG_ERR(FileManager, "fail to allocate memory"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + DA_LOG(FileManager, "mime type = %s", *out_mime_type); + return DA_RESULT_OK; +} + +da_bool_t is_file_exist(const char *file_path) +{ + struct stat dir_state; + int stat_ret; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return DA_FALSE; + } + + stat_ret = stat(file_path, &dir_state); + + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFREG) { + DA_LOG(FileManager, "Exist! %s is a regular file & its size = %lu", file_path, dir_state.st_size); + return DA_TRUE; + } + + return DA_FALSE; + } + return DA_FALSE; + +} + +da_bool_t is_dir_exist(char *file_path) +{ + struct stat dir_state; + int stat_ret; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return DA_FALSE; + } + + stat_ret = stat(file_path, &dir_state); + + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFDIR) { + DA_LOG(FileManager, "Exist! %s is a directory.", file_path); + return DA_TRUE; + } + + return DA_FALSE; + } + return DA_FALSE; +} + +void get_file_size(char *file_path, unsigned long long *out_file_size) +{ + struct stat dir_state; + int stat_ret; + + *out_file_size = -1; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return; + } + + /* Please do not use ftell() to obtain file size, use stat instead. + * This is a guide from www.securecoding.cert.org + * : FIO19-C. Do not use fseek() and ftell() to compute the size of a file + */ + stat_ret = stat(file_path, &dir_state); + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFREG) { + DA_LOG(FileManager, "size = %lu", dir_state.st_size); + *out_file_size = dir_state.st_size; + } + } + return; +} + +da_result_t __saved_file_open(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + char *actual_file_path = DA_NULL; + void *fd = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + return DA_ERR_INVALID_ARGUMENT; + + actual_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + DA_LOG(FileManager, "actual_file_path = %s", actual_file_path); + if (!actual_file_path) + return DA_ERR_INVALID_ARGUMENT; + + + fd = fopen(actual_file_path, "a+"); // for resume + if (fd == DA_NULL) { + DA_LOG_ERR(FileManager, "File open failed"); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = fd; + + DA_LOG(FileManager, "file path for saving = %s", + GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage)); + +ERR: + if (DA_RESULT_OK != ret) { + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; + } + return ret; +} + +da_result_t __set_file_size(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *stage_req_info = DA_NULL; + file_info *file_storage = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + if (!stage) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + stage_req_info = GET_STAGE_TRANSACTION_INFO(stage); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + goto ERR; + + if (GET_REQUEST_HTTP_HDR_CONT_LEN(stage_req_info) != DA_NULL) { + GET_CONTENT_STORE_FILE_SIZE(file_storage) + = GET_REQUEST_HTTP_HDR_CONT_LEN(stage_req_info); + } else { + GET_CONTENT_STORE_FILE_SIZE(file_storage) = 0; + } + DA_LOG(FileManager, "file size = %d", GET_CONTENT_STORE_FILE_SIZE(file_storage)); +ERR: + return ret; + +} + +/* Priority to derive extension + * 1. according to MIME-Type + * 2. if MIME-Type is ambiguous or blank, + * 2-1. derived from field's "filename" attribute + * 2-2. derived from url + * 3. if url does not have extension, leave blank for extension + */ +char *__derive_extension(stage_info *stage) +{ + if (!stage) + return DA_NULL; + + source_info_t *source_info = GET_STAGE_SOURCE_INFO(stage); + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + file_info *file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + char *extension = DA_NULL; + char *url = DA_NULL; + + /* Priority 1 */ + char *mime_type = DA_NULL; + mime_type = GET_CONTENT_STORE_CONTENT_TYPE(file_info_data); + if (mime_type && !is_ambiguous_MIME_Type(mime_type)) { + char *extension = DA_NULL; + da_result_t ret = get_extension_from_mime_type(mime_type, &extension); + if (ret == DA_RESULT_OK && extension) + return extension; + } + + /* Priority 2-1 */ + http_msg_response_t *http_msg_response = DA_NULL; + http_msg_response = request_info->http_info.http_msg_response; + if (http_msg_response) { + char *file_name = DA_NULL; + da_bool_t b_ret = http_msg_response_get_content_disposition(http_msg_response, + DA_NULL, &file_name); + if (b_ret && file_name) { + char *extension = DA_NULL; + DA_LOG(FileManager, "Name from Content-Disposition :[%s]", file_name); + __divide_file_name_into_pure_name_N_extesion(file_name, DA_NULL, &extension); + if (file_name) { + free(file_name); + file_name = DA_NULL; + } + if (extension) + return extension; + } + } + /* Priority 2-2 */ + /* If there is location url from response header in case of redirection, + * it try to parse the extention name from the location url */ + if (GET_REQUEST_HTTP_REQ_LOCATION(request_info)) + url = GET_REQUEST_HTTP_REQ_LOCATION(request_info); + else + url = GET_SOURCE_BASIC_URL(source_info); + if (url) { + DA_LOG(FileManager, "url:[%s]", url); + da_bool_t b_ret = da_get_extension_name_from_url(url, &extension); + if (b_ret && extension) + return extension; + } + + return DA_NULL; +} + +/** Priority for deciding file name + * 1. what client wants, which is conveyed by DA_FEATURE_FILE_NAME + * 2. 'filename' option on HTTP response header's Content-Disposition field + * 3. requesting URL + * 4. Otherwise, define it as "No name" + */ +da_result_t __get_candidate_file_name(stage_info *stage, char **out_pure_file_name, char **out_extension) +{ + da_result_t ret = DA_RESULT_OK; + source_info_t *source_info = DA_NULL; + char *pure_file_name = DA_NULL; + char *extension = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + if (!stage || !out_pure_file_name) + return DA_ERR_INVALID_ARGUMENT; + + source_info = GET_STAGE_SOURCE_INFO(stage); + if (!source_info) + return DA_ERR_INVALID_ARGUMENT; + + /* Priority 1 */ + if (!pure_file_name && GET_DL_USER_FILE_NAME(GET_STAGE_DL_ID(stage))) { + __divide_file_name_into_pure_name_N_extesion( + GET_DL_USER_FILE_NAME(GET_STAGE_DL_ID(stage)), + &pure_file_name, &extension); + } + + /* Priority 2 */ + if (!pure_file_name) { + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + http_msg_response_t *http_msg_response = DA_NULL; + http_msg_response = request_info->http_info.http_msg_response; + if (http_msg_response) { + char *file_name = DA_NULL; + da_bool_t b_ret = http_msg_response_get_content_disposition(http_msg_response, + DA_NULL, &file_name); + if (b_ret && file_name) { + DA_LOG(FileManager, "Name from Content-Disposition :[%s]", file_name); + __divide_file_name_into_pure_name_N_extesion(file_name, &pure_file_name, DA_NULL); + if (file_name) { + free(file_name); + file_name = DA_NULL; + } + } + } + } + + /* Priority 3 */ + if (!pure_file_name) { + char *url = DA_NULL; + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + /* If there is location url from response header in case of redirection, + * it try to parse the file name from the location url */ + if (GET_REQUEST_HTTP_REQ_LOCATION(request_info)) + url = GET_REQUEST_HTTP_REQ_LOCATION(request_info); + else + url = GET_SOURCE_BASIC_URL(source_info); + if (url) { + DA_LOG(FileManager, "url: [%s]", url); + da_get_file_name_from_url(url, &pure_file_name); + } + } + + /* Priority 4 */ + if (!pure_file_name) { + pure_file_name = strdup(NO_NAME_TEMP_STR); + if (!pure_file_name) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + } + + *out_pure_file_name = pure_file_name; + pure_file_name = DA_NULL; + DA_LOG(FileManager, "candidate file name [%s]", *out_pure_file_name); + + if (out_extension) { + if (extension) { + *out_extension = extension; + extension = DA_NULL; + } else { + *out_extension = __derive_extension(stage); + DA_LOG(FileManager, "candidate extension [%s]", *out_extension); + } + } + + if (extension) + free(extension); + + return DA_RESULT_OK; + +ERR: + if (extension) + free(extension); + + return ret; +} + +da_result_t __decide_file_path(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + char *temp_dir = DA_NULL; + char *extension = DA_NULL; + char *file_name_without_extension = DA_NULL; + char *tmp_file_path = DA_NULL; + char *user_install_path = DA_NULL; + file_info *file_info_data = DA_NULL; + int len = 0; + DA_LOG_FUNC_START(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_info_data) + return DA_ERR_INVALID_ARGUMENT; + + + /* If the installed path which user want is set, the temporary directory is same to the installation directory. + * Otherwise, the default temporary directory is used. + */ + user_install_path = GET_DL_USER_INSTALL_PATH(GET_STAGE_DL_ID(stage)); + if (user_install_path) { + len = strlen(user_install_path); + temp_dir = (char *)calloc(len + 1, sizeof(char)); + if (!temp_dir) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + memcpy(temp_dir, user_install_path, len); + temp_dir[len] = '\0'; + + } else { + ret = get_default_install_dir(&temp_dir); + if (DA_RESULT_OK != ret || DA_NULL == temp_dir) { + goto ERR; + } + } + + ret = __get_candidate_file_name(stage, &file_name_without_extension, &extension); + if (ret != DA_RESULT_OK) + goto ERR; + + // for resume + tmp_file_path = get_full_path_avoided_duplication(temp_dir, file_name_without_extension, extension); + if (tmp_file_path) { + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)) + = tmp_file_path; + tmp_file_path = DA_NULL; + } else { + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + if (file_name_without_extension && !GET_CONTENT_STORE_PURE_FILE_NAME(file_info_data)) { + GET_CONTENT_STORE_PURE_FILE_NAME(file_info_data) = file_name_without_extension; + file_name_without_extension = DA_NULL; + } + + if (extension && !GET_CONTENT_STORE_EXTENSION(file_info_data)) { + GET_CONTENT_STORE_EXTENSION(file_info_data) = extension; + extension = DA_NULL; + } + +ERR: + DA_LOG(FileManager, "decided file path = %s", GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data)); + if (temp_dir) { + free(temp_dir); + temp_dir = DA_NULL; + } + if (file_name_without_extension) { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + if (extension) { + free(extension); + extension = DA_NULL; + } + return ret; +} + +char *get_full_path_avoided_duplication(char *in_dir, char *in_candidate_file_name, char *in_extension) +{ + char *dir = in_dir; + char *file_name = in_candidate_file_name; + char *extension = in_extension; + char *final_path = DA_NULL; + + int final_path_len = 0; + int extension_len = 0; + + int suffix_count = 0; /* means suffix on file name. up to "_99" */ + const int max_suffix_count = 99; + int suffix_len = (int)log10(max_suffix_count+1) + 1; /* 1 means "_" */ + + if (!in_dir || !in_candidate_file_name) + return DA_NULL; + +// DA_LOG_FUNC_START(FileManager); + DA_LOG(FileManager, "in_candidate_file_name=[%s], in_extension=[%s]", in_candidate_file_name, in_extension); + + if (extension) + extension_len = strlen(extension); + + /* first 1 for "/", second 1 for ".", last 1 for DA_NULL */ + final_path_len = strlen(dir) + 1 + strlen(file_name) + 1 + + suffix_len + extension_len + 1; + + final_path = (char*)calloc(1, final_path_len); + if (!final_path) { + DA_LOG_ERR(FileManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_NULL; + } + + do { + /* e.g) /tmp/abc.jpg + * if there is no extension name, just make a file name without extension */ + if (0 == extension_len) { + if (suffix_count == 0) { + snprintf(final_path, final_path_len, + "%s/%s", dir, file_name); + } else { + snprintf(final_path, final_path_len, + "%s/%s_%d", dir, file_name, suffix_count); + } + } else { + if (suffix_count == 0) { + snprintf(final_path, final_path_len, + "%s/%s.%s", dir, file_name, extension); + } else { + snprintf(final_path, final_path_len, + "%s/%s_%d.%s", + dir, file_name, suffix_count, extension); + } + } + + if (is_file_exist(final_path)) { + suffix_count++; + if (suffix_count > max_suffix_count) { + free(final_path); + final_path = DA_NULL; + break; + } else { + memset(final_path, 0x00, final_path_len); + continue; + } + } + + break; + } while (1); + + DA_LOG(FileManager, "decided path = [%s]", final_path); + return final_path; +} + +da_result_t __divide_file_name_into_pure_name_N_extesion(const char *in_file_name, char **out_pure_file_name, char **out_extension) +{ + char *file_name = DA_NULL; + char *tmp_ptr = DA_NULL; + char temp_file[DA_MAX_FILE_PATH_LEN] = {0,}; + char tmp_ext[DA_MAX_STR_LEN] = {0,}; + int len = 0; + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(FileManager); + + if (!in_file_name) + return DA_ERR_INVALID_ARGUMENT; + + file_name = (char *)in_file_name; + tmp_ptr = strrchr(file_name, '.'); + if (tmp_ptr) + tmp_ptr++; + if (tmp_ptr && out_extension) { + strncpy((char*) tmp_ext, tmp_ptr, sizeof(tmp_ext) - 1); + *out_extension = strdup((const char*) tmp_ext); + DA_LOG(FileManager, "extension [%s]", *out_extension); + } + + if (!out_pure_file_name) + return ret; + + if (tmp_ptr) + len = tmp_ptr - file_name - 1; + else + len = strlen(file_name); + + if (len >= DA_MAX_FILE_PATH_LEN) { + strncpy((char*) temp_file, file_name, + DA_MAX_FILE_PATH_LEN - 1); + } else { + strncpy((char*) temp_file, file_name, len); + } + + delete_prohibited_char((char*) temp_file, + strlen((char*) temp_file)); + if (strlen(temp_file) < 1) { + *out_pure_file_name = strdup(NO_NAME_TEMP_STR); + } else { + *out_pure_file_name = strdup( + (const char*) temp_file); + } + + DA_LOG(FileManager, "pure file name [%s]", *out_pure_file_name); + return ret; +} + +da_result_t __file_write_buf_make_buf(file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + buffer = (char*) calloc(DOWNLOAD_NOTIFY_LIMIT, 1); + if (DA_NULL == buffer) { + DA_LOG_ERR(FileManager, "Calloc failure "); + ret = DA_ERR_FAIL_TO_MEMALLOC; + } else { + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + GET_CONTENT_STORE_FILE_BUFFER(file_storage) = buffer; + } + + return ret; +} + +da_result_t __file_write_buf_destroy_buf(file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(FileManager); + + if (GET_CONTENT_STORE_FILE_BUFFER(file_storage)) + free(GET_CONTENT_STORE_FILE_BUFFER(file_storage)); + + GET_CONTENT_STORE_FILE_BUFFER(file_storage) = DA_NULL; + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + + return ret; +} + +da_result_t __file_write_buf_flush_buf(stage_info *stage, file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + int buffer_size = 0; + int write_success_len = 0; + void *fd = DA_NULL; + + // DA_LOG_FUNC_START(FileManager); + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + if (buffer_size == 0) { + DA_LOG_ERR(FileManager, "no data on buffer.."); + return ret; + } + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (DA_NULL == fd) { + DA_LOG_ERR(FileManager, "There is no file handle."); + + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + write_success_len = fwrite(buffer, sizeof(char), buffer_size, + (FILE *) fd); + /* FIXME : This can be necessary later due to progressive download. + * The solution for reducing fflush is needed */ + //fflush((FILE *) fd); + if (write_success_len != buffer_size) { + DA_LOG_ERR(FileManager, "write fails "); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + += write_success_len; + DA_LOG(FileManager, "write %d bytes", write_success_len); + + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_TRUE; + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + +ERR: + return ret; +} + +da_result_t __file_write_buf_copy_to_buf(file_info *file_storage, char *body, + int body_len) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + int buffer_size = 0; + + // DA_LOG_FUNC_START(FileManager); + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + memcpy(buffer + buffer_size, body, body_len); + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) += body_len; + + return ret; +} + +da_result_t __file_write_buf_directly_write(stage_info *stage, + file_info *file_storage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + int write_success_len = 0; + void *fd = DA_NULL; + + // DA_LOG_FUNC_START(FileManager); + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (DA_NULL == fd) { + DA_LOG_ERR(FileManager, "There is no file handle."); + + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + write_success_len = fwrite(body, sizeof(char), body_len, + (FILE *) fd); + /* FIXME : This can be necessary later due to progressive download. + * The solution for reducing fflush is needed */ + //fflush((FILE *) fd); + if (write_success_len != body_len) { + DA_LOG_ERR(FileManager, "write fails "); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + += write_success_len; + DA_LOG(FileManager, "write %d bytes", write_success_len); + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_TRUE; + +ERR: + return ret; +} + +da_result_t file_write_ongoing(stage_info *stage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + int buffer_size = 0; + char *buffer = DA_NULL; + + // DA_LOG_FUNC_START(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) { + DA_LOG_ERR(FileManager, "file_info is empty."); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_FALSE; + + if (DA_NULL == buffer) { + if (body_len < DOWNLOAD_NOTIFY_LIMIT) { + ret = __file_write_buf_make_buf(file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } else { + ret = __file_write_buf_directly_write(stage, + file_storage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + } + } else { + if (DOWNLOAD_NOTIFY_LIMIT <= body_len) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __file_write_buf_directly_write(stage, + file_storage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + + } else if ((DOWNLOAD_NOTIFY_LIMIT - buffer_size) <= body_len) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } else { + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } + } + +ERR: + if (ret != DA_RESULT_OK) { + if (file_storage) { + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + if (GET_CONTENT_STORE_FILE_BUFFER(file_storage)) { + free( + GET_CONTENT_STORE_FILE_BUFFER(file_storage)); + GET_CONTENT_STORE_FILE_BUFFER(file_storage) + = DA_NULL; + } + } + } + return ret; +} + +da_result_t file_write_complete(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info*file_storage = DA_NULL; + char *buffer = DA_NULL; + unsigned int buffer_size = 0; + void *fd = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) { + DA_LOG_ERR(FileManager, "file_info is DA_NULL."); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + if (DA_NULL == buffer) { + DA_LOG_ERR(FileManager, "file buffer is DA_NULL"); + } else { + if (buffer_size != 0) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + } + __file_write_buf_destroy_buf(file_storage); + } + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + + if (fd) { + fclose(fd); + fd = DA_NULL; + } + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; +ERR: + return ret; +} + +da_result_t start_file_writing(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_info_data = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + ret = get_mime_type(stage, + &GET_CONTENT_STORE_CONTENT_TYPE(file_info_data)); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __decide_file_path(stage); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __set_file_size(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = 0; + + ret = __saved_file_open(stage); + +ERR: + return ret; +} + + +da_result_t start_file_writing_append(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(FileManager); + + ret = __saved_file_open(stage); + + return ret; +} + +// for resume with new download request +da_result_t start_file_writing_append_with_new_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + char *original_file_path = DA_NULL; + char *temp_file_path = DA_NULL; + char *extension = DA_NULL; + char *file_name_without_extension = DA_NULL; + req_dl_info *request_info = DA_NULL; + unsigned long long temp_file_size = 0; + + DA_LOG_FUNC_START(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + return DA_ERR_INVALID_ARGUMENT; + request_info = GET_STAGE_TRANSACTION_INFO(stage); + if (!request_info) + return DA_ERR_INVALID_ARGUMENT; + temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(request_info); + if (!temp_file_path) + return DA_ERR_INVALID_ARGUMENT; + original_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + + GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage) = strdup(temp_file_path); + + if (original_file_path) + free(original_file_path); + + ret = get_mime_type(stage, + &GET_CONTENT_STORE_CONTENT_TYPE(file_storage)); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __get_candidate_file_name(stage, &file_name_without_extension, &extension); + if (ret != DA_RESULT_OK) + goto ERR; + + if (file_name_without_extension) { + if (!GET_CONTENT_STORE_PURE_FILE_NAME(file_storage)) { + GET_CONTENT_STORE_PURE_FILE_NAME(file_storage) = file_name_without_extension; + file_name_without_extension = DA_NULL; + } else { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + } + + if (extension) { + if (!GET_CONTENT_STORE_EXTENSION(file_storage)) { + GET_CONTENT_STORE_EXTENSION(file_storage) = extension; + extension = DA_NULL; + } else { + free(extension); + extension = DA_NULL; + } + } + + ret = __set_file_size(stage); + if (DA_RESULT_OK != ret) + goto ERR; + get_file_size(temp_file_path, &temp_file_size); + if (temp_file_size < 1) + goto ERR; + + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = temp_file_size; + + ret = __saved_file_open(stage); + return ret; +ERR: + if (file_name_without_extension) { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + + if (extension) { + free(extension); + extension = DA_NULL; + } + return ret; +} + +da_result_t discard_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + FILE *f_handle = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + + f_handle = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (f_handle) { + fclose(f_handle); + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; + } + return ret; +} + +void clean_paused_file(stage_info *stage) +{ + file_info *file_info_data = DA_NULL; + char *paused_file_path = DA_NULL; + FILE *fd = DA_NULL; + + DA_LOG_FUNC_START(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_info_data); + if (fd) { + fclose(fd); + GET_CONTENT_STORE_FILE_HANDLE(file_info_data) = DA_NULL; + } + + paused_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data); + remove_file((const char*) paused_file_path); + + return; +} + +da_result_t replace_content_file_in_stage(stage_info *stage, + const char *dest_dd_file_path) +{ + da_result_t ret = DA_RESULT_OK; + char *dd_file_path = DA_NULL; + int len; + + DA_LOG_FUNC_START(FileManager); + + if (!dest_dd_file_path + && (DA_FALSE == is_file_exist(dest_dd_file_path))) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + dd_file_path + =GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)); + + if (DA_NULL != dd_file_path) { + remove_file((const char*) dd_file_path); + free(dd_file_path); + } + len = strlen(dest_dd_file_path); + dd_file_path = calloc(1, len + 1); + if (!dd_file_path) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(dd_file_path, dest_dd_file_path, len); + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)) + = dd_file_path; + +ERR: + return ret; + +} + +da_result_t copy_file(const char *src, const char *dest) +{ + FILE *fs = DA_NULL; + FILE *fd = DA_NULL; + int freadnum = 0; + int fwritenum = 0; + char buff[4096] = { 0, }; + + DA_LOG_FUNC_START(FileManager); + + /* open files to copy */ + fs = fopen(src, "rb"); + if (!fs) { + DA_LOG_ERR(FileManager, "Fail to open src file"); + return DA_ERR_FAIL_TO_ACCESS_FILE; + } + + fd = fopen(dest, "wb"); + if (!fd) { + DA_LOG_ERR(FileManager, "Fail to open dest file"); + + fclose(fs); + return DA_ERR_FAIL_TO_ACCESS_FILE; + } + + /* actual copy */ + while (!feof(fs)) { + memset(buff, 0x00, 4096); + freadnum = fread(buff, sizeof(char), sizeof(buff), fs); + if (freadnum > 0) { + fwritenum = fwrite(buff, sizeof(char), freadnum, fd); + if (fwritenum <= 0) { + DA_LOG(FileManager, "written = %d",fwritenum); + break; + } + } else { + DA_LOG(FileManager, "read = %d",freadnum); + break; + } + } + + fclose(fd); + fclose(fs); + + return DA_RESULT_OK; +} + +da_result_t create_dir(const char *install_dir) +{ + da_result_t ret = DA_RESULT_OK; + /* read/write/search permissions for owner and group, + * and with read/search permissions for others. */ + if (mkdir(install_dir, S_IRWXU | S_IRWXG | S_IRWXO)) { + DA_LOG_ERR(FileManager, "Fail to creaate directory [%s]", install_dir); + ret = DA_ERR_FAIL_TO_ACCESS_STORAGE; + } else { + DA_LOG(FileManager, "[%s] is created!", install_dir); + } + return ret; +} + + +da_result_t get_default_dir(char **out_path) +{ + char *tmp_default_path = DA_NULL; + int len = 0; + + if (!out_path) { + DA_LOG_ERR(ClientNoti, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + len = strlen(DA_DEFAULT_FILE_DIR_PATH); + tmp_default_path = calloc(len + 1, sizeof(char)); + if (!tmp_default_path) { + return DA_ERR_FAIL_TO_MEMALLOC; + } + + memcpy(tmp_default_path, DA_DEFAULT_FILE_DIR_PATH, len); + tmp_default_path[len] = '\0'; + + *out_path = tmp_default_path; + + DA_LOG_VERBOSE(FileManager, "default temp path = [%s]", *out_path); + + return DA_RESULT_OK; +} + +da_result_t get_default_install_dir(char **out_path) +{ + char *default_path = DA_NULL; + da_storage_type_t type; + da_result_t ret = DA_RESULT_OK; + int len = 0; + + if (!out_path) { + DA_LOG_ERR(ClientNoti, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + ret = get_storage_type(&type); + if (DA_RESULT_OK != ret) + return ret; + if (type == DA_STORAGE_MMC) + len = strlen(DA_DEFAULT_INSTALL_PATH_FOR_MMC); + else + len = strlen(DA_DEFAULT_INSTALL_PATH_FOR_PHONE); + + default_path = calloc(len + 1, sizeof(char)); + if (!default_path) { + return DA_ERR_FAIL_TO_MEMALLOC; + } + if (type == DA_STORAGE_MMC) + memcpy(default_path, DA_DEFAULT_INSTALL_PATH_FOR_MMC, len); + else // DA_STROAGE_PHONE + memcpy(default_path, DA_DEFAULT_INSTALL_PATH_FOR_PHONE, len); + default_path[len] = '\0'; + + *out_path = default_path; + + DA_LOG_VERBOSE(FileManager, "default temp path = [%s]", *out_path); + return DA_RESULT_OK; +} diff --git a/src/agent/download-agent-http-mgr.c b/src/agent/download-agent-http-mgr.c new file mode 100755 index 0000000..52d72c9 --- /dev/null +++ b/src/agent/download-agent-http-mgr.c @@ -0,0 +1,1796 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-utils.h" +#include "download-agent-debug.h" +#include "download-agent-client-mgr.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-http-msg-handler.h" +#include "download-agent-file.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-plugin-http-interface.h" + +da_result_t make_default_http_request_hdr(const char *url, + char **user_request_header, + int user_request_heaer_count, + http_msg_request_t **out_http_msg_request, + char *user_request_etag, + char *user_request_temp_file_path); +da_result_t create_resume_http_request_hdr(stage_info *stage, + http_msg_request_t **out_resume_request); + +da_result_t start_new_transaction(stage_info *stage); +da_result_t set_http_request_hdr(stage_info *stage); +da_result_t make_transaction_info_and_start_transaction(stage_info *stage); + +da_result_t pause_for_flow_control(stage_info *stage); +da_result_t unpause_for_flow_control(stage_info *stage); + +da_result_t handle_any_input(stage_info *stage); +da_result_t handle_event_control(stage_info *stage, q_event_t *event); +da_result_t handle_event_http(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_packet(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_final(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_abort(stage_info *stage, q_event_t *event); + +da_result_t exchange_url_from_header_for_redirection(stage_info *stage, + http_msg_response_t *http_msg_response); + +da_result_t handle_event_abort(stage_info *stage); +da_result_t handle_event_cancel(stage_info *stage); +da_result_t handle_event_suspend(stage_info *stage); +da_result_t handle_event_resume(stage_info *stage); +da_result_t handle_http_hdr(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status); +da_result_t handle_http_status_code(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status); +da_result_t handle_http_body(stage_info *stage, char *body, int body_len); + +da_result_t set_hdr_fields_on_download_info(stage_info *stage); + +da_result_t _check_content_type_is_matched(stage_info *stage); +da_result_t _check_enough_memory_for_this_download(stage_info *stage); +da_result_t _check_downloaded_file_size_is_same_with_header_content_size( + stage_info *stage); + +da_result_t _check_resume_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response); +da_result_t _check_this_partial_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response); + +da_result_t _cancel_transaction(stage_info *stage); +da_result_t _disconnect_transaction(stage_info *stage); + +void __parsing_user_request_header(char *user_request_header, + char **out_field, char **out_value); + +http_mgr_t http_mgr; + +da_result_t init_http_mgr(void) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(HTTPManager); + + if (http_mgr.is_http_init == DA_FALSE) { + http_mgr.is_http_init = DA_TRUE; + ret = PI_http_init(); + } + + return ret; +} + + +da_result_t request_to_abort_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + if (!stage) { + DA_LOG_ERR(HTTPManager, "Stage is NULL. download info is already destroyed"); + return DA_ERR_INVALID_ARGUMENT; + } + + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_ABORT"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_ABORT, &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), q_event); + } + +ERR: + return ret; +} + +void deinit_http_mgr(void) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (http_mgr.is_http_init == DA_TRUE) { + http_mgr.is_http_init = DA_FALSE; + PI_http_deinit(); + } + + return; +} + +da_result_t request_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + http_state_t http_state = 0; + da_bool_t need_wait = DA_TRUE; + + queue_t *queue = DA_NULL; + req_dl_info *req_info = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + queue = GET_DL_QUEUE(slot_id); + req_info = GET_STAGE_TRANSACTION_INFO(stage); + + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(slot_id)); + + CHANGE_HTTP_STATE(HTTP_STATE_READY_TO_DOWNLOAD, stage); + + do { + ret = handle_any_input(stage); + if (ret != DA_RESULT_OK) { + if (DA_RESULT_OK == GET_REQUEST_HTTP_RESULT(req_info)) { + GET_REQUEST_HTTP_RESULT(req_info) = ret; + DA_LOG_CRITICAL(HTTPManager, "setting internal error [%d]", ret); + } + _cancel_transaction(stage); + } + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG_VERBOSE(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + ret = start_new_transaction(stage); + if (ret != DA_RESULT_OK) { + if (DA_RESULT_OK == GET_REQUEST_HTTP_RESULT(req_info)) { + GET_REQUEST_HTTP_RESULT(req_info) = ret; + DA_LOG_CRITICAL(HTTPManager, "setting internal error [%d]", ret); + } + DA_LOG(HTTPManager, "exiting with error..."); + need_wait = DA_FALSE; + break; + } + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_REQUESTED, stage); + break; + + case HTTP_STATE_CANCELED: + case HTTP_STATE_DOWNLOAD_FINISH: + case HTTP_STATE_ABORTED: +#ifdef PAUSE_EXIT + case HTTP_STATE_PAUSED: +#endif + DA_LOG(HTTPManager, "exiting..."); + need_wait = DA_FALSE; + break; + + default: + break; + } + + if (need_wait == DA_TRUE) { + _da_thread_mutex_lock(&(queue->mutex_queue)); + if (DA_FALSE == GET_IS_Q_HAVING_DATA(queue)) { + unpause_for_flow_control(stage); + +// DA_LOG(HTTPManager, "Waiting for input"); + Q_goto_sleep(queue); +// DA_LOG(HTTPManager, "Woke up to receive new packet or control event"); + } + _da_thread_mutex_unlock (&(queue->mutex_queue)); + + } + + } while (need_wait == DA_TRUE); + + ret = GET_REQUEST_HTTP_RESULT(req_info); + DA_LOG(HTTPManager, "--------------Exiting request_http_download! ret = %d", ret); + return ret; +} + +da_result_t request_to_cancel_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_CANCEL"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_CANCEL, &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), q_event); + } + +ERR: + return ret; +} + +da_result_t request_to_suspend_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_PAUSED: + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG_CRITICAL(HTTPManager, "Already paused. http_state = %d", http_state); + ret = DA_ERR_ALREADY_SUSPENDED; + break; + + default: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_SUSPEND"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_SUSPEND, + &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), + q_event); + } + + break; + } + +ERR: + return ret; +} + +da_result_t request_to_resume_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "[%d] http_state = %d", GET_STAGE_DL_ID(stage), http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_PAUSED: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_RESUME"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_RESUME, + &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), + q_event); + } + + break; + + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG_ERR(HTTPManager, "[%d] Fail to resume. Previous pause is not finished. http_state = %d", GET_STAGE_DL_ID(stage), http_state); + ret = DA_ERR_INVALID_STATE; + + break; + + case HTTP_STATE_RESUMED: + ret = DA_ERR_ALREADY_RESUMED; + + break; + + default: + DA_LOG_ERR(HTTPManager, "[%d] Fail to resume. This is not a paused ID. http_state = %d", GET_STAGE_DL_ID(stage), http_state); + ret = DA_ERR_INVALID_STATE; + + break; + } + +ERR: + return ret; +} + +da_result_t start_new_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + ret = set_http_request_hdr(stage); + if (ret != DA_RESULT_OK) + return ret; + + ret = make_transaction_info_and_start_transaction(stage); + return ret; +} + +da_result_t make_default_http_request_hdr(const char *url, + char **user_request_header, + int user_request_header_count, + http_msg_request_t **out_http_msg_request, + char *user_request_etag, + char *user_request_temp_file_path) +{ + da_result_t ret = DA_RESULT_OK; + + http_msg_request_t *http_msg_request = NULL; + char *user_agent = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!url) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + ret = http_msg_request_create(&http_msg_request); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = http_msg_request_set_url(http_msg_request, url); + if (ret != DA_RESULT_OK) + goto ERR; + + user_agent = get_user_agent(); + if (user_agent) + http_msg_request_add_field(http_msg_request, HTTP_FIELD_UAGENT, + user_agent); + + http_msg_request_add_field(http_msg_request, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); + http_msg_request_add_field(http_msg_request, HTTP_FIELD_ACCEPT_CHARSET, "utf-8"); + + if (user_request_header && user_request_header_count > 0) { + int i = 0; + for (i = 0; i < user_request_header_count; i++) + { + char *field = NULL; + char *value = NULL; + __parsing_user_request_header(user_request_header[i], + &field, &value); + if (field && value) { + http_msg_request_add_field(http_msg_request, field, value); + if (field) { + free(field); + field = NULL; + } + if (value) { + free(value); + value= NULL; + } + } else { + if (field) { + free(field); + field = NULL; + } + if (value) { + free(value); + value= NULL; + } + DA_LOG_ERR(HTTPManager, "Fail to parse user request header"); + } + } + } else + DA_LOG(HTTPManager, "no user reqeust header inserted"); + + if (user_request_etag) { + char buff[64] = {0,}; + unsigned long long size = 0; + http_msg_request_add_field(http_msg_request, + HTTP_FIELD_IF_RANGE, user_request_etag); + get_file_size(user_request_temp_file_path, &size); + snprintf(buff, sizeof(buff)-1, "bytes=%llu-", size); + http_msg_request_add_field(http_msg_request, + HTTP_FIELD_RANGE, buff); + } + + *out_http_msg_request = http_msg_request; + +ERR: + if (ret != DA_RESULT_OK) + http_msg_request_destroy(&http_msg_request); + if (user_agent) + free(user_agent); + return ret; +} + +da_result_t set_http_request_hdr(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_info = DA_NULL; + + char *url = DA_NULL; + char **user_request_header = DA_NULL; + int user_request_header_count = 0; + char *user_request_etag = DA_NULL; + char *user_request_temp_file_path = DA_NULL; + http_msg_request_t* http_msg_request = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (DA_NULL == + (url = GET_REQUEST_HTTP_REQ_URL(request_info))) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + user_request_header = GET_REQUEST_HTTP_USER_REQUEST_HEADER( + request_info); + user_request_header_count = GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT( + request_info); + user_request_etag = GET_REQUEST_HTTP_USER_REQUEST_ETAG( + request_info); + user_request_temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH( + request_info); + if (user_request_etag) { + DA_LOG(HTTPManager, "user_request_etag[%s]",user_request_etag); + } else { + DA_LOG_ERR(HTTPManager, "user_request_etag is NULL"); + } + ret = make_default_http_request_hdr(url, user_request_header, + user_request_header_count, &http_msg_request, + user_request_etag, user_request_temp_file_path); + if (ret == DA_RESULT_OK) + request_info->http_info.http_msg_request = http_msg_request; + +ERR: + return ret; + +} + +da_result_t make_transaction_info_and_start_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + req_dl_info *request_info = DA_NULL; + + input_for_tranx_t *input_for_tranx = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (GET_REQUEST_HTTP_REQ_URL(request_info) == DA_NULL) { + DA_LOG_ERR(HTTPManager, "url is NULL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + input_for_tranx = (input_for_tranx_t*) calloc(1, + sizeof(input_for_tranx_t)); + if (input_for_tranx == DA_NULL) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + input_for_tranx->proxy_addr = get_proxy_address(); + input_for_tranx->queue = GET_DL_QUEUE(slot_id); + + input_for_tranx->http_method = PI_HTTP_METHOD_GET; + input_for_tranx->http_msg_request + = request_info->http_info.http_msg_request; + } + + ret = PI_http_start_transaction(input_for_tranx, + &(GET_REQUEST_HTTP_TRANS_ID(request_info))); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + if (input_for_tranx) { + free(input_for_tranx); + input_for_tranx = DA_NULL; + } + + return ret; +} + +da_result_t make_req_dl_info_http(stage_info *stage, req_dl_info *out_info) +{ + char *url = DA_NULL; + char **user_request_header = DA_NULL; + int user_request_header_count = 0; + char *user_request_etag = DA_NULL; + char *user_request_temp_file_path = DA_NULL; + int dl_id = -1; + source_info_t *source_info = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!stage) { + DA_LOG_ERR(HTTPManager, "stage is NULL"); + return DA_ERR_INVALID_ARGUMENT; + } + + source_info = GET_STAGE_SOURCE_INFO(stage); + + url = source_info->source_info_type.source_info_basic->url; + user_request_header = + source_info->source_info_type.source_info_basic->user_request_header; + user_request_header_count = + source_info->source_info_type.source_info_basic->user_request_header_count; + dl_id = source_info->source_info_type.source_info_basic->dl_id; + user_request_etag = GET_DL_USER_ETAG(GET_STAGE_DL_ID(stage)); + user_request_temp_file_path = GET_DL_USER_TEMP_FILE_PATH(GET_STAGE_DL_ID(stage)); + + DA_LOG(HTTPManager, "url [%s]", url); + + if (url) { + GET_REQUEST_HTTP_REQ_URL(out_info) = url; + GET_REQUEST_HTTP_USER_REQUEST_HEADER(out_info) = user_request_header; + GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT(out_info) = + user_request_header_count; + GET_REQUEST_HTTP_USER_REQUEST_ETAG(out_info) = + user_request_etag; + GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(out_info) = + user_request_temp_file_path; + } else { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + return DA_ERR_INVALID_URL; + } + + _da_thread_mutex_init(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage)), NULL); + + return DA_RESULT_OK; +} + +da_result_t pause_for_flow_control(stage_info *stage) +{ + return DA_RESULT_OK; +} + +da_result_t unpause_for_flow_control(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + // DA_LOG_FUNC_START(HTTPManager); + + PI_http_unpause_transaction( + GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage))); + return ret; +} +da_result_t handle_event_abort(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t state = 0; + + DA_LOG_FUNC_START(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + switch (state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + case HTTP_STATE_REDIRECTED: + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_DOWNLOAD_STARTED: + case HTTP_STATE_DOWNLOADING: + case HTTP_STATE_REQUEST_CANCEL: + case HTTP_STATE_REQUEST_PAUSE: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_CANCELED: + case HTTP_STATE_PAUSED: + case HTTP_STATE_RESUMED: + case HTTP_STATE_ABORTED: + /* IF the network session is terminated due to some error, + * the state can be aborted.(data aborted case) */ + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_ABORTED, stage); + _disconnect_transaction(stage); + break; + case HTTP_STATE_DOWNLOAD_FINISH: + break; + default: + DA_LOG_ERR(HTTPManager, "have to check the flow for this case"); + break; + } + return ret; +} + +da_result_t handle_event_cancel(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t state = 0; + + DA_LOG_FUNC_START(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + switch (state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + case HTTP_STATE_PAUSED: + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_DOWNLOAD_STARTED: + case HTTP_STATE_DOWNLOADING: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_RESUMED: + _cancel_transaction(stage); + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_CANCEL, stage); + break; + + case HTTP_STATE_DOWNLOAD_FINISH: + break; + + case HTTP_STATE_REQUEST_CANCEL: + DA_LOG(HTTPManager, "HTTP_STATE_REQUEST_CANCEL : cancel is already in progress... "); + break; + + default: + DA_LOG_ERR(HTTPManager, "have to check the flow for this case"); + break; + } + + return ret; +} + +da_result_t handle_event_suspend(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG(HTTPManager, "already requested to pause! do nothing"); + break; + + case HTTP_STATE_READY_TO_DOWNLOAD: + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + break; + + default: + //send_client_paused_info(GET_STAGE_DL_ID(stage)); + _cancel_transaction(stage); + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) = DA_RESULT_OK; + DA_LOG_CRITICAL(HTTPManager, "[%d] cleanup internal error", GET_STAGE_DL_ID(stage)); + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_PAUSE,stage); + break; + } + + return ret; +} + +da_result_t handle_event_resume(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_msg_request_t *resume_request = NULL; + + http_state_t http_state = 0; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + if (http_state != HTTP_STATE_PAUSED) { + DA_LOG_ERR(HTTPManager, "Not HTTP_STATE_PAUSED! http_state = %d", http_state); + ret = DA_ERR_INVALID_STATE; + goto ERR; + } + + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) = DA_RESULT_OK; + DA_LOG_CRITICAL(HTTPManager, "[%d] cleanup internal error", GET_STAGE_DL_ID(stage)); + + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_RESUME,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); + + ret = create_resume_http_request_hdr(stage, &resume_request); + if (ret != DA_RESULT_OK) + goto ERR; + + if (GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request) + free(GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request); + + GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request + = resume_request; + + make_transaction_info_and_start_transaction(stage); + +ERR: + return ret; + +} + +da_result_t create_resume_http_request_hdr(stage_info *stage, + http_msg_request_t **out_resume_request) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + req_dl_info *request_info = NULL; + + http_msg_response_t *first_response = NULL; + http_msg_request_t *resume_request = NULL; + + char *value = NULL; + char *url = NULL; + unsigned int downloaded_data_size = 0; + char downloaded_data_size_to_str[32] = { 0, }; + + char *etag_from_response = NULL; + char *date_from_response = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (!(url = GET_REQUEST_HTTP_REQ_URL(GET_STAGE_TRANSACTION_INFO(stage)))) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + first_response = request_info->http_info.http_msg_response; + if (first_response) { + b_ret = http_msg_response_get_ETag(first_response, &value); + if (b_ret) { + etag_from_response = value; + value = NULL; + DA_LOG(HTTPManager, "[ETag][%s]", etag_from_response); + } + + b_ret = http_msg_response_get_date(first_response, &value); + if (b_ret) { + date_from_response = value; + value = NULL; + DA_LOG(HTTPManager, "[Date][%s]", date_from_response); + } + + downloaded_data_size + = GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)); + DA_LOG(HTTPManager, "downloaded_data_size = %u", downloaded_data_size); + snprintf(downloaded_data_size_to_str, sizeof(downloaded_data_size_to_str), "bytes=%u-", + downloaded_data_size); + DA_LOG(HTTPManager, "downloaded_data_size_to_str = %s", downloaded_data_size_to_str); + } + + ret = make_default_http_request_hdr(url, NULL, 0, &resume_request, NULL, NULL); + if (ret != DA_RESULT_OK) + goto ERR; + + if (etag_from_response) { + http_msg_request_add_field(resume_request, HTTP_FIELD_IF_RANGE, + etag_from_response); + } else { + if (date_from_response) { + http_msg_request_add_field(resume_request, + HTTP_FIELD_IF_RANGE, date_from_response); + } + } + + if (strlen(downloaded_data_size_to_str) > 0) + http_msg_request_add_field(resume_request, HTTP_FIELD_RANGE, + downloaded_data_size_to_str); + + *out_resume_request = resume_request; + +ERR: + if (etag_from_response) { + free(etag_from_response); + etag_from_response = NULL; + } + + if (date_from_response) { + free(date_from_response); + date_from_response = NULL; + } + + return ret; +} + +da_result_t handle_any_input(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = GET_STAGE_DL_ID(stage); + + queue_t *queue = DA_NULL; + q_event_t *event = DA_NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + queue = GET_DL_QUEUE(slot_id); + + Q_pop_event(queue, &event); + if (event == DA_NULL) { + DA_LOG(HTTPManager, "There is no data on the queue!"); + return DA_RESULT_OK; + } + + switch (event->event_type) { + case Q_EVENT_TYPE_CONTROL: + ret = handle_event_control(stage, event); + break; + + case Q_EVENT_TYPE_DATA_HTTP: + ret = handle_event_http(stage, event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + break; + + default: + break; + } + Q_destroy_q_event(&event); + + return ret; +} + +da_result_t handle_event_control(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(HTTPManager); + + if (event->event_type == Q_EVENT_TYPE_CONTROL) { + switch (event->type.q_event_control.control_type) { + case Q_EVENT_TYPE_CONTROL_CANCEL: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_CANCEL"); + ret = handle_event_cancel(stage); + break; + + case Q_EVENT_TYPE_CONTROL_SUSPEND: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_SUSPEND"); + ret = handle_event_suspend(stage); + break; + + case Q_EVENT_TYPE_CONTROL_RESUME: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_RESUME"); + ret = handle_event_resume(stage); + break; + case Q_EVENT_TYPE_CONTROL_ABORT: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_ABORT"); + ret = handle_event_abort(stage); + break; + /* Fixme: need to think how we use this type. For now, this type is not used. */ + case Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED"); + break; + } + } + + return ret; +} + +da_result_t handle_event_http(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_data_http_t *q_event_data_http = DA_NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + if (event->event_type == Q_EVENT_TYPE_DATA_HTTP) { + q_event_data_http = &(event->type.q_event_data_http); + switch (q_event_data_http->data_type) { + case Q_EVENT_TYPE_DATA_PACKET: + ret = handle_event_http_packet(stage, event); + + break; + + case Q_EVENT_TYPE_DATA_FINAL: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_DATA_FINAL"); + ret = handle_event_http_final(stage, event); + + break; + + case Q_EVENT_TYPE_DATA_ABORT: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_DATA_ABORT"); + ret = handle_event_http_abort(stage, event); + + break; + } + } + return ret; +} + +da_result_t handle_event_http_packet(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t is_handle_hdr_success = DA_TRUE; + q_event_data_http_t *received_data = DA_NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + received_data = &(event->type.q_event_data_http); + + if (received_data->http_response_msg) { + ret = handle_http_hdr(stage, received_data->http_response_msg, + received_data->http_response_msg->status_code); + if (DA_RESULT_OK != ret) { + is_handle_hdr_success = DA_FALSE; + } + + received_data->http_response_msg = NULL; + } + + if (received_data->body_len > 0) { + if (is_handle_hdr_success == DA_TRUE) { + ret = handle_http_body(stage, received_data->body_data, + received_data->body_len); + } + /*For all cases body_data should be deleted*/ + free(received_data->body_data); + received_data->body_data = DA_NULL; + } + return ret; +} + +da_result_t handle_event_http_final(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + + http_state_t http_state = 0; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + _disconnect_transaction(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REDIRECTED: + CHANGE_HTTP_STATE(HTTP_STATE_READY_TO_DOWNLOAD,stage); + break; + + case HTTP_STATE_DOWNLOAD_REQUESTED: + DA_LOG(HTTPManager, "case HTTP_STATE_DOWNLOAD_REQUESTED"); + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + break; + + case HTTP_STATE_DOWNLOADING: + DA_LOG(HTTPManager, "case HTTP_STATE_DOWNLOADING"); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) { + discard_download(stage); + goto ERR; + } + /* ret = _check_downloaded_file_size_is_same_with_header_content_size(stage); + if(ret != DA_RESULT_OK) + { + discard_download(stage) ; + goto ERR; + } + */ + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + break; + + case HTTP_STATE_REQUEST_PAUSE: + if (GET_CONTENT_STORE_FILE_HANDLE(GET_STAGE_CONTENT_STORE_INFO(stage))) { + ret = file_write_complete(stage); + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = DA_FALSE; + } + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + DA_LOG(HTTPManager, "Server Notification code is set to NULL"); + break; + + case HTTP_STATE_ABORTED: + case HTTP_STATE_CANCELED: + discard_download(stage); + break; + + case HTTP_STATE_REQUEST_CANCEL: + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED, stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + default: + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + break; + } + +ERR: + /* When file complete is failed */ + if (DA_RESULT_OK != ret) { + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + } + return ret; +} + +da_result_t handle_event_http_abort(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + DA_LOG_FUNC_START(HTTPManager); + + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) + = event->type.q_event_data_http.error_type; + DA_LOG_CRITICAL(HTTPManager, "set internal error code : [%d]", GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))); + _disconnect_transaction(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REQUEST_PAUSE: + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + case HTTP_STATE_REQUEST_CANCEL: + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + break; + + default: + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + break; + } +ERR: + return ret; +} + +da_result_t handle_http_hdr(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + http_state_t http_state = 0; + + DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_REQUEST_PAUSE: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_REDIRECTED: + ret = handle_http_status_code(stage, http_msg_response, + http_status); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + case HTTP_STATE_REQUEST_CANCEL: + DA_LOG(HTTPManager, "Cancel is in progress.. http_state = %d", http_state); + break; + + default: + DA_LOG_ERR(HTTPManager, "http_state = %d", http_state); + goto ERR; + } + +ERR: + return ret; +} + +da_result_t handle_http_status_code(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + req_dl_info *request_info = DA_NULL; + http_state_t http_state = 0; + + DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_response + = http_msg_response; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + store_http_status(slot_id, http_status); + + switch (http_status) { + case 200: + case 201: + case 202: + case 203: + if (http_state == HTTP_STATE_REQUEST_RESUME) + clean_paused_file(stage); + ret = set_hdr_fields_on_download_info(stage); + if (ret != DA_RESULT_OK) + goto ERR; + ret = _check_content_type_is_matched(stage); + if (ret != DA_RESULT_OK) + goto ERR; + ret = _check_enough_memory_for_this_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_STARTED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); // ? + break; + + case 206: + DA_LOG(HTTPManager, "HTTP Status is %d - Partial download for resume!",http_status); + /* The resume can be started with start API. + * So the state should be not HTTP_STATE_RESUME_REQUESTED but HTTP_STATE_DOWNLOAD_REQUESTED*/ + if (http_state == HTTP_STATE_DOWNLOAD_REQUESTED) { + ret = _check_resume_download_is_available(stage, + http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_STARTED,stage); + + } else if (http_state == HTTP_STATE_REQUEST_RESUME) { + ret = _check_this_partial_download_is_available(stage, + http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_RESUMED,stage); + } else { + DA_LOG_ERR(HTTPManager, "This download is not resumed, revoke"); + ret = DA_ERR_INVALID_STATE; + goto ERR; + } + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); + break; + + case 300: + case 301: + case 302: + case 303: + case 305: + case 306: + case 307: + DA_LOG(HTTPManager, "HTTP Status is %d - redirection!",http_status); + ret = exchange_url_from_header_for_redirection(stage, http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_REDIRECTED,stage); + http_msg_response_destroy(&http_msg_response); + break; + + case 100: + case 101: + case 102: + case 204: + case 304: + DA_LOG(HTTPManager, "HTTP Status is %d - 204 means server got the request, but no content to reply back, 304 means not modified!",http_status); + ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT; + break; + + case 416: // Requested range not satisfiable + case 503: + case 504: + default: + GET_REQUEST_HTTP_RESULT(request_info) + = DA_ERR_UNREACHABLE_SERVER; + DA_LOG_CRITICAL(HTTPManager, "set internal error code : DA_ERR_UNREACHABLE_SERVER [%d]", DA_ERR_UNREACHABLE_SERVER); + break; + } + +ERR: + return ret; +} + +da_result_t exchange_url_from_header_for_redirection(stage_info *stage, + http_msg_response_t *http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + char *location = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (http_msg_response_get_location(http_msg_response, &location)) { + DA_LOG(HTTPManager, "location = %s\n", location); + GET_REQUEST_HTTP_REQ_LOCATION(GET_STAGE_TRANSACTION_INFO(stage)) = location; + } + + return ret; +} + +da_result_t handle_http_body(stage_info *stage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + int slot_id = DA_INVALID_ID; + + // DA_LOG_FUNC_START(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + if (DA_RESULT_OK + != GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))) { + DA_LOG_CRITICAL(HTTPManager, "ignore because internal error code is set with [%d]", + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))); + return ret; + } + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + // DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + if (http_state == HTTP_STATE_DOWNLOAD_STARTED) { + // resume case + if (GET_REQUEST_HTTP_USER_REQUEST_ETAG(GET_STAGE_TRANSACTION_INFO(stage))) + ret = start_file_writing_append_with_new_download(stage); + else + ret = start_file_writing(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOADING, stage); + send_client_update_dl_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_PURE_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)), + GET_CONTENT_STORE_EXTENSION(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + } else if (http_state == HTTP_STATE_RESUMED) { + ret = start_file_writing_append(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOADING,stage); + send_client_update_dl_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_PURE_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)), + GET_CONTENT_STORE_EXTENSION(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + } + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + // DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REDIRECTED: + DA_LOG(HTTPManager, "Just ignore http body, because this body is not for redirection one."); + break; + + case HTTP_STATE_DOWNLOADING: + /* Should this function before updating download info + * Because it extract mime type at once only if first download updating at client */ + ret = file_write_ongoing(stage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + if ((DA_TRUE == + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)))) { + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = DA_FALSE; + } + break; + + case HTTP_STATE_REQUEST_PAUSE: + ret = file_write_ongoing(stage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + default: + DA_LOG(HTTPManager, "Do nothing! http_state is in case %d", http_state); + + goto ERR; + } + +ERR: + return ret; +} + +/* Function should be renamed , as it is actually not setting the header fields in download info */ +da_result_t set_hdr_fields_on_download_info(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + req_dl_info *request_info = DA_NULL; + http_msg_response_t *http_msg_response = NULL; + + char *value = NULL; + unsigned long long size = 0; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + http_msg_response + = request_info->http_info.http_msg_response; + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "There is no header data!!"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + b_ret = http_msg_response_get_content_type(http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info) = value; + value = NULL; + DA_LOG_VERBOSE(HTTPManager, "[Content-Type][%s] - stored", GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info)); + } + + b_ret = http_msg_response_get_content_length(http_msg_response, + &size); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info) = size; + size = 0; + DA_LOG_VERBOSE(HTTPManager, "[Content-Length][%d] - stored", GET_REQUEST_HTTP_HDR_CONT_LEN(request_info)); + } + + b_ret = http_msg_response_get_ETag(http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_ETAG(request_info) = value; + value = NULL; + DA_LOG_VERBOSE(HTTPManager, "[ETag][%s] - stored ", GET_REQUEST_HTTP_HDR_ETAG(request_info)); + } + +ERR: + return ret; +} + +da_result_t _check_content_type_is_matched(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_info = DA_NULL; + source_info_t *source_info = DA_NULL; + char *content_type_from_server = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + source_info = GET_STAGE_SOURCE_INFO(stage); + + content_type_from_server = GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info); + if (content_type_from_server == DA_NULL) { + DA_LOG(HTTPManager, "http header has no Content-Type field, no need to compare"); + return DA_RESULT_OK; + } + + return ret; +} + +da_result_t _check_enough_memory_for_this_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + req_dl_info *request_info = DA_NULL; + + long long cont_len = 0; + da_storage_size_t memory; + + DA_LOG_FUNC_START(HTTPManager); + + memset(&memory, 0x00, sizeof(da_storage_size_t)); + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + cont_len = (long long) GET_REQUEST_HTTP_HDR_CONT_LEN(request_info); + if (cont_len) { + ret = get_available_memory(DA_STORAGE_PHONE, &memory); + if (DA_RESULT_OK == ret) { + DA_LOG(HTTPManager, "Memory avail: %lu, Memory block :%lu Content: %llu",memory.b_available,memory.b_size, cont_len); + if (memory.b_available < ((cont_len + + SAVE_FILE_BUFFERING_SIZE_50KB) + / memory.b_size)) /* 50KB buffering */ + { + ret = DA_ERR_DISK_FULL; + goto ERR; + } + } + } + +ERR: + return ret; +} + +da_result_t _check_this_partial_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + char *origin_ETag = NULL; + char *new_ETag = NULL; + unsigned long long remained_content_len = 0; + da_storage_size_t memory; + char *value = NULL; + unsigned long long size = 0; + + DA_LOG_FUNC_START(HTTPManager); + + origin_ETag + = GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)); + + b_ret = http_msg_response_get_content_length(new_http_msg_response, + &size); + if (b_ret) { + remained_content_len = size; + size = 0; + DA_LOG(HTTPManager, "[remained_content_len][%lu]", remained_content_len); + } + + b_ret = http_msg_response_get_ETag(new_http_msg_response, &value); + if (b_ret) { + new_ETag = value; + value = NULL; + DA_LOG(HTTPManager, "[new ETag][%s]", new_ETag); + } else { + goto ERR; + } + + if (origin_ETag && new_ETag && + 0 != strncmp(origin_ETag, new_ETag, strlen(new_ETag))) { + DA_LOG_ERR(HTTPManager, "ETag is not identical! revoke!"); + /* FIXME Later : Need to detail error exception handling */ + ret = DA_ERR_NETWORK_FAIL; + /*ret = DA_ERR_MISMATCH_HTTP_HEADER; */ + goto ERR; + } + + if (remained_content_len) { + ret = get_available_memory(DA_STORAGE_PHONE, &memory); + if (DA_RESULT_OK == ret) { + DA_LOG(HTTPManager, "Memory avail: %lu, Memory block :%lu Content: %llu", + memory.b_available,memory.b_size, remained_content_len); + if (memory.b_available < ((remained_content_len + + SAVE_FILE_BUFFERING_SIZE_50KB) + / memory.b_size)) /* 50KB buffering */ + { + ret = DA_ERR_DISK_FULL; + goto ERR; + } + } + } + +ERR: + if (new_ETag) { + free(new_ETag); + new_ETag = DA_NULL; + } + + return ret; +} + +da_result_t _check_resume_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + char *origin_ETag = NULL; + char *new_ETag = NULL; + unsigned long long remained_content_len = 0; + da_storage_size_t memory; + char *value = NULL; + unsigned long long size = 0; + char *temp_file_path = DA_NULL; + req_dl_info *request_info = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + origin_ETag + = GET_REQUEST_HTTP_USER_REQUEST_ETAG(request_info); + + b_ret = http_msg_response_get_content_length(new_http_msg_response, + &size); + if (b_ret) { + remained_content_len = size; + size = 0; + DA_LOG(HTTPManager, "[remained_content_len][%lu]", remained_content_len); + } + + b_ret = http_msg_response_get_ETag(new_http_msg_response, &value); + if (b_ret) { + new_ETag = value; + value = NULL; + DA_LOG(HTTPManager, "[new ETag][%s]", new_ETag); + } else { + goto ERR; + } + + if (origin_ETag && new_ETag && + 0 != strncmp(origin_ETag, new_ETag, strlen(new_ETag))) { + DA_LOG_ERR(HTTPManager, "ETag is not identical! revoke!"); + /* FIXME Later : Need to detail error exception handling */ + ret = DA_ERR_NETWORK_FAIL; + /*ret = DA_ERR_MISMATCH_HTTP_HEADER; */ + goto ERR; + } + + if (remained_content_len) { + ret = get_available_memory(DA_STORAGE_PHONE, &memory); + if (DA_RESULT_OK == ret) { + DA_LOG(HTTPManager, "Memory avail: %lu, Memory block :%lu Content: %llu", + memory.b_available,memory.b_size, remained_content_len); + if (memory.b_available < ((remained_content_len + + SAVE_FILE_BUFFERING_SIZE_50KB) + / memory.b_size)) /* 50KB buffering */ + { + ret = DA_ERR_DISK_FULL; + goto ERR; + } + } + } + b_ret = http_msg_response_get_content_type(new_http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info) = value; + value = NULL; + DA_LOG(HTTPManager, "[Content-Type][%s]", + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info)); + } + temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(request_info); + get_file_size(temp_file_path, &size); + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info) = remained_content_len + size; + DA_LOG(HTTPManager, "[Content-Length][%d]", + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info)); + + +ERR: + if (new_ETag) { + free(new_ETag); + new_ETag = DA_NULL; + } + + return ret; +} + + +da_result_t _check_downloaded_file_size_is_same_with_header_content_size( + stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + req_dl_info *request_info = DA_NULL; + file_info *file_info_data = DA_NULL; + + char *real_file_path = DA_NULL; + unsigned long long content_size_from_real_file = 0; + unsigned long long content_size_from_http_header = 0; + + DA_LOG_FUNC_START(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + + content_size_from_http_header + = GET_CONTENT_STORE_FILE_SIZE(file_info_data); + + if (content_size_from_http_header > 0) { + real_file_path + = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data); + + get_file_size(real_file_path, + &content_size_from_real_file); + + if (content_size_from_real_file + != content_size_from_http_header) { + DA_LOG_ERR(HTTPManager, "size from header = %llu, real size = %llu, DA_ERR_MISMATCH_CONTENT_SIZE", + content_size_from_http_header, content_size_from_real_file); + ret = DA_ERR_MISMATCH_CONTENT_SIZE; + } + } + + return ret; +} + +da_result_t _disconnect_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + int transaction_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(HTTPManager); + + transaction_id + = GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)); + + DA_LOG(HTTPManager, "transaction_id = %d slot_id = %d", transaction_id, GET_STAGE_DL_ID(stage)); + + if (transaction_id != DA_INVALID_ID) { + ret = PI_http_disconnect_transaction(transaction_id); + GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)) + = DA_INVALID_ID; + } + + return ret; +} + +da_result_t _cancel_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + int transaction_id = DA_INVALID_ID; + + DA_LOG_FUNC_START(HTTPManager); + + transaction_id + = GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)); + + DA_LOG(HTTPManager, "transaction_id = %d", transaction_id); + + if (transaction_id != DA_INVALID_ID) { + http_state_t state = 0; + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + if (state <= HTTP_STATE_DOWNLOAD_REQUESTED) + ret = PI_http_cancel_transaction(transaction_id, DA_TRUE); + else + ret = PI_http_cancel_transaction(transaction_id, DA_FALSE); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + } + + return ret; +} + +void __parsing_user_request_header(char *user_request_header, + char **out_field, char **out_value) +{ + int len = 0; + char *pos = NULL; + char *temp_pos = NULL; + char *field = NULL; + char *value = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!user_request_header) { + DA_LOG_ERR(HTTPManager, "user_request_header is NULL"); + goto ERR; + } + + pos = strchr(user_request_header, ':'); + if (!pos) { + DA_LOG_ERR(HTTPManager, "Fail to parse"); + goto ERR; + } + temp_pos = (char *)user_request_header; + while (*temp_pos) + { + if (temp_pos == pos || *temp_pos == ' ') { + len = temp_pos - user_request_header; + break; + } + temp_pos++; + } + if (len < 1) { + DA_LOG_ERR(HTTPManager, "Wrong field name"); + goto ERR; + } + field = (char *)calloc(1, len + 1); + if (!field) { + DA_LOG_ERR(HTTPManager, "Fail to calloc"); + goto ERR; + } + strncpy(field, user_request_header, len); + pos++; + while (*pos) + { + if (*pos != ' ') + break; + pos++; + } + len = strlen(pos) + 1; + value = (char *)calloc(1, len + 1); + if (!value) { + DA_LOG_ERR(HTTPManager, "Fail to calloc"); + goto ERR; + } + strncpy(value, pos, len); + *out_field = field; + *out_value = value; + DA_LOG(HTTPManager, "field[%s], value[%s]", field, value); + + return; +ERR: + if (field) { + free(field); + field = NULL; + } + return; +} + diff --git a/src/agent/download-agent-http-misc.c b/src/agent/download-agent-http-misc.c new file mode 100755 index 0000000..67483b0 --- /dev/null +++ b/src/agent/download-agent-http-misc.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-http-misc.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-client-mgr.h" + +#define DEFAULT_HTTP_ACCEPT_HEADERS \ + "Accept-Language: en\r\n" \ + "Accept-Charset: utf-8\r\n" \ + + +char *get_user_agent() +{ + char *uagent_str = DA_NULL; + + DA_LOG_FUNC_START(Default); + + uagent_str = get_client_user_agent_string(); + if (!uagent_str) { + da_result_t ret = DA_RESULT_OK; + ret = get_user_agent_string(&uagent_str); + if (ret != DA_RESULT_OK) + return NULL; + } + return uagent_str; +} + +da_bool_t is_supporting_protocol(const char *protocol) +{ + if((protocol == NULL) || (1 > strlen(protocol))) + { + return DA_FALSE; + } + + if(!strcasecmp(protocol, "http")) + { + return DA_TRUE; + } + else if(!strcasecmp(protocol, "https")) + { + return DA_TRUE; + } + else + { + return DA_FALSE; + } + +} diff --git a/src/agent/download-agent-http-msg-handler.c b/src/agent/download-agent-http-msg-handler.c new file mode 100755 index 0000000..05a0a68 --- /dev/null +++ b/src/agent/download-agent-http-msg-handler.c @@ -0,0 +1,1343 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "download-agent-http-msg-handler.h" +#include "download-agent-debug.h" +#include "download-agent-http-misc.h" +#include "download-agent-encoding.h" + +// '.' and ';' are request from Vodafone +#define IS_TERMINATING_CHAR(c) ( ((c) == ';') || ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) +#define IS_TERMINATING_CHAR_EX(c) ( ((c) == '"') || ((c) == ';') || ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) +#define IS_URI_TERMINATING_CHAR(c) ( ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) + +enum parsing_type { + WITH_PARSING_OPTION, + WITHOUT_PARSING_OPTION +}; + +static da_result_t __http_header_add_field(http_header_t **head, + const char *field, const char *value, enum parsing_type type); +static void __http_header_destroy_all_field(http_header_t **head); +static da_bool_t __get_http_header_for_field( + http_msg_response_t *http_msg_response, const char *in_field, + http_header_t **out_header); +static void __exchange_header_value(http_header_t *header, + const char *in_raw_value); + +static http_header_options_t *__create_http_header_option(const char *field, + const char *value); +static void __http_header_destroy_all_option(http_header_options_t **head); +static da_bool_t __get_http_header_option_for_field( + http_header_options_t *header_option, const char *in_field, + char **out_value); + +static http_header_options_t *__parsing_N_create_option_str(char *org_str); +static http_header_options_t *__parsing_options(char *org_str); +static void __parsing_raw_value(http_header_t *http_header); + +da_result_t http_msg_request_create(http_msg_request_t **http_msg_request) +{ + http_msg_request_t *temp_http_msg_request = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + temp_http_msg_request = (http_msg_request_t *)calloc(1, + sizeof(http_msg_request_t)); + if (!temp_http_msg_request) { + *http_msg_request = NULL; + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + temp_http_msg_request->http_method = NULL; + temp_http_msg_request->url = NULL; + temp_http_msg_request->head = NULL; + temp_http_msg_request->http_body = NULL; + + *http_msg_request = temp_http_msg_request; + DA_LOG(HTTPManager, "http_msg_request: %x", (unsigned int)(*http_msg_request)); + + return DA_RESULT_OK; +} + +void http_msg_request_destroy(http_msg_request_t **http_msg_request) +{ + http_msg_request_t *temp_http_msg_request = *http_msg_request; + + DA_LOG_FUNC_START(HTTPManager); + + if (temp_http_msg_request) { + if (temp_http_msg_request->http_method) { + free(temp_http_msg_request->http_method); + temp_http_msg_request->http_method = NULL; + } + + if (temp_http_msg_request->url) { + free(temp_http_msg_request->url); + temp_http_msg_request->url = NULL; + } + + if (temp_http_msg_request->http_body) { + free(temp_http_msg_request->http_body); + temp_http_msg_request->http_body = NULL; + } + + __http_header_destroy_all_field(&(temp_http_msg_request->head)); + + free(temp_http_msg_request); + *http_msg_request = NULL; + } + +} + +da_result_t http_msg_request_set_method(http_msg_request_t *http_msg_request, + const char *method) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request || !method) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + // ToDo: check method is valid + + http_msg_request->http_method = strdup(method); + + DA_LOG(HTTPManager, "http method : %s", http_msg_request->http_method); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_method(http_msg_request_t *http_msg_request, + const char **method) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->http_method) { + *method = http_msg_request->http_method; + return DA_RESULT_OK; + } else { + *method = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +da_result_t http_msg_request_set_url(http_msg_request_t *http_msg_request, + const char *url) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "http_msg_request is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (!url) { + DA_LOG_ERR(HTTPManager, "url is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_URL; + } + + http_msg_request->url = strdup(url); + + DA_LOG(HTTPManager, "http url : %s", http_msg_request->url); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_url(http_msg_request_t *http_msg_request, + const char **url) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "http_msg_request is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->url) { + *url = http_msg_request->url; + return DA_RESULT_OK; + } else { + *url = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +da_result_t http_msg_request_set_body(http_msg_request_t *http_msg_request, + const char *body) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (!body) + return DA_RESULT_OK; + + http_msg_request->http_body = strdup(body); + + DA_LOG(HTTPManager, "http body : %s", http_msg_request->http_body); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_body(http_msg_request_t *http_msg_request, + const char **body) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->http_body) { + *body = http_msg_request->http_body; + return DA_RESULT_OK; + } else { + *body = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +/* FIXME later : check to free filed and value after this API is called */ +da_result_t http_msg_request_add_field(http_msg_request_t *http_msg_request, + const char *field, const char *value) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + return __http_header_add_field(&(http_msg_request->head), field, value, WITHOUT_PARSING_OPTION); +} + +da_result_t http_msg_response_create(http_msg_response_t **http_msg_response) +{ + http_msg_response_t *temp_http_msg_response = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + temp_http_msg_response = (http_msg_response_t *)calloc(1, + sizeof(http_msg_response_t)); + if (!temp_http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } else { + temp_http_msg_response->status_code = 0; + temp_http_msg_response->head = NULL; + + *http_msg_response = temp_http_msg_response; + + return DA_RESULT_OK; + } +} + +void http_msg_response_destroy(http_msg_response_t **http_msg_response) +{ + http_msg_response_t *temp_http_msg_response = *http_msg_response; + + DA_LOG_FUNC_START(HTTPManager); + if (temp_http_msg_response) { + __http_header_destroy_all_field(&(temp_http_msg_response->head)); + + free(temp_http_msg_response); + *http_msg_response = DA_NULL; + } +} + +da_result_t http_msg_response_set_status_code( + http_msg_response_t *http_msg_response, int status_code) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + http_msg_response->status_code = status_code; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_get_status_code( + http_msg_response_t *http_msg_response, int *status_code) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *status_code = http_msg_response->status_code; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_add_field(http_msg_response_t *http_msg_response, + const char *field, const char *value) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + return __http_header_add_field(&(http_msg_response->head), field, value, WITH_PARSING_OPTION); +} + +da_result_t __http_header_add_field(http_header_t **head, + const char *field, const char *value, enum parsing_type type) +{ + http_header_t *pre = NULL; + http_header_t *cur = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + DA_LOG(HTTPManager, "[%s][%s]", field, value); + + pre = cur = *head; + while (cur) { + pre = cur; + /* Replace default value with user wanted value + * Remove the value which is stored before and add a new value. + */ + if (cur->field && cur->raw_value && + strncmp(cur->field, field, strlen(field)) == 0) { + DA_LOG(HTTPManager, "Remove value for replacement [%s][%s]", cur->field, cur->raw_value); + if (cur->field) { + free(cur->field); + cur->field = NULL; + } + if (cur->raw_value) { + free(cur->raw_value); + cur->raw_value= NULL; + } + } + cur = cur->next; + } + + cur = (http_header_t *)calloc(1, sizeof(http_header_t)); + if (cur) { + cur->field = strdup(field); + cur->raw_value = strdup(value); + cur->options = NULL; + cur->next = NULL; + + if (type == WITHOUT_PARSING_OPTION) { + cur->value = strdup(value); + cur->options = NULL; + } else { + __parsing_raw_value(cur); + } + + if (pre) + pre->next = cur; + else + *head = cur; + } else { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + return DA_RESULT_OK; +} + +void __http_header_destroy_all_field(http_header_t **head) +{ + http_header_t *pre = NULL; + http_header_t *cur = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + cur = *head; + + while (cur) { + if (cur->field) { + DA_LOG_VERBOSE(HTTPManager, "field= %s", cur->field); + free(cur->field); + cur->field = DA_NULL; + } + + if (cur->value) { + free(cur->value); + cur->value = DA_NULL; + } + + if (cur->raw_value) { + free(cur->raw_value); + cur->raw_value = DA_NULL; + } + + __http_header_destroy_all_option(&(cur->options)); + + pre = cur; + cur = cur->next; + + free(pre); + } + + *head = DA_NULL; +} + +http_header_options_t *__create_http_header_option(const char *field, + const char *value) +{ + http_header_options_t *option = NULL; + + option = (http_header_options_t *)calloc(1, + sizeof(http_header_options_t)); + if (option) { + if (field) + option->field = strdup(field); + + if (value) + option->value = strdup(value); + + option->next = NULL; + } + + return option; +} + +void __http_header_destroy_all_option(http_header_options_t **head) +{ + http_header_options_t *pre = NULL; + http_header_options_t *cur = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + cur = *head; + + while (cur) { + if (cur->field) { + DA_LOG_VERBOSE(HTTPManager, "field= %s", cur->field); + free(cur->field); + cur->field = DA_NULL; + } + + if (cur->value) { + free(cur->value); + cur->value = DA_NULL; + } + + pre = cur; + cur = cur->next; + + free(pre); + } + + *head = DA_NULL; +} + +da_result_t http_msg_request_get_iter(http_msg_request_t *http_msg_request, + http_msg_iter_t *http_msg_iter) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *http_msg_iter = http_msg_request->head; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_get_iter(http_msg_response_t *http_msg_response, + http_msg_iter_t *http_msg_iter) +{ + // DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *http_msg_iter = http_msg_response->head; + // DA_LOG(HTTPManager, "retrieve iter = 0x%x", (unsigned int)http_msg_iter); + + return DA_RESULT_OK; +} + +da_bool_t http_msg_get_field_with_iter(http_msg_iter_t *http_msg_iter, + char **out_field, char **out_value) +{ + http_header_t *cur = *http_msg_iter; + + // DA_LOG_FUNC_START(HTTPManager); + + // DA_LOG(HTTPManager, "getting iter = 0x%x", (unsigned int)cur); + + if (cur) { + *out_field = cur->field; + *out_value = cur->value; + *http_msg_iter = cur->next; + + return DA_TRUE; + } else { + // DA_LOG(HTTPManager, "end of iter"); + return DA_FALSE; + } +} + +da_bool_t http_msg_get_header_with_iter(http_msg_iter_t *http_msg_iter, + char **out_field, http_header_t **out_header) +{ + http_header_t *cur = *http_msg_iter; + + // DA_LOG_FUNC_START(HTTPManager); + + // DA_LOG(HTTPManager, "getting iter = 0x%x", (unsigned int)cur); + + if (cur) { + *out_field = cur->field; + *out_header = cur; + *http_msg_iter = cur->next; + + return DA_TRUE; + } else { + // DA_LOG(HTTPManager, "end of iter"); + return DA_FALSE; + } +} + +http_header_options_t *__parsing_N_create_option_str(char *org_str) +{ + char *option_field = NULL; + char *option_value = NULL; + int option_field_len = 0; + int option_value_len = 0; + + char *org_pos = NULL; + int org_str_len = 0; + + char *working_str = NULL; + char *working_pos = NULL; + char *working_pos_field_start = NULL; + char *working_pos_value_start = NULL; + + da_bool_t is_inside_quotation = DA_FALSE; + da_bool_t is_working_for_field = DA_TRUE; + int i = 0; + http_header_options_t *option = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + if (!org_str) + return NULL; + + org_str_len = strlen(org_str); + if (org_str_len <= 0) + return NULL; + + working_str = (char *)calloc(1, org_str_len + 1); + if (!working_str) + return NULL; + + org_pos = org_str; + working_pos_field_start = working_pos = working_str; + + for (i = 0; i < org_str_len; i++) { + if (*org_pos == '"') + is_inside_quotation = !is_inside_quotation; + + if (is_inside_quotation) { + // Leave anything including blank if it is inside of double quotation mark. + *working_pos = *org_pos; + is_working_for_field ? option_field_len++ + : option_value_len++; + working_pos++; + org_pos++; + } else { + if (*org_pos == ' ') { + org_pos++; + } else if (*org_pos == '=') { + if (is_working_for_field) { + is_working_for_field = DA_FALSE; + working_pos_value_start = working_pos; + } + + org_pos++; + } else { + *working_pos = *org_pos; + is_working_for_field ? option_field_len++ + : option_value_len++; + working_pos++; + org_pos++; + } + } + } + + if (option_field_len > 0 && working_pos_field_start) { + option_field = (char *)calloc(1, option_field_len + 1); + if (option_field) + strncpy(option_field, working_pos_field_start, + option_field_len); + } + + if (option_value_len > 0 && working_pos_value_start) { + option_value = (char *)calloc(1, option_value_len + 1); + if (option_value) + strncpy(option_value, working_pos_value_start, + option_value_len); + } + + if (working_str) { + free(working_str); + working_pos = working_str = NULL; + } + + DA_LOG(HTTPManager, "option_field = [%s], option_value = [%s]", + option_field, option_value); + + if (option_field || option_value) { + option = __create_http_header_option( + option_field, option_value); + if (option_field) { + free(option_field); + option_field = NULL; + } + + if (option_value) { + free(option_value); + option_value = NULL; + } + } + return option; +} + +http_header_options_t *__parsing_options(char *org_str) +{ + da_result_t ret = DA_RESULT_OK; + http_header_options_t *head = NULL; + http_header_options_t *pre = NULL; + http_header_options_t *cur = NULL; + + int wanted_str_len = 0; + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + char *cur_pos = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!org_str) + return NULL; + + /* Do Not use strtok(). It's not thread safe. */ + // DA_LOG_CRITICAL(HTTPManager, "org_str = %s", org_str); + + cur_pos = org_str; + + while (cur_pos) { + wanted_str_start = cur_pos; + wanted_str_end = strchr(cur_pos, ';'); + if (wanted_str_end) { + cur_pos = wanted_str_end + 1; + } else { + wanted_str_end = org_str + strlen(org_str); + cur_pos = NULL; + } + + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char *)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + // DA_LOG_CRITICAL(HTTPManager, "wanted_str = [%s]", wanted_str); + cur = __parsing_N_create_option_str(wanted_str); + if (pre) { + pre->next = cur; + pre = cur; + } else { + head = pre = cur; + } + + free(wanted_str); + wanted_str = NULL; + } + +ERR: + if (ret != DA_RESULT_OK) + __http_header_destroy_all_option(&head); + + return head; +} + +void __parsing_raw_value(http_header_t *http_header_field) +{ + char *raw_value = NULL; + char *option_str_start = NULL; + + char *trimed_value = NULL; + int trimed_value_len = 0; + + char *trimed_value_start = NULL; + char *trimed_value_end = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + raw_value = http_header_field->raw_value; + // DA_LOG_CRITICAL(HTTPManager, "raw_value = [%s]", raw_value); + + if (!raw_value) + return; + + trimed_value_start = raw_value; + + trimed_value_end = strchr(raw_value, ';'); + if (!trimed_value_end) { + // No options + http_header_field->value = strdup(raw_value); + http_header_field->options = NULL; + + return; + } + + // for trimed value + trimed_value_len = trimed_value_end - trimed_value_start; + + trimed_value = (char *)calloc(1, trimed_value_len + 1); + if (!trimed_value) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return; + } + strncpy(trimed_value, trimed_value_start, trimed_value_len); + http_header_field->value = trimed_value; + + // for option parsing + option_str_start = trimed_value_end + 1; + + http_header_field->options = __parsing_options(option_str_start); + + /////////////// show + http_header_options_t *cur = NULL; + + cur = http_header_field->options; + while (cur) { + DA_LOG(HTTPManager, "field = [%s], value = [%s]", cur->field, cur->value); + cur = cur->next; + } + +} + +da_bool_t __get_http_header_option_for_field( + http_header_options_t *header_option, const char *in_field, + char **out_value) +{ + http_header_options_t *cur = NULL; + + // DA_LOG_FUNC_START(HTTPManager); + + if (!header_option) { + DA_LOG_ERR(HTTPManager, "input header_option is NULL."); + return DA_FALSE; + } + + cur = header_option; + while (cur) { + if (cur->field) { + if (!strncmp(cur->field, in_field, strlen(cur->field)) && + cur->value) { + DA_LOG(HTTPManager, "[%s][%s]", cur->field, cur->value); + *out_value = cur->value; + return DA_TRUE; + } + + } + cur = cur->next; + } + + return DA_FALSE; +} + +da_bool_t __get_http_header_for_field(http_msg_response_t *http_msg_response, + const char *in_field, http_header_t **out_header) +{ + http_msg_iter_t http_msg_iter; + http_header_t *header = NULL; + char *field = NULL; + + //DA_LOG_FUNC_START(HTTPManager); + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_header_with_iter(&http_msg_iter, &field, &header)) { + if (field && header && !strncmp(field, in_field, strlen(field))) { + DA_LOG_VERBOSE(HTTPManager, "[%s][%s]", field, header->value); + *out_header = header; + return DA_TRUE; + } + } + + return DA_FALSE; +} + +void __exchange_header_value(http_header_t *header, const char *in_raw_value) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (!header || !in_raw_value) + return; + + __http_header_destroy_all_option(&(header->options)); + + if (header->value) { + free(header->value); + header->value = DA_NULL; + } + + if (header->raw_value) + free(header->raw_value); + header->raw_value = strdup(in_raw_value); + + __parsing_raw_value(header); +} + +da_bool_t http_msg_response_get_content_type( + http_msg_response_t *http_msg_response, char **out_type) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Content-Type", + &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Content-Type"); + return DA_FALSE; + } + + if (out_type) + *out_type = strdup(header->value); + + return DA_TRUE; +} + +void http_msg_response_set_content_type(http_msg_response_t *http_msg_response, + const char *in_type) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response || !in_type) + return; + + b_ret = __get_http_header_for_field(http_msg_response, "Content-Type", + &header); + if (b_ret) { + if (header->raw_value && (!strncmp(header->raw_value, in_type, + strlen(header->raw_value)))) + return; + + DA_LOG(HTTPManager, "exchange Content-Type to [%s] from [%s]", in_type, header->value); + __exchange_header_value(header, in_type); + } else { + __http_header_add_field(&(http_msg_response->head), + "Content-Type", in_type, WITH_PARSING_OPTION); + } +} + +da_bool_t http_msg_response_get_content_length( + http_msg_response_t *http_msg_response, unsigned long long *out_length) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, + "Content-Length", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Content-Length"); + return DA_FALSE; + } + + if (out_length) + *out_length = atoll(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_content_disposition( + http_msg_response_t *http_msg_response, char **out_disposition, + char **out_file_name) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + char *file_name = NULL; + + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + char *decoded_str = NULL; + int wanted_str_len = 0; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, + "Content-Disposition", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Content-Disposition"); + return DA_FALSE; + } + + if (out_disposition) + *out_disposition = strdup(header->value); + + if (!out_file_name) + return DA_FALSE; + + b_ret = __get_http_header_option_for_field(header->options, "filename", + &file_name); + if (!b_ret) { + DA_LOG(HTTPManager, "no option"); + return DA_FALSE; + } + + // eliminate double quotation mark if it exists on derived value + wanted_str_start = strchr(file_name, '"'); + if (!wanted_str_start) { + *out_file_name = strdup(file_name); + return DA_TRUE; + } else { + // DA_LOG(HTTPManager, "wanted_str_start = [%s]", wanted_str_start); + wanted_str_start++; + wanted_str_end = strchr(wanted_str_start, '"'); + if (wanted_str_end) { + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char*)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_FALSE; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + b_ret = is_base64_encoded_word(wanted_str); + if (b_ret) { + DA_LOG(HTTPManager, "It's base64 encoded-word string"); + if (DA_RESULT_OK == decode_base64_encoded_str( + wanted_str, &decoded_str)) { + DA_LOG(HTTPManager, "base64 decoded str = [%s]", decoded_str); + free(wanted_str); + wanted_str = decoded_str; + decoded_str = NULL; + } else { + DA_LOG(HTTPManager, "Fail to base64 decode. Just use un-decoded string."); + } + } else { + DA_LOG(HTTPManager, "It's NOT base64 encoded-word string"); + } + decode_url_encoded_str(wanted_str, &decoded_str); + /* If it is url encoded string */ + if (decoded_str) { + DA_LOG(HTTPManager, "Url decoded str = [%s]", decoded_str); + free(wanted_str); + wanted_str = decoded_str; + decoded_str = NULL; + } + + *out_file_name = wanted_str; + + DA_LOG(HTTPManager, "out_file_name = [%s]", *out_file_name); + + return DA_TRUE; + } else { + DA_LOG_ERR(HTTPManager, "Not matched \" !"); + return DA_FALSE; + } + } +} + +da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "ETag", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no ETag"); + return DA_FALSE; + } + + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Date", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Date"); + return DA_FALSE; + } + + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Location", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Location"); + return DA_FALSE; + } + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_result_t http_msg_response_get_boundary( + http_msg_response_t *http_msg_response, char **out_val) +{ + da_result_t ret = DA_RESULT_OK; + + http_msg_iter_t http_msg_iter; + char *field = NULL; + char *value = NULL; + char *boundary = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_field_with_iter(&http_msg_iter, &field, &value)) { + if ((field != DA_NULL) && (value != DA_NULL)) { + if (!strncmp(field, "Content-Type", + strlen("Content-Type"))) { + char *org_str = NULL; + char *boundary_str_start = NULL; + char *boundary_value_start = NULL; + char *boundary_value_end = NULL; + int boundary_value_len = 0; + + org_str = value; + + boundary_str_start + = strstr(org_str, "boundary"); + if (boundary_str_start) { + DA_LOG(HTTPManager, "boundary_str_start = %s", boundary_str_start); + // this "Content-Type" value has "boundary" in it, so get the value + boundary_value_start = strchr( + boundary_str_start, '"'); + boundary_value_start += 1; // start without " + + boundary_value_end = strchr( + boundary_value_start, '"'); + boundary_value_len = boundary_value_end + - boundary_value_start; + + DA_LOG(HTTPManager, "boundary_value_start = %s", boundary_value_start); + DA_LOG(HTTPManager, "boundary_value_end = %s", boundary_value_end); + DA_LOG(HTTPManager, "boundary_value_len = %d", boundary_value_len); + + } else { + // no "boundary" field on this "Content-Type" value + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + // end of clear + + boundary = (char *)calloc(1, + boundary_value_len + 1); + if (!boundary) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + + goto ERR; + } + strncpy(boundary, boundary_value_start, + boundary_value_len); + DA_LOG(HTTPManager, "[boundary][%s]", boundary); + break; + } + } + } + + *out_val = boundary; + +ERR: + return ret; +} + +char *get_http_response_header_raw(http_msg_response_t *http_msg_response) +{ + http_msg_iter_t http_msg_iter; + http_header_t *header = NULL; + char *field = NULL; + char tmp_buf[1024*4] = {0,}; + char line_buf[1024] = {0,}; + int len = 0; + char *buff = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_header_with_iter(&http_msg_iter, &field, &header)) { + if (field && header) { + // FIXME later :: buffer length is more than total length. think about getting header's conent length from libsoup + len = strlen(field) + strlen(header->value) + 2; + snprintf(line_buf, len,"%s:%s", field, header->value); + strncat(tmp_buf, line_buf, len); + strcat(tmp_buf, "\n"); + } + } + if (strlen(tmp_buf) > 0) { + buff = (char *)calloc(1, strlen(tmp_buf) + 1); + if (buff == DA_NULL) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_NULL; + } + memcpy(buff, tmp_buf, strlen(tmp_buf)); + DA_LOG(HTTPManager, "\n---raw response header---\n%s\n------\n",buff); + return buff; + } else { + return DA_NULL; + } +} + +char *_stristr(const char *long_str, const char *find_str) +{ + int i = 0; + int length_long = 0; + int length_find = 0; + char *ret_ptr = NULL; + char *org_ptr = NULL; + char *look_ptr = NULL; + + if (long_str == NULL || find_str == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return NULL; + } + + length_long = strlen(long_str); + length_find = strlen(find_str); + + org_ptr = (char*)calloc(1, length_long + 1); + + if (org_ptr == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return NULL; + } + + look_ptr = (char*)calloc(1, length_find + 1); + + if (look_ptr == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + free(org_ptr); + return NULL; + } + + while (i < length_long) { + if (isalpha(long_str[i]) != 0) { + if (isupper(long_str[i]) != 0) { + org_ptr[i] = long_str[i]; + } else { + org_ptr[i] = toupper(long_str[i]); + } + } else { + org_ptr[i] = long_str[i]; + } + + i++; + } + + i = 0; + + while (i < length_find) { + if (isalpha(find_str[i]) != 0) { + if (isupper(find_str[i]) != 0) { + look_ptr[i] = find_str[i]; + } else { + look_ptr[i] = toupper(find_str[i]); + } + } else { + look_ptr[i] = find_str[i]; + } + + i++; + } + + ret_ptr = strstr(org_ptr, look_ptr); + + if (ret_ptr == 0) { + free(org_ptr); + free(look_ptr); + return NULL; + } else { + i = ret_ptr - org_ptr; + } + + free(org_ptr); + free(look_ptr); + + return (char*)(long_str + i); +} + +/* This is not used. But it can be needed if there is no http header parser at http library.*/ +da_bool_t extract_attribute_from_header( + char *szHeadStr, + const char *szFindStr, + char **ppRtnValue) +{ + + char *pValuePos = NULL; + int index = 0; + int startPos = 0; + int strLen = 0; + int need_to_end_quataion_mark = 0; + + if (szHeadStr == DA_NULL || szFindStr == DA_NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return DA_FALSE; + } + + if (strlen(szHeadStr) <= 0 || strlen(szFindStr) <= 0) { + DA_LOG_ERR(Default,"INVALID ARGUMENT");; + + return DA_FALSE; + } + + if (ppRtnValue == NULL) { + return DA_FALSE; + } + + pValuePos = _stristr(szHeadStr, (char*)szFindStr); + if (pValuePos == NULL) { + *ppRtnValue = NULL; + goto ERR; + } + + index = strlen(szFindStr); + + while (pValuePos[index] != ':' && pValuePos[index] != '=') { + index++; + + if (pValuePos[index] == '\0') { + return DA_FALSE; + } + } + + index++; + + /* jump space */ + while (pValuePos[index] == ' ') { + index++; + } + + /* jump quatation mark */ + while (pValuePos[index] == '"') { + need_to_end_quataion_mark = 1; + index++; + } + + startPos = index; + + /* Find the end of data. */ + if (0 == strcmp(szFindStr, "Location"))//terminate character list does not contain ';' in case of URI + { + while (DA_FALSE == IS_URI_TERMINATING_CHAR(pValuePos[index])) { + index++; + } + } else if (need_to_end_quataion_mark) { + while (DA_FALSE == IS_TERMINATING_CHAR_EX(pValuePos[index])) { + index++; + } + } else { + while (DA_FALSE == IS_TERMINATING_CHAR(pValuePos[index])) { + index++; + } + } + + strLen = index - startPos; + + if (strLen < 1) { + DA_LOG_ERR(Default," strLen is < 1"); + goto ERR; + } + + *ppRtnValue = (char*)calloc(1, sizeof(char) * (strLen + 1)); + + if (*ppRtnValue == NULL) { + DA_LOG_ERR(Default," *ppRtnValue is NULL"); + goto ERR; + } + + strncpy(*ppRtnValue, pValuePos + startPos, strLen); + *(*ppRtnValue + strLen) = '\0'; + + return DA_TRUE; + +ERR: + + if (*ppRtnValue) { + free(*ppRtnValue); + *ppRtnValue = NULL; + } + + return DA_FALSE; +} + diff --git a/src/agent/download-agent-http-queue.c b/src/agent/download-agent-http-queue.c new file mode 100755 index 0000000..b81f9d8 --- /dev/null +++ b/src/agent/download-agent-http-queue.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-http-queue.h" +#include "download-agent-http-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-pthread.h" + +void init_q_event_data_http(q_event_t *q_event); +void init_q_event_control(q_event_t *q_event); + +void Q_init_queue(queue_t *queue) +{ + queue->having_data = DA_FALSE; + queue->control_head = DA_NULL; + queue->data_head = DA_NULL; + queue->queue_size = 0; + + _da_thread_mutex_init(&(queue->mutex_queue), DA_NULL); + _da_thread_cond_init(&(queue->cond_queue), DA_NULL); +} + +void Q_destroy_queue(queue_t *queue) +{ + q_event_t *event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + do { + Q_pop_event(queue, &event); + Q_destroy_q_event(&event); + } while(event); + + queue->having_data = DA_FALSE; + queue->control_head = DA_NULL; + queue->data_head = DA_NULL; + queue->queue_size = 0; + + _da_thread_mutex_destroy(&(queue->mutex_queue)); + _da_thread_cond_destroy(&(queue->cond_queue)); +} + +void Q_init_q_event(q_event_t *q_event) +{ + switch(q_event->event_type) { + case Q_EVENT_TYPE_DATA_HTTP: + init_q_event_data_http(q_event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + break; + + case Q_EVENT_TYPE_CONTROL: + init_q_event_control(q_event); + break; + } + + q_event->size = 0; + q_event->next = DA_NULL; +} + +void Q_destroy_q_event(q_event_t **in_q_event) +{ + q_event_t *q_event = DA_NULL; + q_event = *in_q_event; + + if(q_event == DA_NULL) + return; + +// DA_LOG(HTTPManager, "destroying size = %d", q_event->size); + + switch(q_event->event_type) { + case Q_EVENT_TYPE_DATA_HTTP: + init_q_event_data_http(q_event); + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + + case Q_EVENT_TYPE_CONTROL: + init_q_event_control(q_event); + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + } +} + +da_result_t Q_make_control_event(q_event_type_control control_type, q_event_t **out_event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + q_event = (q_event_t *)calloc(1, sizeof(q_event_t)); + if(q_event == DA_NULL) { + DA_LOG_ERR(HTTPManager, "calloc fail for q_event"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + + *out_event = DA_NULL; + } else { + q_event->event_type = Q_EVENT_TYPE_CONTROL; + q_event->type.q_event_control.control_type = control_type; + q_event->next = DA_NULL; + + *out_event = q_event; + } + + return ret; +} + +da_result_t Q_make_http_data_event(q_event_type_data data_type, q_event_t **out_event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + +// DA_LOG_FUNC_START(HTTPManager); + + q_event = (q_event_t *)calloc(1, sizeof(q_event_t)); + if(q_event == DA_NULL) { + DA_LOG_ERR(HTTPManager, "calloc fail for q_event"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + *out_event = DA_NULL; + } else { + q_event->event_type = Q_EVENT_TYPE_DATA_HTTP; + q_event->type.q_event_data_http.data_type = data_type; + q_event->next = DA_NULL; + + *out_event = q_event; + +// DA_LOG(HTTPManager, "made event = %x", *out_event); + } + + return ret; + +} + +da_result_t Q_set_status_code_on_http_data_event(q_event_t *q_event, int status_code) +{ + da_result_t ret = DA_RESULT_OK; + +// DA_LOG_FUNC_START(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "status_code can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.status_code = status_code; + +// DA_LOG(HTTPManager, "status_code = %d, q_event = %x", q_event->type.q_event_data_http.status_code, q_event); + +ERR: + return ret; + +} + +da_result_t Q_set_http_body_on_http_data_event(q_event_t *q_event, int body_len, char *body_data) +{ + da_result_t ret = DA_RESULT_OK; + +// DA_LOG_FUNC_START(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "http body can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.body_len = body_len; + q_event->type.q_event_data_http.body_data = body_data; + q_event->size = body_len; + +// DA_LOG(HTTPManager, "body_len = %d, body_data = %x, q_event = %x", q_event->type.q_event_data_http.body_len, q_event->type.q_event_data_http.body_data, q_event); + +ERR: + return ret; + +} + +da_result_t Q_set_error_type_on_http_data_event(q_event_t *q_event, int error_type) +{ + da_result_t ret = DA_RESULT_OK; + +// DA_LOG_FUNC_START(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "error_type can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.error_type = error_type; + + DA_LOG(HTTPManager, "error_type = %d, q_event = %p", q_event->type.q_event_data_http.error_type, q_event); + +ERR: + return ret; + +} + +da_bool_t Q_push_event(const queue_t *in_queue, const q_event_t *in_event) +{ + da_bool_t b_ret = DA_FALSE; + queue_t *queue = (queue_t *)in_queue; + + _da_thread_mutex_lock (&(queue->mutex_queue)); + b_ret = Q_push_event_without_lock(in_queue, in_event); + _da_thread_mutex_unlock (&(queue->mutex_queue)); + + return b_ret; +} + +da_bool_t Q_push_event_without_lock(const queue_t *in_queue, const q_event_t *in_event) +{ + da_bool_t b_ret = DA_FALSE; + queue_t *queue = (queue_t *)in_queue; + q_event_t *event = (q_event_t *)in_event; + q_event_type event_type; + q_event_t *head = DA_NULL; + q_event_t *cur = DA_NULL; + +// DA_LOG_FUNC_START(HTTPManager); +// DA_LOG(HTTPManager, "queue = %x", in_queue); + + event_type = event->event_type; + +// _da_thread_mutex_lock (&(queue->mutex_queue)); + + if(event_type == Q_EVENT_TYPE_CONTROL) { + head = queue->control_head; + if(head == DA_NULL) { + queue->control_head = event; + } else { + cur = head; + + while(cur->next != DA_NULL) { + cur = cur->next; + } + cur->next= event; + } + b_ret = DA_TRUE; + } else { + if((event->size == 0) || (queue->queue_size < MAX_QUEUE_SIZE)) { + head = queue->data_head; + if(head == DA_NULL) { + queue->data_head = event; + } else { + cur = head; + while(cur->next != DA_NULL) { + cur = cur->next; + } + cur->next= event ; + } + + queue->queue_size += event->size; +// DA_LOG(HTTPManager, "queue size is %d", queue->queue_size); + + b_ret = DA_TRUE; + } else { + DA_LOG_CRITICAL(HTTPManager, "rejected event's size is %d queue_size %d", event->size, queue->queue_size); + b_ret = DA_FALSE; + } + } + + queue->having_data = DA_TRUE; + Q_wake_up(queue); +// _da_thread_mutex_unlock (&(queue->mutex_queue)); + return b_ret; +} + +void Q_pop_event(const queue_t *in_queue, q_event_t **out_event) +{ + queue_t *queue = (queue_t*)in_queue; + +// DA_LOG_FUNC_START(HTTPManager); +// DA_LOG(HTTPManager, "queue = %x", in_queue); + + /** Pop Priority + * 1. If there are control event, control event should pop first + * 2. If there is no control event, data event should pop + * 3. If there is no control and data event on queue, pop NULL + */ + + _da_thread_mutex_lock (&(queue->mutex_queue)); + + if(queue->control_head != DA_NULL) {/* Priority 1 */ + *out_event = queue->control_head; + queue->control_head = queue->control_head->next; + } else { + if(queue->data_head != DA_NULL) {/* Priority 2 */ + *out_event = queue->data_head; + queue->data_head = queue->data_head->next; + queue->queue_size -= (*out_event)->size; +// DA_LOG(HTTPManager, "queue size is %d", queue->queue_size); + } else {/* Priority 3 */ + *out_event = DA_NULL; + } + } + + if((queue->control_head == DA_NULL) && (queue->data_head == DA_NULL)) { + queue->having_data = DA_FALSE; + } else { + queue->having_data = DA_TRUE; + } + + _da_thread_mutex_unlock (&(queue->mutex_queue)); + +} + +void Q_goto_sleep(const queue_t *in_queue) +{ +// DA_LOG_FUNC_START(HTTPManager); + DA_LOG_VERBOSE(HTTPManager, "sleep for %p", in_queue); + +//** SHOULD NOT use mutex **// + +// _da_thread_mutex_lock (&(in_queue->mutex_queue)); + _da_thread_cond_wait((pthread_cond_t*)(&(in_queue->cond_queue)),(pthread_mutex_t*) (&(in_queue->mutex_queue))); +// _da_thread_mutex_unlock (&(in_queue->mutex_queue)); +} + +void Q_wake_up(const queue_t *in_queue) +{ +// DA_LOG_FUNC_START(HTTPManager); + DA_LOG_VERBOSE(HTTPManager, "wake up for %p", in_queue); + +//** SHOULD NOT use mutex **// + +// _da_thread_mutex_lock (&(in_queue->mutex_queue)); + _da_thread_cond_signal((pthread_cond_t*)(&(in_queue->cond_queue))); +// _da_thread_mutex_unlock (&(in_queue->mutex_queue)); +} + +void init_q_event_data_http(q_event_t *q_event) +{ + q_event_data_http_t *q_event_data_http; + +// DA_LOG_FUNC_START(HTTPManager); + + if(q_event->event_type == Q_EVENT_TYPE_DATA_HTTP) { + q_event_data_http = &(q_event->type.q_event_data_http); + + if(q_event_data_http) { + q_event_data_http->status_code = DA_NULL; + if(q_event_data_http->http_response_msg) { + http_msg_response_destroy(&(q_event_data_http->http_response_msg)); + } + + if(q_event_data_http->body_len > 0 ) { + if (q_event_data_http->body_data) { + free(q_event_data_http->body_data); + q_event_data_http->body_data = DA_NULL; + } + } + q_event_data_http->error_type = DA_NULL; + } + } +} + +void init_q_event_control(q_event_t *q_event) +{ + q_event_control_t *q_event_control; + +// DA_LOG_FUNC_START(HTTPManager); + + if(q_event->event_type == Q_EVENT_TYPE_CONTROL) { + q_event_control = &(q_event->type.q_event_control); + if(q_event_control) { + q_event_control->control_type = DA_NULL; + } + } + +} diff --git a/src/agent/download-agent-interface.c b/src/agent/download-agent-interface.c new file mode 100755 index 0000000..2ac32dd --- /dev/null +++ b/src/agent/download-agent-interface.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-interface.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-client-mgr.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-basic.h" +#include "download-agent-file.h" + +int da_init( + da_client_cb_t *da_client_callback) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(Default); + + if (!da_client_callback) { + ret = DA_ERR_INVALID_ARGUMENT; + return ret; + } + + ret = init_log_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_client_app_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = reg_client_app(da_client_callback); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_http_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_download_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = create_saved_dir(); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + if (DA_RESULT_OK != ret) + da_deinit(); + + DA_LOG_CRITICAL(Default, "Return ret = %d", ret); + + return ret; +} + +/* TODO:: deinit should clean up all the clients... */ +int da_deinit() +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(Default); + + deinit_http_mgr(); + deinit_download_mgr(); + /* Do not clean temporary download path + * The client can resume or restart download with temporary file in case of failed download. + */ + dereg_client_app(); + DA_LOG(Default, "====== da_deinit EXIT ====="); + + return ret; +} + +int da_start_download( + const char *url, + int *download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_START(Default); + + *download_id = DA_INVALID_ID; + + if (DA_FALSE == is_valid_url(url, &ret)) + goto ERR; + + DA_LOG(Default, "url = %s", url); + + ret = start_download(url, download_id); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + DA_LOG_CRITICAL(Default, "Return: Dl req id = %d, ret = %d", *download_id, ret); + return ret; +} + +int da_start_download_with_extension( + const char *url, + extension_data_t *extension_data, + int *download_id +) +{ + da_result_t ret = DA_RESULT_OK; + int req_header_count = 0; + int i = 0; + + DA_LOG_FUNC_START(Default); + + *download_id = DA_INVALID_ID; + + if (DA_FALSE == is_valid_url(url, &ret)) + goto ERR; + + DA_LOG(Default, "url = %s", url); + + if (ret != DA_RESULT_OK) + goto ERR; + if (!extension_data) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + if (extension_data->request_header_count > 0) { + DA_LOG_VERBOSE(Default, "input request_header_count = [%d]", + extension_data->request_header_count); + for (i = 0; i < extension_data->request_header_count; i++) { + if (extension_data->request_header[i]) { + req_header_count++; + DA_LOG_VERBOSE(Default, "request_header = [%s]", + extension_data->request_header[i]); + } + } + DA_LOG(Default, "actual request_header_count = [%d]", req_header_count); + if (extension_data->request_header_count != req_header_count) { + DA_LOG_ERR(Default, "Request header count is not matched with number of request header array"); + extension_data->request_header = NULL; + extension_data->request_header_count = 0; + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + } + + if (extension_data->install_path) { + if (!is_dir_exist(extension_data->install_path)) + return DA_ERR_INVALID_INSTALL_PATH; + DA_LOG_VERBOSE(Default, "install_path = [%s]", extension_data->install_path); + } + + if (extension_data->file_name) + DA_LOG_VERBOSE(Default, "file_name = [%s]", extension_data->file_name); + if (extension_data->temp_file_path) + DA_LOG_VERBOSE(Default, "temp_file_path = [%s]", extension_data->temp_file_path); + if (extension_data->etag) + DA_LOG_VERBOSE(Default, "etag = [%s]", extension_data->etag); + + if (extension_data->user_data) + DA_LOG_VERBOSE(Default, "user_data = [%p]", extension_data->user_data); + + ret = start_download_with_extension(url, download_id, extension_data); + +ERR: + DA_LOG_CRITICAL(Default, "Return: Dl req id = %d, ret = %d", *download_id, ret); + return ret; +} + +int da_cancel_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Cancel for dl_id = %d", download_id); + + ret = cancel_download(download_id); + + DA_LOG_CRITICAL(Default, "Return: Cancel id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_suspend_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Suspend for dl_id = %d", download_id); + + ret = suspend_download(download_id, DA_TRUE); + + DA_LOG_CRITICAL(Default, "Return: Suspend id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_suspend_download_without_update(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Suspend for dl_id = %d", download_id); + + ret = suspend_download(download_id, DA_FALSE); + + DA_LOG_CRITICAL(Default, "Return: Suspend id = %d, ret = %d", download_id, ret); + return ret; +} + + +int da_resume_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Resume for dl_id = %d", download_id); + + ret = resume_download(download_id); + + DA_LOG_CRITICAL(Default, "Return: Resume id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_is_valid_download_id(int download_id) +{ + da_bool_t ret = DA_FALSE; + ret = is_valid_download_id(download_id); + return ret; +} diff --git a/src/agent/download-agent-mime-util.c b/src/agent/download-agent-mime-util.c new file mode 100755 index 0000000..ef7f4ed --- /dev/null +++ b/src/agent/download-agent-mime-util.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "download-agent-debug.h" +#include "download-agent-mime-util.h" +#include "download-agent-pthread.h" + +#define IS_PROHIBITED_CHAR(c) ((c) == ';' || (c) == '\\' || (c) == '/' || (c) == ':' || (c) == '*' || (c) == '?' || (c) == '"' || (c) == '>' || (c) == '<' || (c) == '|' || (c) == '(' || (c) == ')') +#define IS_SPACE_CHARACTER(c) ((c) == ' ' || (c) == '\t') + +#define DD_MIME_STR "application/vnd.oma.dd+xml" +#define DD_EXT_STR "*.dd" +#define DRM_MIME_STR "application/vnd.oma.drm.message" +#define DRM_EXT_STR "*.dcf" +#define MAX_EXT_TABLE_INDEX 16 +Ext_translation_table ext_trans_table [MAX_EXT_TABLE_INDEX] = { + {"*.xla", "*.xls"}, + {"*.pot", "*.ppt"}, + {"*.xsl", "*.xml"}, + {"*.spl", "*.swf"}, + {"*.oga", "*.ogg"}, + {"*.jpe", "*.jpg"},//5 + {"*.CSSL", "*.css"}, + {"*.htm", "*.html"}, + {"*.hxx", "*.hpp"}, + {"*.c++", "*.cpp"}, + {"CMakeLists.txt", "*.cmake"},//10 + {"*.ime", "*.imy"}, + {"Makefile", "makefile"}, + {"*.3g2", "*.3gp"}, + {"*.mp2", "*.mpg"}, + {"*.divx", "*.avi"},//15 + }; +/* This is samsung mime policy + * 1. if the mime is audio/m4a, the extension name is defined as "m4a" for launching music player +*/ +#ifdef _SAMSUNG_MIME_POLICY +#define MAX_SEC_MIME_TABLE_INDEX 1 +struct sec_mime_table_t { + char *mime; + char *ext; +}; +struct sec_mime_table_t sec_mime_table [MAX_SEC_MIME_TABLE_INDEX] = { + {"audio/m4a", "m4a"}, +}; +#endif + +const char *ambiguous_MIME_Type_list[] = { + "text/plain", + "application/octet-stream" +}; + +/* Because xdgmime is not thread safety, this mutex is necessary */ +pthread_mutex_t mutex_for_xdgmime = PTHREAD_MUTEX_INITIALIZER; + +da_bool_t is_ambiguous_MIME_Type(const char *in_mime_type) +{ +// DA_LOG_FUNC_START(Default); + + if (!in_mime_type) + return DA_FALSE; + + int index = 0; + int list_size = sizeof(ambiguous_MIME_Type_list) / sizeof(const char *); + for (index = 0 ; index < list_size ; index++) { + if (0 == strncmp(in_mime_type, ambiguous_MIME_Type_list[index], + strlen(ambiguous_MIME_Type_list[index]))) { + DA_LOG(Default,"It is ambiguous! [%s]", ambiguous_MIME_Type_list[index]); + return DA_TRUE; + } + } + + return DA_FALSE; +} + +da_result_t da_mime_get_ext_name(char *mime, char **ext) +{ + da_result_t ret = DA_RESULT_OK; + const char **extlist = DA_NULL; + const char *unaliased_mimetype = DA_NULL; + char ext_temp[DA_MAX_STR_LEN] = {0,}; + char *temp = NULL; + + DA_LOG_FUNC_START(Default); + + if (DA_NULL == mime || DA_NULL == ext) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default,"Invalid mime type"); + goto ERR; + } + DA_LOG_VERBOSE(Default,"mime str[%s]ptr[%p]len[%d]",mime,mime,strlen(mime)); + /* unaliased_mimetype means representative mime among similar types */ + _da_thread_mutex_lock(&mutex_for_xdgmime); + unaliased_mimetype = xdg_mime_unalias_mime_type(mime); + _da_thread_mutex_unlock(&mutex_for_xdgmime); + + if (unaliased_mimetype == DA_NULL) { + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG_ERR(Default,"Invalid mime type : No unsaliased mime type"); + goto ERR; + } + DA_LOG(Default,"unaliased_mimetype[%s]\n",unaliased_mimetype); + + /* Get extension name from shared-mime-info */ + _da_thread_mutex_lock(&mutex_for_xdgmime); + extlist = xdg_mime_get_file_names_from_mime_type(unaliased_mimetype); + _da_thread_mutex_unlock(&mutex_for_xdgmime); + if (extlist == DA_NULL || *extlist == DA_NULL) { + int i = 0; + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG(Default,"No extension list"); +#ifdef _SAMSUNG_MIME_POLICY + for (i = 0; i < MAX_SEC_MIME_TABLE_INDEX; i++) + { + if (strncmp(sec_mime_table[i].mime, mime, strlen(mime)) == 0) { + strncpy(ext_temp, sec_mime_table[i].ext, DA_MAX_STR_LEN-1); + ret = DA_RESULT_OK; + break; + } + } +#endif + } else { /* For drm case, this else statement is needed */ + DA_LOG(Default,"extlist[%s]\n",*extlist); + strncpy(ext_temp, *extlist, DA_MAX_STR_LEN); + /* If only one extension name is existed, don't enter here */ + while (*extlist != NULL) { + int i = 0; + /* If there are existed many extension names, + * try to search common extension name from table + * with first mime type at extension list*/ + for (i = 0; i < MAX_EXT_TABLE_INDEX; i++) + { + if (strncmp(ext_trans_table[i].standard,*extlist, + strlen(*extlist)) == 0) { + memset(ext_temp, 0x00, DA_MAX_STR_LEN); + strncpy(ext_temp,ext_trans_table[i].normal, DA_MAX_STR_LEN-1); + break; + } + } + DA_LOG(Default,"index[%d]\n",i); + /* If there is a mime at extension transform table */ + if (i < MAX_EXT_TABLE_INDEX) { + break; + } + DA_LOG(Default,"extlist[%s]\n",*extlist); + extlist++; + } + DA_LOG(Default,"extension from shared mime info[%s]",ext_temp); + } + + if (strlen(ext_temp) < 1) { + /* If there is no mime string for OMD descriptor mime type */ + if (strncmp(DD_MIME_STR,mime,strlen(DD_MIME_STR)) == 0) { + strncpy(ext_temp, DD_EXT_STR, DA_MAX_STR_LEN-1); + ret = DA_RESULT_OK; + /* If there is no extension name for "applicaion/vnd.oma.drm.messeages" + * at shared-mime-info*/ + } else if (strncmp(DRM_MIME_STR,mime,strlen(DD_MIME_STR)) == 0) { + strncpy(ext_temp, DRM_EXT_STR, DA_MAX_STR_LEN-1); + /* If there is extension name at extlist, the return value can have an error.*/ + ret = DA_RESULT_OK; + } else { + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG_ERR(Default,"Invalid mime type : no extension name at list"); + } + } + if (ret != DA_RESULT_OK) + goto ERR; + + temp = strchr(ext_temp,'.'); + if (temp == NULL) + temp = ext_temp; + else + temp++; + + DA_LOG(Default,"final extension name:[%s]",temp); + *ext = (char*)calloc(1, strlen(temp) + 1); + if (*ext != DA_NULL) { + strncpy(*ext, temp,strlen(temp)); + } else { + ret = DA_ERR_FAIL_TO_MEMALLOC ; + goto ERR ; + } +ERR: + return ret; +} + +da_bool_t da_get_extension_name_from_url(char *url, char **ext) +{ + da_bool_t ret = DA_TRUE; + char *buff = DA_NULL; + char *temp_str = DA_NULL; + int buf_len = 0; + + DA_LOG_FUNC_START(Default); + + if (DA_NULL == url || DA_NULL == ext) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + return ret; + } + + if ((temp_str = strrchr(url,'/'))) { + if ((buff = strrchr(temp_str,'.'))) { + char *q = DA_NULL; + buff++; + /* check to exist "?" after extension name */ + q = strrchr(buff,'?'); + if (q) { + buf_len = strlen(buff) - strlen(q); + } else { + buf_len = strlen(buff); + } + *ext = (char*) calloc(1, buf_len + 1) ; + + if (DA_NULL == *ext) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Memory Fail"); + goto ERR; + } + strncpy(*ext,buff,buf_len); + DA_LOG(Default,"extention name[%s]",*ext); + return ret; + } + } +ERR: + if (*ext) { + free(*ext); + *ext = DA_NULL; + } + return ret; +} + +/* FIXME move this function to another file */ +da_bool_t da_get_file_name_from_url(char *url, char **name) +{ + da_bool_t ret = DA_TRUE; + char *buff = DA_NULL; + char *Start = NULL; + char *End = NULL; + char c = 0; + int i = 0; + int j = 0; + int len_name = 0; + char name_buff[DA_MAX_FILE_PATH_LEN] = {0,}; + + DA_LOG_FUNC_START(Default); + + if (DA_NULL == url || DA_NULL == name) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + goto ERR; + } + *name = DA_NULL; + if (!strstr(url, "http") && !strstr(url, "https")) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + goto ERR; + } + + buff = (char*) calloc(1, strlen(url) +1); + if(DA_NULL == buff) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Memory Fail"); + goto ERR; + } + + while((c = url[i++]) != 0) { + if(c == '%') { + char buffer[3] = {0,}; + buffer[0] = url[i++]; + buffer[1] = url[i++]; + buff[j++] = (char)strtol(buffer,NULL,16); + } else { + buff[j++] = c; + } + } + End = strstr(buff, "?"); + if (DA_NULL != End) { + Start = End -1; + while(*(Start) != '/') { + Start--; + } + if ((*(Start) == '/') && ((len_name = (End - Start)) > 1)) { + Start++; + if (DA_MAX_FILE_PATH_LEN <= len_name) { + strncpy(name_buff, Start, DA_MAX_FILE_PATH_LEN-1); + name_buff[DA_MAX_FILE_PATH_LEN-1] = '\0'; + } else { + strncpy(name_buff, Start, len_name); + name_buff[len_name] = '\0'; + } + } else { + ret = DA_FALSE; + goto ERR ; /*Name not found*/ + } + } else { + int urlLen = strlen (buff); + int Start_pos = 0; + Start_pos = urlLen - 1; + + while(Start_pos > 0) { + if(buff[Start_pos] == '/') + break; + Start_pos--; + } + Start_pos++; + if (Start_pos == 0 || urlLen - Start_pos <= 0) { + ret = DA_FALSE; + goto ERR; + } + while(Start_pos < urlLen) { + name_buff[len_name++] = buff[Start_pos++]; + if (DA_MAX_FILE_PATH_LEN <= len_name) { + name_buff[DA_MAX_FILE_PATH_LEN-1] ='\0'; + break; + } + } + } + + if (len_name) { + End = strrchr(name_buff, '.'); + if (End != NULL) { + *End = '\0'; + } + DA_LOG(Default,"file name BEFORE removing prohibited character = %s", name_buff); + delete_prohibited_char(name_buff, strlen(name_buff)); + DA_LOG(Default,"file name AFTER removing prohibited character = %s", name_buff); + len_name = strlen(name_buff); + *name = (char*) calloc(1, len_name + 1); + if (*name) { + strncpy(*name, name_buff,len_name); + } + } + DA_LOG(Default,"Extracted file name : %s", *name); +ERR: + if (buff) { + free (buff); + buff = DA_NULL; + } + return ret; +} + +void delete_prohibited_char(char *szTarget, int str_len) +{ + char *chk_str = NULL; + int i = 0; + int j = 0; + int tar_len = 0; + + if(szTarget == NULL || str_len <= 0 || strlen(szTarget) != str_len) { + DA_LOG_ERR(Default,"Invaild Parameter\n"); + return; + } + + chk_str = (char *)calloc(1, str_len + 1); + if(chk_str == NULL) + return; + + while(szTarget[j] != '\0') { + if(IS_PROHIBITED_CHAR(szTarget[j]) == DA_FALSE && + IS_SPACE_CHARACTER(szTarget[j]) == DA_FALSE) { + chk_str[i] = szTarget[j]; + i++; + } + j++; + } + + chk_str[i] = '\0'; + tar_len = strlen(chk_str); + + if(tar_len <= 0) + szTarget[0] = '\0'; + else { + for(i = 0; i < tar_len; i++) + { + szTarget[i] = chk_str[i]; + } + szTarget[i] = '\0'; + } + + if(chk_str != NULL) { + free(chk_str); + } + return; +} + diff --git a/src/agent/download-agent-plugin-conf.c b/src/agent/download-agent-plugin-conf.c new file mode 100755 index 0000000..f0ce27f --- /dev/null +++ b/src/agent/download-agent-plugin-conf.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#ifdef _EFL_PLATFORM +#include +#include +#include +#endif /* _EFL_PLATFORM */ + +#include "download-agent-plugin-conf.h" +#include "download-agent-debug.h" +#include "download-agent-file.h" + +#define DEFAULT_UA_STR "Mozilla/5.0 (Linux; U; Tizen 1.0; en-us) AppleWebKit/534.46 (KHTML, like Gecko) Mobile Tizen Browser/1.0" + +da_result_t __get_conf_string(const char *key, char **out_string); + +da_result_t __get_conf_string(const char *key, char **out_string) +{ +#ifdef _EFL_PLATFORM + if (!key || !out_string) { + DA_LOG_ERR(Default,"Invalid Argument"); + return DA_ERR_INVALID_ARGUMENT; + } + + *out_string = vconf_get_str(key); + return DA_RESULT_OK; +#else + if (out_string) + *out_string = NULL; + + return DA_RESULT_OK; +#endif +} + +da_result_t get_storage_type(da_storage_type_t *type) +{ + da_result_t ret = DA_RESULT_OK; +#ifdef _EFL_PLATFORM + int value = -1; +#endif + + if (!type) { + DA_LOG_ERR(Default,"DA_ERR_CONF_FAIL"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + +#ifdef _EFL_PLATFORM + if (0 != vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_WAP_INT, &value)) { + DA_LOG_ERR(Default,"DA_ERR_CONF_FAIL"); + ret = DA_ERR_FAIL_TO_GET_CONF_VALUE; + goto ERR; + } + + switch (value) { + case SETTING_DEF_MEMORY_PHONE: + DA_LOG(Default,"Storage set - DA_STORAGE_PHONE"); + *type = DA_STORAGE_PHONE; + break; + case SETTING_DEF_MEMORY_MMC: + *type = DA_STORAGE_MMC; + DA_LOG(Default,"Storage set - DA_STORAGE_MMC"); + break; + case SETTING_DEF_MEMORY_MAX: + *type = DA_STORAGE_SYSTEM; + DA_LOG(Default,"Storage set - DA_STORAGE_SYSTEM"); + break; + default: + DA_LOG_ERR(Default,"DA_ERR_CONF_FAIL"); + ret = DA_ERR_FAIL_TO_GET_CONF_VALUE; + break; + } +#endif + +ERR: + return ret; + +} + +da_result_t get_user_agent_string(char **uagent_str) +{ + da_result_t ret = DA_RESULT_OK; +#ifdef _EFL_PLATFORM + char *key = DA_NULL; +#endif + + DA_LOG_FUNC_START(Default); + + if (!uagent_str) { + DA_LOG_ERR(Default,"Invalid Argument"); + return DA_ERR_INVALID_ARGUMENT; + } + +#ifdef _EFL_PLATFORM + key = VCONFKEY_BROWSER_USER_AGENT; + ret = __get_conf_string(key, uagent_str); + if(ret == DA_RESULT_OK) { + if(*uagent_str) { + DA_LOG(Default,"getting uagent_str = \n%s", *uagent_str); + return ret; + } + } + DA_LOG_ERR(Default,"No UA information from vconf !!"); + *uagent_str = strdup(DEFAULT_UA_STR); + DA_LOG(Default,"Set default UA"); +#else + *uagent_str = strdup(DEFAULT_UA_STR); +#endif + return ret; +} + +char *get_proxy_address(void) +{ +#ifdef _EFL_PLATFORM + char *proxy = NULL; + char *proxyRet = NULL; + connection_h handle = NULL; + connection_address_family_e family = CONNECTION_ADDRESS_FAMILY_IPV4; + + DA_LOG_FUNC_START(Default); + if (connection_create(&handle) < 0) { + DA_LOG_ERR(Default,"Fail to create connection handle"); + return NULL; + } + + if (connection_get_proxy(handle, family, &proxyRet) < 0) { + DA_LOG_ERR(Default,"Fail to get proxy address"); + connection_destroy(handle); + return NULL; + } + + if (proxyRet) { + DA_LOG(Default,"===== Proxy address[%s] =====", proxyRet); + proxy = strdup(proxyRet); + free(proxyRet); + proxyRet = NULL; + connection_destroy(handle); + return proxy; + } + + if (connection_destroy(handle) < 0) { + DA_LOG_ERR(Default,"Fail to desctory connection handle"); + return NULL; + } + return NULL; +#else + return NULL; +#endif +} diff --git a/src/agent/download-agent-plugin-libsoup.c b/src/agent/download-agent-plugin-libsoup.c new file mode 100755 index 0000000..fe4b015 --- /dev/null +++ b/src/agent/download-agent-plugin-libsoup.c @@ -0,0 +1,961 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "download-agent-debug.h" +#include "download-agent-plugin-libsoup.h" +#include "download-agent-http-misc.h" +#include "download-agent-utils.h" +#include "download-agent-pthread.h" + +pthread_mutex_t mutex_for_session_table = PTHREAD_MUTEX_INITIALIZER; + +pi_session_table_t pi_session_table[MAX_SESSION_COUNT] = { { 0, }, }; +da_bool_t using_content_sniffing; + +da_bool_t _pi_http_is_this_session_table_entry_using( + const int in_session_table_entry); + +da_result_t PI_http_init(void) +{ + DA_LOG_FUNC_START(HTTPManager); + + using_content_sniffing = DA_TRUE; + + return DA_RESULT_OK; +} + +void PI_http_deinit(void) +{ + DA_LOG_FUNC_START(HTTPManager); + + return; +} + +da_result_t _set_proxy_on_soup_session(SoupSession *session, char *proxy_addr) +{ + da_result_t ret = DA_RESULT_OK; + + + if (proxy_addr && strlen(proxy_addr) > 0) { + DA_LOG_CRITICAL(HTTPManager,"received proxy = %s \n", proxy_addr); + if (!strstr(proxy_addr, "0.0.0.0")) { + if (strstr((const char *)proxy_addr, "http") == DA_NULL) { + /* DA_LOG(Default,"There is no \"http://\" on received uri, so, add it."); */ + + char *tmp_str = DA_NULL; + int needed_len = 0; + + needed_len = strlen(proxy_addr) + strlen( + SCHEME_HTTP) + 1; + tmp_str = (char *) calloc(1, needed_len); + if (!tmp_str) { + DA_LOG_ERR(HTTPManager,"DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + snprintf(tmp_str, needed_len, "%s%s", + SCHEME_HTTP, proxy_addr); + + g_object_set(session, SOUP_SESSION_PROXY_URI, + soup_uri_new(tmp_str), NULL); + + free(tmp_str); + } else { + DA_LOG(HTTPManager,"There is \"http\" on uri, so, push this address to soup directly."); + g_object_set(session, SOUP_SESSION_PROXY_URI, + soup_uri_new(proxy_addr), NULL); + } + } + } else { + DA_LOG(HTTPManager,"There is no proxy value"); + } +ERR: + return ret; +} + +void _fill_soup_msg_header(SoupMessage *msg, + const input_for_tranx_t *input_for_tranx) +{ + SoupMessageHeaders *headers = msg->request_headers; + + http_msg_request_t *input_http_msg_request; + http_msg_iter_t http_msg_iter; + http_msg_iter_t http_msg_iter_pre; + + char *field; + char *value; + + input_http_msg_request = input_for_tranx->http_msg_request; + + http_msg_request_get_iter(input_http_msg_request, &http_msg_iter); + http_msg_iter_pre = http_msg_iter; + while (http_msg_get_field_with_iter(&http_msg_iter, &field, &value)) { + if ((field != DA_NULL) && (value != DA_NULL)) { + DA_LOG(HTTPManager,"[%s] %s", field, value); + soup_message_headers_append(headers, field, value); + } + http_msg_iter_pre = http_msg_iter; + } + + if (input_http_msg_request->http_body) { + char body_len_str[16] = { 0, }; + int body_len = strlen(input_http_msg_request->http_body); + + snprintf(body_len_str, sizeof(body_len_str), "%d", body_len); + + soup_message_headers_append(headers, "Content-Length", + body_len_str); + soup_message_headers_append(headers, "Content-Type", + "text/plain"); + soup_message_body_append(msg->request_body, SOUP_MEMORY_COPY, + input_http_msg_request->http_body, body_len); + } +} + +da_result_t PI_http_start_transaction(const input_for_tranx_t *input_for_tranx, + int *out_tranx_id) +{ + da_result_t ret = DA_RESULT_OK; + + int session_table_entry = -1; + pi_http_method_t pi_http_method = PI_HTTP_METHOD_GET; + + queue_t *queue = DA_NULL; + + char *url = DA_NULL; + + SoupSession *session = DA_NULL; + SoupMessage *msg = DA_NULL; + + DA_LOG_FUNC_START(HTTPManager); + + if (DA_FALSE == _pi_http_is_valid_input_for_tranx(input_for_tranx)) { + DA_LOG_ERR(HTTPManager,"input_for_tranx is invalid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + queue = input_for_tranx->queue; + pi_http_method = input_for_tranx->http_method; + url = input_for_tranx->http_msg_request->url; + } + + session_table_entry = _pi_http_get_avaiable_session_table_entry(); + if (session_table_entry == -1) { + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + DA_LOG(HTTPManager,"session_table_entry = %d", session_table_entry); + + if (DA_FALSE == _pi_http_register_queue_to_session_table( + session_table_entry, queue)) { + _pi_http_destroy_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + /* modified by keunsoon.lee 2010-09-20 use sync_new() instead of async_new() for make different soup thread from UI main thread*/ + session = soup_session_sync_new(); + /* session=soup_session_async_new(); */ + if (!session) { + DA_LOG_ERR(HTTPManager,"Fail to create session"); + return DA_ERR_INVALID_URL; + } + DA_LOG(HTTPManager,"session[%p]", session); +/* + SoupLogger* logger = soup_logger_new(SOUP_LOGGER_LOG_BODY, -1); + soup_logger_attach(logger, session); + g_object_unref(logger); +*/ + if (DA_FALSE == _pi_http_register_session_to_session_table( + session_table_entry, session)) { + _pi_http_init_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + g_object_set(session, SOUP_SESSION_MAX_CONNS, MAX_SESSION_COUNT, NULL); + /* Set timeout unlimited time to resume a download which has ETag when the network is re-connected + * => This is changed to 180 seconds due to limitation of max downloading items. + */ + g_object_set(session, SOUP_SESSION_TIMEOUT, MAX_TIMEOUT, NULL); + + _set_proxy_on_soup_session(session, input_for_tranx->proxy_addr); + + switch (pi_http_method) { + case PI_HTTP_METHOD_GET: + msg = soup_message_new(METHOD_GET, url); + break; + case PI_HTTP_METHOD_POST: + msg = soup_message_new(METHOD_POST, url); + break; + case PI_HTTP_METHOD_HEAD: + msg = soup_message_new(METHOD_HEAD, url); + break; + default: + DA_LOG_ERR(HTTPManager,"Cannot enter here"); + break; + } + DA_LOG(HTTPManager,"msg[%p]", msg); + /* if it is failed to create a msg, the url can be invalid, becasue of the input argument of soup_message_new API */ + if (msg == NULL) { + DA_LOG_ERR(HTTPManager,"Fail to create message"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + _fill_soup_msg_header(msg, input_for_tranx); + + g_signal_connect(msg, "restarted", G_CALLBACK(_pi_http_restarted_cb), + NULL); /* for redirection case */ + g_signal_connect(msg, "got-headers", + G_CALLBACK(_pi_http_gotheaders_cb), NULL); + g_signal_connect(msg, "got-chunk", G_CALLBACK(_pi_http_gotchunk_cb), + NULL); + + if (using_content_sniffing) { + soup_session_add_feature_by_type(session, SOUP_TYPE_CONTENT_SNIFFER); + g_signal_connect(msg, "content-sniffed", + G_CALLBACK(_pi_http_contentsniffed_cb), NULL); + } else { + soup_message_disable_feature(msg, SOUP_TYPE_CONTENT_SNIFFER); + } + + soup_session_queue_message(session, msg, _pi_http_finished_cb, NULL); +// g_signal_connect(msg, "finished", G_CALLBACK(_pi_http_finished_cb), NULL); + + if (DA_FALSE == _pi_http_register_msg_to_session_table( + session_table_entry, msg)) { + _pi_http_destroy_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + *out_tranx_id = session_table_entry; + DA_LOG(HTTPManager,"*out_tranx_id = %d", *out_tranx_id); + +ERR: + return ret; +} + +da_result_t PI_http_disconnect_transaction(int in_tranx_id) +{ + da_result_t ret = DA_RESULT_OK; + int session_table_entry = -1; + + DA_LOG_FUNC_START(HTTPManager); + + DA_LOG(HTTPManager,"in_tranx_id = %d", in_tranx_id); + + session_table_entry = in_tranx_id; + + _pi_http_destroy_session_table_entry(session_table_entry); + + return ret; +} + +da_result_t PI_http_cancel_transaction(int in_tranx_id, da_bool_t abort_option) +{ + da_result_t ret = DA_RESULT_OK; + SoupSession *session; + SoupMessage *msg; + int session_table_entry = -1; + + DA_LOG_FUNC_START(HTTPManager); + + DA_LOG(HTTPManager,"in_tranx_id = %d", in_tranx_id); + + session_table_entry = in_tranx_id; + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) { + DA_LOG_CRITICAL(HTTPManager,"not using session"); + return ret; + } + session = GET_SESSION_FROM_TABLE_ENTRY(session_table_entry); + msg = GET_MSG_FROM_TABLE_ENTRY(session_table_entry); + + if (DA_NULL == session) { + DA_LOG_ERR(HTTPManager,"invalid session = %p", session); + goto ERR; + } + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid message = %p", msg); + goto ERR; + } + DA_LOG(HTTPManager,"Call soup cancel API : abort option[%d]",abort_option); + if (abort_option) + soup_session_abort(session); + else + soup_session_cancel_message(session, msg, SOUP_STATUS_CANCELLED); + DA_LOG(HTTPManager,"Call soup cancel API-Done"); +ERR: + return ret; +} + +void PI_http_pause_transaction(int transaction_id) +{ + int session_table_entry = -1; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + DA_LOG_FUNC_START(HTTPManager); + + DA_LOG(HTTPManager,"in_tranx_id = %d", transaction_id); + + session_table_entry = transaction_id; + + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) + return; + + mutex = &(pi_session_table[session_table_entry].mutex); + cond = &(pi_session_table[session_table_entry].cond); + + _da_thread_mutex_lock (mutex); + + if (pi_session_table[session_table_entry].is_paused == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"paused!"); + pi_session_table[session_table_entry].is_paused = DA_TRUE; + _da_thread_cond_wait(cond, mutex); + } else { + DA_LOG_CRITICAL(HTTPManager,"NOT paused!"); + } + + _da_thread_mutex_unlock (mutex); + +} + +void PI_http_unpause_transaction(int transaction_id) +{ + int session_table_entry = -1; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + /* DA_LOG_FUNC_START(Default); */ + + session_table_entry = transaction_id; + + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) + return; + + mutex = &(pi_session_table[session_table_entry].mutex); + cond = &(pi_session_table[session_table_entry].cond); + + _da_thread_mutex_lock (mutex); + + if (pi_session_table[session_table_entry].is_paused == DA_TRUE) { + DA_LOG_CRITICAL(HTTPManager,"wake up!"); + pi_session_table[session_table_entry].is_paused = DA_FALSE; + _da_thread_cond_signal(cond); + } + + _da_thread_mutex_unlock (mutex); + +} + +da_bool_t _pi_http_is_valid_input_for_tranx( + const input_for_tranx_t *input_for_tranx) +{ + if (!(input_for_tranx->http_msg_request)) { + DA_LOG_ERR(HTTPManager,"http_msg_request is NULL"); + return DA_FALSE; + } + + if (!((input_for_tranx->http_method == PI_HTTP_METHOD_GET) || + (input_for_tranx->http_method == PI_HTTP_METHOD_POST) || + (input_for_tranx->http_method == PI_HTTP_METHOD_HEAD))) { + DA_LOG_ERR(HTTPManager,"http_method is neither GET or POST or HEAD"); + return DA_FALSE; + } + + return DA_TRUE; +} + +da_bool_t _pi_http_is_this_session_table_entry_using( + const int in_session_table_entry) +{ + da_bool_t is_using = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(in_session_table_entry)) + return DA_FALSE; + + _da_thread_mutex_lock (&mutex_for_session_table); + + is_using = pi_session_table[in_session_table_entry].is_using; + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return is_using; +} + +void _pi_http_init_session_table_entry(const int in_session_table_entry) +{ + int entry = in_session_table_entry; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) + return; + +// _da_thread_mutex_lock (&mutex_for_session_table); + + pi_session_table[entry].is_using = DA_TRUE; + pi_session_table[entry].msg = NULL; + pi_session_table[entry].session = NULL; + pi_session_table[entry].queue = NULL; + + _da_thread_mutex_init(&(pi_session_table[entry].mutex), DA_NULL); + _da_thread_cond_init(&(pi_session_table[entry].cond), NULL); + pi_session_table[entry].is_paused = DA_FALSE; + +// _da_thread_mutex_unlock (&mutex_for_session_table); + + return; +} + +void _pi_http_destroy_session_table_entry(const int in_session_table_entry) +{ + int entry = in_session_table_entry; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) + return; + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_paused == DA_TRUE) + PI_http_unpause_transaction(entry); + + /* Warning! Do not g_object_unref(msg) here! + * soup_session_queue_message() steals msg's reference count, + * so, we don't need to do anything for memory management. + * + * But, if using soup_session_send_message(), MUST call g_object_unref(msg). */ + /* if (pi_session_table[entry].msg) + g_object_unref(pi_session_table[entry].msg); */ + + pi_session_table[entry].msg = NULL; + + /* FIXME Cannot g_object_unref(session) here, + * because msg inside this session is not destoryed yet. + * The msg's reference count is stealed by soup_session_queue_message(), + * and it will be destroyed when _pi_http_finished_cb() is returned. + * For now, this _pi_http_destroy_session_table_entry() is called inside + * _pi_http_finished_cb(), so, g_object_unref(session) is not working. + * Should find out call this function after _pi_http_finished_cb(). */ + if (pi_session_table[entry].session) + g_object_unref(pi_session_table[entry].session); + else + DA_LOG_ERR(HTTPManager,"session is NULL. Cannot unref this."); + DA_LOG(HTTPManager,"unref session [%p]",pi_session_table[entry].session); + + pi_session_table[entry].session = NULL; + + pi_session_table[entry].queue = NULL; + pi_session_table[entry].is_paused = DA_FALSE; + pi_session_table[entry].is_using = DA_FALSE; + + _da_thread_mutex_destroy(&(pi_session_table[entry].mutex)); + _da_thread_cond_destroy(&(pi_session_table[entry].cond)); + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return; +} + +int _pi_http_get_avaiable_session_table_entry(void) +{ + int i; + int avaiable_entry = -1; + + _da_thread_mutex_lock (&mutex_for_session_table); + + for (i = 0; i < MAX_SESSION_COUNT; i++) { + if (pi_session_table[i].is_using == DA_FALSE) { + /* pi_session_table[i].is_using = DA_TRUE; */ + DA_LOG_VERBOSE(HTTPManager,"available entry = %d", i); + + avaiable_entry = i; + + break; + } + } + _pi_http_init_session_table_entry(avaiable_entry); + _da_thread_mutex_unlock (&mutex_for_session_table); + + return avaiable_entry; +} + +da_bool_t _pi_http_register_queue_to_session_table( + const int in_session_table_entry, const queue_t *in_queue) +{ + int entry = in_session_table_entry; + queue_t *queue = (queue_t *) in_queue; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + DA_LOG_ERR(HTTPManager,"this entry [%d] is not using", entry); + ret = DA_FALSE; + } else { + pi_session_table[entry].queue = queue; + DA_LOG_VERBOSE(HTTPManager,"queue = %p", pi_session_table[entry].queue); + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +da_bool_t _pi_http_register_session_to_session_table( + const int in_session_table_entry, SoupSession *session) +{ + int entry = in_session_table_entry; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + if (DA_NULL == session) { + DA_LOG_ERR(HTTPManager,"invalid session = %p",session); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + ret = DA_FALSE; + } else { + pi_session_table[entry].session = session; + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +da_bool_t _pi_http_register_msg_to_session_table( + const int in_session_table_entry, SoupMessage *msg) +{ + int entry = in_session_table_entry; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid msg = %p",msg); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + ret = DA_FALSE; + } else { + pi_session_table[entry].msg = msg; + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +queue_t *_pi_http_get_queue_from_session_table_entry( + const int in_session_table_entry) +{ + int entry = in_session_table_entry; + queue_t *out_queue = NULL; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return out_queue; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + out_queue = pi_session_table[entry].queue; + _da_thread_mutex_unlock (&mutex_for_session_table); + + return out_queue; +} + +void _pi_http_store_read_header_to_queue(SoupMessage *msg, const char *sniffedType) +{ + da_result_t ret = DA_RESULT_OK; + + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + q_event_type_data da_event_type_data; + + int session_table_entry = -1; + SoupMessageHeadersIter headers_iter; + + const char *header_name; + const char *header_value; + + http_msg_response_t *http_msg_response = NULL; + + /* DA_LOG_FUNC_START(Default); */ + + if (msg->response_headers) { + ret = http_msg_response_create(&http_msg_response); + if (ret != DA_RESULT_OK) + return; + + http_msg_response_set_status_code(http_msg_response, + msg->status_code); + + DA_LOG(HTTPManager,"\n----raw header---------------------------------------------"); + DA_LOG_CRITICAL(HTTPManager,"status code = %d", msg->status_code); + soup_message_headers_iter_init(&headers_iter, + msg->response_headers); + while (soup_message_headers_iter_next(&headers_iter, + &header_name, &header_value)) { + if ((header_name != DA_NULL) && (header_value + != DA_NULL)) { + http_msg_response_add_field(http_msg_response, + header_name, header_value); + } + } + DA_LOG(HTTPManager,"\n-------------------------------------------------------------\n"); + + } + + if (using_content_sniffing && sniffedType) + http_msg_response_set_content_type(http_msg_response, sniffedType); + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + da_event_type_data = Q_EVENT_TYPE_DATA_PACKET; + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + ret = Q_make_http_data_event(da_event_type_data, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_status_code_on_http_data_event(da_event, msg->status_code); + da_event->type.q_event_data_http.http_response_msg + = http_msg_response; + + Q_push_event(da_queue, da_event); + } + return; + +ERR: + if (DA_RESULT_OK != ret) + http_msg_response_destroy(&http_msg_response); + + return; +} + +void _pi_http_store_read_data_to_queue(SoupMessage *msg, const char *body_data, + int received_body_len) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + char *body_buffer = NULL; + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + q_event_type_data da_event_type_data; + int session_table_entry = -1; + int http_status = -1; + + /* DA_LOG_FUNC_START(Default); */ + + http_status = msg->status_code; + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + if (received_body_len == 0) { + DA_LOG(HTTPManager,"Q_EVENT_TYPE_DATA_FINAL"); + da_event_type_data = Q_EVENT_TYPE_DATA_FINAL; + } else { + da_event_type_data = Q_EVENT_TYPE_DATA_PACKET; + if (received_body_len > 0) { + body_buffer = (char*) calloc(1, received_body_len); + DA_LOG_VERBOSE(HTTPManager,"body_buffer[%p]msg[%p]",body_buffer,msg); + if (body_buffer == DA_NULL) { + DA_LOG_ERR(HTTPManager,"DA_ERR_FAIL_TO_MEMALLOC"); + goto ERR; + } + memcpy(body_buffer, body_data, received_body_len); + } + } + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + ret = Q_make_http_data_event(da_event_type_data, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_status_code_on_http_data_event(da_event, http_status); + Q_set_http_body_on_http_data_event(da_event, received_body_len, + body_buffer); + + _da_thread_mutex_lock (&(da_queue->mutex_queue)); + b_ret = Q_push_event_without_lock(da_queue, da_event); + if (b_ret == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"----------------------------------------fail to push!"); + + pthread_mutex_t *session_mutex = NULL; + pthread_cond_t *session_cond = NULL; + + session_mutex + = &(pi_session_table[session_table_entry].mutex); + session_cond + = &(pi_session_table[session_table_entry].cond); + + /* MUST keep this order for these mutexes */ + _da_thread_mutex_lock (session_mutex); + _da_thread_mutex_unlock (&(da_queue->mutex_queue)); + + if (pi_session_table[session_table_entry].is_paused + == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"paused!"); + pi_session_table[session_table_entry].is_paused + = DA_TRUE; + _da_thread_cond_wait(session_cond, session_mutex); + } else { + DA_LOG_CRITICAL(HTTPManager,"NOT paused!"); + } + + _da_thread_mutex_unlock (session_mutex); + + DA_LOG_CRITICAL(HTTPManager,"wake up! push again"); + Q_push_event(da_queue, da_event); + } else { + _da_thread_mutex_unlock (&(da_queue->mutex_queue)); + } + + } + + return; + +ERR: + if (DA_RESULT_OK != ret) { + if (DA_NULL != body_buffer) { + free(body_buffer); + } + } + + return; +} + +int _translate_error_code(int soup_error) +{ + DA_LOG_CRITICAL(HTTPManager, "soup error code[%d]", soup_error); + switch (soup_error) { + case SOUP_STATUS_CANT_RESOLVE: + case SOUP_STATUS_CANT_RESOLVE_PROXY: + case SOUP_STATUS_CANT_CONNECT: + case SOUP_STATUS_CANT_CONNECT_PROXY: + case SOUP_STATUS_IO_ERROR: + case SOUP_STATUS_MALFORMED: + case SOUP_STATUS_TRY_AGAIN: + return DA_ERR_NETWORK_FAIL; + case SOUP_STATUS_SSL_FAILED: + return DA_ERR_SSL_FAIL; + case SOUP_STATUS_REQUEST_TIMEOUT: + return DA_ERR_HTTP_TIMEOUT; + case SOUP_STATUS_TOO_MANY_REDIRECTS: + return DA_ERR_TOO_MANY_REDIECTS; + default: + return DA_ERR_NETWORK_FAIL; + } +} + +void _pi_http_store_neterr_to_queue(SoupMessage *msg) +{ + da_result_t ret = DA_RESULT_OK; + int error_type = -1; + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + int session_table_entry = -1; + + DA_LOG_FUNC_START(HTTPManager); + + error_type = _translate_error_code(msg->status_code); + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + DA_LOG_CRITICAL(HTTPManager,"Q_EVENT_TYPE_DATA_ABORT"); + ret = Q_make_http_data_event(Q_EVENT_TYPE_DATA_ABORT, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_error_type_on_http_data_event(da_event, error_type); + + Q_push_event(da_queue, da_event); + } + +ERR: + return; +} + +int _pi_http_get_session_table_entry_from_message(SoupMessage *msg) +{ + + int out_entry = -1; + int i; + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid message = %p", msg); + return out_entry; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + for (i = 0; i < MAX_SESSION_COUNT; i++) { + if (pi_session_table[i].is_using == DA_TRUE) { + if (pi_session_table[i].msg == msg) { + out_entry = i; + break; + } + } + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + if (i == MAX_SESSION_COUNT) { + DA_LOG_ERR(HTTPManager,"fail to find message = %p", msg); + } + + return out_entry; + +} + +void _pi_http_finished_cb(SoupSession *session, SoupMessage *msg, gpointer data) +{ + char *url = NULL; + + DA_LOG_FUNC_START(HTTPManager); + + url = soup_uri_to_string(soup_message_get_uri(msg), DA_FALSE); + + DA_LOG(HTTPManager,"status_code[%d], reason[%s], url[%s]",msg->status_code,msg->reason_phrase,url); + + if (url) { + free(url); + url = NULL; + } + + if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { + if (msg->status_code == SOUP_STATUS_CANCELLED) { + _pi_http_store_read_data_to_queue(msg, DA_NULL, 0); + } else { + _pi_http_store_neterr_to_queue(msg); + } + } else { + _pi_http_store_read_data_to_queue(msg, DA_NULL, 0); + } + +} + +/* this callback is called in case of redirection */ +void _pi_http_restarted_cb(SoupMessage *msg, gpointer data) +{ + DA_LOG_FUNC_START(HTTPManager); + /* Location URL is needed when extracting the file name from url. + * So, the response header should be handled by http mgr.*/ + _pi_http_store_read_header_to_queue(msg, NULL); +} + +void _pi_http_gotheaders_cb(SoupMessage *msg, gpointer data) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) { + DA_LOG(HTTPManager,"Redirection !!"); + if (SOUP_STATUS_NOT_MODIFIED != msg->status_code) + return; + } + + soup_message_body_set_accumulate(msg->response_body, DA_FALSE); + + if (!using_content_sniffing) + _pi_http_store_read_header_to_queue(msg, NULL); + else + DA_LOG(HTTPManager,"ignore because content sniffing is turned on"); +} + +void _pi_http_contentsniffed_cb(SoupMessage *msg, const char *sniffedType, + GHashTable *params, gpointer data) +{ + DA_LOG_FUNC_START(HTTPManager); + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) { + DA_LOG(HTTPManager,"Redirection !!"); + if (SOUP_STATUS_NOT_MODIFIED != msg->status_code) + return; + } + + if (using_content_sniffing) + _pi_http_store_read_header_to_queue(msg, sniffedType); +} + +void _pi_http_gotchunk_cb(SoupMessage *msg, SoupBuffer *chunk, gpointer data) +{ +// DA_LOG_FUNC_START(HTTPManager); + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) + return; + + if (chunk->data && chunk->length > 0) { + _pi_http_store_read_data_to_queue(msg, chunk->data, + chunk->length); + } +} + diff --git a/src/agent/download-agent-utils-dl-id-history.c b/src/agent/download-agent-utils-dl-id-history.c new file mode 100755 index 0000000..1d06722 --- /dev/null +++ b/src/agent/download-agent-utils-dl-id-history.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-agent-type.h" +#include "download-agent-utils.h" +#include "download-agent-utils-dl-id-history.h" + +da_result_t init_dl_id_history(dl_id_history_t *dl_id_history) +{ + da_result_t ret = DA_RESULT_OK; + + /* Initial dl_id_history will be starting number for dl_id. + * dl_id will be sequentially increased from the dl_id_history, + * then dl_id_history will be updated. */ + _da_thread_mutex_init(&(dl_id_history->mutex), DA_NULL); + _da_thread_mutex_lock(&(dl_id_history->mutex)); + get_random_number(&(dl_id_history->starting_num)); + dl_id_history->cur_dl_id = DA_INVALID_ID; + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + DA_LOG_CRITICAL(Default,"starting num = %d", dl_id_history->starting_num); + return ret; +} + +da_result_t deinit_dl_id_history(dl_id_history_t *dl_id_history) +{ + da_result_t ret = DA_RESULT_OK; + + _da_thread_mutex_lock(&(dl_id_history->mutex)); + dl_id_history->starting_num = DA_INVALID_ID; + dl_id_history->cur_dl_id = DA_INVALID_ID; + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + _da_thread_mutex_destroy(&(dl_id_history->mutex)); + + return ret; +} + +int get_available_dl_id(dl_id_history_t *dl_id_history) +{ + int dl_id = 0; + + _da_thread_mutex_lock(&(dl_id_history->mutex)); + + if (dl_id_history->cur_dl_id == DA_INVALID_ID) + dl_id_history->cur_dl_id = dl_id_history->starting_num; + else if (dl_id_history->cur_dl_id > 254) + dl_id_history->cur_dl_id = 1; + else + dl_id_history->cur_dl_id++; + + dl_id = dl_id_history->cur_dl_id; + + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + DA_LOG_CRITICAL(Default,"dl_id = %d", dl_id); + return dl_id; +} diff --git a/src/agent/download-agent-utils.c b/src/agent/download-agent-utils.c new file mode 100755 index 0000000..3ac9710 --- /dev/null +++ b/src/agent/download-agent-utils.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-file.h" +#include "download-agent-http-misc.h" +#include "download-agent-mime-util.h" +#include "download-agent-utils.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-dl-info-util.h" + +#define DA_HTTP_HEADER_CONTENT_TYPE "Content-Type" +#define DA_HTTP_HEADER_CONTENT_LENGTH "Content-Length" +#define DA_FILE_NUMBER_LIMIT (1024*1024) + +typedef struct _da_descriptor_mime_table_t { + char *content_type; + da_mime_type_id_t mime_type; +} da_descriptor_mime_table_t; + +da_descriptor_mime_table_t + descriptor_mime_table[] = { + {"", DA_MIME_TYPE_NONE}, + /* DRM1.0 */ + {"application/vnd.oma.drm.message", + DA_MIME_TYPE_DRM1_MESSATE}, /* drm1.0 FL.CD*/ + {"", DA_MIME_TYPE_END}}; + +void get_random_number(int *out_num) +{ + int temp = DA_INVALID_ID; + unsigned int seed = (unsigned)time(0); + + temp = (int)(rand_r(&seed) % 100 + 1.0); + *out_num = temp; +} + +da_result_t get_extension_from_mime_type(char *mime_type, char **extension) +{ + da_result_t ret = DA_RESULT_OK; + char *ext = DA_NULL; + + DA_LOG_FUNC_START(Default); + if (DA_NULL == mime_type || DA_NULL == extension) { + DA_LOG_ERR(Default,"received mime_type is null"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + DA_LOG(Default,"input mime type = %s", mime_type); + if (DA_RESULT_OK != (ret = da_mime_get_ext_name(mime_type, &ext))) { + DA_LOG_ERR(Default,"can't find proper extension!"); + goto ERR; + } + *extension = ext; + DA_LOG(Default,"found extension = %s", *extension); + +ERR: + return ret; +} + +int read_data_from_file(char *file, char **out_buffer) +{ + FILE *fd; + unsigned long long file_size = -1; + char *buffer = NULL; + int buffer_len = 0; + size_t read_len = 0; + + *out_buffer = NULL; + + if (!file) + return 0; + + /* open file with "rb", because fread() handles the file as binary mode */ + fd = fopen(file, "rb"); + if (!fd) { + DA_LOG_ERR(FileManager,"File open err! received file path = [%s]", file); + return 0; + } + + get_file_size(file, &file_size); + if (file_size <= 0) { + DA_LOG_ERR(FileManager,"file size is [%llu]", file_size); + fclose(fd); + return 0; + } + + /* A guide from www.securecoding.cert.org + * : FIO17-C. Do not rely on an ending null character when using fread() + * + * buffer is initialized with null through calloc(), so, it is always null-terminated even if fread() failed. + * allocate memory one more byte to ensure null-terminated even if the file is not null-terminated. + */ + buffer_len = sizeof(char) * file_size; + buffer = (char *)calloc(1, buffer_len + 1); + if (buffer) { + read_len = fread(buffer, sizeof(char), file_size, fd); + if (read_len == file_size) { + *out_buffer = buffer; + } else { + DA_LOG_ERR(FileManager,"File Read Not Complete read length = %d", read_len); + free(buffer); + buffer = NULL; + buffer_len = 0; + } + } else { + buffer_len = 0; + } + + fclose(fd); + + return buffer_len; +} + +da_result_t get_available_memory( + da_storage_type_t storage_type, + da_storage_size_t *avail_memory) +{ + da_result_t ret = DA_RESULT_OK; + int fs_ret = 0; + struct statfs filesys_info = {0, }; + char *default_install_dir = NULL; + + DA_LOG_FUNC_START(Default); + + if (!avail_memory) + return DA_ERR_INVALID_ARGUMENT; + + ret = get_default_install_dir(&default_install_dir); + + if (ret == DA_RESULT_OK && default_install_dir) { + fs_ret = statfs(default_install_dir, &filesys_info); + } else { + return DA_ERR_FAIL_TO_ACCESS_STORAGE; + } + + if (fs_ret != 0) { + DA_LOG_ERR(Default,"Phone file path :statfs error - [%d]", errno); + free(default_install_dir); + return DA_ERR_INVALID_INSTALL_PATH; + } + + avail_memory->b_available = filesys_info.f_bavail; + avail_memory->b_size = filesys_info.f_bsize; + + DA_LOG(Default, "Memory type : %d", storage_type); + DA_LOG_VERBOSE(Default, "Available Memory(f_bavail) : %lu", filesys_info.f_bavail); + DA_LOG_VERBOSE(Default, "Available Memory(f_bsize) : %d", filesys_info.f_bsize); + DA_LOG(Default, "Available Memory(kbytes) : %lu", (filesys_info.f_bavail/1024)*filesys_info.f_bsize); + + free(default_install_dir); + return DA_RESULT_OK; +} + +da_mime_type_id_t get_mime_type_id(char *content_type) +{ + int i = 0; + + DA_LOG_FUNC_START(Default); + + DA_LOG(Default,"received content_type = %s", content_type); + + if (content_type == NULL) { + DA_LOG_ERR(Default, "No Mime Type\n"); + return DA_MIME_TYPE_NONE; + } + + while(descriptor_mime_table[i].mime_type != DA_MIME_TYPE_END) + { + if (!strcmp(descriptor_mime_table[i].content_type, content_type)) { + break; + } + i++; + } + DA_LOG(Default, "dd mime type check: index[%d] type[%d]", i, descriptor_mime_table[i].mime_type); + return descriptor_mime_table[i].mime_type; +} + + + +da_bool_t is_valid_url(const char *url, da_result_t *err_code) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + int wanted_str_len = 0; + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + + if ((DA_NULL == url) || (1 > strlen(url))) { + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + wanted_str_start = (char*)url; + wanted_str_end = strstr(url, "://"); + if (!wanted_str_end) { + DA_LOG_ERR(Default,"No protocol on this url"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char*)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(Default,"DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + b_ret = is_supporting_protocol(wanted_str); + if (!b_ret) { + ret = DA_ERR_UNSUPPORTED_PROTOCAL; + goto ERR; + } + +ERR: + if (wanted_str) { + free(wanted_str); + wanted_str = NULL; + } + + if (err_code) + *err_code = ret; + + return b_ret; +} + +da_result_t move_file(const char *from_path, const char *to_path) +{ + da_result_t ret = DA_RESULT_OK; + + if (!from_path || !to_path) + return DA_ERR_INVALID_ARGUMENT; + + if (rename(from_path, to_path) != 0) { + DA_LOG_CRITICAL(FileManager,"rename failed : syserr[%d]",errno); + if (errno == EXDEV) { + DA_LOG_CRITICAL(FileManager,"File system is diffrent. Try to copy a file"); + ret = copy_file(from_path, to_path); + if (ret == DA_RESULT_OK) { + remove_file(from_path); + } else { + if (is_file_exist(to_path)) + remove_file(to_path); + ret = DA_ERR_FAIL_TO_INSTALL_FILE; + } + } else { + ret = DA_ERR_FAIL_TO_INSTALL_FILE; + } + } + return ret; +} + +void remove_file(const char *file_path) +{ + DA_LOG_FUNC_START(FileManager); + + if (file_path && is_file_exist(file_path)) { + DA_LOG(FileManager,"remove file [%s]", file_path); + if (unlink(file_path) < 0) { + DA_LOG_ERR(FileManager,"file removing failed."); + } + } +} diff --git a/src/agent/include/download-agent-basic.h b/src/agent/include/download-agent-basic.h new file mode 100755 index 0000000..340a34a --- /dev/null +++ b/src/agent/include/download-agent-basic.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Basic_H +#define _Download_Agent_Basic_H + +#include + +#include "download-agent-type.h" +#include "download-agent-interface.h" +#include "download-agent-dl-mgr.h" + +da_result_t start_download(const char *url, int *dl_id); +da_result_t start_download_with_extension(const char *url , int *dl_id, extension_data_t *extension_data); + +#endif diff --git a/src/agent/include/download-agent-client-mgr.h b/src/agent/include/download-agent-client-mgr.h new file mode 100755 index 0000000..f4c6234 --- /dev/null +++ b/src/agent/include/download-agent-client-mgr.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Client_Mgr_H +#define _Download_Agent_Client_Mgr_H + +#include + +#include "download-agent-type.h" +#include "download-agent-interface.h" + +#include "download-agent-pthread.h" + +typedef enum { + Q_CLIENT_NOTI_TYPE_STARTED_INFO = 0, + Q_CLIENT_NOTI_TYPE_PROGRESS_INFO, + Q_CLIENT_NOTI_TYPE_PAUSED_INFO, + Q_CLIENT_NOTI_TYPE_FINISHED_INFO, + Q_CLIENT_NOTI_TYPE_TERMINATE, +} client_noti_type; + +typedef struct _client_noti_t client_noti_t; +struct _client_noti_t { + int slot_id; + void *user_data; + client_noti_type noti_type; + union _client_type { + user_download_info_t update_dl_info; + user_progress_info_t update_progress_info; + user_paused_info_t paused_info; + user_finished_info_t finished_info; + } type; + + client_noti_t *next; +}; + +typedef struct _client_queue_t { + da_bool_t having_data; + client_noti_t *client_q_head; + pthread_mutex_t mutex_client_queue; + pthread_cond_t cond_client_queue; +} client_queue_t; + +typedef struct _client_app_info_t { + da_client_cb_t client_callback; + char *client_user_agent; +} client_app_info_t; + +typedef struct _client_app_mgr_t { + da_bool_t is_init; + client_queue_t client_queue; + client_app_info_t client_app_info; + pthread_t thread_id; + da_bool_t is_thread_init; + pthread_mutex_t mutex_client_mgr; +} client_app_mgr_t; + +da_result_t init_client_app_mgr(void); +da_bool_t is_client_app_mgr_init(void); + +da_result_t reg_client_app(da_client_cb_t *da_client_callback); +da_result_t dereg_client_app(void); + +da_result_t send_client_paused_info (int slot_id); +da_result_t send_client_update_dl_info (int slot_id, int dl_id, + char *file_type, unsigned long int file_size, char *tmp_saved_path, + char *pure_file_name, char *etag, char *extension); +da_result_t send_client_update_progress_info (int slot_id, int dl_id, + unsigned long int received_size); +da_result_t send_client_finished_info (int slot_id, int dl_id, + char *saved_path, char *etag, int error, int http_status); + +char *get_client_user_agent_string(void); + +void push_client_noti(client_noti_t *client_noti); + +#endif diff --git a/src/agent/include/download-agent-debug.h b/src/agent/include/download-agent-debug.h new file mode 100755 index 0000000..e12a40b --- /dev/null +++ b/src/agent/include/download-agent-debug.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Debug_H +#define _Download_Agent_Debug_H + +#include "download-agent-type.h" + +#define DA_DEBUG_ENV_KEY "DOWNLOAD_AGENT_DEBUG" +#define DA_DEBUG_CONFIG_FILE_PATH "/tmp/.download_agent.conf" + +#define IS_LOG_ON(channel) (DALogBitMap & (0x1<<(channel))) + +typedef enum { + Soup, + HTTPManager, + FileManager, + DRMManager, + DownloadManager, + ClientNoti, + HTTPMessageHandler, + Encoding, + QueueManager, + Parsing, + Thread, + Default, + DA_LOG_CHANNEL_MAX +} da_log_channel; + +extern int DALogBitMap; + +da_result_t init_log_mgr(void); + +#ifdef NODEBUG + #define DA_LOG(channel, format, ...) ((void)0) + #define DA_LOG_CRITICAL(channel, format, ...) ((void)0) + #define DA_LOG_VERBOSE(channel, format, ...) ((void)0) + #define DA_LOG_ERR(channel, format, ...) ((void)0) + #define DA_LOG_FUNC_START(channel, ...) ((void)0) + +#else /* NODEBUG */ +#include +#include +#include + +#ifdef DA_DEBUG_USING_DLOG + #include + #ifdef LOG_TAG + #undef LOG_TAG + #endif /* LOG_TAG */ + #define LOG_TAG "DOWNLOAD_AGENT" + + #define DA_LOG(channel, format, ...) LOGD_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__); + #define DA_LOG_CRITICAL(channel, format, ...) LOGE_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__); + #define DA_LOG_VERBOSE(channel, format, ...) LOGV_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__); + #define DA_LOG_ERR(channel, format, ...) LOGE_IF(IS_LOG_ON(channel), "ERR! "format, ##__VA_ARGS__); + #define DA_LOG_FUNC_START(channel, ...) LOGV_IF(IS_LOG_ON(channel), "starting..."); +#else /* DA_DEBUG_USING_DLOG */ + #include + #include + + #define DA_LOG(channel, format, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][%s(): %d] "format"\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__, ##__VA_ARGS__) \ + : ((void)0);\ + }while(0) + #define DA_LOG_ERR(channel, format, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][ERR][%s(): %d]\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__, ##__VA_ARGS__) \ + : ((void)0); \ + }while(0) + #define DA_LOG_FUNC_START(channel, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][%s(): %d] starting\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__) \ + : ((void)0); \ + }while(0) + #define DA_LOG_CRITICAL DA_LOG + #define DA_LOG_VERBOSE DA_LOG +#endif /* DA_DEBUG_USING_DLOG */ +#endif /* NDEBUG */ +#endif /* _Download_Agent_Debug_H */ diff --git a/src/agent/include/download-agent-defs.h b/src/agent/include/download-agent-defs.h new file mode 100755 index 0000000..3b2e77b --- /dev/null +++ b/src/agent/include/download-agent-defs.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Defs_H +#define _Download_Agent_Defs_H + +#ifndef DEPRECATED +#define DEPRECATED __attribute__((deprecated)) +#endif + +/** + * Max count to download files simultaneously. \n + * Main reason for this restriction is because of Network bandwidth. + */ +#define DA_MAX_DOWNLOAD_REQ_AT_ONCE 50 + +#define DA_RESULT_OK 0 + +#define DA_TRUE 1 +#define DA_FALSE 0 +#define DA_NULL 0 +#define DA_INVALID_ID -1 + +#define DA_RESULT_USER_CANCELED -10 + +// InputError Input error (-100 ~ -199) +// Client passed wrong parameter +#define DA_ERR_INVALID_ARGUMENT -100 +#define DA_ERR_INVALID_DL_REQ_ID -101 +#define DA_ERR_INVALID_URL -103 +#define DA_ERR_INVALID_INSTALL_PATH -104 +#define DA_ERR_INVALID_MIME_TYPE -105 + +// Client passed correct parameter, but Download Agent rejects the request because of internal policy. +#define DA_ERR_ALREADY_CANCELED -160 +#define DA_ERR_ALREADY_SUSPENDED -161 +#define DA_ERR_ALREADY_RESUMED -162 +#define DA_ERR_CANNOT_SUSPEND -170 +#define DA_ERR_CANNOT_RESUME -171 +#define DA_ERR_INVALID_STATE -190 +#define DA_ERR_ALREADY_MAX_DOWNLOAD -191 +#define DA_ERR_UNSUPPORTED_PROTOCAL -192 + +// System error (-200 ~ -299) +#define DA_ERR_FAIL_TO_MEMALLOC -200 +#define DA_ERR_FAIL_TO_CREATE_THREAD -210 +#define DA_ERR_FAIL_TO_OBTAIN_MUTEX -220 +#define DA_ERR_FAIL_TO_ACCESS_FILE -230 +#define DA_ERR_DISK_FULL -240 + +// Platform error (-300 ~ -399) +#define DA_ERR_FAIL_TO_GET_CONF_VALUE -300 +#define DA_ERR_FAIL_TO_ACCESS_STORAGE -310 +#define DA_ERR_DLOPEN_FAIL -330 + +// Network error (-400 ~ -499) +#define DA_ERR_NETWORK_FAIL -400 +#define DA_ERR_UNREACHABLE_SERVER -410 +#define DA_ERR_HTTP_TIMEOUT -420 +#define DA_ERR_SSL_FAIL -430 +#define DA_ERR_TOO_MANY_REDIECTS -440 + +// HTTP error - not conforming with HTTP spec (-500 ~ -599) +#define DA_ERR_MISMATCH_CONTENT_TYPE -500 +#define DA_ERR_MISMATCH_CONTENT_SIZE -501 +#define DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT -502 + +// DRM error - not conforming with DRM spec (-700 ~ -799) +#define DA_ERR_DRM_FAIL -700 +#define DA_ERR_DRM_FILE_FAIL -710 + +// install error (-800 ~ -899) +#define DA_ERR_FAIL_TO_INSTALL_FILE -800 + +#endif + diff --git a/src/agent/include/download-agent-dl-info-util.h b/src/agent/include/download-agent-dl-info-util.h new file mode 100755 index 0000000..e78491f --- /dev/null +++ b/src/agent/include/download-agent-dl-info-util.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Dl_Info_Util_H +#define _Download_Agent_Dl_Info_Util_H + +#include "download-agent-type.h" +#include "download-agent-http-queue.h" +#include "download-agent-utils-dl-id-history.h" + +#define DA_MAX_DOWNLOAD_ID DA_MAX_DOWNLOAD_REQ_AT_ONCE +#define DA_MAX_TYPE_COUNT 10 + +#define DOWNLOAD_NOTIFY_LIMIT (1024*32) //bytes +extern pthread_mutex_t mutex_download_state[]; + +typedef enum { + DOWNLOAD_STATE_IDLE = 0, + DOWNLOAD_STATE_NEW_DOWNLOAD = 10, /* stage */ + DOWNLOAD_STATE_FINISH = 50, /* stage */ + DOWNLOAD_STATE_PAUSED = 60, /* http */ + DOWNLOAD_STATE_CANCELED = 70, /* http */ + DOWNLOAD_STATE_ABORTED = 80 /* satge */ +} download_state_t; + +typedef enum { + HTTP_STATE_READY_TO_DOWNLOAD = 0, + HTTP_STATE_REDIRECTED = 1, + HTTP_STATE_DOWNLOAD_REQUESTED = 2, + HTTP_STATE_DOWNLOAD_STARTED = 3, + HTTP_STATE_DOWNLOADING = 4, + HTTP_STATE_DOWNLOAD_FINISH = 5, + HTTP_STATE_REQUEST_CANCEL = 6, + HTTP_STATE_REQUEST_PAUSE = 7, + HTTP_STATE_REQUEST_RESUME = 8, + HTTP_STATE_CANCELED = 9, + HTTP_STATE_PAUSED = 10, + HTTP_STATE_RESUMED = 11, + HTTP_STATE_ABORTED = 12, +} http_state_t; + +typedef struct _client_input_basic_t { + char *req_url; + char **user_request_header; + int user_request_header_count; +} client_input_basic_t; + +typedef struct _client_input_t { + void *user_data; + char *install_path; + char *file_name; + char *etag; + char *temp_file_path; + client_input_basic_t client_input_basic; +} client_input_t; + +typedef struct _download_thread_input { + int slot_id; + client_input_t *client_input; +} download_thread_input; + +typedef struct _source_info_basic_t { + int dl_id; + char *url; + char **user_request_header; + int user_request_header_count; +} source_info_basic_t; + +typedef struct _source_info_t { + union _source_info_type { + source_info_basic_t *source_info_basic; + } source_info_type; +} source_info_t; + +#define GET_SOURCE_TYPE(SOURCE) ((SOURCE)->source_type) +#define GET_SOURCE_BASIC(SOURCE) ((SOURCE)->source_info_type.source_info_basic) +#define GET_SOURCE_BASIC_URL(SOURCE) (GET_SOURCE_BASIC(SOURCE)->url) + +typedef struct _req_dl_info { + http_info_t http_info; + + /* This is just pointer assignment from stage source info. */ + char *destination_url; + /* The location url is assigned here in case of redirection. + * At this time, the pointer should be freed. */ + char *location_url; + char **user_request_header; + int user_request_header_count; + char *user_request_etag; + char *user_request_temp_file_path; + + http_state_t http_state; + pthread_mutex_t mutex_http_state; + + da_result_t result; + /*************** will be depreciated ***********************/ + /* ToDo : previous http_info should be saved in case of pause */ + char *content_type_from_header; /* calloced in set hdr fiels on download info */ + int content_len_from_header; + char *etag_from_header; + + unsigned long int downloaded_data_size; + + int invloved_transaction_id; +} req_dl_info; + +#define GET_REQUEST_HTTP_RESULT(REQUEST) (REQUEST->result) +#define GET_REQUEST_HTTP_TRANS_ID(REQUEST) (REQUEST->invloved_transaction_id) +#define GET_REQUEST_HTTP_REQ_URL(REQUEST) (REQUEST->destination_url) +#define GET_REQUEST_HTTP_REQ_LOCATION(REQUEST) (REQUEST->location_url) +#define GET_REQUEST_HTTP_USER_REQUEST_HEADER(REQUEST) (REQUEST->user_request_header) +#define GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT(REQUEST) (REQUEST->user_request_header_count) +#define GET_REQUEST_HTTP_USER_REQUEST_ETAG(REQUEST) (REQUEST->user_request_etag) +#define GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(REQUEST) (REQUEST->user_request_temp_file_path) +#define GET_REQUEST_HTTP_HDR_ETAG(REQUEST) (REQUEST->etag_from_header) +#define GET_REQUEST_HTTP_HDR_CONT_TYPE(REQUEST) (REQUEST->content_type_from_header) +#define GET_REQUEST_HTTP_HDR_CONT_LEN(REQUEST) (REQUEST->content_len_from_header) +#define GET_REQUEST_HTTP_CONTENT_OFFSET(REQUEST)(REQUEST->downloaded_data_size) +#define GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE) (GET_STAGE_TRANSACTION_INFO(STAGE)->mutex_http_state) +#define GET_HTTP_STATE_ON_STAGE(STAGE) (GET_STAGE_TRANSACTION_INFO(STAGE)->http_state) +#define CHANGE_HTTP_STATE(STATE,STAGE) {\ + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE)));\ + GET_HTTP_STATE_ON_STAGE(STAGE) = STATE;\ + DA_LOG_CRITICAL(Default, "Changed http_state to - [%d] ", GET_HTTP_STATE_ON_STAGE(STAGE));\ + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE)));\ +} + +typedef struct _file_info { + void *file_handle; + char *pure_file_name; + char *extension; + char *file_name_final; /* malloced in set_file_path_for_final_saving */ + char *content_type; /* malloced in make file info. */ + char *add_to_buffer; + unsigned int file_size; /* http header's Content-Length has higher priority than DD's */ + unsigned int total_bytes_written_to_file; /* current written file size */ + unsigned int bytes_written_to_file; + unsigned int current_buffer_len; +} file_info; + +#define GET_CONTENT_STORE_PURE_FILE_NAME(FILE_CNTXT) (FILE_CNTXT)->pure_file_name +#define GET_CONTENT_STORE_EXTENSION(FILE_CNTXT) (FILE_CNTXT)->extension +#define GET_CONTENT_STORE_ACTUAL_FILE_NAME(FILE_CNTXT) (FILE_CNTXT)->file_name_final +#define GET_CONTENT_STORE_FILE_HANDLE(FILE_CNTXT) (FILE_CNTXT)->file_handle +#define GET_CONTENT_STORE_FILE_SIZE(FILE_CNTXT) (FILE_CNTXT)->file_size +#define GET_CONTENT_STORE_CURRENT_FILE_SIZE(FILE_CNTXT) (FILE_CNTXT)->total_bytes_written_to_file +#define IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(FILE_CNTXT) (FILE_CNTXT)->bytes_written_to_file +#define GET_CONTENT_STORE_FILE_BUFFER(FILE_CNTXT) (FILE_CNTXT)->add_to_buffer +#define GET_CONTENT_STORE_FILE_BUFF_LEN(FILE_CNTXT) ((FILE_CNTXT)->current_buffer_len) +#define GET_CONTENT_STORE_CONTENT_TYPE(FILE_CNTXT) (FILE_CNTXT)->content_type + +typedef struct _stage_info { + int dl_id; + source_info_t dl_request; + req_dl_info dl_tansaction_context; + file_info dl_content_storage; + struct _stage_info *next_stage_info; +} stage_info; + +#define GET_STAGE_DL_ID(STAGE) ((STAGE)->dl_id) +#define GET_STAGE_SOURCE_INFO(STAGE) (&((STAGE)->dl_request)) +#define GET_STAGE_TRANSACTION_INFO(STAGE) (&((STAGE)->dl_tansaction_context)) +#define GET_STAGE_CONTENT_STORE_INFO(STAGE) (&((STAGE)->dl_content_storage)) +#define GET_STAGE_INSTALLATION_INFO(STAGE) (&((STAGE)->post_dl_context)) + +typedef struct { + da_bool_t is_using; + int slot_id; + int dl_id; + pthread_t active_dl_thread_id; + download_state_t state; + stage_info *download_stage_data; + queue_t queue; + int http_status; + da_bool_t enable_pause_update; + // FIXME have client_input itself, not to have each of them + char *user_install_path; + char *user_file_name; + char *user_etag; + char *user_temp_file_path; + void *user_data; +} dl_info_t; + +#define GET_DL_THREAD_ID(ID) (download_mgr.dl_info[ID].active_dl_thread_id) +#define GET_DL_STATE_ON_ID(ID) (download_mgr.dl_info[ID].state) +#define GET_DL_STATE_ON_STAGE(STAGE) (GET_DL_STATE_ON_ID(GET_STAGE_DL_ID(STAGE))) +#define GET_DL_CURRENT_STAGE(ID) (download_mgr.dl_info[ID].download_stage_data) +#define GET_DL_ID(ID) (download_mgr.dl_info[ID].dl_id) +#define GET_DL_QUEUE(ID) &(download_mgr.dl_info[ID].queue) +#define GET_DL_ENABLE_PAUSE_UPDATE(ID) (download_mgr.dl_info[ID].enable_pause_update) +#define GET_DL_USER_INSTALL_PATH(ID) (download_mgr.dl_info[ID].user_install_path) +#define GET_DL_USER_FILE_NAME(ID) (download_mgr.dl_info[ID].user_file_name) +#define GET_DL_USER_ETAG(ID) (download_mgr.dl_info[ID].user_etag) +#define GET_DL_USER_TEMP_FILE_PATH(ID) (download_mgr.dl_info[ID].user_temp_file_path) +#define GET_DL_USER_DATA(ID) (download_mgr.dl_info[ID].user_data) +#define IS_THIS_DL_ID_USING(ID) (download_mgr.dl_info[ID].is_using) + +#define CHANGE_DOWNLOAD_STATE(STATE,STAGE) {\ + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(STAGE)]);\ + GET_DL_STATE_ON_STAGE(STAGE) = STATE;\ + DA_LOG_CRITICAL(Default, "Changed download_state to - [%d] ", GET_DL_STATE_ON_STAGE(STAGE));\ + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(STAGE)]);\ + } + +typedef struct _download_mgr_t { + da_bool_t is_init; + dl_info_t dl_info[DA_MAX_DOWNLOAD_ID]; + dl_id_history_t dl_id_history; + /* FIXME: This is temporary solution to prevent crash on following case; + * 1) OMA download(that is, DA's libsoup is using) is on progressing on Browser + * 2) User push END hard key + * 3) da_deinit() is called. - on UI thread + * 4) cancel_download(all) is called. + * 5) plugin-libsoup.c calls soup_session_cancel_message(). + * 6) da_deinit() is finished and process is over. + * 7) soup's callback for soup_session_cancel_message() is trying to be called - on UI thread + * 8) Browser crashed because the callback address is no longer exist. + * + * Here is a temporary solution; + * If cancel is from da_deinit(), plugin-libsoup.c will not call soup_session_cancel_message(). + * So, append following variable to recognize this. + **/ + //da_bool_t is_progressing_deinit; +} download_mgr_t; + +extern download_mgr_t download_mgr; + +da_result_t init_download_mgr(); +da_result_t deinit_download_mgr(void); +void init_download_info(int slot_id); +void destroy_download_info(int slot_id); +void *Add_new_download_stage(int slot_id); +void remove_download_stage(int slot_id, stage_info *in_stage); +void empty_stage_info(stage_info *in_stage); +void clean_up_client_input_info(client_input_t *client_input); +da_result_t get_available_slot_id(int *available_id); +da_result_t get_slot_id_for_dl_id(int dl_id , int* slot_id); +da_bool_t is_valid_slot_id(int slot_id); +void store_http_status(int dl_id, int status); +int get_http_status(int dl_id); +#endif /* _Download_Agent_Dl_Info_Util_H */ diff --git a/src/agent/include/download-agent-dl-mgr.h b/src/agent/include/download-agent-dl-mgr.h new file mode 100755 index 0000000..b273fd8 --- /dev/null +++ b/src/agent/include/download-agent-dl-mgr.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Dl_Mgr_H +#define _Download_Agent_Dl_Mgr_H + +#include "download-agent-type.h" +#include "download-agent-dl-info-util.h" + +da_result_t cancel_download(int dl_id); +da_result_t suspend_download(int dl_id, da_bool_t is_enable_cb); +da_result_t resume_download (int dl_id); + +da_result_t requesting_download(stage_info *stage); +da_result_t handle_after_download(stage_info *stage); +da_result_t send_user_noti_and_finish_download_flow( + int slot_id, char *installed_path, char *etag); + +da_bool_t is_valid_download_id(int dl_id); +#endif diff --git a/src/agent/include/download-agent-encoding.h b/src/agent/include/download-agent-encoding.h new file mode 100755 index 0000000..c5c7fe5 --- /dev/null +++ b/src/agent/include/download-agent-encoding.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Encoding_H +#define _Download_Agent_Encoding_H + +#include "download-agent-type.h" + +da_bool_t is_base64_encoded_word(const char *in_str); +da_result_t decode_base64_encoded_str(const char *in_encoded_str, + char **out_decoded_ascii_str); +void decode_url_encoded_str(const char *in_encoded_str, char **out_str); + +#endif // _Download_Agent_Encoding_H diff --git a/src/agent/include/download-agent-file.h b/src/agent/include/download-agent-file.h new file mode 100755 index 0000000..3bada44 --- /dev/null +++ b/src/agent/include/download-agent-file.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_File_H +#define _Download_Agent_File_H + +#include +#include +#include + +#include "download-agent-type.h" +#include "download-agent-dl-mgr.h" + +#define DA_DEFAULT_FILE_DIR_PATH "/opt/usr/media/.tmp_download" +#define DA_DEFAULT_INSTALL_PATH_FOR_PHONE "/opt/usr/media/Downloads" +#define DA_DEFAULT_INSTALL_PATH_FOR_MMC "/opt/storage/sdcard/Downloads" + +da_bool_t is_file_exist(const char *file_path); +da_bool_t is_dir_exist(char *dir_path); + +void get_file_size(char *file_path, unsigned long long *out_file_size); + +da_result_t clean_files_from_dir(char *dir_path); +da_result_t create_saved_dir(void); + +da_result_t file_write_ongoing(stage_info *stage, char *body, int body_len); +da_result_t file_write_complete(stage_info *stage); +da_result_t start_file_writing(stage_info *stage); +da_result_t start_file_writing_append(stage_info *stage); +da_result_t start_file_writing_append_with_new_download(stage_info *stage); + +da_result_t get_mime_type(stage_info *stage, char **out_mime_type); +da_result_t discard_download(stage_info *stage) ; +void clean_paused_file(stage_info *stage); +da_result_t replace_content_file_in_stage(stage_info *stage, const char *dest_dd_file_path); +da_result_t decide_tmp_file_path(stage_info *stage); +char *get_full_path_avoided_duplication(char *in_dir, char *in_candidate_file_name, char *in_extension); + +da_result_t copy_file(const char *src, const char *dest); +da_result_t create_dir(const char *install_dir); +da_result_t get_default_dir(char **out_path); +da_result_t get_default_install_dir(char **out_path); + +#endif diff --git a/src/agent/include/download-agent-http-mgr.h b/src/agent/include/download-agent-http-mgr.h new file mode 100755 index 0000000..6653af0 --- /dev/null +++ b/src/agent/include/download-agent-http-mgr.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Http_Mgr_H +#define _Download_Agent_Http_Mgr_H + +#include + +#include "download-agent-type.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-http-queue.h" + +#define DA_MAX_SESSION_INFO DA_MAX_DOWNLOAD_ID +#define DA_MAX_TRANSACTION_INFO 10 +#define DA_MAX_TRANSACTION_MUTEX DA_MAX_SESSION_INFO*DA_MAX_TRANSACTION_INFO + +typedef struct _http_mgr_t +{ + da_bool_t is_init; + da_bool_t is_http_init; +}http_mgr_t; + +extern http_mgr_t http_mgr; + +da_result_t init_http_mgr(void); +void deinit_http_mgr(void); +da_result_t make_req_dl_info_http(stage_info *stage, req_dl_info *out_info); +da_result_t request_http_download(stage_info *stage); +da_result_t request_to_cancel_http_download(stage_info *stage); +da_result_t request_to_abort_http_download(stage_info *stage); +da_result_t request_to_suspend_http_download(stage_info *stage); +da_result_t request_to_resume_http_download(stage_info *stage); + +#endif diff --git a/src/agent/include/download-agent-http-misc.h b/src/agent/include/download-agent-http-misc.h new file mode 100755 index 0000000..3bf137b --- /dev/null +++ b/src/agent/include/download-agent-http-misc.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Http_Misc_H +#define _Download_Agent_Http_Misc_H + +#include + +#include "download-agent-type.h" + +#define SCHEME_HTTP "http://" +#define SCHEME_HTTPS "https://" +#define SCHEME_CID "cid:" + +#define METHOD_GET "GET" +#define METHOD_POST "POST" +#define METHOD_HEAD "HEAD" + +#define HTTP_TAG_UAGENT "User-Agent: " +#define HTTP_TAG_HOST "Host: " +#define HTTP_TAG_UAPROF "X-Wap-Profile: " +#define HTTP_TAG_CONTENT_LENGTH "Content-Length: " +#define HTTP_TAG_CONTENT_TYPE "Content-Type: " +#define HTTP_TAG_IF_MATCH "If-Match: " +#define HTTP_TAG_RANGE "Range: " +#define HTTP_TAG_IF_RANGE "If-Range: " + +#define END_OF_FIELD "\r\n" + +char *get_user_agent(); + +da_bool_t is_supporting_protocol(const char *protocol); + +#endif diff --git a/src/agent/include/download-agent-http-msg-handler.h b/src/agent/include/download-agent-http-msg-handler.h new file mode 100755 index 0000000..29d0ae4 --- /dev/null +++ b/src/agent/include/download-agent-http-msg-handler.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Http_Msg_Handler_H +#define _Download_Agent_Http_Msg_Handler_H + +#include "download-agent-type.h" + +#define HTTP_FIELD_UAGENT "User-Agent" +#define HTTP_FIELD_HOST "Host" +#define HTTP_FIELD_UAPROF "X-Wap-Profile" +#define HTTP_FIELD_CONTENT_LENGTH "Content-Length" +#define HTTP_FIELD_CONTENT_TYPE "Content-Type" +#define HTTP_FIELD_IF_MATCH "If-Match" +#define HTTP_FIELD_RANGE "Range" +#define HTTP_FIELD_IF_RANGE "If-Range" +#define HTTP_FIELD_ACCEPT_LANGUAGE "Accept-Language" +#define HTTP_FIELD_ACCEPT_CHARSET "Accept-Charset" + +typedef struct _http_header_options_t http_header_options_t; +struct _http_header_options_t{ + char *field; + char *value; + + http_header_options_t *next; +}; + +typedef struct _http_header_t http_header_t; +struct _http_header_t{ + char *field; + char *value; + http_header_options_t *options; + + char *raw_value; // raw string including options + + http_header_t *next; +}; + +typedef struct{ + char *http_method; + char *url; + http_header_t *head; + char *http_body; +}http_msg_request_t; + + +typedef struct{ + int status_code; + http_header_t *head; +}http_msg_response_t; + +typedef http_header_t *http_msg_iter_t; + + +typedef struct{ + http_msg_request_t *http_msg_request; + http_msg_response_t *http_msg_response; +}http_info_t; + + +da_result_t http_msg_request_create(http_msg_request_t **http_msg_request); +void http_msg_request_destroy(http_msg_request_t **http_msg_request); + +da_result_t http_msg_request_set_method(http_msg_request_t *http_msg_request, const char *method); +da_result_t http_msg_request_get_method(http_msg_request_t *http_msg_request, const char **method); + +da_result_t http_msg_request_set_url(http_msg_request_t *http_msg_request, const char *url); +da_result_t http_msg_request_get_url(http_msg_request_t *http_msg_request, const char **url); + +da_result_t http_msg_request_set_body(http_msg_request_t *http_msg_request, const char *body); +da_result_t http_msg_request_get_body(http_msg_request_t *http_msg_request, const char **body); + +da_result_t http_msg_request_add_field(http_msg_request_t *http_msg_request, const char *field, const char *value); + + +da_result_t http_msg_response_create(http_msg_response_t **http_msg_response); +void http_msg_response_destroy(http_msg_response_t **http_msg_response); + +da_result_t http_msg_response_set_status_code(http_msg_response_t *http_msg_response, int status_code); +da_result_t http_msg_response_get_status_code(http_msg_response_t *http_msg_response, int *status_code); + +da_result_t http_msg_response_add_field(http_msg_response_t *http_msg_response, const char *field, const char *value); + +/* Caution! Caller must free memory for every "char** out_xxx" for followings */ +da_bool_t http_msg_response_get_content_type(http_msg_response_t *http_msg_response, char **out_type); +void http_msg_response_set_content_type(http_msg_response_t *http_msg_response, const char *in_type); + +da_bool_t http_msg_response_get_content_length(http_msg_response_t *http_msg_response, unsigned long long *out_length); +da_bool_t http_msg_response_get_content_disposition(http_msg_response_t *http_msg_response, char **out_disposition, char **out_file_name); +da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value); +da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, char **out_value); +da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, char **out_value); +// should be refactored later +da_result_t http_msg_response_get_boundary(http_msg_response_t *http_msg_response, char **out_val); + + +da_result_t http_msg_request_get_iter(http_msg_request_t *http_msg_request, http_msg_iter_t *http_msg_iter); +da_result_t http_msg_response_get_iter(http_msg_response_t *http_msg_response, http_msg_iter_t *http_msg_iter); + +// should remove later +da_bool_t http_msg_get_field_with_iter(http_msg_iter_t *http_msg_iter, char **field, char **value); +da_bool_t http_msg_get_header_with_iter(http_msg_iter_t *http_msg_iter, char **out_field, http_header_t **out_header); + +char *get_http_response_header_raw(http_msg_response_t *http_msg_response); + +da_bool_t extract_attribute_from_header(char *szHeadStr, const char *szFindStr, char **ppRtnValue); +#endif // _Download_Agent_Http_Msg_Handler_H diff --git a/src/agent/include/download-agent-http-queue.h b/src/agent/include/download-agent-http-queue.h new file mode 100755 index 0000000..68bb910 --- /dev/null +++ b/src/agent/include/download-agent-http-queue.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Http_Queue_H +#define _Download_Agent_Http_Queue_H + + +#include "download-agent-type.h" +#include "download-agent-http-msg-handler.h" + +#include +#include + +#define MAX_QUEUE_SIZE 1024*64 + +typedef enum +{ + Q_EVENT_TYPE_DATA_HTTP, + Q_EVENT_TYPE_DATA_DRM, + Q_EVENT_TYPE_CONTROL, +}q_event_type; + +typedef enum +{ + Q_EVENT_TYPE_CONTROL_CANCEL, + Q_EVENT_TYPE_CONTROL_SUSPEND, + Q_EVENT_TYPE_CONTROL_RESUME, + Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED, + Q_EVENT_TYPE_CONTROL_ABORT, +// [090205][jungki]not used yet. +// Q_EVENT_TYPE_CONTROL_USER_CONFIRM_RESULT, +// Q_EVENT_TYPE_CONTROL_INSTALL_RESULT, +}q_event_type_control; + +typedef enum +{ + Q_EVENT_TYPE_DATA_PACKET, + Q_EVENT_TYPE_DATA_FINAL, + Q_EVENT_TYPE_DATA_ABORT, +}q_event_type_data; + +typedef struct _q_event_data_http_t +{ + q_event_type_data data_type; + + int status_code; + + http_msg_response_t* http_response_msg; + + int body_len; + char *body_data; + + da_result_t error_type; +}q_event_data_http_t; + +typedef struct _q_event_control_t +{ + q_event_type_control control_type; +}q_event_control_t; + +typedef struct _q_event_t q_event_t; +struct _q_event_t +{ + int size; + q_event_type event_type; + union _type + { + q_event_data_http_t q_event_data_http; + q_event_control_t q_event_control; + } type; + + q_event_t *next; +}; + +typedef struct _queue_t +{ + da_bool_t having_data; + + q_event_t *control_head; + q_event_t *data_head; + + pthread_mutex_t mutex_queue; + pthread_cond_t cond_queue; + + int queue_size; +}queue_t; + +void Q_init_queue(queue_t *queue); +void Q_destroy_queue(queue_t *queue); + +void Q_init_q_event(q_event_t *q_event); +void Q_destroy_q_event(q_event_t **q_event); + +da_result_t Q_make_control_event(q_event_type_control control_type, q_event_t **out_event); + +da_result_t Q_make_http_data_event(q_event_type_data data_type, q_event_t **out_event); +da_result_t Q_set_status_code_on_http_data_event(q_event_t *q_event, int status_code); +da_result_t Q_set_http_body_on_http_data_event(q_event_t *q_event, int body_len, char *body_data); +da_result_t Q_set_error_type_on_http_data_event(q_event_t *q_event, int error_type); + + +da_bool_t Q_push_event(const queue_t *in_queue, const q_event_t *in_event); +da_bool_t Q_push_event_without_lock(const queue_t *in_queue, const q_event_t *in_event); +void Q_pop_event(const queue_t *in_queue, q_event_t **out_event); + +#define GET_IS_Q_HAVING_DATA(QUEUE) (QUEUE->having_data) + +void Q_goto_sleep(const queue_t *in_queue); +void Q_wake_up(const queue_t *in_queue); + + +#endif diff --git a/src/agent/include/download-agent-interface.h b/src/agent/include/download-agent-interface.h new file mode 100755 index 0000000..d51aed6 --- /dev/null +++ b/src/agent/include/download-agent-interface.h @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Interface_H +#define _Download_Agent_Interface_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "download-agent-defs.h" +#include + +/** + * @struct user_paused_info_t + * @brief Download Agent will send its state through this structure. + * @see da_paused_info_cb + * @par + * This is used only by callback /a user_paused_info_t. \n + */ +typedef struct { + /// download request id for this notification + int download_id; +} user_paused_info_t; + +/** + * @struct user_progress_info_t + * @brief Download Agent will send current downloading file's information through this structure. + * @see da_progress_info_cb + * @par + * This is used only by callback /a da_progress_info_cb. \n + */ +typedef struct { + /// download request id for this updated download information + int download_id; + /// received size of chunked data. + unsigned long int received_size; +} user_progress_info_t; + +/** + * @struct user_download_info_t + * @brief Download Agent will send current download's information through this structure. + * @see da_started_info_cb + * @par + * This is used only by callback /a da_started_info_cb. \n + */ +typedef struct { + /// download request id for this updated download information + int download_id; + /// file's mime type from http header. + char *file_type; + /// file size from http header. + unsigned long int file_size; + /// This is temporary file path. + char *tmp_saved_path; + /// This is the file name for showing to user. + char *content_name; + /// etag string value for resume download, + char *etag; +} user_download_info_t; + +typedef struct { + /// download request id for this updated download information + int download_id; + /// This has only file name for now. + char *saved_path; + /// etag string value for resume download, + /// This is returned when the download is failed and the etag is received from content server + char *etag; + /// convey error code if necessary, or it is zero. + int err; + /// http status code if necessary, or it is zero. + int http_status; +} user_finished_info_t; + +typedef struct { + const char **request_header; + int request_header_count; + const char *install_path; + const char *file_name; + const char *temp_file_path; /* For resume download, the "etag" value should be existed together */ + const char *etag; /* For resume download */ + void *user_data; +} extension_data_t; + +/** + * @typedef da_paused_cb + * @brief Download Agent will call this function to paused its state. + * + * This is user callback function registered on \a da_init. \n + * + * @remarks For the most of time, this state is just informative, so, user doesn't need to do any action back to Download Agent. + * + * @warning Download will be holding until getting user confirmation result through the function. + * + * @param[in] state state from Download Agent + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_paused_info_cb) (user_paused_info_t *paused_info, void *user_param); + +/** + * @brief Download Agent will call this function to update received size of download-requested file. + * + * This is user callback function registered on \a da_init. \n + * This is informative, so, user doesn't need to do any action back to Download Agent.\n + * + * @param[in] progress_info updated downloading information + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_progress_info_cb) (user_progress_info_t *progress_info, void *user_param); + +/** + * @brief Download Agent will call this function to update mime type, temp file name, total file sizeand installed path. + * + * This is user callback function registered on \a da_init. \n + * This is informative, so, user doesn't need to do any action back to Download Agent.\n + * + * @param[in] download_info updated download information + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_started_info_cb) (user_download_info_t *download_info, void *user_param); + +typedef void (*da_finished_info_cb) (user_finished_info_t *finished_info, void *user_param); + /** + * @struct da_client_cb_t + * @brief This structure convey User's callback functions for \a da_init + * @see da_init + */ +typedef struct { + /// callback to convey download information + da_started_info_cb update_dl_info_cb; + /// callback to convey downloading information while downloading including received file size + da_progress_info_cb update_progress_info_cb; + /// callback to convey saved path + da_finished_info_cb finished_info_cb; + /// callback to convey etag value + da_paused_info_cb paused_info_cb; +} da_client_cb_t; + +/** + * @fn int da_init (da_client_cb_t *da_client_callback) + * @brief This function initiates Download Agent and registers user callback functions. + * @warning This should be called at once when client application is initialized before using other Download Agent APIs + * @warning This function is paired with da_deinit function. + * + * @pre None. + * @post None. + * + * @param[in] da_client_callback User callback function structure. The type is struct data pointer. + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * @remarks User MUST call this function first rather than any other DA APIs. \n + * Please do not call UI code at callback function in direct. \n + * It is better that it returns as soon as copying the data of callback functon. \n + * @see da_deinit + * @par Example + * @code + * #include + * + * void da_started_info_cb(user_download_info_t *download_info,void *user_param); + * void da_progress_info_cb(user_downloading_info_t *downloading_info,void *user_param); + * void da_finished_cb(user_finished_info_t *complted_info, void *user_param); + * void da_paused_info_cb(user_paused_info_t *paused_info, void *user_param); + * + * int download_initialize() + * { + * int da_ret; + * da_client_cb_t da_cb = {0}; + * + * da_cb.update_dl_info_cb = &update_download_info_cb; + * da_cb.update_progress_info_cb = &progress_info_cb; + * da_cb.finished_info_cb = &finished_info_cb; + * da_cb.paused_info_cb = &paused_cb; + * + * da_ret = da_init (&da_cb, 0); + * if (da_ret == DA_RESULT_OK) { + * // printf("successed\n"); + * return true; + * } else { + * // printf("failed with error code %d\n", da_ret); + * return fail; + * } + * } + * @endcode + */ +int da_init(da_client_cb_t *da_client_callback); + + /** + * @fn int da_deinit () + * @brief This function deinitiates Download Agent. + * + * This function destroys all infomation for client manager. + * When Download Agent is not used any more, please call this function. + * Usually when client application is destructed, this is needed. + * + * @remarks This is paired with da_init. \n + * The client Id should be the one from /a da_init(). \n + * Otherwise, it cannot excute to deinitialize. \n + * + * @pre da_init() must be called in advance. + * @post None. + * + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * @see da_init + * @par Example + * @code + * #include + * + * + * int download_deinitialize() + * { + * int da_ret; + * da_ret = da_deinit(); + * if(da_ret == DA_RESULT_OK) { + * // printf("successed\n"); + * return true; + * } else { + * // printf("failed with error code %d\n", da_ret); + * return fail; + * } + * } + @endcode + */ +int da_deinit(); + + /** + * @fn int da_start_download(const char *url, int *download_id) + * @brief This function starts to download a content on passed URL. + * + * Useful information and result are conveyed through following callbacks. + * @li da_started_info_cb + * @li da_progress_cb + * + * @pre da_init() must be called in advance. + * @post None. + * @remarks + * Downloaded file is automatically registered to system. (e.g. File DB) \n + * If there is another file has same name on registering directory, new one's name would have numbering postfix. \n + * (e.g. abc.mp3 to abc_1.mp3) + * + * @param[in] url url to start download + * @param[out] download_id assigned download request id for this URL + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * + * @see None. + * + * @par Example + * @code + * #include + * + * int da_ret; + * int download_id; + * char *url = "http://www.test.com/sample.mp3"; + * + * da_ret = da_start_download(url,&download_id); + * if (da_ret == DA_RESULT_OK) + * printf("download requesting is successed\n"); + * else + * printf("download requesting is failed with error code %d\n", da_ret); + * @endcode + */ +int da_start_download(const char *url, int *download_id); + +/** +* @fn int da_start_download_with_extension(const char *url, extension_data_t ext_data, int *download_id) +* @brief This function starts to download a content on passed URL with passed extension. +* +* Useful information and result are conveyed through following callbacks. +* @li da_started_info_cb +* @li da_progress_cb +* +* @pre da_init() must be called in advance. +* @post None. +* @remarks This API operation is exactly same with da_start_download(), except for input properties. \n +* +* @param[in] url url to start download +* @param[in] ext_data extension data +* @param[out] download_id assigned download request id for this URL +* @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. +* +* +* @par Example +* @code + #include + + int da_ret; + int download_id; + extension_data_t ext_data = {0,}; + const char *url = "https://www.test.com/sample.mp3"; + const char *install_path = "/myFiles/music"; + const char *my_data = strdup("data"); + ext_data.install_path = install_path; + ext_data.user_data = (void *)my_data; + + da_ret = da_start_download_with_extension(url, &download_id, &ext_data); + if (da_ret == DA_RESULT_OK) + printf("download requesting is successed\n"); + else + printf("download requesting is failed with error code %d\n", da_ret); + @endcode +*/ +int da_start_download_with_extension(const char *url, + extension_data_t *ext_data, + int *download_id +); + + +/** + * @fn int da_cancel_download(int download_id) + * @brief This function cancels a download for passed download_id. + * + * Client can use this function if user wants to cancel already requested download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then previous requested download can be keep downloading. + * @remarks After calling this function, all information for the download_id will be deleted. So, client cannot request anything for the download_id. + * + * @pre There should be exist ongoing or suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see None. + * + * @par Example + * @code + #include + + int da_ret; + int download_id; + + da_ret = da_cancel_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully canceled.\n", download_id); + } + else { + // in this case, downloading with download_id is keep ongoing. + printf("failed to cancel with error code %d\n", da_ret); + } + @endcode + */ +int da_cancel_download(int download_id); + + +/** + * @fn int da_suspend_download(int download_id) + * @brief This function suspends downloading for passed download_id. + * + * Client can use this function if user wants to suspend already requested download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then previous requested download can be keep downloading. + * @remarks After calling this function, all information for the download_id will be remained. So, client can request resume for the download_id. + * @remarks Client should cancel or resume for this download_id, or all information for the download_id will be leaved forever. + * + * @pre There should be exist ongoing download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see da_resume_download() + * @see da_cancel_download() + * + * @par Example + * @code + #include + + int da_ret; + int download_id; + + da_ret = da_suspend_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully suspended.\n", download_id); + } + else { + // in this case, downloading with download_id is keep ongoing. + printf("failed to suspend with error code %d\n", da_ret); + } + @endcode + */ +int da_suspend_download(int download_id); + +int da_suspend_download_without_update(int download_id); +/** + * @fn int da_resume_download(int download_id) + * @brief This function resumes downloading for passed download_id. + * + * Client can use this function if user wants to resume suspended download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then requested download can be not to resume. + * + * @pre There should be exist suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see da_suspend_download() + * + * @par Example + * @code + #include + + int da_ret; + int download_id; + + da_ret = da_resume_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully resumed.\n", download_id); + } + else { + // in this case, downloading with download_id is keep suspended. + printf("failed to resume with error code %d\n", da_ret); + } + @endcode + */ +int da_resume_download(int download_id); + +/** + * @fn int da_is_valid_download_id(int download_id) + * @brief This function return the download id is valid and the download thread is still alive. + * + * Client can use this function if user wants to resume download. + * If the download id is vaild and the download thread is alive, it can resume download with using da_resume_download() + * If the the download thread was already terminated due to restarting the process, + * it can resume download with using da_start_download_with_extension() + * + * + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then requested download can be not to resume. + * + * @pre There should be exist suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return 1 for success, or 0 for fail + * + */ +int da_is_valid_download_id(int download_id); + +#ifdef __cplusplus +} +#endif + +#endif //_Download_Agent_Interface_H + + diff --git a/src/agent/include/download-agent-mime-util.h b/src/agent/include/download-agent-mime-util.h new file mode 100755 index 0000000..2e03c80 --- /dev/null +++ b/src/agent/include/download-agent-mime-util.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Mime_Table_H +#define _Download_Agent_Mime_Table_H + +#include "download-agent-type.h" + +#define NO_EXTENSION_NAME_STR "dat" + +typedef struct { + char *standard; + char *normal; +} Ext_translation_table; + +da_bool_t is_ambiguous_MIME_Type(const char *in_mime_type); +da_bool_t da_get_extension_name_from_url(char *url, char **ext); +da_result_t da_mime_get_ext_name(char *mime, char **ext); +da_bool_t da_get_file_name_from_url(char *url, char **name) ; +void delete_prohibited_char(char *szTarget, int str_len); +#endif diff --git a/src/agent/include/download-agent-plugin-conf.h b/src/agent/include/download-agent-plugin-conf.h new file mode 100755 index 0000000..a549c23 --- /dev/null +++ b/src/agent/include/download-agent-plugin-conf.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Plugin_Conf_H +#define _Download_Agent_Plugin_Conf_H + +#include "download-agent-type.h" +#include "download-agent-interface.h" +#include "download-agent-utils.h" + +da_result_t get_user_agent_string(char **uagent_str); +da_result_t get_storage_type(da_storage_type_t *type); +char *get_proxy_address(void); + +#endif diff --git a/src/agent/include/download-agent-plugin-http-interface.h b/src/agent/include/download-agent-plugin-http-interface.h new file mode 100755 index 0000000..b9698bb --- /dev/null +++ b/src/agent/include/download-agent-plugin-http-interface.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Plugin_Http_Interface_H +#define _Download_Agent_Plugin_Http_Interface_H + +#include "download-agent-type.h" +#include "download-agent-http-msg-handler.h" + +typedef enum { + PI_HTTP_METHOD_GET = 1, + PI_HTTP_METHOD_POST = 2, + PI_HTTP_METHOD_HEAD = 3 +} pi_http_method_t; + + +typedef struct _input_for_tranx_t { + pi_http_method_t http_method; + + char *proxy_addr; + queue_t *queue; + + http_msg_request_t* http_msg_request; +} input_for_tranx_t; + + + +da_result_t PI_http_init(void); +void PI_http_deinit(void); + +da_result_t PI_http_start_transaction(const input_for_tranx_t *input_for_tranx, int *out_tranx_id); +da_result_t PI_http_cancel_transaction(int in_tranx_id, da_bool_t abort_option); +da_result_t PI_http_disconnect_transaction(int in_tranx_id); +void PI_http_pause_transaction(int transaction_id); +void PI_http_unpause_transaction(int transaction_id); + +#endif + diff --git a/src/agent/include/download-agent-plugin-libsoup.h b/src/agent/include/download-agent-plugin-libsoup.h new file mode 100755 index 0000000..8160042 --- /dev/null +++ b/src/agent/include/download-agent-plugin-libsoup.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Plugin_Libsoup_H +#define _Download_Agent_Plugin_Libsoup_H + +#include +#include + +#include "download-agent-http-queue.h" +#include "download-agent-pthread.h" +#include "download-agent-plugin-http-interface.h" + +typedef struct _pi_session_table_t { + da_bool_t is_using; + SoupSession *session; + SoupMessage *msg; + queue_t *queue; + pthread_mutex_t mutex; + pthread_cond_t cond; + da_bool_t is_paused; +} pi_session_table_t; + +extern pi_session_table_t pi_session_table[]; + +#define MAX_SESSION_COUNT DA_MAX_DOWNLOAD_REQ_AT_ONCE +#define MAX_TIMEOUT 60 // second + +#define IS_VALID_SESSION_TABLE_ENTRY(ENTRY) ((((ENTRY) < 0) || ((ENTRY) > MAX_SESSION_COUNT-1)) ? 0 : 1) + + +#define GET_SESSION_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].session) +#define GET_MSG_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].msg) +#define GET_QUEUE_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].queue) + + +da_bool_t _pi_http_is_valid_input_for_tranx(const input_for_tranx_t *input_for_tranx); + +void _pi_http_init_session_table_entry(const int in_session_table_entry); +void _pi_http_destroy_session_table_entry(const int in_session_table_entry); +int _pi_http_get_avaiable_session_table_entry(void); + +da_bool_t _pi_http_register_queue_to_session_table(const int session_table_entry, const queue_t *in_queue); +da_bool_t _pi_http_register_session_to_session_table(const int in_session_table_entry, SoupSession *session); +da_bool_t _pi_http_register_msg_to_session_table(const int in_session_table_entry, SoupMessage *msg); + +queue_t *_pi_http_get_queue_from_session_table_entry(const int in_session_table_entry); +int _pi_http_get_session_table_entry_from_message(SoupMessage *msg); + +void _pi_http_store_read_data_to_queue(SoupMessage *msg, const char *body_data, int received_body_len); +void _pi_http_store_read_header_to_queue(SoupMessage *msg, const char *sniffedType); +void _pi_http_store_neterr_to_queue(SoupMessage *msg); + + +void _pi_http_finished_cb(SoupSession *session, SoupMessage *msg, gpointer data); +void _pi_http_restarted_cb(SoupMessage *msg, gpointer data); +void _pi_http_gotheaders_cb(SoupMessage *msg, gpointer data); +void _pi_http_contentsniffed_cb(SoupMessage *msg, const char *sniffedType, GHashTable *params, gpointer data); +void _pi_http_gotchunk_cb(SoupMessage *msg, SoupBuffer *chunk, gpointer data); + + +#endif diff --git a/src/agent/include/download-agent-pthread.h b/src/agent/include/download-agent-pthread.h new file mode 100755 index 0000000..8523567 --- /dev/null +++ b/src/agent/include/download-agent-pthread.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Pthread_H +#define _Download_Agent_Pthread_H + +#include +#include +#include + +#include "download-agent-type.h" +#include "download-agent-debug.h" + +#define _da_thread_mutex_init(mutex_add, attr) { \ + int ret = 0; \ + do{ \ + ret = pthread_mutex_init(mutex_add, attr); \ + if (0 == ret){ \ + break; \ + } \ + else if(EINVAL == ret){ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with EINVAL."); \ + break; \ + } \ + else if(ENOMEM == ret){ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with ENOMEM."); \ + break; \ + } \ + else{ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with %d.", ret); \ + break; \ + } \ + }while(1); \ + } + +#define _da_thread_cond_init(cond_add, attr) do{ \ + if (0 != pthread_cond_init(cond_add, attr)){\ + DA_LOG_ERR(Default, "pthread_cond_init FAIL");} \ + }while(0) + + + +#define _da_thread_mutex_lock(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_lock(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with EINVAL.");\ + break;\ + }\ + else if(EDEADLK == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with EDEADLK.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + + +#define _da_thread_mutex_unlock(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_unlock(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with EINVAL.");\ + break;\ + }\ + else if(EPERM == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with EPERM.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + + +#define _da_thread_cond_signal(cond_add) do{ \ + if (0 != pthread_cond_signal(cond_add)){\ + DA_LOG_ERR(Default, "pthread_cond_signal FAIL");} \ + }while(0) + + + +#define _da_thread_cond_wait(cond_add, mutex_add) do{ \ + if (0 != pthread_cond_wait(cond_add, mutex_add)){\ + DA_LOG_ERR(Default, "pthread_cond_wait FAIL");} \ + }while(0) + +#define _da_thread_cond_timed_wait(cond_add, mutex_add, time) do{ \ + if (0 != pthread_cond_timedwait(cond_add, mutex_add, time)){\ + DA_LOG_ERR(Default, "pthread_cond_wait FAIL");} \ + }while(0) + + +#define _da_thread_cond_destroy(cond_add) do{ \ + if (0 != pthread_cond_destroy(cond_add)){\ + DA_LOG_ERR(Default, "pthread_cond_destroy FAIL");} \ + }while(0) + +#define _da_thread_mutex_destroy(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_destroy(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with EINVAL.");\ + break;\ + }\ + else if(EBUSY == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with EBUSY.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + +#endif diff --git a/src/agent/include/download-agent-type.h b/src/agent/include/download-agent-type.h new file mode 100755 index 0000000..86fa5ca --- /dev/null +++ b/src/agent/include/download-agent-type.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Types_H +#define _Download_Agent_Types_H + +#include "download-agent-defs.h" + +typedef int da_result_t; +typedef int da_bool_t; + +#define IS_NOT_VALID_ID(x) (x <= DA_INVALID_ID) + +#define DA_MAX_URI_LEN 1024 +#define DA_MAX_FULL_PATH_LEN 356 // need configuration +#define DA_MAX_FILE_PATH_LEN 256 // need configuration +#define DA_MAX_STR_LEN 256 +#define DA_MAX_MIME_STR_LEN 256 +#define DA_MAX_PROXY_ADDR_LEN 64 // e.g. 100.200.300.400:10000 + +#endif + diff --git a/src/agent/include/download-agent-utils-dl-id-history.h b/src/agent/include/download-agent-utils-dl-id-history.h new file mode 100755 index 0000000..3e32048 --- /dev/null +++ b/src/agent/include/download-agent-utils-dl-id-history.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Utils_Hash_Table_H +#define _Download_Agent_Utils_Hash_Table_H + +#include "download-agent-pthread.h" + +typedef struct _dl_id_history_t dl_id_history_t; +struct _dl_id_history_t { + int starting_num; + int cur_dl_id; + pthread_mutex_t mutex; +}; + +da_result_t init_dl_id_history(dl_id_history_t *dl_id_history); +da_result_t deinit_dl_id_history(dl_id_history_t *dl_id_history); + +int get_available_dl_id(dl_id_history_t *dl_id_history); + + +#endif /* _Download_Agent_Utils_Hash_Table_H */ diff --git a/src/agent/include/download-agent-utils.h b/src/agent/include/download-agent-utils.h new file mode 100755 index 0000000..4361ad5 --- /dev/null +++ b/src/agent/include/download-agent-utils.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _Download_Agent_Utils_H +#define _Download_Agent_Utils_H + +#include +#include "download-agent-defs.h" +#include "download-agent-interface.h" +#include "download-agent-dl-mgr.h" + +/* Todo : move these to mime-util.c */ +#define MIME_DRM_MESSAGE "application/vnd.oma.drm.message" +#define MIME_ODF "application/vnd.oasis.opendocument.formula" +#define MIME_OMA_DD "application/vnd.oma.dd+xml" +#define MIME_MIDP_JAR "application/vnd.sun.j2me.java-archive" +#define MIME_MULTIPART_MESSAGE "multipart/related" +#define MIME_TEXT_PLAIN "text/plain" + +#define SAVE_FILE_BUFFERING_SIZE_50KB (50*1024) +#define SAVE_FILE_BUFFERING_SIZE_5MB (5*1024*1024) + +#define DA_SLEEP(x) \ + do \ + { \ + struct timespec interval,remainder; \ + interval.tv_sec = (unsigned int)((x)/1000); \ + interval.tv_nsec = (((x)-(interval.tv_sec*1000))*1000000); \ + nanosleep(&interval,&remainder); \ + } while(0) + +typedef enum { + DA_STORAGE_PHONE, /*To Store in Phone memory*/ + DA_STORAGE_MMC, /*To Store in MMC */ + DA_STORAGE_SYSTEM /*To Store in both Phone and MMC*/ +} da_storage_type_t; + +typedef struct _da_storage_size_t { + unsigned long b_available; + unsigned long b_size; +} da_storage_size_t; + +typedef enum { + DA_MIME_TYPE_NONE, + DA_MIME_TYPE_DRM1_MESSATE, + DA_MIME_TYPE_END +} da_mime_type_id_t; + +void get_random_number(int *out_num); +da_result_t get_available_dd_id(int *available_id); +da_result_t get_extension_from_mime_type(char *mime_type, char **extension); +da_mime_type_id_t get_mime_type_id(char *content_type); +da_result_t get_available_memory(da_storage_type_t storage_type, da_storage_size_t *avail_memory); +da_bool_t is_valid_url(const char *url, da_result_t *err_code); + +int read_data_from_file(char *file, char**out_buffer); +da_result_t move_file(const char *from_path, const char *to_path); +void remove_file(const char *file_path); +char *_stristr(const char *long_str, const char *find_str); + +#endif diff --git a/src/download-provider-da-interface.c b/src/download-provider-da-interface.c new file mode 100755 index 0000000..2d875c3 --- /dev/null +++ b/src/download-provider-da-interface.c @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-request.h" + +#include "download-agent-defs.h" +#include "download-agent-interface.h" + +int dp_is_file_exist(const char *file_path) +{ + struct stat file_state; + int stat_ret; + + if (file_path == NULL) { + TRACE_ERROR("[NULL-CHECK] file path is NULL"); + return -1; + } + + stat_ret = stat(file_path, &file_state); + + if (stat_ret == 0) + if (file_state.st_mode & S_IFREG) + return 0; + + return -1; +} + +static int __change_error(int err) +{ + int ret = DP_ERROR_NONE; + switch (err) { + case DA_RESULT_OK: + ret = DP_ERROR_NONE; + break; + case DA_ERR_INVALID_ARGUMENT: + ret = DP_ERROR_INVALID_PARAMETER; + break; + case DA_ERR_FAIL_TO_MEMALLOC: + ret = DP_ERROR_OUT_OF_MEMORY; + break; + case DA_ERR_UNREACHABLE_SERVER: + ret = DP_ERROR_NETWORK_UNREACHABLE; + break; + case DA_ERR_HTTP_TIMEOUT: + ret = DP_ERROR_CONNECTION_TIMED_OUT; + break; + case DA_ERR_DISK_FULL: + ret = DP_ERROR_NO_SPACE; + break; + case DA_ERR_INVALID_STATE: + ret = DP_ERROR_INVALID_STATE; + break; + case DA_ERR_NETWORK_FAIL: + ret = DP_ERROR_CONNECTION_FAILED; + break; + case DA_ERR_INVALID_URL: + ret = DP_ERROR_INVALID_URL; + break; + case DA_ERR_INVALID_INSTALL_PATH: + ret = DP_ERROR_INVALID_DESTINATION; + break; + case DA_ERR_ALREADY_MAX_DOWNLOAD: + ret = DP_ERROR_TOO_MANY_DOWNLOADS; + break; + case DA_ERR_FAIL_TO_CREATE_THREAD: + case DA_ERR_FAIL_TO_OBTAIN_MUTEX: + case DA_ERR_FAIL_TO_ACCESS_FILE: + case DA_ERR_FAIL_TO_GET_CONF_VALUE: + case DA_ERR_FAIL_TO_ACCESS_STORAGE: + ret = DP_ERROR_IO_ERROR; + break; + } + return ret; +} + +static void __download_info_cb(user_download_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + if (!user_data) { + TRACE_ERROR("[NULL-CHECK] user_data"); + return ; + } + dp_request *request = (dp_request *) user_data; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + return ; + } + + int request_id = request->id; + + // update info before sending event + if (info->file_type) { + TRACE_INFO("[STARTED][%d] [%s]", request_id, info->file_type); + if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_MIMETYPE, + DP_DB_COL_TYPE_TEXT, info->file_type) == 0) { + + if (info->tmp_saved_path) { + TRACE_INFO("[PATH][%d] being written to [%s]", + request_id, info->tmp_saved_path); + if (dp_db_set_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_TMP_SAVED_PATH, DP_DB_COL_TYPE_TEXT, + info->tmp_saved_path) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + + if (info->file_size > 0) { + TRACE_INFO + ("[FILE-SIZE][%d] [%lld]", request_id, info->file_size); + CLIENT_MUTEX_LOCK(&(request->mutex)); + request->file_size = info->file_size; + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + if (dp_db_set_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_CONTENT_SIZE, + DP_DB_COL_TYPE_INT64, &info->file_size) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + + if (info->content_name) { + TRACE_INFO + ("[CONTENTNAME][%d] [%s]", request_id, info->content_name); + if (dp_db_set_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_CONTENT_NAME, + DP_DB_COL_TYPE_TEXT, info->content_name) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + if (info->etag) { + TRACE_INFO("[ETAG][%d] [%s]", request_id, info->etag); + if (dp_db_replace_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ETAG, + DP_DB_COL_TYPE_TEXT, info->etag) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + } else { + TRACE_ERROR + ("[ERROR][%d][SQL] failed to insert downloadinfo", + request_id); + } + } + + CLIENT_MUTEX_LOCK(&(request->mutex)); + + request->state = DP_STATE_DOWNLOADING; + if (dp_db_set_column(request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + + if (request->group && request->group->event_socket >= 0 && + request->state_cb) + dp_ipc_send_event(request->group->event_socket, + request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, 0); + + if (request->auto_notification) + request->noti_priv_id = + dp_set_downloadinginfo_notification + (request->id, request->packagename); + + CLIENT_MUTEX_UNLOCK(&(request->mutex)); +} + +static void __progress_cb(user_progress_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + if (!user_data) { + TRACE_ERROR("[NULL-CHECK] user_data"); + return ; + } + dp_request *request = (dp_request *) user_data; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d", + request->id, request->agent_id, info->download_id); + return ; + } + + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->state == DP_STATE_DOWNLOADING) { + request->received_size = info->received_size; + time_t tt = time(NULL); + struct tm *localTime = localtime(&tt); + // send event every 1 second. + if (request->progress_lasttime != localTime->tm_sec) { + request->progress_lasttime = localTime->tm_sec; + if (request->progress_cb && request->group && + request->group->event_socket >= 0 && + request->received_size > 0) + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, + request->received_size); + if (request->auto_notification) + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); +} + +static void __finished_cb(user_finished_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + TRACE_INFO("Agent ID[%d] err[%d] http_status[%d]", + info->download_id, info->err, info->http_status); + if (!user_data) { + TRACE_ERROR("[NULL-CHECK] user_data"); + return ; + } + dp_request *request = (dp_request *) user_data; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d", + request->id, request->agent_id, info->download_id); + return ; + } + + CLIENT_MUTEX_LOCK(&(request->mutex)); + int request_id = request->id; + dp_credential cred = request->credential; + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + dp_state_type state = DP_STATE_NONE; + dp_error_type errorcode = DP_ERROR_NONE; + + // update info before sending event + if (dp_db_update_date + (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + if (info->http_status > 0) + if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS, + DP_DB_COL_TYPE_INT, &info->http_status) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + if (info->err == DA_RESULT_OK) { + if (info->saved_path) { + char *str = NULL; + char *content_name = NULL; + + str = strrchr(info->saved_path, '/'); + if (str) { + str++; + content_name = dp_strdup(str); + TRACE_INFO("[PARSE][%d] content_name [%s]", + request_id, content_name); + } + TRACE_INFO + ("[chown][%d] [%d][%d]", request_id, cred.uid, cred.gid); + if (chown(info->saved_path, cred.uid, cred.gid) < 0) + TRACE_STRERROR("[ERROR][%d] chown", request_id); + if (dp_db_replace_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_SAVED_PATH, + DP_DB_COL_TYPE_TEXT, info->saved_path) == 0) { + if (content_name != NULL) { + if (dp_db_set_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_CONTENT_NAME, + DP_DB_COL_TYPE_TEXT, content_name) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + } else { + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + if (content_name != NULL) + free(content_name); + + errorcode = DP_ERROR_NONE; + state = DP_STATE_COMPLETED; + + TRACE_INFO("[COMPLETED][%d] saved to [%s]", + request_id, info->saved_path); + } else { + TRACE_ERROR("Cannot enter here"); + TRACE_ERROR("[ERROR][%d] No SavedPath", request_id); + errorcode = DP_ERROR_INVALID_DESTINATION; + state = DP_STATE_FAILED; + } + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->file_size == 0) { + request->file_size = request->received_size; + if (dp_db_replace_column + (request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_CONTENT_SIZE, + DP_DB_COL_TYPE_INT64, &request->file_size ) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } else if (info->err == DA_RESULT_USER_CANCELED) { + state = DP_STATE_CANCELED; + errorcode = DP_ERROR_NONE; + TRACE_INFO("[CANCELED][%d]", request_id); + } else { + state = DP_STATE_FAILED; + errorcode = __change_error(info->err); + TRACE_INFO("[FAILED][%d][%s]", request_id, + dp_print_errorcode(errorcode)); + } + + if (dp_db_set_column + (request_id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &state) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + if (errorcode != DP_ERROR_NONE) { + if (dp_db_set_column(request_id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT, + &errorcode) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + } + + // need MUTEX LOCK + CLIENT_MUTEX_LOCK(&(request->mutex)); + + request->state = state; + request->error = errorcode; + + // stay on memory till called destroy by client or timeout + if (request->group != NULL && request->group->event_socket >= 0) { + /* update the received file size. + * The last received file size cannot update + * because of reducing update algorithm*/ + if (request->received_size > 0) + dp_ipc_send_event(request->group->event_socket, + request->id, DP_STATE_DOWNLOADING, request->error, + request->received_size); + if (request->state_cb) + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + request->group->queued_count--; + } + + // to prevent the crash . check packagename of request + if (request->auto_notification && request->packagename != NULL) + request->noti_priv_id = + dp_set_downloadedinfo_notification(request->noti_priv_id, + request->id, request->packagename, request->state); + + request->stop_time = (int)time(NULL); + + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + + dp_thread_queue_manager_wake_up(); +} + +static void __paused_cb(user_paused_info_t *info, void *user_data) +{ + TRACE_INFO(""); + dp_request *request = (dp_request *) user_data; + if (!request) { + TRACE_ERROR("[NULL-CHECK] request"); + return ; + } + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK][%d] agent_id : %d req_id %d", + request->id, request->agent_id, info->download_id); + return ; + } + + CLIENT_MUTEX_LOCK(&(request->mutex)); + int request_id = request->id; + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + + if (dp_db_update_date + (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + // need MUTEX LOCK + CLIENT_MUTEX_LOCK(&(request->mutex)); + + request->state = DP_STATE_PAUSED; + + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } + + if (request->group && + request->group->event_socket >= 0 && request->state_cb) + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + + if (request->group) + request->group->queued_count--; + + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + + dp_thread_queue_manager_wake_up(); +} + +int dp_init_agent() +{ + int da_ret = 0; + da_client_cb_t da_cb = { + __download_info_cb, + __progress_cb, + __finished_cb, + __paused_cb + }; + da_ret = da_init(&da_cb); + if (da_ret != DA_RESULT_OK) { + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +void dp_deinit_agent() +{ + da_deinit(); +} + +// 0 : success +// -1 : failed +dp_error_type dp_cancel_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (da_cancel_download(req_id) == DA_RESULT_OK) + return 0; + return -1; +} + +// 0 : success +// -1 : failed +dp_error_type dp_pause_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (da_suspend_download(req_id) == DA_RESULT_OK) + return 0; + return -1; +} + + +// 0 : success +// -1 : failed +// -2 : pended +dp_error_type dp_start_agent_download(dp_request *request) +{ + int da_ret = -1; + int req_dl_id = -1; + dp_error_type errorcode = DP_ERROR_NONE; + extension_data_t ext_data = {0,}; + + TRACE_INFO(""); + if (!request) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + return DP_ERROR_INVALID_PARAMETER; + } + + char *url = dp_request_get_url(request->id, request, &errorcode); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] URL is NULL", request->id); + return DP_ERROR_INVALID_URL; + } + char *destination = + dp_request_get_destination(request->id, request, &errorcode); + if (destination != NULL) + ext_data.install_path = destination; + + char *filename = + dp_request_get_filename(request->id, request, &errorcode); + if (filename != NULL) + ext_data.file_name = filename; + + // call start_download() of download-agent + + char *tmp_saved_path = + dp_request_get_tmpsavedpath(request->id, request, &errorcode); + if (tmp_saved_path) { + char *etag = dp_request_get_etag(request->id, request, &errorcode); + if (etag) { + TRACE_INFO("[RESUME][%d]", request->id); + ext_data.etag = etag; + ext_data.temp_file_path = tmp_saved_path; + } else { + /* FIXME later : It is better to handle the unlink function in download agaent module + * or in upload the request data to memory after the download provider process is restarted */ + TRACE_INFO("[RESTART][%d] try to remove tmp file [%s]", + request->id, tmp_saved_path); + if (dp_is_file_exist(tmp_saved_path) == 0) + if (unlink(tmp_saved_path) != 0) + TRACE_STRERROR + ("[ERROR][%d] remove file", request->id); + } + } + + // get headers list from httpheaders table(DB) + int headers_count = dp_db_get_cond_rows_count + (request->id, DP_DB_TABLE_HTTP_HEADERS, NULL, 0, NULL); + if (headers_count > 0) { + ext_data.request_header = calloc(headers_count, sizeof(char*)); + if (ext_data.request_header != NULL) { + ext_data.request_header_count = dp_db_get_http_headers_list + (request->id, (char**)ext_data.request_header); + } + } + + ext_data.user_data = (void *)request; + + // call start API of agent lib + da_ret = + da_start_download_with_extension(url, &ext_data, &req_dl_id); + if (ext_data.request_header_count > 0) { + int len = 0; + int i = 0; + len = ext_data.request_header_count; + for (i = 0; i < len; i++) { + if (ext_data.request_header[i]) + free((void *)(ext_data.request_header[i])); + } + free(ext_data.request_header); + } + free(url); + free(destination); + free(filename); + free(tmp_saved_path); + + // if start_download() return error cause of maximun download limitation, + // set state to DOWNLOAD_STATE_PENDED. + if (da_ret == DA_ERR_ALREADY_MAX_DOWNLOAD) { + TRACE_INFO("[PENDING][%d] DA_ERR_ALREADY_MAX_DOWNLOAD [%d]", + request->id, da_ret); + return DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (da_ret != DA_RESULT_OK) { + TRACE_ERROR("[ERROR][%d] DP_ERROR_CONNECTION_FAILED [%d]", + request->id, da_ret); + return __change_error(da_ret); + } + TRACE_INFO("[SUCCESS][%d] agent_id [%d]", request->id, req_dl_id); + request->agent_id = req_dl_id; + return DP_ERROR_NONE; +} + +dp_error_type dp_resume_agent_download(int req_id) +{ + int da_ret = -1; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return DP_ERROR_INVALID_PARAMETER; + } + da_ret = da_resume_download(req_id); + if (da_ret == DA_RESULT_OK) + return DP_ERROR_NONE; + else if (da_ret == DA_ERR_INVALID_STATE) + return DP_ERROR_INVALID_STATE; + return __change_error(da_ret); +} + +// 1 : alive +// 0 : not alive +int dp_is_alive_download(int req_id) +{ + int da_ret = 0; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return 0; + } + da_ret = da_is_valid_download_id(req_id); + return da_ret; +} + diff --git a/src/download-provider-db.c b/src/download-provider-db.c new file mode 100755 index 0000000..0c9d5b6 --- /dev/null +++ b/src/download-provider-db.c @@ -0,0 +1,1319 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include "download-provider-config.h" +#include "download-provider-db.h" +#include "download-provider-slots.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +//BASIC +#define DP_DB_BASIC_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ?" +#define DP_DB_BASIC_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ?" +#define DP_DB_BASIC_INSERT_QUERY_FORMAT "INSERT INTO %s (id, %s) VALUES (?, ?)" +#define DP_DB_BASIC_NOW_DATE_QUERY_FORMAT "UPDATE %s SET %s = DATETIME('now') WHERE id = ?" + +// COND +#define DP_DB_COND_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ? AND %s = ?" +#define DP_DB_COND_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ? AND %s = ?" + +typedef enum { + DP_DB_QUERY_TYPE_GET = 10, + DP_DB_QUERY_TYPE_SET = 20, + DP_DB_QUERY_TYPE_INSERT = 30, + DP_DB_QUERY_TYPE_NOW_DATE = 40 +} db_query_type; + +sqlite3 *g_dp_db_handle = 0; + +// called when terminating process +void dp_db_close() +{ + if (g_dp_db_handle) + db_util_close(g_dp_db_handle); + g_dp_db_handle = 0; +} + +// called when launching process or in every API +int dp_db_open() +{ + if (g_dp_db_handle == 0) { + if (db_util_open(DATABASE_FILE, &g_dp_db_handle, + DB_UTIL_REGISTER_HOOK_METHOD) != SQLITE_OK) { + TRACE_ERROR("failed db_util_open [%s][%s]", DATABASE_FILE, + sqlite3_errmsg(g_dp_db_handle)); + dp_db_close(); + return -1; + } + sqlite3_exec(g_dp_db_handle, "PRAGMA journal_mode=PERSIST;", 0, 0, 0); + sqlite3_exec(g_dp_db_handle, "PRAGMA foreign_keys=ON;", 0, 0, 0); + } + return g_dp_db_handle ? 0 : -1; +} + +static int __dp_sql_open() +{ + return dp_db_open(); +} + +static void __dp_finalize(sqlite3_stmt *stmt) +{ + if (sqlite3_finalize(stmt) != SQLITE_OK) + TRACE_ERROR("failed sqlite3_finalize [%s]", + sqlite3_errmsg(g_dp_db_handle)); +} + +int dp_db_get_count_by_limit_time() +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT count(id) FROM logging \ + WHERE createtime < DATETIME('now','-48 hours')", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state FROM logging WHERE \ + createtime < DATETIME('now','-48 hours') \ + ORDER BY createtime ASC LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW && i < limit) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + + TRACE_INFO("ID : %d", requests[i].request->id); + TRACE_INFO("state : %d", requests[i].request->state); + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_crashed_list(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state, packagename FROM logging WHERE \ + (state = ? OR state = ? OR state = ?) \ + AND createtime > DATETIME('now','-48 hours') LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, DP_STATE_QUEUED) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 2, DP_STATE_DOWNLOADING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 3, DP_STATE_CONNECTING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 4, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + // packagename + buffer = (char *)(sqlite3_column_text(stmt, 2)); + requests[i].request->packagename = NULL; + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + requests[i].request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(requests[i].request->packagename, buffer, + buffer_length * sizeof(char)); + requests[i].request->packagename[buffer_length] = '\0'; + } + } + + TRACE_INFO("ID : %d", requests[i].request->id); + TRACE_INFO("state : %d", requests[i].request->state); + TRACE_INFO("packagename : %s", requests[i].request->packagename); + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_limit_rows(int limit) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (limit <= 0) { + TRACE_ERROR("[CHECK LIMIT] %d", limit); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // apply "ON DELETE CASCADE" + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "DELETE FROM logging WHERE id NOT IN \ + (SELECT id FROM logging ORDER BY createtime ASC LIMIT ?)", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +dp_request *dp_db_load_logging_request(int id) +{ + int errorcode = SQLITE_OK; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + dp_request *request = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT state, errorcode, startcount, packagename \ + FROM logging WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + request = dp_request_new(); + if (request == NULL) { + TRACE_ERROR("dp_request_new failed"); + __dp_finalize(stmt); + return NULL; + } + request->id = id; + request->state = sqlite3_column_int(stmt, 0); + request->error = sqlite3_column_int(stmt, 1); + request->startcount = sqlite3_column_int(stmt, 2); + + buffer = (char *)(sqlite3_column_text(stmt, 3)); + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(request->packagename, buffer, + buffer_length * sizeof(char)); + request->packagename[buffer_length] = '\0'; + } + } + } else { + TRACE_ERROR("sqlite3_step is failed. [%s] errorcode[%d]", + sqlite3_errmsg(g_dp_db_handle), errorcode); + __dp_finalize(stmt); + return NULL; + } + __dp_finalize(stmt); + return request; +} + +int dp_db_remove_all(int id) +{ + #if 0 + dp_db_remove(id, DP_DB_TABLE_REQUEST_INFO); + dp_db_remove(id, DP_DB_TABLE_DOWNLOAD_INFO); + dp_db_remove(id, DP_DB_TABLE_HTTP_HEADERS); + dp_db_remove(id, DP_DB_TABLE_NOTIFICATION); + #endif + // apply "ON DELETE CASCADE" + dp_db_remove(id, DP_DB_TABLE_LOG); + return -1; +} + +int dp_db_remove(int id, char *table) +{ + int errorcode = SQLITE_OK; + int query_len = 0; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? "; + // 2 means the length of one %s + query_len = strlen(query_format) - 2 + strlen(table); + if (query_len < strlen(query_format)) { + TRACE_ERROR("[CHECK QUERY FORMAT] [%s][%s]", + query_format, table); + return -1; + } + + query = (char *)calloc((query_len + 1), sizeof(char)); + if (query == NULL) { + TRACE_STRERROR("[CALLOC]"); + return -1; + } + query[query_len] = '\0'; + + ret = snprintf(query, query_len + 1, query_format, table); + + if (ret < 0) { + TRACE_STRERROR("[CHECK COMBINE] [%s]", query); + free(query); + return -1; + } + + // check error of sqlite3_prepare_v2 + if (sqlite3_prepare_v2 + (g_dp_db_handle, query, -1, &stmt, NULL) != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + free(query); + return -1; + } + free(query); + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_query(sqlite3 *handle, + db_query_type type, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_BASIC_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_BASIC_SET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_INSERT) { + query_format = DP_DB_BASIC_INSERT_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_NOW_DATE) { + query_format = DP_DB_BASIC_NOW_DATE_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table); + else + query = sqlite3_mprintf(query_format, table, column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + TRACE_INFO("[QUERY] %s", query); + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; +} + +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_INSERT, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // VALUES ( id ) + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + int check_id = dp_db_get_int_column(id, table, DP_DB_COL_ID); + if (check_id != id) // INSERT + return dp_db_insert_column(id, table, column, datatype, value); + // UPDATE + return dp_db_set_column(id, table, column, datatype, value); +} + +// success : 0 +// error : -1 +char *dp_db_get_text_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + __dp_finalize(stmt); + return NULL; +} + +int dp_db_get_int_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int columnvalue = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +long long dp_db_get_int64_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + long long columnvalue = sqlite3_column_int64(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_update_date(int id, char *table, char *column) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_NOW_DATE, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("Failed : [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_cond_query(sqlite3 *handle, + db_query_type type, char *table, + char *column, char *cond_column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_COND_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_COND_SET_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table, cond_column); + else + query = sqlite3_mprintf(query_format, table, column, cond_column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + TRACE_INFO("[QUERY] %s", query); + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; + +} + +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, void *condvalue) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 3, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 3, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + int errorcode = SQLITE_OK; + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return NULL; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + __dp_finalize(stmt); + return NULL; +} + +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? AND %s = ?"; + + query = sqlite3_mprintf(query_format, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return -1; + } + + TRACE_INFO("[QUERY] %s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (condcolumn) + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ? AND %s = ?", + table, condcolumn); + else + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ?", table); + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_INFO("[QUERY] %s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condcolumn) { + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_http_headers_list(int id, char **headers) +{ + int errorcode = SQLITE_OK; + int i = 0; + int headers_index = 0; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT header_field, header_data FROM httpheaders WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + int buffer_length = 0; + char *header_field = (char *)(sqlite3_column_text(stmt, 0)); + char *header_data = (char *)(sqlite3_column_text(stmt, 1)); + i++; + // REF : http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + buffer_length = strlen(header_field) + strlen(header_data) + 1; + char *headers_buffer = calloc(buffer_length + 1, sizeof(char)); + if (headers_buffer == NULL) { + TRACE_ERROR("[CALLOC] headers_buffer"); + continue; + } + int len = snprintf(headers_buffer, buffer_length + 1, + "%s:%s", header_field, header_data); + if (len <= 0) { + if (headers_buffer) + free(headers_buffer); + continue; + } else { + headers_buffer[len] = '\0'; + } + headers[headers_index++] = headers_buffer; + } + + __dp_finalize(stmt); + return headers_index; +} diff --git a/src/download-provider-main.c b/src/download-provider-main.c new file mode 100755 index 0000000..79968ba --- /dev/null +++ b/src/download-provider-main.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef DP_SUPPORT_DBUS_ACTIVATION +#include +#endif + +#include "download-provider-config.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-slots.h" +#include "download-provider-db.h" +#include "download-provider-network.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-da-interface.h" + +// declare functions +int dp_lock_pid(char *path); +void *dp_thread_requests_manager(void *arg); + +// declare global variables +// need for libsoup, decided the life-time by mainloop. +GMainLoop *g_main_loop_handle = 0; + +#ifdef DP_SUPPORT_DBUS_ACTIVATION +static int __register_dbus_service(void) +{ + DBusError dbus_error; + DBusConnection *dp_dbus_connection = NULL; + + dbus_error_init(&dbus_error); + + dp_dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error); + if (dp_dbus_connection == NULL) { + TRACE_ERROR("[DBUS] dbus_bus_get: %s", dbus_error.message); + dbus_error_free(&dbus_error); + return -1; + } + + if (dbus_bus_request_name + (dp_dbus_connection, DP_DBUS_SERVICE_DBUS, 0, &dbus_error) < 0 || + dbus_error_is_set(&dbus_error)) { + TRACE_ERROR("[DBUS] request_name %s", dbus_error.message); + dbus_error_free(&dbus_error); + dbus_connection_unref(dp_dbus_connection); + dp_dbus_connection = NULL; + return -1; + } + dbus_connection_unref(dp_dbus_connection); + dp_dbus_connection = NULL; + return 0; +} + +#endif + +void dp_terminate(int signo) +{ + TRACE_INFO("Received SIGTERM"); + if (g_main_loop_is_running(g_main_loop_handle)) + g_main_loop_quit(g_main_loop_handle); +} + +static gboolean __dp_idle_start_service(void *data) +{ + TRACE_INFO("Launch threads ....."); + + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("failed to init pthread attr"); + dp_terminate(SIGTERM); + return FALSE; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR("failed to set detach option"); + dp_terminate(SIGTERM); + return FALSE; + } + + // create thread for managing QUEUEs + if (pthread_create + (&thread_pid, &thread_attr, dp_thread_queue_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + + // start service, accept url-download ( client package ) + if (pthread_create + (&thread_pid, &thread_attr, dp_thread_requests_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + return FALSE; +} + +int main(int argc, char **argv) +{ + dp_privates *privates = NULL; + int lock_fd = -1; + + if (chdir("/") < 0) { + TRACE_STRERROR("failed to call setsid or chdir"); + exit(EXIT_FAILURE); + } + +#if 0 + // close all console I/O + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif + + if (signal(SIGTERM, dp_terminate) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + // write IPC_FD_PATH. and lock + if ((lock_fd = dp_lock_pid(DP_LOCK_PID)) < 0) { + TRACE_ERROR + ("It need to check download-provider is already alive"); + TRACE_ERROR("Or fail to create pid file in (%s)", + DP_LOCK_PID); + exit(EXIT_FAILURE); + } + // if exit socket file, delete it + if (access(DP_IPC, F_OK) == 0) { + unlink(DP_IPC); + } + + g_type_init(); + + privates = (dp_privates *) calloc(1, sizeof(dp_privates)); + if (!privates) { + TRACE_ERROR("[CRITICAL] failed to alloc for private info"); + goto DOWNLOAD_EXIT; + } + privates->groups = dp_client_group_slots_new(DP_MAX_GROUP); + if (privates->groups == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for groups"); + goto DOWNLOAD_EXIT; + } + privates->requests = dp_request_slots_new(DP_MAX_REQUEST); + if (privates->requests == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for requests"); + goto DOWNLOAD_EXIT; + } + + // ready socket ( listen ) + privates->listen_fd = dp_accept_socket_new(); + if (privates->listen_fd < 0) { + TRACE_ERROR("[CRITICAL] failed to bind SOCKET"); + goto DOWNLOAD_EXIT; + } + +#ifdef DP_SUPPORT_DBUS_ACTIVATION + TRACE_INFO("SUPPORT DBUS-ACTIVATION"); + if (__register_dbus_service() < 0) { + TRACE_ERROR("[LAUNCH ERROR] __register_dbus_service"); + goto DOWNLOAD_EXIT; + } +#else + TRACE_INFO("Not SUPPORT DBUS-ACTIVATION"); +#endif + + dp_db_open(); + + // convert to request type, insert all to privates->requests + // timeout of request thread will start these jobs by queue thread + // load all list from (queue table) + if (dp_db_crashed_list(privates->requests, DP_MAX_REQUEST) > 0) { + int i = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (!privates->requests[i].request) + continue; + dp_request *request = privates->requests[i].request; + TRACE_INFO + ("ID [%d] state[%d]", request->id, request->state); + + // load to memory, Can be started automatically. + if (request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_CONNECTING) { + request->state = DP_STATE_QUEUED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + if (request->state == DP_STATE_QUEUED) { + int auto_download = dp_db_get_int_column(request->id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + if (auto_download == 1) { + // auto retry... defaultly, show notification + request->auto_notification = 1; + request->start_time = (int)time(NULL); + continue; + } + // do not retry this request + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_SYSTEM_DOWN; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT, + &request->error) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + // if wanna restart, call continue before this line. + // default. update state/error. move to history. unload memory + // remove from memory + dp_request_free(request); + privates->requests[i].request = NULL; + } + } // query crashed_list + + if (argc != 2 || memcmp(argv[1], "service", 7) != 0) { + // in first launch in booting time, not request. terminate by self + if (dp_get_request_count(privates->requests) <= 0) { + TRACE_INFO("First Boot, No Request"); + goto DOWNLOAD_EXIT; + } + } + + dp_clear_downloadinginfo_notification(); + + if (dp_init_agent() != DP_ERROR_NONE) { + TRACE_ERROR("[CRITICAL] failed to init agent"); + goto DOWNLOAD_EXIT; + } + + privates->connection = 0; + privates->network_status = DP_NETWORK_TYPE_OFF; + if (dp_network_connection_init(privates) < 0) { + TRACE_INFO("use instant network check"); + privates->connection = 0; + } + + // libsoup need mainloop. + g_main_loop_handle = g_main_loop_new(NULL, 0); + + g_idle_add(__dp_idle_start_service, privates); + + g_main_loop_run(g_main_loop_handle); + +DOWNLOAD_EXIT : + + TRACE_INFO("Download-Provider will be terminated."); + + dp_deinit_agent(); + + if (privates != NULL) { + + if (privates->connection) + dp_network_connection_destroy(privates->connection); + + if (privates->listen_fd >= 0) { + dp_socket_free(privates->listen_fd); + privates->listen_fd = -1; + } + dp_request_slots_free(privates->requests, DP_MAX_REQUEST); + privates->requests = NULL; + dp_client_group_slots_free(privates->groups, DP_MAX_GROUP); + privates->groups = NULL; + free(privates); + privates = NULL; + } + dp_db_close(); + + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + + // if exit socket file, delete it + if (access(DP_IPC, F_OK) == 0) { + unlink(DP_IPC); + } + // delete pid file + if (access(DP_LOCK_PID, F_OK) == 0) { + close(lock_fd); + unlink(DP_LOCK_PID); + } + exit(EXIT_SUCCESS); +} diff --git a/src/download-provider-network.c b/src/download-provider-network.c new file mode 100755 index 0000000..699e8de --- /dev/null +++ b/src/download-provider-network.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-pthread.h" +#include "download-provider-network.h" + +extern pthread_mutex_t g_dp_queue_mutex; +extern pthread_cond_t g_dp_queue_cond; + +#if 0 +typedef enum +{ + CONNECTION_TYPE_DISCONNECTED = 0, /**< Disconnected */ + CONNECTION_TYPE_WIFI = 1, /**< Wi-Fi type */ + CONNECTION_TYPE_CELLULAR = 2, /**< Cellular type */ + CONNECTION_TYPE_ETHERNET = 3, /**< Ethernet type */ + CONNECTION_TYPE_BT = 4, /**< Bluetooth type */ +} connection_type_e; +typedef enum +{ + CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE = 0, /**< Out of service */ + CONNECTION_CELLULAR_STATE_FLIGHT_MODE = 1, /**< Flight mode */ + CONNECTION_CELLULAR_STATE_ROAMING_OFF = 2, /**< Roaming is turned off */ + CONNECTION_CELLULAR_STATE_CALL_ONLY_AVAILABLE = 3, /**< Call is only available */ + CONNECTION_CELLULAR_STATE_AVAILABLE = 4, /**< Available but not connected yet */ + CONNECTION_CELLULAR_STATE_CONNECTED = 5, /**< Connected */ +} connection_cellular_state_e +typedef enum +{ + CONNECTION_WIFI_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_WIFI_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_WIFI_STATE_CONNECTED = 2, /**< Connected state */ +} connection_wifi_state_e; +typedef enum +{ + CONNECTION_ETHERNET_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_ETHERNET_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_ETHERNET_STATE_CONNECTED = 2, /**< Connected state */ +} connection_ethernet_state_e; +typedef enum +{ + CONNECTION_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + CONNECTION_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + CONNECTION_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory error */ + CONNECTION_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid Operation */ + CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED = TIZEN_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED, /**< Address family not supported */ + CONNECTION_ERROR_OPERATION_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0401, /**< Operation failed */ + CONNECTION_ERROR_ITERATOR_END = TIZEN_ERROR_NETWORK_CLASS|0x0402, /**< End of iteration */ + CONNECTION_ERROR_NO_CONNECTION = TIZEN_ERROR_NETWORK_CLASS|0x0403, /**< There is no connection */ + CONNECTION_ERROR_NOW_IN_PROGRESS = TIZEN_ERROR_NOW_IN_PROGRESS, /** Now in progress */ + CONNECTION_ERROR_ALREADY_EXISTS = TIZEN_ERROR_NETWORK_CLASS|0x0404, /**< Already exists */ + CONNECTION_ERROR_OPERATION_ABORTED = TIZEN_ERROR_NETWORK_CLASS|0x0405, /**< Operation is aborted */ + CONNECTION_ERROR_DHCP_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0406, /**< DHCP failed */ + CONNECTION_ERROR_INVALID_KEY = TIZEN_ERROR_NETWORK_CLASS|0x0407, /**< Invalid key */ + CONNECTION_ERROR_NO_REPLY = TIZEN_ERROR_NETWORK_CLASS|0x0408, /**< No reply */ +} connection_error_e; + + +static void __print_connection_errorcode_to_string(connection_error_e errorcode) +{ + switch(errorcode) + { + case CONNECTION_ERROR_INVALID_PARAMETER : + TRACE_INFO("CONNECTION_ERROR_INVALID_PARAMETER"); + break; + case CONNECTION_ERROR_OUT_OF_MEMORY : + TRACE_INFO("CONNECTION_ERROR_OUT_OF_MEMORY"); + break; + case CONNECTION_ERROR_INVALID_OPERATION : + TRACE_INFO("CONNECTION_ERROR_INVALID_OPERATION"); + break; + case CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED : + TRACE_INFO("CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED"); + break; + case CONNECTION_ERROR_OPERATION_FAILED : + TRACE_INFO("CONNECTION_ERROR_OPERATION_FAILED"); + break; + case CONNECTION_ERROR_ITERATOR_END : + TRACE_INFO("CONNECTION_ERROR_ITERATOR_END"); + break; + case CONNECTION_ERROR_NO_CONNECTION : + TRACE_INFO("CONNECTION_ERROR_NO_CONNECTION"); + break; + case CONNECTION_ERROR_NOW_IN_PROGRESS : + TRACE_INFO("CONNECTION_ERROR_NOW_IN_PROGRESS"); + break; + case CONNECTION_ERROR_ALREADY_EXISTS : + TRACE_INFO("CONNECTION_ERROR_ALREADY_EXISTS"); + break; + case CONNECTION_ERROR_OPERATION_ABORTED : + TRACE_INFO("CONNECTION_ERROR_OPERATION_ABORTED"); + break; + case CONNECTION_ERROR_DHCP_FAILED : + TRACE_INFO("CONNECTION_ERROR_DHCP_FAILED"); + break; + case CONNECTION_ERROR_INVALID_KEY : + TRACE_INFO("CONNECTION_ERROR_INVALID_KEY"); + break; + case CONNECTION_ERROR_NO_REPLY : + TRACE_INFO("CONNECTION_ERROR_NO_REPLY"); + break; + default : + TRACE_INFO("CONNECTION_ERROR_NONE"); + break; + } +} +#endif + + + + + + + +////////////////////////////////////////////////////////////////////////// +/// @brief check the status in more detail by connection type +/// @return dp_network_type +static dp_network_type __dp_get_network_connection_status(connection_h connection, connection_type_e type) +{ + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if (type == CONNECTION_TYPE_WIFI) { + connection_wifi_state_e wifi_state; + wifi_state = CONNECTION_WIFI_STATE_DEACTIVATED; + if (connection_get_wifi_state + (connection, &wifi_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_wifi_state"); + if (wifi_state == CONNECTION_WIFI_STATE_CONNECTED) { + TRACE_INFO("[CONNECTION_WIFI] CONNECTED"); + network_type = DP_NETWORK_TYPE_WIFI; + } else { + TRACE_INFO("[CONNECTION_WIFI] [%d]", wifi_state); + } + } else if (type == CONNECTION_TYPE_CELLULAR) { + connection_cellular_state_e cellular_state; + cellular_state = CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE; + if (connection_get_cellular_state + (connection, &cellular_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_cellular_state"); + if (cellular_state == CONNECTION_CELLULAR_STATE_CONNECTED) { + TRACE_INFO("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + network_type = DP_NETWORK_TYPE_DATA_NETWORK; + } else { + TRACE_INFO("[CONNECTION_CELLULAR] [%d]", cellular_state); + } + } else if (type == CONNECTION_TYPE_ETHERNET) { + connection_ethernet_state_e ethernet_state; + ethernet_state = CONNECTION_ETHERNET_STATE_DISCONNECTED; + if (connection_get_ethernet_state + (connection, ðernet_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_ethernet_state"); + if (ethernet_state == CONNECTION_ETHERNET_STATE_CONNECTED) { + TRACE_INFO("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + network_type = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_INFO("[CONNECTION_ETHERNET] [%d]", ethernet_state); + } + } else { + TRACE_INFO("[DISCONNECTED]"); + network_type = DP_NETWORK_TYPE_OFF; + } + return network_type; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief [callback] called whenever changed network status +/// @todo care requests by network status +static void __dp_network_connection_type_changed_cb(connection_type_e type, void *data) +{ + TRACE_INFO("type[%d]", type); + dp_privates *privates = (dp_privates*)data; + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + #if 1 // this callback guarantee that already connectdd + if (type == CONNECTION_TYPE_WIFI) { + TRACE_INFO("[CONNECTION_WIFI] CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_WIFI; + } else if (type == CONNECTION_TYPE_CELLULAR) { + TRACE_INFO("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_DATA_NETWORK; + } else if (type == CONNECTION_TYPE_ETHERNET) { + TRACE_INFO("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_INFO("[DISCONNECTED]"); + privates->network_status = DP_NETWORK_TYPE_OFF; + } + if (privates->network_status != DP_NETWORK_TYPE_OFF) + pthread_cond_signal(&g_dp_queue_cond); + #else + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + #endif + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create connection handle & regist callback +/// @return 0 : success -1 : failed +int dp_network_connection_init(dp_privates *privates) +{ + int retcode = 0; + + TRACE_INFO(""); + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return -1; + } + if ((retcode = connection_create(&privates->connection)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return -1; + } + if ((retcode = connection_set_type_changed_cb + (privates->connection, __dp_network_connection_type_changed_cb, + privates)) != CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_set_type_changed_cb [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(privates->connection, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief destroy connection handle +void dp_network_connection_destroy(connection_h connection) +{ + TRACE_INFO(""); + connection_unset_type_changed_cb (connection); + connection_destroy(connection); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status using connection API +/// @todo the standard of enabled networking can be changed later +/// @return Network type +dp_network_type dp_get_network_connection_instant_status() +{ + int retcode = 0; + connection_h network_handle = NULL; + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if ((retcode = connection_create(&network_handle)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return DP_NETWORK_TYPE_OFF; + } + + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(network_handle, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(network_handle); + return DP_NETWORK_TYPE_OFF; + } + network_type = + __dp_get_network_connection_status(network_handle, type); + + if (connection_destroy(network_handle) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_destroy"); + + return network_type; +} diff --git a/src/download-provider-notification.c b/src/download-provider-notification.c new file mode 100755 index 0000000..f04b010 --- /dev/null +++ b/src/download-provider-notification.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "bundle.h" +#include "notification.h" +#include "appsvc.h" + +#include "download-provider-notification.h" +#include "download-provider-request.h" +#include "download-provider-db.h" +#include "download-provider-log.h" + +#include +#define S_(s) dgettext("sys_string", s) + +#define DP_NOTIFICATION_ICON_PATH IMAGE_DIR"/Q02_Notification_Download_failed.png" +/* This should be same value of SERVICE_OPERATION_DOWNLOAD_NOTIFICATION from download.h */ +#define DP_DOWNLOAD_NOTI_OPERATION "http://tizen.org/appcontrol/operation/download_notification" + +static void __print_app_error_message(int ret) +{ + switch (ret) { + case APPSVC_RET_OK: + TRACE_INFO("APPSVC_RET_OK"); + break; + case APPSVC_RET_ELAUNCH: + TRACE_ERROR("APPSVC_RET_ELAUNCH"); + break; + case APPSVC_RET_ENOMATCH: + TRACE_ERROR("APPSVC_RET_ENOMATCH"); + break; + case APPSVC_RET_EINVAL: + TRACE_ERROR("APPSVC_RET_EINVAL"); + break; + case APPSVC_RET_ERROR: + TRACE_ERROR("APPSVC_RET_ERROR"); + break; + } +} + +static void __print_notification_error_message(int ret) +{ + switch (ret) { + case NOTIFICATION_ERROR_INVALID_DATA: + TRACE_ERROR("NOTIFICATION_ERROR_INVALID_DATA"); + break; + case NOTIFICATION_ERROR_NO_MEMORY: + TRACE_ERROR("NOTIFICATION_ERROR_NO_MEMORY"); + break; + case NOTIFICATION_ERROR_FROM_DB: + TRACE_ERROR("NOTIFICATION_ERROR_FROM_DB"); + break; + case NOTIFICATION_ERROR_ALREADY_EXIST_ID: + TRACE_ERROR("NOTIFICATION_ERROR_ALREADY_EXIST_ID"); + break; + case NOTIFICATION_ERROR_FROM_DBUS: + TRACE_ERROR("NOTIFICATION_ERROR_FROM_DBUS"); + break; + case NOTIFICATION_ERROR_NOT_EXIST_ID: + TRACE_ERROR("NOTIFICATION_ERROR_NOT_EXIST_ID"); + break; + default: + TRACE_ERROR("Unknown error"); + break; + } +} + +static char *__get_string_status(dp_state_type state) +{ + char *message = NULL; + switch (state) { + case DP_STATE_COMPLETED: + //message = S_("IDS_COM_POP_SUCCESS"); + message = "Completed"; + break; + case DP_STATE_CANCELED: + //message = S_("IDS_COM_POP_CANCELLED"); + message = "Canceled"; + break; + case DP_STATE_FAILED: + //message = S_("IDS_COM_POP_FAILED"); + message = "Failed"; + break; + default: + break; + } + return message; +} + +int dp_set_downloadinginfo_notification(int id, char *packagename) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + +#ifdef NOTI_NEW_VERSION_API + noti_handle = notification_create(NOTIFICATION_TYPE_ONGOING); +#else + noti_handle = notification_new(NOTIFICATION_TYPE_ONGOING, + NOTIFICATION_GROUP_ID_NONE, NOTIFICATION_PRIV_ID_NONE); +#endif + + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_TITLE, content_name, NULL, + NOTIFICATION_VARIABLE_TYPE_NONE); + if (content_name) + free(content_name); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%d]", err); + notification_free(noti_handle); + return -1; + } + + err = notification_set_image(noti_handle, + NOTIFICATION_IMAGE_TYPE_ICON, DP_NOTIFICATION_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%d]", err); + notification_free(noti_handle); + return -1; + } + + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + + if (packagename && + appsvc_set_pkgname(b, packagename) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + if (appsvc_set_operation(b, DP_DOWNLOAD_NOTI_OPERATION) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set noti operation"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + char *extra_key = + dp_db_get_text_column(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY); + char *extra_value = + dp_db_get_text_column(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE); + + if (extra_key && extra_value) { + if (appsvc_add_data(b, extra_key, extra_value) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set add data"); + free(extra_key); + free(extra_value); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + if (extra_key) + free(extra_key); + if (extra_value) + free(extra_value); + + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%d]", err); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free(b); + + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%d]", err); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%d]", err); + notification_free(noti_handle); + return -1; + } + + TRACE_INFO("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + + if (priv_id >= 0) { + err = notification_delete_by_priv_id(NULL, NOTIFICATION_TYPE_ONGOING, + priv_id); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] delete notification handle, err", err); + } + } + +#ifdef NOTI_NEW_VERSION_API + noti_handle = notification_create(NOTIFICATION_TYPE_NOTI); +#else + noti_handle = notification_new(NOTIFICATION_TYPE_NOTI, + NOTIFICATION_GROUP_ID_NONE, NOTIFICATION_PRIV_ID_NONE); +#endif + + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_TITLE, content_name, + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (content_name) + free(content_name); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%d]", err); + notification_free(noti_handle); + return -1; + } + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_CONTENT, + __get_string_status(state), NULL, + NOTIFICATION_VARIABLE_TYPE_NONE); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set text [%d]", err); + notification_free(noti_handle); + return -1; + } + time_t tt = time(NULL); + + err = notification_set_time(noti_handle, tt); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%d]", err); + notification_free(noti_handle); + return -1; + } + + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + + if (state == DP_STATE_COMPLETED) { + if (appsvc_set_operation(b, APPSVC_OPERATION_VIEW) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set operation"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + char *savedpath = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (savedpath && appsvc_set_uri(b, savedpath) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set uri"); + free(savedpath); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + if (savedpath) + free(savedpath); + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + } else if (state == DP_STATE_CANCELED || state == DP_STATE_FAILED) { + if (appsvc_set_operation(b, DP_DOWNLOAD_NOTI_OPERATION) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set noti operation [%d]", err); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + char *extra_key = + dp_db_get_text_column(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY); + char *extra_value = + dp_db_get_text_column(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE); + if (extra_key && extra_value) { + if (appsvc_add_data(b, extra_key, extra_value) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set add data"); + free(extra_key); + free(extra_value); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + if (extra_key) + free(extra_key); + if (extra_value) + free(extra_value); + + if (packagename && + appsvc_set_pkgname(b, packagename) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + } else { + TRACE_ERROR("[CRITICAL] invalid state"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%d]", err); + notification_free(noti_handle); + bundle_free(b); + return -1; + } + + bundle_free(b); + + err = notification_set_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, + DP_NOTIFICATION_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%d]", err); + notification_free(noti_handle); + return -1; + } + + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%d]", err); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%d]", err); + notification_free(noti_handle); + return -1; + } + + TRACE_INFO("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size) +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + if (priv_id < 0) { + TRACE_ERROR("[FAIL] Invalid priv_id[%d]", priv_id); + return; + } + + if (file_size > 0) { + double progress; + progress = received_size / file_size; + err = notification_update_progress(NULL, priv_id, progress); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%d]", err); + } else { + err = notification_update_size(NULL, priv_id, received_size); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%d]", err); + } +} + +void dp_clear_downloadinginfo_notification() +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + err = notification_delete_all_by_type(NULL, NOTIFICATION_TYPE_ONGOING); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] clear noti [%d]", err); + } + return; +} diff --git a/src/download-provider-pid.c b/src/download-provider-pid.c new file mode 100755 index 0000000..bd1a223 --- /dev/null +++ b/src/download-provider-pid.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +////////////////////////////////////////////////////////////////////////// +/// @brief check whether daemon is alive +/// @warning lockfd should be managed without close() +/// @param the patch for locking the file +int dp_lock_pid(char *path) +{ + int lockfd = -1; + if ((lockfd = open(path, O_WRONLY | O_CREAT, (0666 & (~000)))) < 0) { + return -1; + } else if (lockf(lockfd, F_TLOCK, 0) < 0) { + close(lockfd); + return -1; + } + return lockfd; +} diff --git a/src/download-provider-request.c b/src/download-provider-request.c new file mode 100755 index 0000000..869a9e5 --- /dev/null +++ b/src/download-provider-request.c @@ -0,0 +1,720 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include "download-provider.h" +#include "download-provider-log.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-pthread.h" + + +///////// below functions are called by main thread of thread-request.c + + + +////////////////////////////////////////////////////////////////////////// +/// @brief create unique id as integer type +/// @return unique id combined local time and the special calculation +static int __get_download_request_id(void) +{ + int uniquetime = 0; + struct timeval tval; + static int last_uniquetime = 0; + + do { + uniquetime = (int)time(NULL); + gettimeofday(&tval, NULL); + if (tval.tv_usec == 0) + uniquetime = uniquetime + (tval.tv_usec + 1) % 0xfffff; + else + uniquetime = uniquetime + tval.tv_usec; + TRACE_INFO("ID : %d", uniquetime); + } while (last_uniquetime == uniquetime); + last_uniquetime = uniquetime; // store + return uniquetime; +} + +char *dp_print_state(dp_state_type state) +{ + switch(state) + { + case DP_STATE_NONE : + return "NONE"; + case DP_STATE_READY : + return "READY"; + case DP_STATE_QUEUED : + return "QUEUED"; + case DP_STATE_CONNECTING : + return "CONNECTING"; + case DP_STATE_DOWNLOADING : + return "DOWNLOADING"; + case DP_STATE_PAUSE_REQUESTED : + return "PAUSE_REQUESTED"; + case DP_STATE_PAUSED : + return "PAUSED"; + case DP_STATE_COMPLETED : + return "COMPLETED"; + case DP_STATE_CANCELED : + return "CANCELED"; + case DP_STATE_FAILED : + return "FAILED"; + default : + break; + } + return "UNKNOWN"; +} + +char *dp_print_errorcode(dp_error_type errorcode) +{ + switch(errorcode) + { + case DP_ERROR_NONE : + return "NONE"; + case DP_ERROR_INVALID_PARAMETER : + return "INVALID_PARAMETER"; + case DP_ERROR_OUT_OF_MEMORY : + return "OUT_OF_MEMORY"; + case DP_ERROR_IO_ERROR : + return "IO_ERROR"; + case DP_ERROR_NETWORK_UNREACHABLE : + return "NETWORK_UNREACHABLE"; + case DP_ERROR_CONNECTION_TIMED_OUT : + return "CONNECTION_TIMED_OUT"; + case DP_ERROR_NO_SPACE : + return "NO_SPACE"; + case DP_ERROR_FIELD_NOT_FOUND : + return "FIELD_NOT_FOUND"; + case DP_ERROR_INVALID_STATE : + return "INVALID_STATE"; + case DP_ERROR_CONNECTION_FAILED : + return "CONNECTION_FAILED"; + case DP_ERROR_INVALID_URL : + return "INVALID_URL"; + case DP_ERROR_INVALID_DESTINATION : + return "INVALID_DESTINATION"; + case DP_ERROR_QUEUE_FULL : + return "QUEUE_FULL"; + case DP_ERROR_ALREADY_COMPLETED : + return "ALREADY_COMPLETED"; + case DP_ERROR_FILE_ALREADY_EXISTS : + return "FILE_ALREADY_EXISTS"; + case DP_ERROR_TOO_MANY_DOWNLOADS : + return "TOO_MANY_DOWNLOADS"; + case DP_ERROR_NO_DATA : + return "NO_DATA"; + case DP_ERROR_UNHANDLED_HTTP_CODE : + return "UNHANDLED_HTTP_CODE"; + case DP_ERROR_CANNOT_RESUME : + return "CANNOT_RESUME"; + case DP_ERROR_RESPONSE_TIMEOUT : + return "RESPONSE_TIMEOUT"; + case DP_ERROR_REQUEST_TIMEOUT : + return "REQUEST_TIMEOUT"; + case DP_ERROR_SYSTEM_DOWN : + return "SYSTEM_DOWN"; + case DP_ERROR_CLIENT_DOWN : + return "CLIENT_DOWN"; + case DP_ERROR_ID_NOT_FOUND : + return "ID_NOT_FOUND"; + default : + break; + } + return "UNKNOWN"; +} + +char *dp_strdup(char *src) +{ + char *dest = NULL; + size_t src_len = 0; + + if (src == NULL) { + TRACE_ERROR("[CHECK PARAM]"); + return NULL; + } + + src_len = strlen(src); + if (src_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", src_len); + return NULL; + } + + dest = (char *)calloc(src_len + 1, sizeof(char)); + if (dest == NULL) { + TRACE_STRERROR("[CHECK] allocation"); + return NULL; + } + memcpy(dest, src, src_len * sizeof(char)); + dest[src_len] = '\0'; + + return dest; +} + +// check param +// create new slot +// fill info to new slot +// make new id +// save info to QUEUE(DB) +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request **empty_slot) +{ + if (id != -1) { + TRACE_ERROR("[CHECK PROTOCOL] ID not -1"); + return DP_ERROR_INVALID_STATE; + } + if (!group || !empty_slot) { + TRACE_ERROR("[CHECK INTERNAL][%d]", id); + return DP_ERROR_IO_ERROR; + } + // New allocation Slot + dp_request *new_request = dp_request_new(); + if (!new_request) { + TRACE_STRERROR("[CHECK MEMORY][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + + new_request->id = __get_download_request_id(); + new_request->group = group; + if (group->pkgname && strlen(group->pkgname) > 1) + new_request->packagename = dp_strdup(group->pkgname); + new_request->credential = group->credential; + if (new_request->packagename == NULL) { + dp_request_free(new_request); + TRACE_ERROR("[ERROR][%d] OUT_OF_MEMORY [PACKAGENAME]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + new_request->state = DP_STATE_READY; + new_request->error = DP_ERROR_NONE; + if (dp_db_insert_column + (new_request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &new_request->state) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + dp_request_free(new_request); + return DP_ERROR_OUT_OF_MEMORY; + } + if (dp_db_set_column + (new_request->id, DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME, + DP_DB_COL_TYPE_TEXT, new_request->packagename) < 0) + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_update_date + (new_request->id, DP_DB_TABLE_LOG, DP_DB_COL_CREATE_TIME) < 0) + TRACE_ERROR("[CHECK SQL][%d]", id); + + new_request->create_time = (int)time(NULL); + + *empty_slot = new_request; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_url(int id, dp_request *request, char *url) +{ + int length = 0; + if (!url || (length = strlen(url)) <= 1) + return DP_ERROR_INVALID_URL; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL, + DP_DB_COL_TYPE_TEXT, url) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest) +{ + int length = 0; + if (!dest || (length = strlen(dest)) <= 1) + return DP_ERROR_INVALID_DESTINATION; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION, + DP_DB_COL_TYPE_TEXT, dest) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename) +{ + int length = 0; + if (!filename || (length = strlen(filename)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME, + DP_DB_COL_TYPE_TEXT, filename) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + + TRACE_INFO("ID [%d] Filename[%s]", id, filename); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_NOTIFICATION_ENABLE, DP_DB_COL_TYPE_INT, + &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + // update memory + if (request) + request->auto_notification = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_AUTO_DOWNLOAD, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_STATE_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + // update memory + if (request) + request->state_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_PROGRESS_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + // update memory + if (request) + request->progress_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type) +{ + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + return DP_ERROR_ID_NOT_FOUND; + } + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_NETWORK_TYPE, + DP_DB_COL_TYPE_INT, &type) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_IO_ERROR; + } + // update memory + if (request) + request->network_type = type; + return DP_ERROR_NONE; +} + +char *dp_request_get_url(int id, dp_request *request, dp_error_type *errorcode) +{ + char *url = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + url = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL); + if (url == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return url; +} + +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode) +{ + char *dest = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + dest = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION); + if (dest == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return dest; +} + +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode) +{ + char *filename = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + filename = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME); + if (filename == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return filename; +} + +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode) +{ + char *content = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + content = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + if (content == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return content; +} + +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode) +{ + char *etag = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + etag = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ETAG); + if (etag == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return etag; +} + +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *savedpath = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + savedpath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (savedpath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return savedpath; +} + +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *tmppath = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + tmppath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_TMP_SAVED_PATH); + if (tmppath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return tmppath; +} + +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode) +{ + char *mimetype = NULL; + + if (request == NULL) { + dp_state_type state = + dp_db_get_int_column(id, DP_DB_TABLE_LOG, DP_DB_COL_STATE); + + if (state <= DP_STATE_NONE) { + TRACE_ERROR("[ERROR][%d] state[%s]", id, dp_print_state(state)); + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + } + mimetype = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_MIMETYPE); + if (mimetype == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return mimetype; +} + +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode) +{ + dp_request *request = NULL; + + request = dp_db_load_logging_request(id); + if (request == NULL) { + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + *errorcode = DP_ERROR_INVALID_STATE; + dp_request_free(request); + return NULL; + } + return request; +} + diff --git a/src/download-provider-slots.c b/src/download-provider-slots.c new file mode 100755 index 0000000..208f993 --- /dev/null +++ b/src/download-provider-slots.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" + +dp_group_slots *dp_client_group_slots_new(int size) +{ + TRACE_INFO(""); + dp_group_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_group_slots *) calloc(size, + sizeof(dp_group_slots)); + return slots; +} + +dp_request_slots *dp_request_slots_new(int size) +{ + TRACE_INFO(""); + dp_request_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_request_slots *) calloc(size, + sizeof(dp_request_slots)); + return slots; +} + +void dp_request_init(dp_request *request) +{ + TRACE_INFO(""); + if (!request) + return ; + + request->id = -1; + request->agent_id = -1; + request->create_time = 0; + request->start_time = 0; + request->pause_time = 0; + request->stop_time = 0; + request->state = DP_STATE_NONE; + request->error = DP_ERROR_NONE; + request->state_cb = 0; + request->progress_cb = 0; + request->progress_lasttime = 0; + request->received_size = 0; + request->file_size = 0; + request->network_type = DP_NETWORK_TYPE_ALL; + request->startcount = 0; + request->auto_notification = 0; + request->noti_priv_id = -1; + request->packagename = NULL; + request->group = NULL; +} + +dp_request *dp_request_new() +{ + dp_request *request = NULL; + request = (dp_request *) calloc(1, + sizeof(dp_request)); + if (!request) + return NULL; + CLIENT_MUTEX_INIT(&(request->mutex), NULL); + dp_request_init(request); + return request; +} + +int dp_request_free(dp_request *request) +{ + TRACE_INFO(""); + + if (!request) + return -1; + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->packagename) + free(request->packagename); + dp_request_init(request); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + CLIENT_MUTEX_DESTROY(&(request->mutex)); + free(request); + return 0; +} + +int dp_client_group_free(dp_client_group *group) +{ + TRACE_INFO(""); + if (!group) + return -1; + + if (group->cmd_socket > 0) + dp_socket_free(group->cmd_socket); + group->cmd_socket = -1; + if (group->event_socket > 0) + dp_socket_free(group->event_socket); + group->event_socket = -1; + group->queued_count = 0; + if (group->pkgname) + free(group->pkgname); + free(group); + group = NULL; + return 0; +} + +int dp_client_group_slots_free(dp_group_slots *slots, int size) +{ + TRACE_INFO(""); + int i = 0; + if (slots) { + for (; i < size; i++) { + if (slots->group) + dp_client_group_free(slots->group); + slots->group = NULL; + } + free(slots); + } + slots = NULL; + return 0; +} + +int dp_request_slots_free(dp_request_slots *slots, int size) +{ + TRACE_INFO(""); + int i = 0; + if (slots) { + for (; i < size; i++) { + if (slots->request) + dp_request_free(slots->request); + slots->request = NULL; + } + free(slots); + } + slots = NULL; + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return count of requests in slot +int dp_get_request_count(dp_request_slots *slots) +{ + int i = 0; + int count = 0; + + if (!slots) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) + if (slots[i].request) + count++; + return count; +} diff --git a/src/download-provider-socket.c b/src/download-provider-socket.c new file mode 100755 index 0000000..0899ffe --- /dev/null +++ b/src/download-provider-socket.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" + +////////////////////////////////////////////////////////////////////////// +/// @brief write the error to socket +/// @return if success, return 0 +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + + if (fd >= 0 && write(fd, &errorcode, sizeof(dp_error_type)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief write the progressinfo to socket +/// @return if success, return 0 +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR][%d] CHECK FD[%d]", id, fd); + return -1; + } + + dp_event_info eventinfo; + eventinfo.id = id; + eventinfo.state = state; + eventinfo.err = errorcode; + eventinfo.received_size = received_size; + + // write + if (fd >= 0 && write(fd, &eventinfo, sizeof(dp_event_info)) <= 0) { + TRACE_STRERROR("[ERROR][%d] write FD[%d]", id, fd); + return -1; + } + return 0; +} + +// keep the order/ unsigned , str +char *dp_ipc_read_string(int fd) +{ + unsigned length = 0; + char *str = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return NULL; + } + + // read flexible URL from client. + if (read(fd, &length, sizeof(unsigned)) < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return NULL; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return NULL; + } + str = (char *)calloc((length + 1), sizeof(char)); + if (read(fd, str, length * sizeof(char)) < 0) { + TRACE_STRERROR("[ERROR] read FD[%d]", fd); + free(str); + str = NULL; + return NULL; + } + str[length] = '\0'; + return str; +} + +// keep the order/ unsigned , str +int dp_ipc_send_string(int fd, const char *str) +{ + unsigned length = 0; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (!str) { + TRACE_ERROR("[ERROR] CHECK STRING FD[%d]", fd); + return -1; + } + + length = strlen(str); + if (length < 1) { + TRACE_ERROR("[ERROR] CHECK LENGTH FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) <= 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return -1; + } + if (fd >= 0 && write(fd, str, length * sizeof(char)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (!value) { + TRACE_ERROR("[ERROR] CHECK VALUE FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, value, type_size) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + + if (read(fd, value, type_size) < 0) { + TRACE_STRERROR("[ERROR] read FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_accept_socket_new() +{ + int sockfd = -1; + struct sockaddr_un listenaddr; + + if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + TRACE_STRERROR("failed to create socket"); + return -1; + } + + bzero(&listenaddr, sizeof(listenaddr)); + listenaddr.sun_family = AF_UNIX; + strcpy(listenaddr.sun_path, DP_IPC); + + if (bind(sockfd, (struct sockaddr *)&listenaddr, sizeof listenaddr) != + 0) { + TRACE_STRERROR("[CRITICAL] bind"); + close(sockfd); + return -1; + } + + if (chmod(listenaddr.sun_path, 0777) < 0) { + TRACE_STRERROR("[CRITICAL] chmod"); + close(sockfd); + return -1; + } + + // need 3 socket per a group + if (listen(sockfd, DP_MAX_GROUP * 3) != 0) { + TRACE_STRERROR("[CRITICAL] listen"); + close(sockfd); + return -1; + } + return sockfd; +} + +int dp_socket_free(int sockfd) +{ + if (sockfd < 0) + return -1; + shutdown(sockfd, 0); + close(sockfd); + return 0; +} diff --git a/src/download-provider-thread-queue.c b/src/download-provider-thread-queue.c new file mode 100755 index 0000000..746d512 --- /dev/null +++ b/src/download-provider-thread-queue.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" + +void dp_terminate(int signo); + +pthread_mutex_t g_dp_queue_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t g_dp_queue_cond = PTHREAD_COND_INITIALIZER; + + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status is matched with the type setted by user +/// @return matched : 0 mispatch : -1 +static int __is_matched_network(dp_network_type now_state, dp_network_type setted_state) +{ + if (now_state == setted_state + || now_state == DP_NETWORK_TYPE_ETHERNET + || setted_state == DP_NETWORK_TYPE_ALL) + return 0; + #if 0 + if (setted_state == DP_NETWORK_TYPE_ALL + || setted_state == DP_NETWORK_TYPE_DATA_NETWORK + || now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET + || (setted_state == DP_NETWORK_TYPE_WIFI + && (now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET)) + ) + return 0; + #endif + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief the count of slot downloading currently +static unsigned __get_active_count(dp_request_slots *requests) +{ + unsigned count = 0; + unsigned i = 0; + + if (!requests) + return 0; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (requests[i].request) { + if (requests[i].request->state == DP_STATE_CONNECTING || + requests[i].request->state == DP_STATE_DOWNLOADING) + count++; + } + } + return count; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief index of slot which last time is oldest +static int __get_oldest_request_with_network(dp_request_slots *requests, dp_state_type state, dp_network_type now_state) +{ + int i = 0; + int oldest_time = (int)time(NULL); + oldest_time++; // most last time + int oldest_index = -1; + + if (!requests) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (requests[i].request) { + if (requests[i].request->state == state && + requests[i].request->start_time > 0 && + requests[i].request->start_time < oldest_time && + __is_matched_network + (now_state, requests[i].request->network_type) + == 0) { + oldest_time = requests[i].request->start_time; + oldest_index = i; + } + } + } + return oldest_index; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief THREAD function for calling da_start_download_with_extension. +/// @warning da_start_download_with_extension can take long time +/// @param the pointer of memory slot allocated for this request. +/// @todo simplify da_start_download_with_extension +static void *__request_download_start_agent(void *args) +{ + dp_error_type errcode = DP_ERROR_NONE; + + dp_request *request = (dp_request *) args; + if (!request) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + pthread_exit(NULL); + return 0; + } + + if (dp_is_alive_download(request->agent_id)) { + errcode = dp_resume_agent_download(request->agent_id); + } else { + // call agent start function + errcode = dp_start_agent_download(request); + } + + CLIENT_MUTEX_LOCK(&(request->mutex)); + // send to state callback. + if (errcode == DP_ERROR_NONE) { + // CONNECTING + request->state = DP_STATE_CONNECTING; + request->error = DP_ERROR_NONE; + request->startcount++; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STARTCOUNT, + DP_DB_COL_TYPE_INT, &request->startcount) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } else if (errcode == DP_ERROR_TOO_MANY_DOWNLOADS) { + // PENDED + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (errcode == DP_ERROR_CONNECTION_FAILED) { + // FAILED + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_CONNECTION_FAILED; + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + dp_thread_queue_manager_wake_up(); + } else { + request->state = DP_STATE_FAILED; + request->error = errcode; + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + dp_thread_queue_manager_wake_up(); + } + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + pthread_exit(NULL); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create thread. +/// @warning if failed to create thread, change to PENED. +/// @param the pointer of memory slot allocated for this request. +static int __request_download_start_thread(dp_request *request) +{ + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + TRACE_INFO(""); + if (!request) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + return -1; + } + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_attr_init", request->id); + return -1; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR + ("[ERROR][%d] pthread_attr_setdetachstate", request->id); + return -1; + } + + request->state = DP_STATE_CONNECTING; + if (pthread_create(&thread_pid, &thread_attr, + __request_download_start_agent, request) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_create", request->id); + pthread_attr_destroy(&thread_attr); + request->state = DP_STATE_QUEUED; + return -1; + } + pthread_attr_destroy(&thread_attr); + return 0; +} + + +void dp_thread_queue_manager_wake_up() +{ + TRACE_INFO(""); + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_signal(&g_dp_queue_cond); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + + +// Main role : start download, check status of queue. +// No timeout. Wake up by Signal be sent from other thread +void *dp_thread_queue_manager(void *arg) +{ + int i; + int active_count; + dp_client_group *group = NULL; + dp_request *request = NULL; + + TRACE_INFO("Start Queue Thread"); + + dp_privates *privates = (dp_privates*)arg; + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + pthread_cond_init(&g_dp_queue_cond, NULL); + while (privates != NULL && privates->listen_fd >= 0) { + + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_wait(&g_dp_queue_cond, &g_dp_queue_mutex); + + if (privates == NULL || privates->requests == NULL || + privates->listen_fd < 0) { + TRACE_INFO("Terminate Thread"); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + break; + } + + if (!privates->connection) + privates->network_status = + dp_get_network_connection_instant_status(); + + if (privates->network_status == DP_NETWORK_TYPE_OFF) { + TRACE_INFO("[CHECK NETWORK STATE]"); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + continue; + } + + active_count = __get_active_count(privates->requests); + + // Start Conditions + // 1. state is QUEUED + // 2. 1 QUEUED per 1 Group : need not to check max limitation.!! + // if no group, it will be started later. + // 3. most old last time : below conditions need max limitation. + // 4. match network connection type + + // search group having 1 queued_count + // guarantee 1 instant download per 1 group + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + for (i = 0; i < DP_MAX_REQUEST; i++) { + request = privates->requests[i].request; + if (request && request->state == DP_STATE_QUEUED) { + group = privates->requests[i].request->group; + if (group && group->queued_count == 1 && + __is_matched_network(privates->network_status, + request->network_type) == 0 && + __request_download_start_thread(request) == + 0) { + TRACE_INFO + ("[Guarantee Intant Download] Group [%s]", + group->pkgname); + active_count++; + } + } + } + } + + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + TRACE_INFO("[BUSY] Active[%d] Max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + continue; + } + + // can start download more. + // search oldest request + while(active_count < DP_MAX_DOWNLOAD_AT_ONCE) { + i = __get_oldest_request_with_network(privates->requests, + DP_STATE_QUEUED, privates->network_status); + if (i < 0) { + TRACE_INFO + ("No Request to can start now Active[%d] Max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + break; + } + TRACE_INFO + ("QUEUE Status now %d active %d/%d", i, active_count, + DP_MAX_DOWNLOAD_AT_ONCE); + request = privates->requests[i].request; + __request_download_start_thread(request); + active_count++; + } + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + } + pthread_cond_destroy(&g_dp_queue_cond); + pthread_exit(NULL); + return 0; +} diff --git a/src/download-provider-thread-request.c b/src/download-provider-thread-request.c new file mode 100755 index 0000000..a4ae548 --- /dev/null +++ b/src/download-provider-thread-request.c @@ -0,0 +1,1926 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-request.h" +#include "download-provider-da-interface.h" + +void dp_terminate(int signo); + +static char *__print_command(dp_command_type cmd) +{ + switch(cmd) + { + case DP_CMD_CREATE : + return "CREATE"; + case DP_CMD_START : + return "START"; + case DP_CMD_PAUSE : + return "PAUSE"; + case DP_CMD_CANCEL : + return "CANCEL"; + case DP_CMD_DESTROY : + return "DESTROY"; + case DP_CMD_FREE : + return "FREE"; + case DP_CMD_ECHO : + return "ECHO"; + case DP_CMD_SET_URL : + return "SET_URL"; + case DP_CMD_SET_DESTINATION : + return "SET_DESTINATION"; + case DP_CMD_SET_FILENAME : + return "SET_FILENAME"; + case DP_CMD_SET_NOTIFICATION : + return "SET_NOTIFICATION"; + case DP_CMD_SET_STATE_CALLBACK : + return "SET_STATE_CALLBACK"; + case DP_CMD_SET_PROGRESS_CALLBACK : + return "SET_PROGRESS_CALLBACK"; + case DP_CMD_SET_AUTO_DOWNLOAD : + return "SET_AUTO_DOWNLOAD"; + case DP_CMD_SET_NETWORK_TYPE : + return "SET_NETWORK_TYPE"; + case DP_CMD_SET_HTTP_HEADER : + return "SET_HTTP_HEADER"; + case DP_CMD_SET_EXTRA_PARAM : + return "SET_EXTRA_PARAM"; + case DP_CMD_DEL_HTTP_HEADER : + return "DEL_HTTP_HEADER"; + case DP_CMD_GET_HTTP_HEADER : + return "GET_HTTP_HEADER"; + case DP_CMD_GET_URL : + return "GET_URL"; + case DP_CMD_GET_DESTINATION : + return "GET_DESTINATION"; + case DP_CMD_GET_FILENAME : + return "GET_FILENAME"; + case DP_CMD_GET_NOTIFICATION : + return "GET_NOTIFICATION"; + case DP_CMD_GET_STATE_CALLBACK : + return "GET_STATE_CALLBACK"; + case DP_CMD_GET_PROGRESS_CALLBACK : + return "GET_PROGRESS_CALLBACK"; + case DP_CMD_GET_HTTP_HEADERS : + return "GET_HTTP_HEADERS"; + case DP_CMD_GET_EXTRA_PARAM : + return "GET_EXTRA_PARAM"; + case DP_CMD_GET_AUTO_DOWNLOAD : + return "GET_AUTO_DOWNLOAD"; + case DP_CMD_GET_NETWORK_TYPE : + return "GET_NETWORK_TYPE"; + case DP_CMD_GET_SAVED_PATH : + return "GET_SAVED_PATH"; + case DP_CMD_GET_TEMP_SAVED_PATH : + return "GET_TEMP_SAVED_PATH"; + case DP_CMD_GET_MIME_TYPE : + return "GET_MIME_TYPE"; + case DP_CMD_GET_RECEIVED_SIZE : + return "GET_RECEIVED_SIZE"; + case DP_CMD_GET_TOTAL_FILE_SIZE : + return "GET_TOTAL_FILE_SIZE"; + case DP_CMD_GET_CONTENT_NAME : + return "GET_CONTENT_NAME"; + case DP_CMD_GET_HTTP_STATUS : + return "GET_HTTP_STATUS"; + case DP_CMD_GET_ETAG : + return "DP_CMD_GET_ETAG"; + case DP_CMD_GET_STATE : + return "GET_STATE"; + case DP_CMD_GET_ERROR : + return "ERROR"; + case DP_CMD_SET_COMMAND_SOCKET : + return "SET_COMMAND_SOCKET"; + case DP_CMD_SET_EVENT_SOCKET : + return "SET_EVENT_SOCKET"; + default : + break; + } + return "UNKNOWN COMMAND"; +} + +/* compare two string */ +static int __cmp_string(char *s1, char *s2) +{ + size_t s1_len = 0; + size_t s2_len = 0; + + if (!s1 || !s2) { + TRACE_ERROR("[CHECK PARAM]"); + return -1; + } + + s1_len = strlen(s1); + if (s1_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s1_len); + return -1; + } + + s2_len = strlen(s2); + if (s2_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s2_len); + return -1; + } + + if (s1_len != s2_len) { + TRACE_ERROR("[DIFF] len[%d:%d]", s1_len, s2_len); + return -1; + } + + if (strncmp(s1, s2, s1_len) != 0) { + TRACE_ERROR("[DIFF] cmp[%s:%s]", s1, s2); + return -1; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of empty slot +static int __get_empty_request_index(dp_request_slots *slots) +{ + int i = 0; + + if (!slots) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) + if (!slots[i].request) + return i; + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of slot having same ID +/// @param ID want to search +static int __get_same_request_index(dp_request_slots *slots, int id) +{ + int i = 0; + + if (!slots || id < 0) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request) { + if (slots[i].request->id == id) { + return i; + } + } + } + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return string via IPC +static void __send_return_string(int fd, dp_error_type errcode, char* str) +{ + if (fd < 0 || !str) + return ; + dp_ipc_send_errorcode(fd, errcode); + dp_ipc_send_string(fd, str); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return custom value via IPC +static void __send_return_custom_type(int fd, dp_error_type errcode, void *value, size_t type_size) +{ + dp_ipc_send_errorcode(fd, errcode); + dp_ipc_send_custom_type(fd, value, type_size); +} + +// in url-download, make 3 connection before send CREATE command. +// after accepting, fill info to pacakgelist. +// 3 socket per 1 package ( info/request/progress ) +void *dp_thread_requests_manager(void *arg) +{ + fd_set rset, eset, listen_fdset, except_fdset; + struct timeval timeout; // for timeout of select + long flexible_timeout = DP_CARE_CLIENT_MAX_INTERVAL; + int listenfd, clientfd, maxfd; + socklen_t clientlen; + struct sockaddr_un clientaddr; + dp_credential credential; + unsigned i, is_timeout; + int prev_timeout = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + dp_privates *privates = (dp_privates*)arg; + if (!privates || !privates->groups) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + listenfd = privates->listen_fd; + maxfd = listenfd; + + TRACE_INFO("Ready to listen [%d][%s]", listenfd, DP_IPC); + + FD_ZERO(&listen_fdset); + FD_ZERO(&except_fdset); + FD_SET(listenfd, &listen_fdset); + FD_SET(listenfd, &except_fdset); + + while (privates && privates->listen_fd) { + + // select with timeout + // initialize timeout structure for calling timeout exactly + memset(&timeout, 0x00, sizeof(struct timeval)); + timeout.tv_sec = flexible_timeout; + clientfd = -1; + credential.pid = -1; + credential.uid = -1; + credential.gid = -1; + is_timeout = 1; + + rset = listen_fdset; + eset = except_fdset; + + if (select((maxfd + 1), &rset, 0, &eset, &timeout) < 0) { + TRACE_STRERROR("[CRITICAL] select"); + break; + } + + if (!privates) { + TRACE_INFO("Terminate Thread"); + break; + } + + if (FD_ISSET(listenfd, &eset) > 0) { + TRACE_ERROR("[CRITICAL] listenfd Exception of socket"); + break; + } else if (FD_ISSET(listenfd, &rset) > 0) { + // new client(package) called url_download_create API. + // update g_dp_request_max_fd & g_dp_info_max_fd + // add to socket to g_dp_request_rset & g_dp_info_rset + + is_timeout = 0; + + // Anyway accept client. + clientlen = sizeof(clientaddr); + clientfd = accept(listenfd, + (struct sockaddr*)&clientaddr, + &clientlen); + + TRACE_INFO("[New Connection]"); + + if (clientfd < 0) { + TRACE_ERROR("[CRITICAL] accept provider was crashed ?"); + // provider need the time of refresh. + break; + } + + dp_command_type connect_cmd = DP_CMD_NONE; + if (dp_ipc_read_custom_type(clientfd, + &connect_cmd, sizeof(dp_command_type)) < 0) { + TRACE_ERROR("[CRITICAL] CAPI not support CONNECT CMD"); + close(clientfd); + continue; + } + if (connect_cmd <= 0) { + TRACE_ERROR("[CRITICAL] peer terminate ?"); + close(clientfd); + continue; + } + if (connect_cmd != DP_CMD_SET_COMMAND_SOCKET + && connect_cmd != DP_CMD_SET_EVENT_SOCKET) { + TRACE_ERROR("[CRITICAL] Bad access, ignore this client"); + close(clientfd); + continue; + } + + #ifdef SO_PEERCRED + // getting the info of client + socklen_t cr_len = sizeof(credential); + if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, + &credential, &cr_len) == 0) { + TRACE_INFO + ("credential : pid=%d, uid=%d, gid=%d", + credential.pid, credential.uid, credential.gid); + } + #else // In case of not supported SO_PEERCRED + int client_pid = 0; + if (dp_ipc_read_custom_type(clientfd, + &client_pid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + if (client_pid <= 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + credential.pid = client_pid; + credential.uid = 5000; + credential.gid = 5000; + #endif + + struct timeval tv_timeo; // 2.5 sec + tv_timeo.tv_sec = 2; + tv_timeo.tv_usec = 500000; + if (setsockopt(clientfd, SOL_SOCKET, SO_SNDTIMEO, &tv_timeo, + sizeof( tv_timeo ) ) < 0) { + TRACE_STRERROR("[CRITICAL] setsockopt SO_SNDTIMEO"); + close(clientfd); + continue; + } + if (setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo, + sizeof( tv_timeo ) ) < 0) { + TRACE_STRERROR("[CRITICAL] setsockopt SO_SNDTIMEO"); + close(clientfd); + continue; + } + + if (connect_cmd == DP_CMD_SET_COMMAND_SOCKET) { + + // search in groups. + // if same group. update it. + // search same pkg or pid in groups + int group_index = -1; + int pkgname_length = 0; + char *client_pkgname = NULL; + + // getting the package name via pid + int errcode = + app_manager_get_package(credential.pid, &client_pkgname); + if (errcode == APP_MANAGER_ERROR_NONE && client_pkgname + && strlen(client_pkgname) < DP_MAX_STR_LEN) { + TRACE_INFO("package : %s", client_pkgname); + } else + TRACE_ERROR("[CRITICAL] app_manager_get_package"); + + if (client_pkgname + && (pkgname_length = strlen(client_pkgname)) > 1) { + for (i = 0; i < DP_MAX_GROUP; i++) { + if (privates->groups[i].group) { + if (privates->groups[i].group->cmd_socket + <= 0 + || !privates->groups[i].group->pkgname) { + dp_client_group_free + (privates->groups[i].group); + privates->groups[i].group = NULL; + continue; + } + if (strlen + (privates->groups[i].group->pkgname) + == pkgname_length + && strncmp + (privates->groups[i].group->pkgname, + client_pkgname, pkgname_length) + == 0 ) { + // Found Same Group + TRACE_INFO + ("UPDATE Group : I %d [%s] PID [%d] cmd_socket[%d]", + i, client_pkgname, credential.pid, clientfd); + if (privates->groups[i].group->cmd_socket + > 0 + && privates->groups[i].group->cmd_socket + != clientfd) { + FD_CLR + (privates->groups[i].group->cmd_socket, + &listen_fdset); + dp_socket_free + (privates->groups[i].group->cmd_socket); + } + privates->groups[i].group->cmd_socket = clientfd; + FD_SET(privates->groups[i].group->cmd_socket, &listen_fdset); + if (privates->groups[i].group->cmd_socket > maxfd) + maxfd = privates->groups[i].group->cmd_socket; + group_index = i; + break; + } + } + } + } + if (group_index == -1) { // search emtpy slot in groups + // search empty slot in groups + for (i = 0; i < DP_MAX_GROUP; i++) + if (!privates->groups[i].group) + break; + if (i >= DP_MAX_GROUP) { + TRACE_ERROR("[CRITICAL] No space in groups"); + close(clientfd); // how to deal in url-download ? + if (client_pkgname) + free(client_pkgname); + continue; + } + + //// TEST CODE ... to allow sample client ( no package name ). + if (!client_pkgname) { + client_pkgname = dp_strdup("unknown_app"); + TRACE_INFO("package : %s", client_pkgname); + } + + TRACE_INFO + ("New Group : GI %d [%s] PID [%d] cmd_socket[%d]", + i, client_pkgname, credential.pid, clientfd); + + // allocation + privates->groups[i].group = + (dp_client_group *) calloc(1, + sizeof(dp_client_group)); + if (!privates->groups[i].group) { + TRACE_ERROR + ("[CRITICAL] calloc, ignore this client"); + close(clientfd); + if (client_pkgname) + free(client_pkgname); + continue; + } + // fill info + privates->groups[i].group->cmd_socket = clientfd; + privates->groups[i].group->event_socket = -1; + privates->groups[i].group->queued_count = 0; + privates->groups[i].group->pkgname = + dp_strdup(client_pkgname); + privates->groups[i].group->credential.pid = + credential.pid; + privates->groups[i].group->credential.uid = + credential.uid; + privates->groups[i].group->credential.gid = + credential.gid; + FD_SET(privates->groups[i].group->cmd_socket, + &listen_fdset); + if (privates->groups[i].group->cmd_socket > maxfd) + maxfd = privates->groups[i].group->cmd_socket; + TRACE_INFO + ("Group : GI [%d] [%s] S [%d] Max [%d]", + i, client_pkgname, + privates->groups[i].group->cmd_socket, maxfd); + } + if (client_pkgname) + free(client_pkgname); + + } else if (connect_cmd == DP_CMD_SET_EVENT_SOCKET) { + + // search same pid in groups. have same life-cycle with cmd_socket + TRACE_INFO + ("Group event IPC: PID [%d] cmd_socket[%d]", + credential.pid, clientfd); + // search same pkg or pid in groups + for (i = 0; i < DP_MAX_GROUP; i++) { + if (privates->groups[i].group) { + dp_client_group *group = + privates->groups[i].group; + if (group->credential.pid == credential.pid) { + TRACE_INFO + ("Found Group : I %d [%s] PID [%d] \ + socket (%d/%d)", i, + group->pkgname, credential.pid, + group->cmd_socket, group->event_socket); + if (group->event_socket > 0 + && group->event_socket != clientfd) + dp_socket_free(group->event_socket); + group->event_socket = clientfd; + TRACE_INFO + ("Found Group : I %d PID [%d] \ + event_socket[%d]", + i, credential.pid, clientfd); + break; + } + } + } + if (i >= DP_MAX_GROUP) { + TRACE_ERROR + ("[CRITICAL] Not found group for PID [%d]", + credential.pid); + close(clientfd); + continue; + } + } + } // New Connection + + // listen cmd_socket of all group + for (i = 0; i < DP_MAX_GROUP; i++) { + if (!privates->groups[i].group) + continue; + if (privates->groups[i].group->cmd_socket < 0) { + continue; + } + if (FD_ISSET(privates->groups[i].group->cmd_socket, &rset) + > 0) { + dp_client_group *group = privates->groups[i].group; + dp_command client_cmd; + int index = -1; + + is_timeout = 0; + client_cmd.cmd = DP_CMD_NONE; + client_cmd.id = -1; + + if (dp_ipc_read_custom_type(group->cmd_socket, + &client_cmd, sizeof(dp_command)) < 0) { + TRACE_STRERROR("failed to read cmd"); + //Resource temporarily unavailable + continue; + } + + // print detail info + TRACE_INFO + ("[%s][%d] FD[%d] CLIENT[%s] PID[%d] GINDEX[%d]", + __print_command(client_cmd.cmd), client_cmd.id, + group->cmd_socket, group->pkgname, + group->credential.pid, i); + + if (client_cmd.cmd == 0) { // Client meet some Error. + TRACE_INFO + ("[Closed Peer] group[%d][%s] socket[%d]", + i, group->pkgname, group->cmd_socket); + // check all request included to this group + for (index = 0; index < DP_MAX_REQUEST; index++) { + if (!privates->requests[index].request) + continue; + if (!privates->requests[index].request->group) + continue; + if (privates->requests[index].request->id <= 0) + continue; + dp_request *request = + privates->requests[index].request; + + CLIENT_MUTEX_LOCK(&request->mutex); + if (request->group != group || + request->group->cmd_socket != + group->cmd_socket) { + CLIENT_MUTEX_UNLOCK(&request->mutex); + continue; + } + int auto_download = + dp_db_get_int_column(request->id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + if (auto_download <= 0) { + int agentid = request->agent_id; + int requestid = request->id; + int state = request->state; + TRACE_INFO("[CANCEL][%d] [%s] fd[%d]", + requestid, request->group->pkgname, + request->group->cmd_socket); + + if ((state == DP_STATE_READY || + state == DP_STATE_QUEUED || + state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_PAUSE_REQUESTED || + state == DP_STATE_PAUSED)) { + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_CLIENT_DOWN; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", + requestid); + dp_db_remove_all(request->id); + } else { + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, + DP_DB_COL_TYPE_INT, + &request->error) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", + requestid); + } + } + } + CLIENT_MUTEX_UNLOCK(&request->mutex); + dp_request_free(request); + privates->requests[index].request = NULL; + + // call cancel_agent after free. + if (agentid > 0 && + dp_is_alive_download(agentid)) { + TRACE_INFO + ("[CANCEL-AGENT][%d] state [%s]", + requestid, dp_print_state(state)); + if (dp_cancel_agent_download(agentid) < + 0) + TRACE_INFO("[CANCEL FAILURE]"); + } + + continue; + } + + + TRACE_INFO("[DISCONNECT][%d] [%s] fd[%d]", + request->id, request->group->pkgname, + request->group->cmd_socket); + + request->group = NULL; + request->state_cb = 0; + request->progress_cb = 0; + + CLIENT_MUTEX_UNLOCK(&request->mutex); + // yield to agent thread before free + CLIENT_MUTEX_LOCK(&request->mutex); + // free finished slot without client process + if (request->state == DP_STATE_COMPLETED + || request->state == DP_STATE_FAILED + || request->state == DP_STATE_CANCELED) { + TRACE_INFO + ("[FREE][%d] state[%s]", request->id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&request->mutex); + dp_request_free(request); + privates->requests[index].request = NULL; + continue; + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + // clear this group + FD_CLR(group->cmd_socket, &listen_fdset); + dp_client_group_free(group); + privates->groups[i].group = NULL; + continue; + } + + // Echo .client can check whether provider is busy + if (client_cmd.cmd == DP_CMD_ECHO) { + // provider can clear read buffer here + TRACE_INFO("[ECHO] fd[%d]", group->cmd_socket); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + continue; + } + + if (client_cmd.cmd == DP_CMD_CREATE) { + // search empty slot in privates->requests + index = __get_empty_request_index(privates->requests); + if (index < 0) { + TRACE_ERROR("[CHECK] [DP_ERROR_QUEUE_FULL]"); + // Busy, No Space in slot + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_QUEUE_FULL); + } else { + dp_error_type ret = DP_ERROR_NONE; + ret = dp_request_create(client_cmd.id, group, + &privates->requests[index].request); + if (ret == DP_ERROR_NONE) { + TRACE_INFO + ("[CREATE] GOOD ID[%d] SLOT[%d]", + privates->requests[index].request->id, + index); + __send_return_custom_type(group->cmd_socket, + DP_ERROR_NONE, + &privates->requests[index].request->id, + sizeof(int)); + } else { + TRACE_ERROR + ("[ERROR][%s]", dp_print_errorcode(ret)); + dp_ipc_send_errorcode + (group->cmd_socket, ret); + } + } + continue; + } + + // below commands must need ID + // check exception before searching slots. + if (client_cmd.id < 0) { + TRACE_ERROR("[CHECK PROTOCOL] ID should not be -1"); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_PARAMETER); + // disconnect this group, bad client + FD_CLR(group->cmd_socket, &listen_fdset); + dp_client_group_free(group); + privates->groups[i].group = NULL; + continue; + } + + // search id in requests slot + index = __get_same_request_index + (privates->requests, client_cmd.id); + + dp_request *request = NULL; + if (index >= 0) + request = privates->requests[index].request; + + char *auth_pkgname = NULL; + if (request != NULL) { + auth_pkgname = dp_strdup(request->packagename); + } else { + auth_pkgname = dp_db_get_text_column(client_cmd.id, + DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME); + if (auth_pkgname == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_ID_NOT_FOUND); + continue; + } + } + + // auth by pkgname + if (__cmp_string + (group->pkgname, auth_pkgname) < 0) { + TRACE_ERROR + ("[ERROR][%d] Auth [%s]/[%s]", client_cmd.id, + group->pkgname, auth_pkgname); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_INVALID_PARAMETER); + free(auth_pkgname); + continue; + } + if (auth_pkgname != NULL) + free(auth_pkgname); + + if (request != NULL && request->group == NULL) { + // if no group, update group. + request->group = group; + } + + if (client_cmd.cmd == DP_CMD_DESTROY) { + if (request != NULL) { + CLIENT_MUTEX_LOCK(&(request->mutex)); + // call download_cancel API + if (request->agent_id > 0 && + dp_is_alive_download(request->agent_id)) { + TRACE_INFO("[CANCEL-AGENT][%d] agent_id[%d]", + client_cmd.id, request->agent_id); + if (dp_cancel_agent_download + (request->agent_id) < 0) + TRACE_INFO("[CANCEL FAILURE][%d]", + client_cmd.id); + request->state = DP_STATE_CANCELED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + dp_db_remove_all(request->id); + } + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + // Always return GOOD + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + #if 0 + // remove tmp file + char *tmp_path = + dp_request_get_tmpsavedpath + (client_cmd.id, &errorcode); + if ((tmp_path != NULL && strlen(tmp_path) > 0) && + dp_is_file_exist(tmp_path) == 0) { + TRACE_INFO("[REMOVE][%d] TEMP FILE [%s]", + client_cmd.id, tmp_path); + if (unlink(tmp_path) != 0) + TRACE_STRERROR("[ERROR][%d] remove file", + client_cmd.id); + free(tmp_path); + } + // in DESTROY, clear all info + dp_db_remove_all(client_cmd.id); + #else + // in DESTROY, maintain DB logging + if (request != NULL) { + CLIENT_MUTEX_LOCK(&(request->mutex)); + // change state to CANCELED. + if (request->state == DP_STATE_NONE || + request->state == DP_STATE_READY || + request->state == DP_STATE_QUEUED || + request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_PAUSE_REQUESTED || + request->state == DP_STATE_PAUSED) { + request->state = DP_STATE_CANCELED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + } + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + #endif + } else if (client_cmd.cmd == DP_CMD_FREE) { + // [destory]-[return]-[free] + // No return errorcode + if (request != NULL) { + dp_request_free(request); + privates->requests[index].request = NULL; + } + } else if (client_cmd.cmd == DP_CMD_START) { + if (request == NULL) { // Support Re-download + index = + __get_empty_request_index(privates->requests); + if (index < 0) { + TRACE_ERROR + ("[ERROR][%d] DP_ERROR_QUEUE_FULL", + client_cmd.id); + // Busy, No Space in slot + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_QUEUE_FULL); + continue; + } + request = dp_request_load_from_log + (client_cmd.id, &errorcode); + if (request == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", + client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + continue; + } + // restore callback info + request->state_cb = + dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_STATE_EVENT); + request->progress_cb = + dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_PROGRESS_EVENT); + privates->requests[index].request = request; + } + // Check URL + char *url = dp_request_get_url + (client_cmd.id, request, &errorcode); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] DP_ERROR_INVALID_URL", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_INVALID_URL); + continue; + } else { + free(url); + } + if (request->state == DP_STATE_QUEUED || + request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now [%s]", client_cmd.id, + dp_print_state(request->state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_INVALID_STATE); + continue; + } + dp_state_type state = DP_STATE_QUEUED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &state) < 0) { + TRACE_ERROR + ("[ERROR][%d][SQL] try [%s]->[%s]", + client_cmd.id, + dp_print_state(request->state), + dp_print_state(state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + group->queued_count++; + if (dp_db_update_date(request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + request->start_time = (int)time(NULL); + request->pause_time = 0; + request->stop_time = 0; + request->state = state; + request->error = DP_ERROR_NONE; + // need to check how long take to come here. + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + } else if (client_cmd.cmd == DP_CMD_PAUSE) { + if (request == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_ID_NOT_FOUND); + continue; + } + if (dp_db_update_date(request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->state > DP_STATE_DOWNLOADING) { + TRACE_ERROR + ("[ERROR][%d] now [%s]", client_cmd.id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_STATE); + continue; + } + if (request->state <= DP_STATE_QUEUED) { + request->state = DP_STATE_PAUSED; + request->error = DP_ERROR_NONE; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + continue; + } + if (dp_pause_agent_download(request->agent_id) + < 0) { + TRACE_ERROR + ("[ERROR][%d][AGENT][ID:%d] now [%s]", + client_cmd.id, request->agent_id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_STATE); + continue; + } + request->pause_time = (int)time(NULL); + request->state = DP_STATE_PAUSE_REQUESTED; + request->error = DP_ERROR_NONE; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } else if (client_cmd.cmd == DP_CMD_CANCEL) { + if (request == NULL) { + // find id in DB. + dp_state_type state = + dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_LOG, DP_DB_COL_STATE); + if (state > DP_STATE_NONE) { + // if exist & it is paused, change to canceled. + if (state == DP_STATE_PAUSED || + state == DP_STATE_PAUSE_REQUESTED) { + // change state to canceled. + state = DP_STATE_CANCELED; + if (dp_db_set_column + (client_cmd.id, + DP_DB_TABLE_LOG, + DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, + &state) < 0) { + TRACE_ERROR + ("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(state)); + dp_ipc_send_errorcode + (group->cmd_socket, + DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, + DP_ERROR_NONE); + } + } else { + TRACE_ERROR("[ERROR][%d] now [%s]", + client_cmd.id, + dp_print_state(state)); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_STATE); + } + continue; + } + // if not match these conditions, invalied param + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_ID_NOT_FOUND); + continue; + } + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->state >= DP_STATE_COMPLETED) { + // already finished. + TRACE_ERROR("[ERROR][%d] now [%s]", + client_cmd.id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_STATE); + continue; + } + if (request->state <= DP_STATE_QUEUED) { + request->state = DP_STATE_CANCELED; + request->error = DP_ERROR_NONE; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + continue; + } + if (dp_db_update_date(request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + + if (dp_cancel_agent_download(request->agent_id) + < 0) { + TRACE_ERROR("[ERROR][%d][AGENT][ID:%d] now [%s]", + client_cmd.id, + request->agent_id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_INVALID_STATE); + continue; + } + // need to check how long take to come here. + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &request->state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL] try [%s]", + client_cmd.id, + dp_print_state(request->state)); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } else if (client_cmd.cmd == DP_CMD_SET_URL) { + char *url = dp_ipc_read_string(group->cmd_socket); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] DP_ERROR_INVALID_URL", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_INVALID_URL); + continue; + } + dp_error_type ret = + dp_request_set_url(client_cmd.id, request, url); + TRACE_INFO("[SET_URL][%d][%s]", client_cmd.id, url); + dp_ipc_send_errorcode(group->cmd_socket, ret); + free(url); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_DESTINATION) { + char *dest = dp_ipc_read_string(group->cmd_socket); + if (dest == NULL) { + TRACE_ERROR + ("[ERROR][%d] DP_ERROR_INVALID_DESTINATION", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_DESTINATION); + continue; + } + dp_error_type ret = + dp_request_set_destination + (client_cmd.id, request, dest); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO("[SET_DEST][%d][%s]", client_cmd.id, dest); + free(dest); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_FILENAME) { + char *fname = dp_ipc_read_string(group->cmd_socket); + if (fname == NULL) { + TRACE_ERROR + ("[ERROR][%d] DP_ERROR_INVALID_PARAMETER", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_PARAMETER); + continue; + } + dp_error_type ret = + dp_request_set_filename + (client_cmd.id, request, fname); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[SET_FILE][%d][%s]", client_cmd.id, fname); + free(fname); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_NOTIFICATION) { + int value = 0; + if (dp_ipc_read_custom_type(group->cmd_socket, + &value, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_IO_ERROR); + continue; + } + dp_error_type ret = + dp_request_set_notification + (client_cmd.id, request, value); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[SET_NOTI][%d] [%d]", client_cmd.id, value); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_STATE_CALLBACK) { + int value = 0; + if (dp_ipc_read_custom_type(group->cmd_socket, + &value, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_IO_ERROR); + continue; + } + dp_error_type ret = + dp_request_set_state_event + (client_cmd.id, request, value); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[STATE-EVENT][%d][%d]", client_cmd.id, value); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_PROGRESS_CALLBACK) { + int value = 0; + if (dp_ipc_read_custom_type(group->cmd_socket, + &value, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_IO_ERROR); + continue; + } + dp_error_type ret = + dp_request_set_progress_event + (client_cmd.id, request, value); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[PROG-EVENT][%d][%d]", client_cmd.id, value); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_AUTO_DOWNLOAD) { + int value = 0; + if (dp_ipc_read_custom_type(group->cmd_socket, + &value, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_IO_ERROR); + continue; + } + dp_error_type ret = + dp_request_set_auto_download + (client_cmd.id, request, value); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[SET_AUTO][%d][%d]", client_cmd.id, value); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_NETWORK_TYPE) { + int value = 0; + if (dp_ipc_read_custom_type(group->cmd_socket, + &value, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_IO_ERROR); + continue; + } + dp_error_type ret = + dp_request_set_network_type + (client_cmd.id, request, value); + dp_ipc_send_errorcode(group->cmd_socket, ret); + TRACE_INFO + ("[SET_NETTYPE][%d][%d]", client_cmd.id, value); + if (ret != DP_ERROR_NONE) + TRACE_ERROR("[ERROR][%d][%s]", + client_cmd.id, dp_print_errorcode(ret)); + } else if (client_cmd.cmd == DP_CMD_SET_EXTRA_PARAM) { + char *key = dp_ipc_read_string(group->cmd_socket); + if (key == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + char *value = dp_ipc_read_string(group->cmd_socket); + if (value == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + free(key); + continue; + } + + TRACE_INFO("[EXTRA-PARAM][%d][%s][%s]", + client_cmd.id, key, value); + + if (dp_db_replace_column + (client_cmd.id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY, DP_DB_COL_TYPE_TEXT, + key) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + free(key); + free(value); + continue; + } + free(key); + if (dp_db_set_column + (client_cmd.id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE, DP_DB_COL_TYPE_TEXT, + value) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + free(value); + } else if (client_cmd.cmd == DP_CMD_SET_HTTP_HEADER) { + char *field = dp_ipc_read_string(group->cmd_socket); + if (field == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + char *value = dp_ipc_read_string(group->cmd_socket); + if (value == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + free(field); + continue; + } + char *check_field = dp_db_cond_get_text_column + (client_cmd.id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_HEADER_FIELD, + DP_DB_COL_TYPE_TEXT, field); + if (check_field == NULL) { + // INSERT New Field + if (dp_db_insert_column + (client_cmd.id, + DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, + DP_DB_COL_TYPE_TEXT, field) < 0) { + TRACE_ERROR + ("[ERROR][%d][SQL]", client_cmd.id); + free(field); + free(value); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + } else { + free(check_field); + } + // UPDATE Value for Field + if (dp_db_cond_set_column(client_cmd.id, + DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_DATA, + DP_DB_COL_TYPE_TEXT, value, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + field) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + free(field); + free(value); + } else if (client_cmd.cmd == DP_CMD_DEL_HTTP_HEADER) { + char *field = dp_ipc_read_string(group->cmd_socket); + if (field == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + char *check_field = dp_db_cond_get_text_column + (client_cmd.id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_HEADER_FIELD, + DP_DB_COL_TYPE_TEXT, field); + if (check_field == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NO_DATA); + free(field); + continue; + } + free(check_field); + if (dp_db_cond_remove(client_cmd.id, + DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + field) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + } else { + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + } + free(field); + } else if (client_cmd.cmd == DP_CMD_GET_HTTP_HEADER) { + char *field = dp_ipc_read_string(group->cmd_socket); + if (field == NULL) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_IO_ERROR); + continue; + } + char *value = dp_db_cond_get_text_column + (client_cmd.id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_DATA, DP_DB_COL_HEADER_FIELD, + DP_DB_COL_TYPE_TEXT, field); + free(field); + if (value == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NO_DATA); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, value); + free(value); + } + } else if (client_cmd.cmd == DP_CMD_GET_EXTRA_PARAM) { + char *key = dp_db_get_text_column + (client_cmd.id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY); + if (key == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NO_DATA); + continue; + } + char *value = dp_db_get_text_column + (client_cmd.id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE); + if (value == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NO_DATA); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, key); + dp_ipc_send_string(group->cmd_socket, value); + free(value); + } + free(key); + } else if (client_cmd.cmd == DP_CMD_GET_URL) { + char *url = NULL; + errorcode = DP_ERROR_NONE; + url = dp_request_get_url + (client_cmd.id, request, &errorcode); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, url); + free(url); + } + } else if (client_cmd.cmd == DP_CMD_GET_DESTINATION) { + char *dest = NULL; + errorcode = DP_ERROR_NONE; + dest = dp_request_get_destination + (client_cmd.id, request, &errorcode); + if (dest == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, dest); + free(dest); + } + } else if (client_cmd.cmd == DP_CMD_GET_FILENAME) { + char *filename = NULL; + errorcode = DP_ERROR_NONE; + filename = dp_request_get_filename + (client_cmd.id, request, &errorcode); + if (filename == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, filename); + free(filename); + } + } else if (client_cmd.cmd == DP_CMD_GET_NOTIFICATION) { + int enable = 0; + enable = dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_NOTIFICATION_ENABLE); + if (enable < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_NO_DATA); + } else { + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &enable, sizeof(int)); + } + } else if (client_cmd.cmd == DP_CMD_GET_AUTO_DOWNLOAD) { + int enable = 0; + enable = dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + if (enable < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_NO_DATA); + } else { + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &enable, sizeof(int)); + } + } else if (client_cmd.cmd == DP_CMD_GET_NETWORK_TYPE) { + int type = 0; + type = dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NETWORK_TYPE); + if (type < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_NO_DATA); + } else { + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &type, sizeof(dp_network_type)); + } + } else if (client_cmd.cmd == DP_CMD_GET_SAVED_PATH) { + char *savedpath = NULL; + errorcode = DP_ERROR_NONE; + savedpath = dp_request_get_savedpath + (client_cmd.id, request, &errorcode); + if (savedpath == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, savedpath); + free(savedpath); + } + } else if (client_cmd.cmd == DP_CMD_GET_TEMP_SAVED_PATH) { + char *tmppath = NULL; + errorcode = DP_ERROR_NONE; + tmppath = dp_request_get_tmpsavedpath + (client_cmd.id, request, &errorcode); + if (tmppath == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, tmppath); + free(tmppath); + } + } else if (client_cmd.cmd == DP_CMD_GET_MIME_TYPE) { + char *mimetype = NULL; + errorcode = DP_ERROR_NONE; + mimetype = dp_request_get_mimetype + (client_cmd.id, request, &errorcode); + if (mimetype == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, mimetype); + free(mimetype); + } + } else if (client_cmd.cmd == DP_CMD_GET_RECEIVED_SIZE) { + if (request == NULL) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_ID_NOT_FOUND); + } else { + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &request->received_size, + sizeof(unsigned long long)); + } + } else if (client_cmd.cmd == DP_CMD_GET_TOTAL_FILE_SIZE) { + if (request != NULL) { + __send_return_custom_type(group->cmd_socket, + DP_ERROR_NONE, &request->file_size, + sizeof(unsigned long long)); + continue; + } + long long file_size = + dp_db_get_int64_column(client_cmd.id, + DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_CONTENT_SIZE); + if (file_size < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_NO_DATA); + } else { + // casting + unsigned long long total_file_size = file_size; + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &total_file_size, sizeof(total_file_size)); + } + } else if (client_cmd.cmd == DP_CMD_GET_CONTENT_NAME) { + char *content = NULL; + errorcode = DP_ERROR_NONE; + content = dp_request_get_contentname + (client_cmd.id, request, &errorcode); + if (content == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, content); + free(content); + } + } else if (client_cmd.cmd == DP_CMD_GET_HTTP_STATUS) { + int http_status = 0; + http_status = dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS); + if (http_status < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_NO_DATA]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_NO_DATA); + } else { + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &http_status, sizeof(int)); + } + } else if (client_cmd.cmd == DP_CMD_GET_ETAG) { + char *etag = NULL; + errorcode = DP_ERROR_NONE; + etag = dp_request_get_etag + (client_cmd.id, request, &errorcode); + if (etag == NULL) { + TRACE_ERROR("[ERROR][%d] [%s]", client_cmd.id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + __send_return_string + (group->cmd_socket, DP_ERROR_NONE, etag); + free(etag); + } + } else if (client_cmd.cmd == DP_CMD_GET_STATE) { + dp_state_type download_state = DP_STATE_NONE; + if (request == NULL) { + download_state = + dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_LOG, + DP_DB_COL_STATE); + if (download_state < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_ID_NOT_FOUND); + continue; + } + } else { + CLIENT_MUTEX_LOCK(&(request->mutex)); + download_state = request->state; + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + __send_return_custom_type + (group->cmd_socket, DP_ERROR_NONE, + &download_state, sizeof(dp_state_type)); + } else if (client_cmd.cmd == DP_CMD_GET_ERROR) { + errorcode = DP_ERROR_NONE; + if (request == NULL) { + errorcode = + dp_db_get_int_column(client_cmd.id, + DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE); + if (errorcode < 0) { + TRACE_ERROR + ("[ERROR][%d] [DP_ERROR_ID_NOT_FOUND]", + client_cmd.id); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_ID_NOT_FOUND); + continue; + } + } else { + CLIENT_MUTEX_LOCK(&(request->mutex)); + errorcode = request->error; + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + dp_ipc_send_errorcode + (group->cmd_socket, DP_ERROR_NONE); + dp_ipc_send_errorcode + (group->cmd_socket, errorcode); + } else { + TRACE_INFO + ("[UNKNOWN] I [%d] PID [%d]", i, + privates->groups[i].group->credential.pid); + dp_ipc_send_errorcode(group->cmd_socket, + DP_ERROR_INVALID_PARAMETER); + // disconnect this group, bad client + FD_CLR(group->cmd_socket, &listen_fdset); + dp_client_group_free(group); + privates->groups[i].group = NULL; + } + } // FD_ISSET + } // DP_MAX_GROUP + + // timeout + if (is_timeout == 1) { + int now_timeout = (int)time(NULL); + TRACE_INFO("[TIMEOUT] prev %ld, now %ld, setted %ld sec", + prev_timeout, now_timeout, flexible_timeout); + if (prev_timeout == 0) { + prev_timeout = now_timeout; + } else { + if ((now_timeout - prev_timeout) < + DP_CARE_CLIENT_MIN_INTERVAL) { + // this is error. + // terminate Process + TRACE_STRERROR + ("[CRITICAL] Sock exception prev[%ld]now[%ld][%ld]", + prev_timeout, now_timeout, flexible_timeout); + break; + } + } + + // get 48hour old request from log DB + // clear old request + dp_db_limit_rows(DP_LOG_DB_LIMIT_ROWS); + int old_request_count = dp_db_get_count_by_limit_time(); + if (old_request_count > 0) { + if (old_request_count > DP_LOG_DB_CLEAR_LIMIT_ONE_TIME) + old_request_count = DP_LOG_DB_CLEAR_LIMIT_ONE_TIME; + + TRACE_INFO + ("[CLEAR] [%d] old reqeusts", old_request_count); + + dp_request_slots *old_requests = + dp_request_slots_new(old_request_count); + if (old_requests) { + int list_count = dp_db_get_list_by_limit_time + (old_requests, old_request_count); + if (list_count > 0) { + for (i = 0; i < list_count; i++) { + // search on slots by ID. + int index = + __get_same_request_index + (privates->requests, + old_requests[i].request->id); + if (index >= 0) { + dp_request *request = + privates->requests[index].request; + // if downloading..remain it. + CLIENT_MUTEX_LOCK(&(request->mutex)); + if (request->state == + DP_STATE_CONNECTING || + request->state == + DP_STATE_DOWNLOADING) { + CLIENT_MUTEX_UNLOCK + (&(request->mutex)); + continue; + } + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + // unload from slots ( memory ) + dp_request_free(request); + privates->requests[index].request = NULL; + } + // remove tmp file + char *tmp_path = + dp_request_get_tmpsavedpath + (old_requests[i].request->id, + old_requests[i].request, + &errorcode); + if ((tmp_path && strlen(tmp_path) > 0) && + dp_is_file_exist(tmp_path) == 0) { + TRACE_INFO + ("[REMOVE][%d] TEMP FILE [%s]", + old_requests[i].request->id, + tmp_path); + if (unlink(tmp_path) != 0) + TRACE_STRERROR + ("[ERROR][%d] remove file", + old_requests[i].request->id); + free(tmp_path); + } + // remove from DB + dp_db_remove_all(old_requests[i].request->id); + } + } + dp_request_slots_free(old_requests, old_request_count); + } + } + + // clean slots + int ready_requests = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (!privates->requests[i].request) + continue; + dp_request *request = privates->requests[i].request; + + CLIENT_MUTEX_LOCK(&(request->mutex)); + + // If downloading is too slow ? how to deal this request? + // can limit too slot download using starttime.(48 hours) + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING) { + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + continue; + } + + // paused & agent_id not exist.... unload from memory. + if (request->state == DP_STATE_PAUSED && + dp_is_alive_download(request->agent_id) == 0) { + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + TRACE_INFO + ("[FREE] [%d] unavailable agent ID [%d]", + request->id, request->agent_id); + dp_request_free(request); + privates->requests[i].request = NULL; + continue; + } + + // client should call START command in 60 sec + // unload from memory + if (request->start_time <= 0 && + (now_timeout - request->create_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + int download_id = request->id; + dp_state_type state = DP_STATE_FAILED; + errorcode = DP_ERROR_RESPONSE_TIMEOUT; + if (request->group + && request->group->event_socket >= 0 + && request->state_cb) + dp_ipc_send_event + (request->group->event_socket, + download_id, state, errorcode, 0); + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + TRACE_INFO + ("[FREE] no response ID[%d] last access [%ld]", + request->id, request->start_time); + dp_request_free(request); + privates->requests[i].request = NULL; + // no problem although updating is failed. + if (dp_db_set_column + (download_id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, + &state) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } + if (dp_db_set_column + (download_id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT, + &errorcode) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } + continue; + } + + // client should call DESTROY command in 60 sec after finished + if (request->stop_time > 0 && + (now_timeout - request->stop_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + // check state again. stop_time means it's stopped + if (request->state == DP_STATE_COMPLETED || + request->state == DP_STATE_FAILED || + request->state == DP_STATE_CANCELED) { + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + TRACE_INFO("[FREE] by timeout cleaner ID[%d]", + request->id); + dp_request_free(request); + privates->requests[i].request = NULL; + continue; + } + } + + // check after clear + if (request->state == DP_STATE_QUEUED) + ready_requests++; + + CLIENT_MUTEX_UNLOCK(&(request->mutex)); + } + + if (ready_requests > 0) { + //send signal to queue thread will check queue. + dp_thread_queue_manager_wake_up(); + } else { +#ifdef DP_SUPPORT_DBUS_ACTIVATION + // if no request & timeout is bigger than 60 sec + // terminate by self. + if ((now_timeout - prev_timeout) >= flexible_timeout && + dp_get_request_count(privates->requests) <= 0) { + TRACE_INFO("No Request. Terminate Daemon"); + break; + } +#endif + } + prev_timeout = now_timeout; + } // timeout + } + TRACE_INFO("terminate main thread ..."); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; +} diff --git a/src/include/download-provider-config.h b/src/include/download-provider-config.h new file mode 100755 index 0000000..a0ba61d --- /dev/null +++ b/src/include/download-provider-config.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_CONFIG_H +#define DOWNLOAD_PROVIDER2_CONFIG_H + +#include +#include + +#include + +#define DP_LOCK_PID "/tmp/download-provider.lock" + +#define DP_CARE_CLIENT_MIN_INTERVAL 5 +#define DP_CARE_CLIENT_MAX_INTERVAL 60 + +// check this value should be lower than DP_MAX_REQUEST +#define DP_MAX_DOWNLOAD_AT_ONCE 50 + +#define DP_LOG_DB_LIMIT_ROWS 1000 +#define DP_LOG_DB_CLEAR_LIMIT_ONE_TIME 100 + +// Share the structure for all threads +typedef struct { + int listen_fd; + connection_h connection; + dp_network_type network_status; + dp_group_slots *groups; + dp_request_slots *requests; +} dp_privates; + +#endif diff --git a/src/include/download-provider-da-interface.h b/src/include/download-provider-da-interface.h new file mode 100755 index 0000000..dcafc1a --- /dev/null +++ b/src/include/download-provider-da-interface.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_DA_INTERFACE_H +#define DOWNLOAD_PROVIDER2_DA_INTERFACE_H + +int dp_is_file_exist(const char *file_path); +int dp_init_agent(); +void dp_deinit_agent(); +dp_error_type dp_start_agent_download(dp_request *request); +dp_error_type dp_resume_agent_download(int req_id); +dp_error_type dp_pause_agent_download(int req_id); +dp_error_type dp_cancel_agent_download(int req_id); +int dp_is_alive_download(int req_id); + +#endif diff --git a/src/include/download-provider-db.h b/src/include/download-provider-db.h new file mode 100755 index 0000000..1618e67 --- /dev/null +++ b/src/include/download-provider-db.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_DB_H +#define DOWNLOAD_PROVIDER2_DB_H + +#include "download-provider-config.h" +#include "download-provider-slots.h" + +/* + * Memory ( sync with logging ) : id, state, errorcode, startcount, packagename + * DB TABLES + * logging : id, state, errorcode, startcount, createtime, accesstime, packagename + * requestinfo : id, auto_download, network_type, filename, destination, url + * downloadinfo : id, http_status, content_size, mimetype, contentname, saved_path, tmp_saved_path, etag + * httpheaders : id, header_field, header_data + * notification : id, noti_enable, extra_key, extra_data + */ +/* +CREATE TABLE logging +( + id INTEGER UNIQUE PRIMARY KEY, + state INTEGER DEFAULT 0, + errorcode INTEGER DEFAULT 0, + startcount INTEGER DEFAULT 0, + packagename TEXT DEFAULT NULL, + createtime DATE, + accesstime DATE +); + +CREATE TABLE requestinfo +( + id INTEGER UNIQUE PRIMARY KEY, + auto_download BOOLEAN DEFAULT 0, + state_event BOOLEAN DEFAULT 0, + progress_event BOOLEAN DEFAULT 0, + network_type TINYINT DEFAULT 0, + filename TEXT DEFAULT NULL, + destination TEXT DEFAULT NULL, + url TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE downloadinfo +( + id INTEGER UNIQUE PRIMARY KEY, + http_status INTEGER DEFAULT 0, + content_size UNSIGNED BIG INT DEFAULT 0, + mimetype VARCHAR(64) DEFAULT NULL, + content_name TEXT DEFAULT NULL, + saved_path TEXT DEFAULT NULL, + tmp_saved_path TEXT DEFAULT NULL, + etag TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE httpheaders +( + id INTEGER NOT NULL, + header_field TEXT DEFAULT NULL, + header_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE notification +( + id INTEGER UNIQUE PRIMARY KEY, + noti_enable BOOLEAN DEFAULT 0, + extra_key TEXT DEFAULT NULL, + extra_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX requests_index ON logging (id, state, errorcode, packagename, createtime, accesstime); +*/ + +#define DP_DB_TABLE_LOG "logging" +#define DP_DB_TABLE_REQUEST_INFO "requestinfo" +#define DP_DB_TABLE_DOWNLOAD_INFO "downloadinfo" +#define DP_DB_TABLE_HTTP_HEADERS "httpheaders" +#define DP_DB_TABLE_NOTIFICATION "notification" + +#define DP_DB_COL_ID "id" +#define DP_DB_COL_STATE "state" +#define DP_DB_COL_ERRORCODE "errorcode" +#define DP_DB_COL_NETWORK_TYPE "network_type" +#define DP_DB_COL_HTTP_STATUS "http_status" +#define DP_DB_COL_AUTO_DOWNLOAD "auto_download" +#define DP_DB_COL_STATE_EVENT "state_event" +#define DP_DB_COL_PROGRESS_EVENT "progress_event" +#define DP_DB_COL_CONTENT_SIZE "content_size" +#define DP_DB_COL_CREATE_TIME "createtime" +#define DP_DB_COL_ACCESS_TIME "accesstime" +#define DP_DB_COL_STARTCOUNT "startcount" +#define DP_DB_COL_PACKAGENAME "packagename" +#define DP_DB_COL_DESTINATION "destination" +#define DP_DB_COL_FILENAME "filename" +#define DP_DB_COL_CONTENT_NAME "content_name" +#define DP_DB_COL_MIMETYPE "mimetype" +#define DP_DB_COL_ETAG "etag" +#define DP_DB_COL_SAVED_PATH "saved_path" +#define DP_DB_COL_TMP_SAVED_PATH "tmp_saved_path" +#define DP_DB_COL_URL "url" +#define DP_DB_COL_HEADER_FIELD "header_field" +#define DP_DB_COL_HEADER_DATA "header_data" +#define DP_DB_COL_NOTIFICATION_ENABLE "noti_enable" +#define DP_DB_COL_EXTRA_KEY "extra_key" +#define DP_DB_COL_EXTRA_VALUE "extra_data" + +typedef enum { + DP_DB_COL_TYPE_NONE = 0, + DP_DB_COL_TYPE_INT = 10, + DP_DB_COL_TYPE_INT64 = 20, + DP_DB_COL_TYPE_TEXT = 30 +} db_column_data_type; + +int dp_db_open(); +void dp_db_close(); + +int dp_db_remove_all(int id); +int dp_db_remove(int id, char *table); +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +char *dp_db_get_text_column(int id, char *table, char *column); +int dp_db_get_int_column(int id, char *table, char *column); +long long dp_db_get_int64_column(int id, char *table, char *column); +int dp_db_update_date(int id, char *table, char *column); + +// cond : id & cond +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); + +// Special API for http headers +int dp_db_get_http_headers_list(int id, char **headers); + +// For auto-download in booting time +int dp_db_crashed_list(dp_request_slots *requests, int limit); + +// For loading to memory when no in memory +dp_request *dp_db_load_logging_request(int id); + +// For limitation by 48 hours & 1000 rows +int dp_db_limit_rows(int limit); +int dp_db_get_count_by_limit_time(); +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit); +#endif diff --git a/src/include/download-provider-log.h b/src/include/download-provider-log.h new file mode 100755 index 0000000..d07d63b --- /dev/null +++ b/src/include/download-provider-log.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_LOG_H +#define DOWNLOAD_PROVIDER2_LOG_H + +#include +#include + +#define DEBUG_MSG +//#define DEBUG_PRINTF + +#ifdef DEBUG_MSG +#ifdef DEBUG_PRINTF +#include +#define TRACE_ERROR(format, ARG...) \ +{ \ +fprintf(stderr,"[PROVIDER][%s:%d] "format"\n", __FUNCTION__, __LINE__, ##ARG); \ +} +#define TRACE_STRERROR(format, ARG...) \ +{ \ +fprintf(stderr,"[PROVIDER][%s:%d] "format" [%s]\n", __FUNCTION__, __LINE__, ##ARG, strerror(errno)); \ +} +#define TRACE_INFO(format, ARG...) \ +{ \ +fprintf(stderr,"[PROVIDER][%s:%d] "format"\n", __FUNCTION__, __LINE__, ##ARG); \ +} +#else +#include +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "DOWNLOAD_PROVIDER" +#define TRACE_ERROR(format, ARG...) \ +{ \ +LOGE(format, ##ARG); \ +} +#define TRACE_STRERROR(format, ARG...) \ +{ \ +LOGE(format" [%s]", ##ARG, strerror(errno)); \ +} +#define TRACE_INFO(format, ARG...) \ +{ \ +LOGI(format, ##ARG); \ +} +#endif +#else +#define TRACE_DEBUG_MSG(format, ARG...) ; +#endif +#endif diff --git a/src/include/download-provider-network.h b/src/include/download-provider-network.h new file mode 100755 index 0000000..a4d5b7f --- /dev/null +++ b/src/include/download-provider-network.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_NETWORK_H +#define DOWNLOAD_PROVIDER2_NETWORK_H + +#include "download-provider.h" + +dp_network_type dp_get_network_connection_status(connection_h connection, connection_type_e type); +void dp_network_connection_type_changed_cb(connection_type_e type, void *data); +int dp_network_connection_init(dp_privates *privates); +void dp_network_connection_destroy(connection_h connection); +dp_network_type dp_get_network_connection_instant_status(); + +#endif diff --git a/src/include/download-provider-notification.h b/src/include/download-provider-notification.h new file mode 100755 index 0000000..5a9f5b9 --- /dev/null +++ b/src/include/download-provider-notification.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_NOTIFICATION_H +#define DOWNLOAD_PROVIDER2_NOTIFICATION_H + +#include "download-provider-config.h" + +int dp_set_downloadinginfo_notification(int id, char *packagename); +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state); +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size); +void dp_clear_downloadinginfo_notification(void); + +#endif diff --git a/src/include/download-provider-pthread.h b/src/include/download-provider-pthread.h new file mode 100755 index 0000000..8c02c59 --- /dev/null +++ b/src/include/download-provider-pthread.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_PTHREAD_H +#define DOWNLOAD_PROVIDER2_PTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +// download-provider use default style mutex. + +#define CLIENT_MUTEX_LOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_lock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_UNLOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_unlock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_DESTROY(mutex_add) { \ + int ret = 0; \ + ret = pthread_mutex_destroy(mutex_add); \ + if(EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EINVAL."); \ + } else if(ENOMEM == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with ENOMEM."); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with %d.", ret); \ + } \ +} + +#define CLIENT_MUTEX_INIT(mutex_add, attr) { \ + int ret = 0; \ + unsigned retry = 3; \ + while (retry > 0) { \ + ret = pthread_mutex_init(mutex_add, attr); \ + if (0 == ret) { \ + break; \ + } else if(EINVAL == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with EINVAL."); \ + } else if(ENOMEM == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with ENOMEM."); \ + usleep(1000); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with %d.", ret); \ + } \ + retry--; \ + } \ +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/include/download-provider-queue.h b/src/include/download-provider-queue.h new file mode 100755 index 0000000..4a7d877 --- /dev/null +++ b/src/include/download-provider-queue.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_QUEUE_H +#define DOWNLOAD_PROVIDER2_QUEUE_H + +void dp_thread_queue_manager_wake_up(); +void *dp_thread_queue_manager(void *arg); + +#endif diff --git a/src/include/download-provider-request.h b/src/include/download-provider-request.h new file mode 100755 index 0000000..63aee32 --- /dev/null +++ b/src/include/download-provider-request.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_REQUEST_H +#define DOWNLOAD_PROVIDER2_REQUEST_H + +#include "download-provider.h" + +// for Debugging +char *dp_print_state(dp_state_type state); +char *dp_print_errorcode(dp_error_type errorcode); + +char *dp_strdup(char *src); + +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request** empty_slot); +dp_error_type dp_request_set_url(int id, dp_request *request, char *url); +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest); +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename); +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type); +char *dp_request_get_url(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode); +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode); +#endif diff --git a/src/include/download-provider-slots.h b/src/include/download-provider-slots.h new file mode 100755 index 0000000..cccbddf --- /dev/null +++ b/src/include/download-provider-slots.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_SLOTS_H +#define DOWNLOAD_PROVIDER2_SLOTS_H + +#include "download-provider.h" +#include "download-provider-pthread.h" + +// Backgound Daemon should has the limitation of resource. +#define DP_MAX_GROUP 15 +#define DP_MAX_REQUEST 64 + +typedef struct { + pid_t pid; + uid_t uid; + gid_t gid; +} dp_credential; + +typedef struct { + // send command * get return value. + int cmd_socket; + // send event to client + int event_socket; + unsigned queued_count; // start ++, finish or failed -- ( queue & active ) + // fill by app-manager + char *pkgname; + dp_credential credential; +} dp_client_group; + +typedef struct { + int id; // ID created in create request in requests thread. + int agent_id; + int create_time; + int start_time; // setted by START command + int pause_time; // setted by PAUSE command + int stop_time; // time setted by finished_cb + int noti_priv_id; + unsigned state_cb; // set : 1 unset : 0 + unsigned progress_cb; // set : 1 unset : 0 + unsigned startcount; + unsigned auto_notification; + dp_state_type state; // downloading state + dp_error_type error; + dp_network_type network_type; + size_t progress_lasttime; + unsigned long long received_size; // progress + unsigned long long file_size; + char *packagename; + dp_credential credential; + dp_client_group *group; // indicate dp_client_group included this request + pthread_mutex_t mutex; +} dp_request; + +typedef struct { + dp_client_group *group; +} dp_group_slots; + +typedef struct { + dp_request *request; +} dp_request_slots; + + +// functions +dp_group_slots *dp_client_group_slots_new(int size); +dp_request_slots *dp_request_slots_new(int size); +int dp_client_group_free(dp_client_group *group); +int dp_client_group_slots_free(dp_group_slots *slots, int size); +dp_request *dp_request_new(); +void dp_request_init(dp_request *request); +int dp_request_free(dp_request *request); +int dp_request_slots_free(dp_request_slots *slots, int size); +int dp_get_request_count(dp_request_slots *slots); + +#endif diff --git a/src/include/download-provider-socket.h b/src/include/download-provider-socket.h new file mode 100755 index 0000000..7ec708a --- /dev/null +++ b/src/include/download-provider-socket.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_SOCKET_H +#define DOWNLOAD_PROVIDER2_SOCKET_H + +#include "download-provider.h" +#include "download-provider-slots.h" + +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode); +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size); +#if 0 +int dp_ipc_send_progressinfo(int fd, int id, + unsigned long long received_size, unsigned long long file_size, + unsigned int chunk_size); +int dp_ipc_send_stateinfo(int fd, int id, dp_state_type state, + dp_error_type errorcode); +#endif +char *dp_ipc_read_string(int fd); +int dp_ipc_send_string(int fd, const char *str); +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size); +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size); +int dp_accept_socket_new(); +int dp_socket_free(int sockfd); + +#endif diff --git a/src/include/download-provider.h b/src/include/download-provider.h new file mode 100755 index 0000000..5083ecd --- /dev/null +++ b/src/include/download-provider.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_H +#define DOWNLOAD_PROVIDER2_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DP_IPC "/tmp/download-provider" + +#define DP_MAX_STR_LEN_64 64 +#define DP_MAX_STR_LEN 256 +#define DP_MAX_PATH_LEN DP_MAX_STR_LEN +#define DP_MAX_URL_LEN 2048 + +#ifdef DP_SUPPORT_DBUS_ACTIVATION +#define DP_DBUS_ACTIVATION +#define DP_DBUS_SERVICE_DBUS "org.download-provider" +#endif + +#define DP_ECHO_TEST + + typedef enum { + DP_CMD_NONE = 0, + DP_CMD_CREATE = DP_CMD_NONE + 1, + DP_CMD_START = DP_CMD_NONE + 2, + DP_CMD_PAUSE = DP_CMD_NONE + 3, + DP_CMD_CANCEL = DP_CMD_NONE + 4, + DP_CMD_DESTROY = DP_CMD_NONE + 9, + DP_CMD_FREE = DP_CMD_NONE + 10, + DP_CMD_ECHO = DP_CMD_NONE + 15, + DP_CMD_SET_URL = DP_CMD_NONE + 21, + DP_CMD_SET_DESTINATION = DP_CMD_NONE + 22, + DP_CMD_SET_FILENAME = DP_CMD_NONE + 23, + DP_CMD_SET_NOTIFICATION = DP_CMD_NONE + 24, + DP_CMD_SET_STATE_CALLBACK = DP_CMD_NONE + 25, + DP_CMD_SET_PROGRESS_CALLBACK = DP_CMD_NONE + 26, + DP_CMD_SET_AUTO_DOWNLOAD = DP_CMD_NONE + 28, + DP_CMD_SET_NETWORK_TYPE = DP_CMD_NONE + 29, + DP_CMD_SET_HTTP_HEADER = DP_CMD_NONE + 30, + DP_CMD_SET_EXTRA_PARAM = DP_CMD_NONE + 31, + DP_CMD_DEL_HTTP_HEADER = DP_CMD_NONE + 35, + DP_CMD_GET_URL = DP_CMD_NONE + 41, + DP_CMD_GET_DESTINATION = DP_CMD_NONE + 42, + DP_CMD_GET_FILENAME = DP_CMD_NONE + 43, + DP_CMD_GET_NOTIFICATION = DP_CMD_NONE + 44, + DP_CMD_GET_STATE_CALLBACK = DP_CMD_NONE + 45, + DP_CMD_GET_PROGRESS_CALLBACK = DP_CMD_NONE + 46, + DP_CMD_GET_HTTP_HEADERS = DP_CMD_NONE + 47, + DP_CMD_GET_AUTO_DOWNLOAD = DP_CMD_NONE + 48, + DP_CMD_GET_NETWORK_TYPE = DP_CMD_NONE + 49, + DP_CMD_GET_SAVED_PATH = DP_CMD_NONE + 50, + DP_CMD_GET_TEMP_SAVED_PATH = DP_CMD_NONE + 51, + DP_CMD_GET_MIME_TYPE = DP_CMD_NONE + 52, + DP_CMD_GET_HTTP_HEADER = DP_CMD_NONE + 53, + DP_CMD_GET_EXTRA_PARAM = DP_CMD_NONE + 54, + DP_CMD_GET_RECEIVED_SIZE = DP_CMD_NONE + 71, + DP_CMD_GET_TOTAL_FILE_SIZE = DP_CMD_NONE + 72, + DP_CMD_GET_CONTENT_NAME = DP_CMD_NONE + 73, + DP_CMD_GET_HTTP_STATUS = DP_CMD_NONE + 74, + DP_CMD_GET_ETAG = DP_CMD_NONE + 75, + DP_CMD_GET_STATE = DP_CMD_NONE + 81, + DP_CMD_GET_ERROR = DP_CMD_NONE + 91, + DP_CMD_SET_COMMAND_SOCKET = DP_CMD_NONE + 100, + DP_CMD_SET_EVENT_SOCKET = DP_CMD_NONE + 101 + } dp_command_type; + + typedef enum { + DP_STATE_NONE = 0, + DP_STATE_READY = DP_STATE_NONE + 5, // created id, set some info. + DP_STATE_QUEUED = DP_STATE_NONE + 10, // request to start + DP_STATE_CONNECTING = DP_STATE_NONE + 15, // try to connect to url + DP_STATE_DOWNLOADING = DP_STATE_NONE + 20, // started + DP_STATE_PAUSE_REQUESTED = DP_STATE_NONE + 25, + DP_STATE_PAUSED = DP_STATE_NONE + 30, // paused actually + DP_STATE_COMPLETED = DP_STATE_NONE + 40, + DP_STATE_CANCELED = DP_STATE_NONE + 45, // stopped with error + DP_STATE_FAILED = DP_STATE_NONE + 50 // failed with error + } dp_state_type; + + typedef enum { + DP_ERROR_NONE = 0, + DP_ERROR_INVALID_PARAMETER = DP_ERROR_NONE + 1, + DP_ERROR_OUT_OF_MEMORY = DP_ERROR_NONE + 2, + DP_ERROR_IO_ERROR = DP_ERROR_NONE + 3, + DP_ERROR_NETWORK_UNREACHABLE = DP_ERROR_NONE + 4, + DP_ERROR_CONNECTION_TIMED_OUT = DP_ERROR_NONE + 5, + DP_ERROR_NO_SPACE = DP_ERROR_NONE + 6, + DP_ERROR_FIELD_NOT_FOUND = DP_ERROR_NONE + 7, + DP_ERROR_INVALID_STATE = DP_ERROR_NONE + 8, + DP_ERROR_CONNECTION_FAILED = DP_ERROR_NONE + 9, + DP_ERROR_INVALID_URL = DP_ERROR_NONE + 10, + DP_ERROR_INVALID_DESTINATION = DP_ERROR_NONE + 11, + DP_ERROR_QUEUE_FULL = DP_ERROR_NONE + 12, + DP_ERROR_ALREADY_COMPLETED = DP_ERROR_NONE + 13, + DP_ERROR_FILE_ALREADY_EXISTS = DP_ERROR_NONE + 14, + DP_ERROR_TOO_MANY_DOWNLOADS = DP_ERROR_NONE + 15, + DP_ERROR_NO_DATA = DP_ERROR_NONE + 17, + DP_ERROR_UNHANDLED_HTTP_CODE = DP_ERROR_NONE + 18, + DP_ERROR_CANNOT_RESUME = DP_ERROR_NONE + 19, + DP_ERROR_RESPONSE_TIMEOUT = DP_ERROR_NONE + 50, + DP_ERROR_REQUEST_TIMEOUT = DP_ERROR_NONE + 55, + DP_ERROR_SYSTEM_DOWN = DP_ERROR_NONE + 60, + DP_ERROR_CLIENT_DOWN = DP_ERROR_NONE + 65, + DP_ERROR_ID_NOT_FOUND = DP_ERROR_NONE + 90, + DP_ERROR_UNKNOWN = DP_ERROR_NONE + 100 + } dp_error_type; + + typedef enum { + DP_NETWORK_TYPE_OFF = -1, + DP_NETWORK_TYPE_ALL = 0, + DP_NETWORK_TYPE_WIFI = 1, + DP_NETWORK_TYPE_DATA_NETWORK = 2, + DP_NETWORK_TYPE_ETHERNET = 3, + } dp_network_type; + + typedef struct { + unsigned int length; + char *str; + } dp_string; + + typedef struct { + int id; + dp_state_type state; + dp_error_type err; + unsigned long long received_size; + } dp_event_info; + + typedef struct { + dp_command_type cmd; + int id; + } dp_command; + + // Usage IPC : send(dp_command);send(dp_string); + +#ifdef __cplusplus +} +#endif +#endif -- 2.7.4