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