xref: /haiku/src/apps/softwareupdater/CheckManager.cpp (revision 840f23676c6602d5906a73cd3cdec27e6b59d5bc)
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 
36 using namespace BPackageKit;
37 using namespace BPackageKit::BManager::BPrivate;
38 
39 #undef B_TRANSLATION_CONTEXT
40 #define B_TRANSLATION_CONTEXT "CheckManager"
41 
42 
CheckManager(BPackageInstallationLocation location,bool verbose)43 CheckManager::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 
65 void
CheckNetworkConnection()66 CheckManager::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 
85 void
JobFailed(BSupportKit::BJob * job)86 CheckManager::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 
96 void
JobAborted(BSupportKit::BJob * job)97 CheckManager::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 
108 void
NoUpdatesNotification()109 CheckManager::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 
119 void
HandleProblems()120 CheckManager::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 
140 void
ConfirmChanges(bool fromMostSpecific)141 CheckManager::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 
172 void
Warn(status_t error,const char * format,...)173 CheckManager::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 
187 void
ProgressPackageDownloadStarted(const char * packageName)188 CheckManager::ProgressPackageDownloadStarted(const char* packageName)
189 {
190 	if (fVerbose)
191 		_SendNotification(fHeaderChecking.String(), fTextContacting.String());
192 
193 	printf("Downloading %s...\n", packageName);
194 }
195 
196 
197 void
ProgressPackageDownloadActive(const char * packageName,float completionPercentage,off_t bytes,off_t totalBytes)198 CheckManager::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 
248 void
ProgressPackageDownloadComplete(const char * packageName)249 CheckManager::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 
263 void
ProgressPackageChecksumStarted(const char * title)264 CheckManager::ProgressPackageChecksumStarted(const char* title)
265 {
266 	if (fVerbose)
267 		_SendNotification(fHeaderChecking.String(), title);
268 
269 	printf("%s...", title);
270 }
271 
272 
273 void
ProgressPackageChecksumComplete(const char * title)274 CheckManager::ProgressPackageChecksumComplete(const char* title)
275 {
276 	puts("done.");
277 }
278 
279 
280 void
_CountUpdates(InstalledRepository & installationRepository,int32 & updateCount)281 CheckManager::_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 
307 void
_SendNotification(const char * title,const char * text)308 CheckManager::_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