1/*
2 * Copyright 2013-2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler <axeld@pinc-software.de>
7 *		Rene Gollent <rene@gollent.com>
8 *		Ingo Weinhold <ingo_weinhold@gmx.de>
9 *		Brian Hill <supernova@tycho.email>
10 */
11
12
13#include "CheckManager.h"
14
15#include <sys/ioctl.h>
16#include <unistd.h>
17
18#include <Application.h>
19#include <Catalog.h>
20#include <Message.h>
21#include <Messenger.h>
22#include <NetworkInterface.h>
23#include <NetworkRoster.h>
24#include <NodeInfo.h>
25#include <Notification.h>
26#include <Roster.h>
27#include <StringFormat.h>
28
29#include <package/manager/Exceptions.h>
30#include <package/solver/SolverPackage.h>
31#include <package/solver/SolverProblem.h>
32#include <package/solver/SolverProblemSolution.h>
33
34#include "constants.h"
35
36using namespace BPackageKit;
37using namespace BPackageKit::BManager::BPrivate;
38
39#undef B_TRANSLATION_CONTEXT
40#define B_TRANSLATION_CONTEXT "CheckManager"
41
42
43CheckManager::CheckManager(BPackageInstallationLocation location,
44	bool verbose)
45	:
46	BPackageManager(location, &fClientInstallationInterface, this),
47	BPackageManager::UserInteractionHandler(),
48	fClientInstallationInterface(),
49	fVerbose(verbose),
50	fNotificationId("")
51{
52	if (verbose) {
53		app_info info;
54		if (be_app->GetAppInfo(&info)!= B_OK)
55			fVerbose = false;
56		else
57			fNotificationId << info.team;
58	}
59	fHeaderChecking = B_TRANSLATE("Checking for updates");
60	fTextContacting = B_TRANSLATE("Contacting software repositories to check "
61			"for package updates.");
62}
63
64
65void
66CheckManager::CheckNetworkConnection()
67{
68	BNetworkRoster& roster = BNetworkRoster::Default();
69	BNetworkInterface interface;
70	uint32 cookie = 0;
71	while (roster.GetNextInterface(&cookie, interface) == B_OK) {
72		uint32 flags = interface.Flags();
73		if ((flags & IFF_LOOPBACK) == 0
74			&& (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) {
75			return;
76		}
77	}
78
79	// No network connection detected, cannot continue
80	fputs(B_TRANSLATE("No active network connection was found.\n"), stderr);
81	throw BAbortedByUserException();
82}
83
84
85void
86CheckManager::JobFailed(BSupportKit::BJob* job)
87{
88	BString error = job->ErrorString();
89	if (error.Length() > 0) {
90		error.ReplaceAll("\n", "\n*** ");
91		fprintf(stderr, "%s", error.String());
92	}
93}
94
95
96void
97CheckManager::JobAborted(BSupportKit::BJob* job)
98{
99	puts("Job aborted");
100	BString error = job->ErrorString();
101	if (error.Length() > 0) {
102		error.ReplaceAll("\n", "\n*** ");
103		fprintf(stderr, "%s", error.String());
104	}
105}
106
107
108void
109CheckManager::NoUpdatesNotification()
110{
111	if (fVerbose) {
112		BString header = B_TRANSLATE("No updates available");
113		BString text = B_TRANSLATE("There were no updates found.");
114		_SendNotification(header.String(), text.String());
115	}
116}
117
118
119void
120CheckManager::HandleProblems()
121{
122	puts("Encountered problems:");
123
124	int32 problemCount = fSolver->CountProblems();
125	for (int32 i = 0; i < problemCount; i++) {
126		BSolverProblem* problem = fSolver->ProblemAt(i);
127		printf("problem %" B_PRId32 ": %s\n", i + 1,
128			problem->ToString().String());
129	}
130
131	BString title(B_TRANSLATE("Available updates found"));
132	BString text(B_TRANSLATE("Click here to run SoftwareUpdater. Some updates "
133		"will require a problem solution to be selected."));
134	_SendNotification(title, text);
135
136	throw BAbortedByUserException();
137}
138
139
140void
141CheckManager::ConfirmChanges(bool fromMostSpecific)
142{
143	int32 count = fInstalledRepositories.CountItems();
144	int32 updateCount = 0;
145
146	if (fromMostSpecific) {
147		for (int32 i = count - 1; i >= 0; i--)
148			_CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount);
149	} else {
150		for (int32 i = 0; i < count; i++)
151			_CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount);
152	}
153
154	printf("Update count=%" B_PRId32 "\n", updateCount);
155	if (updateCount > 0) {
156		BString title;
157		static BStringFormat formatTitle(B_TRANSLATE(
158			"Software {0, plural, one{update} other{updates}} available"));
159		formatTitle.Format(title, updateCount);
160		BString text;
161		static BStringFormat formatText(B_TRANSLATE("Click here to "
162			"install {0, plural, one{# updated package} "
163			"other{# updated packages}}."));
164		formatText.Format(text, updateCount);
165
166		_SendNotification(title.String(), text.String());
167	}
168	throw BAbortedByUserException();
169}
170
171
172void
173CheckManager::Warn(status_t error, const char* format, ...)
174{
175	va_list args;
176	va_start(args, format);
177	vfprintf(stderr, format, args);
178	va_end(args);
179
180	if (error == B_OK)
181		puts("");
182	else
183		printf(": %s\n", strerror(error));
184}
185
186
187void
188CheckManager::ProgressPackageDownloadStarted(const char* packageName)
189{
190	if (fVerbose)
191		_SendNotification(fHeaderChecking.String(), fTextContacting.String());
192
193	printf("Downloading %s...\n", packageName);
194}
195
196
197void
198CheckManager::ProgressPackageDownloadActive(const char* packageName,
199	float completionPercentage, off_t bytes, off_t totalBytes)
200{
201	static const char* progressChars[] = {
202		"\xE2\x96\x8F",
203		"\xE2\x96\x8E",
204		"\xE2\x96\x8D",
205		"\xE2\x96\x8C",
206		"\xE2\x96\x8B",
207		"\xE2\x96\x8A",
208		"\xE2\x96\x89",
209		"\xE2\x96\x88",
210	};
211
212	int width = 70;
213
214	struct winsize winSize;
215	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0
216		&& winSize.ws_col < 77) {
217		// We need 7 characters for the percent display
218		width = winSize.ws_col - 7;
219	}
220
221	int position;
222	int ipart = (int)(completionPercentage * width);
223	int fpart = (int)(((completionPercentage * width) - ipart) * 8);
224
225	fputs("\r", stdout); // return to the beginning of the line
226
227	for (position = 0; position < width; position++) {
228		if (position < ipart) {
229			// This part is fully downloaded, show a full block
230			fputs(progressChars[7], stdout);
231		} else if (position > ipart) {
232			// This part is not downloaded, show a space
233			fputs(" ", stdout);
234		} else {
235			// This part is partially downloaded
236			fputs(progressChars[fpart], stdout);
237		}
238	}
239
240	// Also print the progress percentage
241	printf(" %3d%%", (int)(completionPercentage * 100));
242
243	fflush(stdout);
244
245}
246
247
248void
249CheckManager::ProgressPackageDownloadComplete(const char* packageName)
250{
251	// Overwrite the progress bar with whitespace
252	fputs("\r", stdout);
253	struct winsize w;
254	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
255	for (int i = 0; i < (w.ws_col); i++)
256		fputs(" ", stdout);
257	fputs("\r\x1b[1A", stdout); // Go to previous line.
258
259	printf("Downloading %s...done.\n", packageName);
260}
261
262
263void
264CheckManager::ProgressPackageChecksumStarted(const char* title)
265{
266	if (fVerbose)
267		_SendNotification(fHeaderChecking.String(), title);
268
269	printf("%s...", title);
270}
271
272
273void
274CheckManager::ProgressPackageChecksumComplete(const char* title)
275{
276	puts("done.");
277}
278
279
280void
281CheckManager::_CountUpdates(InstalledRepository& installationRepository,
282	int32& updateCount)
283{
284	if (!installationRepository.HasChanges())
285		return;
286
287	PackageList& packagesToActivate
288		= installationRepository.PackagesToActivate();
289	PackageList& packagesToDeactivate
290		= installationRepository.PackagesToDeactivate();
291
292	for (int32 i = 0;
293		BSolverPackage* installPackage = packagesToActivate.ItemAt(i);
294		i++) {
295		for (int32 j = 0;
296			BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j);
297			j++) {
298			if (installPackage->Info().Name() == uninstallPackage->Info().Name()) {
299				updateCount++;
300				break;
301			}
302		}
303	}
304}
305
306
307void
308CheckManager::_SendNotification(const char* title, const char* text)
309{
310	BNotification notification(B_INFORMATION_NOTIFICATION);
311	notification.SetGroup(B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"));
312	notification.SetTitle(title);
313	notification.SetContent(text);
314	notification.SetOnClickApp(kAppSignature);
315	if(fVerbose)
316		notification.SetMessageID(fNotificationId);
317	notification.Send();
318}
319